diff --git a/.changeset/update-effect-4.0.0.md b/.changeset/update-effect-4.0.0.md new file mode 100644 index 0000000000..a86aab1a61 --- /dev/null +++ b/.changeset/update-effect-4.0.0.md @@ -0,0 +1,6 @@ +--- +"effect-app": patch +"@effect-app/cli": patch +--- + +Upgrade Effect packages to npm `4.0.0`. diff --git a/.oxlintrc.json b/.oxlintrc.json index 46a530cff0..78db57e540 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -16,6 +16,9 @@ "effecttsgo/schema-sync-in-effect": "off", "effecttsgo/strict-effect-provide": "off", "effecttsgo/unsafe-effect-type-assertion": "off", + "effecttsgo/unstable-api-usage": "off", + "effecttsgo/floating-effect": "off", + "effecttsgo/multiple-effect-provide": "off", "effecttsgo/unknown-in-effect-catch": "off", "effecttsgo/missing-effect-service-dependency": "error", "effecttsgo/effect-fn-opportunity": "warn", diff --git a/package.json b/package.json index 9f20a3d541..021d4d772e 100644 --- a/package.json +++ b/package.json @@ -48,17 +48,17 @@ "@effect-app/cli": "2.1.0-beta.35", "@effect-app/eslint-codegen-model": "workspace:*", "@effect-app/infra": "workspace:*", - "@effect/language-service": "0.87.2", - "@effect/tsgo": "^0.45.0", - "@effect/platform-node": "4.0.0-rc.115", - "@effect/vitest": "4.0.0-rc.115", + "@effect/language-service": "0.87.3", + "@effect/tsgo": "^0.47.2", + "@effect/platform-node": "4.0.0", + "@effect/vitest": "4.0.0", "@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.0-rc.115", + "effect": "4.0.0", "effect-app": "workspace:*", "eslint": "^10.4.1", "json5": "^2.2.3", diff --git a/packages/cli/package.json b/packages/cli/package.json index a81ad54f94..5978025e57 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -14,8 +14,8 @@ "effect-app-cli": "./bin.js" }, "dependencies": { - "@effect/platform-node": "4.0.0-rc.115", - "effect": "4.0.0-rc.115", + "@effect/platform-node": "4.0.0", + "effect": "4.0.0", "js-yaml": "4.2.0" }, "devDependencies": { diff --git a/packages/cli/src/argv-patch.ts b/packages/cli/src/argv-patch.ts index aacb0f1b07..242c561320 100644 --- a/packages/cli/src/argv-patch.ts +++ b/packages/cli/src/argv-patch.ts @@ -1,6 +1,6 @@ // Join wrap args into a single argv element so that // `effect-app-cli index-multi tsc --build` works without quoting. -// The new effect/unstable/cli lexer classifies --flags as LongOption tokens +// The new effect/cli lexer classifies --flags as LongOption tokens // and discards unrecognized ones at the subcommand level. // By joining all args after the subcommand into one string, the lexer // sees a single Value token instead of separate LongOption tokens. diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index ff0b07779b..53c2c2f7ed 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -2,7 +2,7 @@ /* eslint-disable no-empty-pattern */ // import necessary modules from the libraries import { NodeRuntime, NodeServices } from "@effect/platform-node" -import { Argument, Command, Flag, Prompt } from "effect/unstable/cli" +import { Argument, Command, Flag, Prompt } from "effect/cli" import * as Effect from "effect/Effect" import * as FileSystem from "effect/FileSystem" diff --git a/packages/cli/src/os-command.ts b/packages/cli/src/os-command.ts index 6c8e3b48a5..9982a34cc9 100644 --- a/packages/cli/src/os-command.ts +++ b/packages/cli/src/os-command.ts @@ -3,8 +3,8 @@ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" -import { ChildProcess } from "effect/unstable/process" -import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner" +import { ChildProcess } from "effect/process" +import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner" /** * Service for executing shell commands using the Effect platform's Command API. diff --git a/packages/e2e/package.json b/packages/e2e/package.json index 47d3c13aab..16f6e782c1 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -10,13 +10,13 @@ "effect-app": "workspace:*" }, "devDependencies": { - "@effect/atom-vue": "^4.0.0-rc.115", - "@effect/platform-node": "4.0.0-rc.115", - "@effect/vitest": "4.0.0-rc.115", + "@effect/atom-vue": "^4.0.0", + "@effect/platform-node": "4.0.0", + "@effect/vitest": "4.0.0", "@tanstack/vue-query": "5.96.2", "@types/node": "25.9.1", "@vitejs/plugin-vue": "^6.0.7", - "effect": "^4.0.0-rc.115", + "effect": "^4.0.0", "typescript": "~6.0.3", "vitest": "^4.1.7", "vue": "^3.5.35" diff --git a/packages/e2e/test/repoInvalidation.e2e.test.ts b/packages/e2e/test/repoInvalidation.e2e.test.ts index 6091c9dbb9..f65566c6ce 100644 --- a/packages/e2e/test/repoInvalidation.e2e.test.ts +++ b/packages/e2e/test/repoInvalidation.e2e.test.ts @@ -15,13 +15,13 @@ import { DefaultGenericMiddlewares } from "effect-app/middleware" import { makeRepo, RepositoryRegistryLive } from "effect-app/Model" import { MiddlewareMaker, RpcContextMap, RpcMiddleware } from "effect-app/rpc" import * as S from "effect-app/Schema" +import { FetchHttpClient } from "effect/http" import * as ManagedRuntime from "effect/ManagedRuntime" +import * as NetAddress from "effect/net/NetAddress" import * as Option from "effect/Option" +import * as Reactivity from "effect/reactivity/Reactivity" +import { RpcSerialization } from "effect/rpc" import * as Scope from "effect/Scope" -import { FetchHttpClient } from "effect/unstable/http" -import * as NetAddress from "effect/unstable/net/NetAddress" -import * as Reactivity from "effect/unstable/reactivity/Reactivity" -import { RpcSerialization } from "effect/unstable/rpc" import { createServer } from "http" import { createApp, effectScope, ref } from "vue" import { RequestContextMiddleware } from "../../infra/src/internal/RequestContextMiddleware.ts" diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json index 44182bb4ef..0a0014f416 100644 --- a/packages/effect-app/package.json +++ b/packages/effect-app/package.json @@ -26,7 +26,7 @@ "vitest": "^4.1.7" }, "peerDependencies": { - "effect": "^4.0.0-rc.115" + "effect": "^4.0.0" }, "typesVersions": { "*": { diff --git a/packages/effect-app/src/Schema.ts b/packages/effect-app/src/Schema.ts index a20a48b0da..606f9cdd2d 100644 --- a/packages/effect-app/src/Schema.ts +++ b/packages/effect-app/src/Schema.ts @@ -1,4 +1,5 @@ import * as S from "effect/Schema" +import * as EffectSchemaParser from "effect/SchemaParser" import { type Simplify } from "effect/Struct" import type * as Tracer from "effect/Tracer" import type { RequiredKeys } from "effect/Types" @@ -10,7 +11,7 @@ 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" import * as SchemaAST from "./SchemaAST.ts" -import { copy, extendM, type StructuralCopyOrigin } from "./utils.ts" +import { copy, defineOwn, extendM, type StructuralCopyOrigin } from "./utils.ts" // --------------------------------------------------------------------------- // Default helpers — re-exported from effect/Schema @@ -158,18 +159,17 @@ export function Struct( const result = S.Struct(fields).annotate(concurrencyUnbounded) const allowVoidMake = (schema: any): any => { // Normalize omitted input to an empty object so optional/default-only structs can be constructed with make(). - const origMake: any = schema.make - const origMakeOption: any = schema.makeOption - const origMakeEffect: any = schema.makeEffect - schema.make = function(this: any, input: any, options?: any) { - return origMake.call(this, input === undefined ? {} : input, options) - } - schema.makeOption = function(this: any, input: any, options?: any) { - return origMakeOption.call(this, input === undefined ? {} : input, options) - } - schema.makeEffect = function(this: any, input: any, options?: any) { - return origMakeEffect.call(this, input === undefined ? {} : input, options) - } + // Do not read schema.make first: Effect 4 caches it as a non-writable own property. + const origMake = EffectSchemaParser.make(schema) + const origMakeOption = EffectSchemaParser.makeOption(schema) + const origMakeEffect = EffectSchemaParser.makeEffect(schema) + const wrap = (orig: (input: any, options?: any) => any) => + function(this: any, input: any, options?: any) { + return orig.call(this, input === undefined ? {} : input, options) + } + defineOwn(schema, "make", wrap(origMake)) + defineOwn(schema, "makeOption", wrap(origMakeOption)) + defineOwn(schema, "makeEffect", wrap(origMakeEffect)) return schema } // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment diff --git a/packages/effect-app/src/Schema/SpecialOpenApi.ts b/packages/effect-app/src/Schema/SpecialOpenApi.ts index d65457a7dd..44c88e3722 100644 --- a/packages/effect-app/src/Schema/SpecialOpenApi.ts +++ b/packages/effect-app/src/Schema/SpecialOpenApi.ts @@ -11,7 +11,7 @@ * Usage with the OpenApi `Transform` annotation: * * ```ts - * import { OpenApi } from "effect/unstable" + * import { OpenApi } from "effect/http-api" * import { deduplicateOpenApiSchemas } from "./SpecialOpenApi.ts" * * const api = HttpApi.make("myApi") diff --git a/packages/effect-app/src/Schema/brand.ts b/packages/effect-app/src/Schema/brand.ts index 73497ee741..bd42751123 100644 --- a/packages/effect-app/src/Schema/brand.ts +++ b/packages/effect-app/src/Schema/brand.ts @@ -51,12 +51,12 @@ export interface BrandedSchema> extends {} export const fromBrand = >( - constructor: Constructor, + _constructor: Constructor, options?: BrandAnnotations ) => (self: Self): BrandedSchema => { - const branded = S.fromBrand(options?.identifier ?? "Brand", constructor as any)(self as any) - return options ? (branded as any).pipe(S.annotate(options)) : branded as any + const branded = self.pipe(S.brand("Brand")) + return (options ? branded.pipe(S.annotate(options)) : branded) as BrandedSchema } export type Brands

= P extends B.Brand ? B.Brand.Brands

diff --git a/packages/effect-app/src/Schema/email.ts b/packages/effect-app/src/Schema/email.ts index e85ea25423..46cc36d283 100644 --- a/packages/effect-app/src/Schema/email.ts +++ b/packages/effect-app/src/Schema/email.ts @@ -21,7 +21,8 @@ export const Email = S S.refine(isValidEmail as Refinement, { identifier: "Email", description: "an email according to RFC 5322", - jsonSchema: { format: "email", minLength: 3, maxLength: 998 }, + // JSON Schema minLength counts code points; isMinLength(3) is UTF-16 units (ceil(3/2)=2). + jsonSchema: { format: "email", minLength: 2, maxLength: 998 }, // Native Arbitrary cannot invert `isValidEmail`; without a constraint, rejection sampling // discards (almost) every random string. arbitraryConstraint: { diff --git a/packages/effect-app/src/client/apiClientFactory.ts b/packages/effect-app/src/client/apiClientFactory.ts index b134362853..c55d8d562a 100644 --- a/packages/effect-app/src/client/apiClientFactory.ts +++ b/packages/effect-app/src/client/apiClientFactory.ts @@ -4,10 +4,10 @@ import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" +import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/rpc" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import * as Struct from "effect/Struct" -import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/unstable/rpc" import * as Config from "../Config.ts" import * as Context from "../Context.ts" import * as DataDependencies from "../DataDependencies.ts" diff --git a/packages/effect-app/src/http/Request.ts b/packages/effect-app/src/http/Request.ts index d25b381dc5..c42206ee11 100644 --- a/packages/effect-app/src/http/Request.ts +++ b/packages/effect-app/src/http/Request.ts @@ -1,5 +1,5 @@ +import type { HttpClientResponse } from "effect/http/HttpClientResponse" import * as Option from "effect/Option" -import type { HttpClientResponse } from "effect/unstable/http/HttpClientResponse" import * as Effect from "../Effect.ts" import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.ts" diff --git a/packages/effect-app/src/http/internal/lib.ts b/packages/effect-app/src/http/internal/lib.ts index 738870b9fe..6c035c281d 100644 --- a/packages/effect-app/src/http/internal/lib.ts +++ b/packages/effect-app/src/http/internal/lib.ts @@ -1,13 +1,13 @@ -export * as HttpHeaders from "effect/unstable/http/Headers" -export * as HttpBody from "effect/unstable/http/HttpBody" -export * as HttpClient from "effect/unstable/http/HttpClient" -export * as HttpClientError from "effect/unstable/http/HttpClientError" -export * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -export * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" +export * as HttpHeaders from "effect/http/Headers" +export * as HttpBody from "effect/http/HttpBody" +export * as HttpClient from "effect/http/HttpClient" +export * as HttpClientError from "effect/http/HttpClientError" +export * as HttpClientRequest from "effect/http/HttpClientRequest" +export * as HttpClientResponse from "effect/http/HttpClientResponse" // HttpLayerRouter removed in v4 — use HttpRouter instead -export * as HttpMiddleware from "effect/unstable/http/HttpMiddleware" -export * as HttpRouter from "effect/unstable/http/HttpRouter" -export * as HttpServer from "effect/unstable/http/HttpServer" -export * as HttpServerError from "effect/unstable/http/HttpServerError" -export * as HttpServerRequest from "effect/unstable/http/HttpServerRequest" -export * as HttpServerResponse from "effect/unstable/http/HttpServerResponse" +export * as HttpMiddleware from "effect/http/HttpMiddleware" +export * as HttpRouter from "effect/http/HttpRouter" +export * as HttpServer from "effect/http/HttpServer" +export * as HttpServerError from "effect/http/HttpServerError" +export * as HttpServerRequest from "effect/http/HttpServerRequest" +export * as HttpServerResponse from "effect/http/HttpServerResponse" diff --git a/packages/effect-app/src/rpc/Invalidation.ts b/packages/effect-app/src/rpc/Invalidation.ts index 3ae402dbb5..2e6db6dee1 100644 --- a/packages/effect-app/src/rpc/Invalidation.ts +++ b/packages/effect-app/src/rpc/Invalidation.ts @@ -1,5 +1,5 @@ import * as Ref from "effect/Ref" -import { Rpc } from "effect/unstable/rpc" +import { Rpc } from "effect/rpc" import { type ClientForOptions, makeQueryKey } from "../client/clientFor.ts" import * as Context from "../Context.ts" import * as DataDependencies from "../DataDependencies.ts" diff --git a/packages/effect-app/src/rpc/MiddlewareMaker.ts b/packages/effect-app/src/rpc/MiddlewareMaker.ts index 486af10966..4c5b674699 100644 --- a/packages/effect-app/src/rpc/MiddlewareMaker.ts +++ b/packages/effect-app/src/rpc/MiddlewareMaker.ts @@ -2,11 +2,11 @@ import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import { Rpc, type RpcGroup, type RpcSchema } from "effect/rpc" +import { type HandlersFrom } from "effect/rpc/RpcGroup" import * as S from "effect/Schema" import type * as Scope from "effect/Scope" import { type Simplify } from "effect/Types" -import { Rpc, type RpcGroup, type RpcSchema } from "effect/unstable/rpc" -import { type HandlersFrom } from "effect/unstable/rpc/RpcGroup" import * as Context from "../Context.ts" import { PreludeLogger } from "../logger.ts" import { type TypeTestId } from "../TypeTest.ts" diff --git a/packages/effect-app/src/rpc/RpcContextMap.ts b/packages/effect-app/src/rpc/RpcContextMap.ts index d9d53aac3a..a3de0a89a4 100644 --- a/packages/effect-app/src/rpc/RpcContextMap.ts +++ b/packages/effect-app/src/rpc/RpcContextMap.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { type AnyWithProps } from "effect/rpc/Rpc" import type * as S from "effect/Schema" -import { type AnyWithProps } from "effect/unstable/rpc/Rpc" import * as Context from "../Context.ts" import { type RpcDynamic } from "./RpcMiddleware.ts" diff --git a/packages/effect-app/src/rpc/RpcMiddleware.ts b/packages/effect-app/src/rpc/RpcMiddleware.ts index a760fdca43..d4090157e8 100644 --- a/packages/effect-app/src/rpc/RpcMiddleware.ts +++ b/packages/effect-app/src/rpc/RpcMiddleware.ts @@ -3,11 +3,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { type NonEmptyReadonlyArray } from "effect/Array" import type * as Effect from "effect/Effect" +import { type Rpc, RpcMiddleware } from "effect/rpc" +import { type TypeId } from "effect/rpc/RpcMiddleware" import type * as S from "effect/Schema" import type * as Scope from "effect/Scope" import type * as Stream from "effect/Stream" -import { type Rpc, RpcMiddleware } from "effect/unstable/rpc" -import { type TypeId } from "effect/unstable/rpc/RpcMiddleware" import type * as Context from "../Context.ts" import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.ts" diff --git a/packages/effect-app/src/setupRequest.ts b/packages/effect-app/src/setupRequest.ts index 683d9a7d10..a4c02eda7b 100644 --- a/packages/effect-app/src/setupRequest.ts +++ b/packages/effect-app/src/setupRequest.ts @@ -1,5 +1,5 @@ +import { SqlClient } from "effect/sql" import * as Tracer from "effect/Tracer" -import { SqlClient } from "effect/unstable/sql" import { DataDependencyRecorder } from "./DataDependencies.ts" import * as Effect from "./Effect.ts" import * as Layer from "./Layer.ts" diff --git a/packages/effect-app/src/utils/extend.ts b/packages/effect-app/src/utils/extend.ts index db453b731e..b956b3781b 100644 --- a/packages/effect-app/src/utils/extend.ts +++ b/packages/effect-app/src/utils/extend.ts @@ -1,9 +1,28 @@ +/** + * Own-property define that shadows getter-only prototype fields such as + * Effect 4 `Schema.make`. `Object.assign` uses [[Set]] and cannot. + */ +export const defineOwn = (target: object, key: PropertyKey, value: unknown) => { + Object.defineProperty(target, key, { + value, + writable: true, + enumerable: true, + configurable: true + }) +} + +const assignOwn = (target: T, source: object): T => { + for (const key of Reflect.ownKeys(source)) { + const desc = Object.getOwnPropertyDescriptor(source, key) + if (!desc?.enumerable) continue + defineOwn(target, key, (source as Record)[key]) + } + return target +} + export const extend = (a: T, ext: X) => { - Object.assign(a, ext) + assignOwn(a, ext) return a as T & X } -export const extendM = (a: T, ext: (a: T) => X) => { - Object.assign(a, ext(a)) - return a as typeof a & X -} +export const extendM = (a: T, ext: (a: T) => X) => extend(a, ext(a)) diff --git a/packages/effect-app/test/moreStrings.test.ts b/packages/effect-app/test/moreStrings.test.ts index a2a4aa7878..88d0143384 100644 --- a/packages/effect-app/test/moreStrings.test.ts +++ b/packages/effect-app/test/moreStrings.test.ts @@ -1,6 +1,6 @@ import * as S from "effect-app/Schema" +import * as Arbitrary from "effect/Arbitrary" import * as Effect from "effect/Effect" -import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { customRandom, urlAlphabet } from "nanoid" import { test } from "vitest" diff --git a/packages/effect-app/test/schema.test.ts b/packages/effect-app/test/schema.test.ts index da644edb4a..bb9a15d324 100644 --- a/packages/effect-app/test/schema.test.ts +++ b/packages/effect-app/test/schema.test.ts @@ -491,7 +491,7 @@ describe("JSON Schema", () => { title: "Email", description: "an email according to RFC 5322", format: "email", - minLength: 3, + minLength: 2, maxLength: 998 } } @@ -509,7 +509,7 @@ describe("JSON Schema", () => { title: "Email", description: "an email according to RFC 5322", format: "email", - minLength: 3, + minLength: 2, maxLength: 998 } } diff --git a/packages/infra/docs/workflow-engine.md b/packages/infra/docs/workflow-engine.md index 93c2dc61ca..0bf083cfeb 100644 --- a/packages/infra/docs/workflow-engine.md +++ b/packages/infra/docs/workflow-engine.md @@ -8,7 +8,7 @@ This package provides two custom durable `WorkflowEngine` implementations for (single container, per-execution partition key). Both implement the low-level `WorkflowEngine.Encoded` contract from -`effect/unstable/workflow/WorkflowEngine` and wrap it with `makeUnsafe`. They are +`effect/workflow/WorkflowEngine` and wrap it with `makeUnsafe`. They are drop-in alternatives to Effect's built-in `ClusterWorkflowEngine`, trading cluster-grade routing for a much lighter operational footprint: they need only a database, not the full cluster stack (ShardManager, Runners, MessageStorage). diff --git a/packages/infra/package.json b/packages/infra/package.json index e37e0cf9ba..aa512dd6c1 100644 --- a/packages/infra/package.json +++ b/packages/infra/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@azure/cosmos": "^4.9.3", "@azure/service-bus": "^7.9.5", - "@effect/sql-sqlite-node": "4.0.0-rc.115", + "@effect/sql-sqlite-node": "4.0.0", "@sentry/opentelemetry": "10.55.0", "@types/better-sqlite3": "^7.6.13", "@types/node": "25.9.1", @@ -39,12 +39,12 @@ "peerDependencies": { "@azure/cosmos": "^4.9.3", "@azure/service-bus": "^7.9.5", - "@effect/vitest": "^4.0.0-rc.115", + "@effect/vitest": "^4.0.0", "@sendgrid/helpers": "^8.0.0", "@sendgrid/mail": "^8.1.6", "@sentry/node": "10.55.0", "@sentry/opentelemetry": "10.55.0", - "effect": "^4.0.0-rc.115", + "effect": "^4.0.0", "jwt-decode": "^4.0.0", "redis": "^3.1.2", "redlock": "^4.2.0" diff --git a/packages/infra/src/ClusterCosmos.ts b/packages/infra/src/ClusterCosmos.ts index e6767e7c72..3663d61521 100644 --- a/packages/infra/src/ClusterCosmos.ts +++ b/packages/infra/src/ClusterCosmos.ts @@ -4,17 +4,17 @@ import * as Effect from "effect-app/Effect" import * as Layer from "effect-app/Layer" import * as Option from "effect-app/Option" import * as Cause from "effect/Cause" +import { PersistenceError } from "effect/cluster/ClusterError" +import type * as Envelope from "effect/cluster/Envelope" +import * as MessageStorage from "effect/cluster/MessageStorage" +import { SaveResultEncoded } from "effect/cluster/MessageStorage" +import type * as Reply from "effect/cluster/Reply" +import * as RunnerStorage from "effect/cluster/RunnerStorage" +import * as ShardId from "effect/cluster/ShardId" +import * as ShardingConfig from "effect/cluster/ShardingConfig" +import * as Snowflake from "effect/cluster/Snowflake" import * as Duration from "effect/Duration" import * as Redacted from "effect/Redacted" -import { PersistenceError } from "effect/unstable/cluster/ClusterError" -import type * as Envelope from "effect/unstable/cluster/Envelope" -import * as MessageStorage from "effect/unstable/cluster/MessageStorage" -import { SaveResultEncoded } from "effect/unstable/cluster/MessageStorage" -import type * as Reply from "effect/unstable/cluster/Reply" -import * as RunnerStorage from "effect/unstable/cluster/RunnerStorage" -import * as ShardId from "effect/unstable/cluster/ShardId" -import * as ShardingConfig from "effect/unstable/cluster/ShardingConfig" -import * as Snowflake from "effect/unstable/cluster/Snowflake" import { CosmosClient, CosmosClientLayer, type CosmosContainerThroughput, createContainerIfNotExists } from "./cosmos-client.ts" import { annotateCosmosResponse, annotateDb } from "./otel.ts" @@ -562,10 +562,20 @@ export const makeMessageStorage = Effect.fnUntraced(function*(options?: StorageO }) .pipe(annotate("saveReply"), refailPersistence, withTracerDisabled), - clearReplies: (requestId) => + clearReplies: (requestId, options) => Effect .gen(function*() { const id = String(requestId) + const messages = yield* queryMessages( + "SELECT * FROM c WHERE c.type = 'message' AND c.requestId = @requestId", + [{ name: "@requestId", value: id }] + ) + const request = messages.find((message) => message.kind === "Request") + if (!request) return + if ( + options?.expectedReplyId !== undefined + && request.lastReplyId !== String(options.expectedReplyId) + ) return const replies = yield* queryReplies( "SELECT * FROM c WHERE c.type = 'reply' AND c.requestId = @requestId AND c.kind = 'WithExit'", [ @@ -573,10 +583,6 @@ export const makeMessageStorage = Effect.fnUntraced(function*(options?: StorageO ] ) yield* deleteDocs(replies) - const messages = yield* queryMessages( - "SELECT * FROM c WHERE c.type = 'message' AND c.requestId = @requestId", - [{ name: "@requestId", value: id }] - ) yield* deleteDocs(messages.filter((message) => message.kind === "Interrupt")) yield* patchDocs( messages.filter((message) => message.kind !== "Interrupt"), @@ -668,6 +674,20 @@ export const makeMessageStorage = Effect.fnUntraced(function*(options?: StorageO withTracerDisabled ), + resetRequests: (requestIds) => { + if (requestIds.length === 0) return Effect.void + return queryMessages( + "SELECT * FROM c WHERE c.type = 'message' AND c.processed = false AND (ARRAY_CONTAINS(@requestIds, c.id) OR ARRAY_CONTAINS(@requestIds, c.requestId))", + [{ name: "@requestIds", value: Array.from(requestIds, String) }] + ) + .pipe( + Effect.flatMap((docs) => patchDocs(docs, () => [{ op: "set", path: "/lastRead", value: null }])), + annotate("resetRequests"), + refailPersistence, + withTracerDisabled + ) + }, + resetAddresses: (addresses) => { if (addresses.length === 0) return Effect.void const match = addressClauses(addresses, "r") diff --git a/packages/infra/src/ClusterServiceBus.ts b/packages/infra/src/ClusterServiceBus.ts index 13d60402d6..f9b5d3a3a3 100644 --- a/packages/infra/src/ClusterServiceBus.ts +++ b/packages/infra/src/ClusterServiceBus.ts @@ -2,20 +2,20 @@ import { ServiceBusClient, type ServiceBusClientOptions, type ServiceBusMessage, import * as Context from "effect-app/Context" import * as Effect from "effect-app/Effect" import * as Layer from "effect-app/Layer" +import * as ClusterError from "effect/cluster/ClusterError" +import { Persisted } from "effect/cluster/ClusterSchema" +import type * as Message from "effect/cluster/Message" +import type * as MessageStorage from "effect/cluster/MessageStorage" +import type * as RunnerHealth from "effect/cluster/RunnerHealth" +import * as Runners from "effect/cluster/Runners" +import type * as RunnerStorage from "effect/cluster/RunnerStorage" +import * as Sharding from "effect/cluster/Sharding" +import type * as ShardingConfig from "effect/cluster/ShardingConfig" +import * as Snowflake from "effect/cluster/Snowflake" import * as Data from "effect/Data" +import type * as Rpc from "effect/rpc/Rpc" import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" -import * as ClusterError from "effect/unstable/cluster/ClusterError" -import { Persisted } from "effect/unstable/cluster/ClusterSchema" -import type * as Message from "effect/unstable/cluster/Message" -import type * as MessageStorage from "effect/unstable/cluster/MessageStorage" -import type * as RunnerHealth from "effect/unstable/cluster/RunnerHealth" -import * as Runners from "effect/unstable/cluster/Runners" -import type * as RunnerStorage from "effect/unstable/cluster/RunnerStorage" -import * as Sharding from "effect/unstable/cluster/Sharding" -import type * as ShardingConfig from "effect/unstable/cluster/ShardingConfig" -import * as Snowflake from "effect/unstable/cluster/Snowflake" -import type * as Rpc from "effect/unstable/rpc/Rpc" export interface ClusterServiceBusSender { readonly sendMessages: (messages: ServiceBusMessage) => Promise diff --git a/packages/infra/src/SQL/Model.ts b/packages/infra/src/SQL/Model.ts index 13ec964094..30abf24269 100644 --- a/packages/infra/src/SQL/Model.ts +++ b/packages/infra/src/SQL/Model.ts @@ -17,13 +17,13 @@ import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" import * as RequestResolver from "effect/RequestResolver" import * as Schema from "effect/Schema" +import * as VariantSchema from "effect/schema/VariantSchema" import * as Getter from "effect/SchemaGetter" import * as Transformation from "effect/SchemaTransformation" import type { Scope } from "effect/Scope" -import * as VariantSchema from "effect/unstable/schema/VariantSchema" -import { SqlClient } from "effect/unstable/sql/SqlClient" -import * as SqlResolver from "effect/unstable/sql/SqlResolver" -import * as SqlSchema from "effect/unstable/sql/SqlSchema" +import { SqlClient } from "effect/sql/SqlClient" +import * as SqlResolver from "effect/sql/SqlResolver" +import * as SqlSchema from "effect/sql/SqlSchema" import { type DbSystem, withDbSpan } from "../otel.ts" const { @@ -75,7 +75,7 @@ export { * @category constructors * @example * import * as Schema from "effect/Schema" - * import { Model } from "effect/unstable/schema" + * import { Model } from "effect/schema" * * export const GroupId = Schema.Number.pipe(Schema.brand("GroupId")) * @@ -332,7 +332,7 @@ export interface Date extends Schema.decodeTo, S */ export const Date: Date = Schema.String.pipe( Schema.decodeTo(Schema.DateTimeUtc, { - decode: Getter.dateTimeUtcFromInput().map(DateTime.removeTime), + decode: Getter.map(Getter.dateTimeUtcFromInput(), DateTime.removeTime), encode: Getter.transform(DateTime.formatIsoDate) }) ) diff --git a/packages/infra/src/Store/Memory.ts b/packages/infra/src/Store/Memory.ts index a9938610fb..37c5233930 100644 --- a/packages/infra/src/Store/Memory.ts +++ b/packages/infra/src/Store/Memory.ts @@ -241,7 +241,7 @@ export function memFilter(f: F } return r.map((i) => { - const [keys, entries] = pipe( + const [entries, keys] = pipe( sel, Array.partition((entry) => typeof entry === "string" ? Result.fail(String(entry)) : Result.succeed(entry)) ) diff --git a/packages/infra/src/Store/SQL.ts b/packages/infra/src/Store/SQL.ts index f851e7443e..ab3ae95f3a 100644 --- a/packages/infra/src/Store/SQL.ts +++ b/packages/infra/src/Store/SQL.ts @@ -10,8 +10,8 @@ import { type FilterArgs, type PersistenceModelType, type StorageConfig, type St import * as Context from "effect/Context" import * as Layer from "effect/Layer" import * as LayerMap from "effect/LayerMap" +import { SqlClient } from "effect/sql" import * as Struct from "effect/Struct" -import { SqlClient } from "effect/unstable/sql" import { DatabaseError, OptimisticConcurrencyException } from "../errors.ts" import { InfraLogger } from "../logger.ts" import { annotateDb, type DbSystem } from "../otel.ts" diff --git a/packages/infra/src/Store/SQL/Pg.ts b/packages/infra/src/Store/SQL/Pg.ts index 72f907182d..98d9e045d2 100644 --- a/packages/infra/src/Store/SQL/Pg.ts +++ b/packages/infra/src/Store/SQL/Pg.ts @@ -7,8 +7,8 @@ import type { FieldValues } from "effect-app/Model/filter/types" import type { ComputedProjectionIrExpression } from "effect-app/Model/query" import * as Option from "effect-app/Option" import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, storeId, StoreMaker } from "effect-app/Store" +import { SqlClient } from "effect/sql" import * as Struct from "effect/Struct" -import { SqlClient } from "effect/unstable/sql" import { DatabaseError, OptimisticConcurrencyException } from "../../errors.ts" import { InfraLogger } from "../../logger.ts" import { annotateDb } from "../../otel.ts" diff --git a/packages/infra/src/Store/index.ts b/packages/infra/src/Store/index.ts index 030a415862..1019eefdd0 100644 --- a/packages/infra/src/Store/index.ts +++ b/packages/infra/src/Store/index.ts @@ -4,7 +4,7 @@ import * as Layer from "effect-app/Layer" import { RepositoryRegistryLive } from "effect-app/Model" import type { StorageConfig } from "effect-app/Store" import * as Redacted from "effect/Redacted" -import type { SqlClient } from "effect/unstable/sql" +import type { SqlClient } from "effect/sql" import { CosmosStoreLayer } from "./Cosmos.ts" import { DiskStoreLayer } from "./Disk.ts" import { MemoryStoreLive } from "./Memory.ts" diff --git a/packages/infra/src/WorkflowEngineCosmos.ts b/packages/infra/src/WorkflowEngineCosmos.ts index deb7c9b2b5..b10b7e3658 100644 --- a/packages/infra/src/WorkflowEngineCosmos.ts +++ b/packages/infra/src/WorkflowEngineCosmos.ts @@ -42,8 +42,8 @@ import * as FiberMap from "effect/FiberMap" import * as Redacted from "effect/Redacted" import * as Schedule from "effect/Schedule" import type * as Scope from "effect/Scope" -import * as Workflow from "effect/unstable/workflow/Workflow" -import { type Encoded, makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/unstable/workflow/WorkflowEngine" +import * as Workflow from "effect/workflow/Workflow" +import { type Encoded, makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/workflow/WorkflowEngine" import { randomUUID } from "node:crypto" import { CosmosClient, CosmosClientLayer, type CosmosContainerThroughput, createContainerIfNotExists } from "./cosmos-client.ts" import { OptimisticConcurrencyException } from "./errors.ts" diff --git a/packages/infra/src/WorkflowEngineSqlite.ts b/packages/infra/src/WorkflowEngineSqlite.ts index cafdf1b305..16aaa22436 100644 --- a/packages/infra/src/WorkflowEngineSqlite.ts +++ b/packages/infra/src/WorkflowEngineSqlite.ts @@ -43,9 +43,9 @@ import * as Fiber from "effect/Fiber" import * as FiberMap from "effect/FiberMap" import * as Schedule from "effect/Schedule" import type * as Scope from "effect/Scope" -import { SqlClient } from "effect/unstable/sql" -import * as Workflow from "effect/unstable/workflow/Workflow" -import { type Encoded, makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/unstable/workflow/WorkflowEngine" +import { SqlClient } from "effect/sql" +import * as Workflow from "effect/workflow/Workflow" +import { type Encoded, makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/workflow/WorkflowEngine" import { randomUUID } from "node:crypto" import { OptimisticConcurrencyException } from "./errors.ts" import { annotateDb } from "./otel.ts" diff --git a/packages/infra/src/arbs.ts b/packages/infra/src/arbs.ts index 653e5863a6..b5d3941a43 100644 --- a/packages/infra/src/arbs.ts +++ b/packages/infra/src/arbs.ts @@ -3,8 +3,8 @@ import { faker } from "@faker-js/faker" import { setFaker } from "effect-app/faker" import type * as S from "effect-app/Schema" +import * as Arbitrary from "effect/Arbitrary" import * as Effect from "effect/Effect" -import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import * as FastCheck from "fast-check" import { Random } from "fast-check" import { congruential32 } from "pure-rand/generator/congruential32" diff --git a/packages/infra/src/routing.ts b/packages/infra/src/routing.ts index edce4156cf..555683449f 100644 --- a/packages/infra/src/routing.ts +++ b/packages/infra/src/routing.ts @@ -17,9 +17,9 @@ import { type TypeTestId } from "effect-app/TypeTest" import { typedKeysOf, typedValuesOf } from "effect-app/utils" import * as Predicate from "effect/Predicate" import * as Ref from "effect/Ref" +import { Rpc, RpcGroup, type RpcSerialization, RpcServer } from "effect/rpc" import type * as Scope from "effect/Scope" import * as Stream from "effect/Stream" -import { Rpc, RpcGroup, type RpcSerialization, RpcServer } from "effect/unstable/rpc" import { type LayerUtils } from "./layerUtils.ts" import { RequestType as RequestTypeAnnotation } from "./routing/middleware.ts" diff --git a/packages/infra/src/routing/middleware/middleware.ts b/packages/infra/src/routing/middleware/middleware.ts index 1b84d226d7..c61b6a584c 100644 --- a/packages/infra/src/routing/middleware/middleware.ts +++ b/packages/infra/src/routing/middleware/middleware.ts @@ -8,8 +8,8 @@ import * as Config from "effect/Config" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import { type Rpc } from "effect/rpc" import * as Schema from "effect/Schema" -import { type Rpc } from "effect/unstable/rpc" import { logError, reportError } from "../../errorReporter.ts" import { InfraLogger } from "../../logger.ts" import { WithNsTransaction } from "../../Store/SQL.ts" diff --git a/packages/infra/test/cluster-cosmos.test.ts b/packages/infra/test/cluster-cosmos.test.ts index 9a4824819e..e2e678b73f 100644 --- a/packages/infra/test/cluster-cosmos.test.ts +++ b/packages/infra/test/cluster-cosmos.test.ts @@ -1,10 +1,10 @@ import { assert, describe, expect, it } from "@effect/vitest" import { Context, Duration, Effect, Exit, Fiber, Latch, Layer, Option, Redacted, Schema } from "effect" +import { ClusterSchema, ClusterWorkflowEngine, Entity, EntityAddress, EntityId, EntityType, Envelope, Message, MessageStorage, Reply, Runner, RunnerAddress, RunnerHealth, Runners, RunnerStorage, ShardId, Sharding, ShardingConfig, Snowflake } from "effect/cluster" +import { Headers } from "effect/http" +import { Rpc, RpcSchema } from "effect/rpc" import { TestClock } from "effect/testing" -import { ClusterSchema, ClusterWorkflowEngine, Entity, EntityAddress, EntityId, EntityType, Envelope, Message, MessageStorage, Reply, Runner, RunnerAddress, RunnerHealth, Runners, RunnerStorage, ShardId, Sharding, ShardingConfig, Snowflake } from "effect/unstable/cluster" -import { Headers } from "effect/unstable/http" -import { Rpc, RpcSchema } from "effect/unstable/rpc" -import { DurableDeferred, Workflow } from "effect/unstable/workflow" +import { DurableDeferred, Workflow } from "effect/workflow" import { layerCosmos } from "../src/ClusterCosmos.js" const cosmosUrl = process.env["COSMOS_TEST_URL"] @@ -86,6 +86,28 @@ describe.skipIf(!cosmosUrl)("ClusterCosmos MessageStorage", () => { }) .pipe(Effect.provide(layerFor()))) + it.effect("releases selected request claims without changing replies", () => + Effect + .gen(function*() { + const storage = yield* MessageStorage.MessageStorage + const shardId = testShardId("message-reset-requests") + const request1 = yield* makeRequest({ payload: { id: 1 }, shardId }) + const request2 = yield* makeRequest({ payload: { id: 2 }, shardId }) + assert.strictEqual((yield* storage.saveRequest(request1))._tag, "Success") + assert.strictEqual((yield* storage.saveRequest(request2))._tag, "Success") + + let messages = yield* storage.unprocessedMessages([request1.envelope.address.shardId]) + assert.deepStrictEqual(messages.map((message) => requestPayloadId(message)).sort(), [1, 2]) + + messages = yield* storage.unprocessedMessages([request1.envelope.address.shardId]) + assert.strictEqual(messages.length, 0) + + yield* storage.resetRequests([request1.envelope.requestId]) + messages = yield* storage.unprocessedMessages([request1.envelope.address.shardId]) + assert.deepStrictEqual(messages.map((message) => requestPayloadId(message)), [1]) + }) + .pipe(Effect.provide(layerFor()))) + it.effect("returns each unprocessed message to only one concurrent node poll", () => Effect .gen(function*() { diff --git a/packages/infra/test/cluster-servicebus.test.ts b/packages/infra/test/cluster-servicebus.test.ts index 0023735de1..b8de52113a 100644 --- a/packages/infra/test/cluster-servicebus.test.ts +++ b/packages/infra/test/cluster-servicebus.test.ts @@ -1,9 +1,9 @@ import type { ServiceBusMessage, ServiceBusReceiverOptions } from "@azure/service-bus" import { assert, describe, it } from "@effect/vitest" import { Context, Effect, Layer, Option, Schema, Stream } from "effect" -import { ClusterSchema, EntityAddress, EntityId, EntityType, Envelope, Message, MessageStorage, Runners, ShardId, Sharding, ShardingConfig, Snowflake } from "effect/unstable/cluster" -import { Headers } from "effect/unstable/http" -import { Rpc } from "effect/unstable/rpc" +import { ClusterSchema, EntityAddress, EntityId, EntityType, Envelope, Message, MessageStorage, Runners, ShardId, Sharding, ShardingConfig, Snowflake } from "effect/cluster" +import { Headers } from "effect/http" +import { Rpc } from "effect/rpc" import { type ClusterServiceBusClient, layerClientFrom, layerRunners, makeStoragePoller } from "../src/ClusterServiceBus.js" describe("ClusterServiceBus", () => { diff --git a/packages/infra/test/cluster-sqlite.test.ts b/packages/infra/test/cluster-sqlite.test.ts index c44bcc4e8f..ac2460f644 100644 --- a/packages/infra/test/cluster-sqlite.test.ts +++ b/packages/infra/test/cluster-sqlite.test.ts @@ -2,8 +2,8 @@ import { NodeCrypto } from "@effect/platform-node" import { SqliteClient } from "@effect/sql-sqlite-node" import { assert, describe, it } from "@effect/vitest" import { Context, Duration, Effect, Exit, Fiber, Layer, Option, Schema } from "effect" -import { ClusterSchema, ClusterWorkflowEngine, RunnerAddress, RunnerHealth, Runners, Sharding, ShardingConfig, SqlMessageStorage, SqlRunnerStorage } from "effect/unstable/cluster" -import { DurableDeferred, Workflow } from "effect/unstable/workflow" +import { ClusterSchema, ClusterWorkflowEngine, RunnerAddress, RunnerHealth, Runners, Sharding, ShardingConfig, SqlMessageStorage, SqlRunnerStorage } from "effect/cluster" +import { DurableDeferred, Workflow } from "effect/workflow" const testRunId = `${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}` const runnerPortBase = 10000 + Date.now() % 40000 diff --git a/packages/infra/test/controller.test.ts b/packages/infra/test/controller.test.ts index ee33df9e00..5d95a17028 100644 --- a/packages/infra/test/controller.test.ts +++ b/packages/infra/test/controller.test.ts @@ -11,8 +11,8 @@ import * as RpcX from "effect-app/rpc" import { MiddlewareMaker } from "effect-app/rpc" import * as S from "effect-app/Schema" import { TypeTestId } from "effect-app/TypeTest" +import { type RpcSerialization } from "effect/rpc" import * as Scope from "effect/Scope" -import { type RpcSerialization } from "effect/unstable/rpc" import { DefaultGenericMiddlewaresLive, DevModeMiddlewareLive } from "../src/routing/middleware.js" import { sort } from "../src/routing/tsort.js" import { AllowAnonymous, AllowAnonymousLive, CustomError1, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElse, SomeService, Test, TestLive } from "./fixtures.js" @@ -253,7 +253,7 @@ export class SomethingService extends Context.Service()( static Default = Layer.effect(this, this.make) } -declare const a: { +declare const _a: { (opt: { a: 1 }): void (opt: { a: 2 }): void (opt: { b: 3 }): void diff --git a/packages/infra/test/requires.test.ts b/packages/infra/test/requires.test.ts index f4718e9b23..e08d97122e 100644 --- a/packages/infra/test/requires.test.ts +++ b/packages/infra/test/requires.test.ts @@ -9,8 +9,8 @@ import { MiddlewareMaker } from "effect-app/rpc" import * as S from "effect-app/Schema" import type { unhandled } from "effect-app/Types" import * as Result from "effect/Result" -import { Rpc } from "effect/unstable/rpc" -import { type SuccessValue } from "effect/unstable/rpc/RpcMiddleware" +import { Rpc } from "effect/rpc" +import { type SuccessValue } from "effect/rpc/RpcMiddleware" import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js" export class RequiresSomeMiddleware diff --git a/packages/infra/test/router-generator.test.ts b/packages/infra/test/router-generator.test.ts index ae4bf3ed9e..82e964a935 100644 --- a/packages/infra/test/router-generator.test.ts +++ b/packages/infra/test/router-generator.test.ts @@ -12,7 +12,7 @@ import { MiddlewareMaker } from "effect-app/rpc" import * as S from "effect-app/Schema" import { type TypeTestId } from "effect-app/TypeTest" import { type ConfigError } from "effect/Config" -import { type RpcSerialization } from "effect/unstable/rpc/RpcSerialization" +import { type RpcSerialization } from "effect/rpc/RpcSerialization" import { DefaultGenericMiddlewaresLive, DevModeMiddlewareLive } from "../src/routing/middleware.js" import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElse, SomeService, Test, TestLive } from "./fixtures.js" diff --git a/packages/infra/test/routing-interruptibility.test.ts b/packages/infra/test/routing-interruptibility.test.ts index 6744d2f0d9..b807407ef3 100644 --- a/packages/infra/test/routing-interruptibility.test.ts +++ b/packages/infra/test/routing-interruptibility.test.ts @@ -5,7 +5,7 @@ import * as Effect from "effect/Effect" import * as Fiber from "effect/Fiber" import * as Layer from "effect/Layer" import * as Ref from "effect/Ref" -import { Rpc, RpcGroup, RpcTest } from "effect/unstable/rpc" +import { Rpc, RpcGroup, RpcTest } from "effect/rpc" import { applyRequestTypeInterruptibility } from "../src/routing.js" import { ConfigureInterruptibilityMiddlewareLive, RequestType } from "../src/routing/middleware.js" diff --git a/packages/infra/test/rpc-context-map-streaming.test.ts b/packages/infra/test/rpc-context-map-streaming.test.ts index 462b483f9a..c5ab2a23b6 100644 --- a/packages/infra/test/rpc-context-map-streaming.test.ts +++ b/packages/infra/test/rpc-context-map-streaming.test.ts @@ -44,12 +44,12 @@ import { MiddlewareMaker } from "effect-app/rpc" import * as S from "effect-app/Schema" import { getContextMap } from "effect-app/Store" import * as Effect from "effect/Effect" +import { FetchHttpClient } from "effect/http" import * as Layer from "effect/Layer" +import * as NetAddress from "effect/net/NetAddress" import * as Option from "effect/Option" +import { RpcSerialization } from "effect/rpc" import * as Stream from "effect/Stream" -import { FetchHttpClient } from "effect/unstable/http" -import * as NetAddress from "effect/unstable/net/NetAddress" -import { RpcSerialization } from "effect/unstable/rpc" import { createServer } from "http" import { RequestContextMiddleware } from "../src/internal/RequestContextMiddleware.js" import { makeRouter } from "../src/routing.js" diff --git a/packages/infra/test/rpc-e2e-invalidation.test.ts b/packages/infra/test/rpc-e2e-invalidation.test.ts index 88689c9b81..f6ed3addda 100644 --- a/packages/infra/test/rpc-e2e-invalidation.test.ts +++ b/packages/infra/test/rpc-e2e-invalidation.test.ts @@ -25,13 +25,13 @@ import { TaggedErrorClass } from "effect-app/Schema" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" +import { FetchHttpClient } from "effect/http" import * as Layer from "effect/Layer" +import * as NetAddress from "effect/net/NetAddress" import * as Option from "effect/Option" import * as Ref from "effect/Ref" +import { RpcSerialization } from "effect/rpc" import * as Stream from "effect/Stream" -import { FetchHttpClient } from "effect/unstable/http" -import * as NetAddress from "effect/unstable/net/NetAddress" -import { RpcSerialization } from "effect/unstable/rpc" import { createServer } from "http" import { RequestContextMiddleware } from "../src/internal/RequestContextMiddleware.js" import { makeRouter } from "../src/routing.js" diff --git a/packages/infra/test/rpc-multi-middleware.test.ts b/packages/infra/test/rpc-multi-middleware.test.ts index f3a3b8fc20..eb81d6c89e 100644 --- a/packages/infra/test/rpc-multi-middleware.test.ts +++ b/packages/infra/test/rpc-multi-middleware.test.ts @@ -8,11 +8,11 @@ import * as RpcX from "effect-app/rpc" import * as S from "effect-app/Schema" import * as Console from "effect/Console" import * as Effect from "effect/Effect" +import { FetchHttpClient } from "effect/http" import * as Layer from "effect/Layer" import * as Ref from "effect/Ref" import * as Result from "effect/Result" -import { FetchHttpClient } from "effect/unstable/http" -import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc" +import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/rpc" import { createServer } from "http" import { DefaultGenericMiddlewaresLive } from "../src/routing.js" import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js" diff --git a/packages/infra/test/rpc-stream-fullstack.test.ts b/packages/infra/test/rpc-stream-fullstack.test.ts index f2513da5c1..981dfa2dae 100644 --- a/packages/infra/test/rpc-stream-fullstack.test.ts +++ b/packages/infra/test/rpc-stream-fullstack.test.ts @@ -19,12 +19,12 @@ import * as S from "effect-app/Schema" import { TaggedErrorClass } from "effect-app/Schema" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" +import { FetchHttpClient } from "effect/http" import * as Layer from "effect/Layer" +import * as NetAddress from "effect/net/NetAddress" import * as Option from "effect/Option" +import { RpcSerialization } from "effect/rpc" import * as Stream from "effect/Stream" -import { FetchHttpClient } from "effect/unstable/http" -import * as NetAddress from "effect/unstable/net/NetAddress" -import { RpcSerialization } from "effect/unstable/rpc" import { createServer } from "http" import { makeRouter } from "../src/routing.js" import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js" diff --git a/packages/infra/test/workflow-engine-cosmos.test.ts b/packages/infra/test/workflow-engine-cosmos.test.ts index 4f84516f9f..476849068f 100644 --- a/packages/infra/test/workflow-engine-cosmos.test.ts +++ b/packages/infra/test/workflow-engine-cosmos.test.ts @@ -17,7 +17,7 @@ */ import { assert, describe, it } from "@effect/vitest" import { Context, Duration, Effect, Exit, Layer, Option, Redacted, Schema } from "effect" -import { Activity, DurableDeferred, Workflow, WorkflowEngine } from "effect/unstable/workflow" +import { Activity, DurableDeferred, Workflow, WorkflowEngine } from "effect/workflow" import { CosmosClient, CosmosClientLayer } from "../src/cosmos-client.js" import { layerCosmos } from "../src/WorkflowEngineCosmos.js" diff --git a/packages/infra/test/workflow-engine-sqlite.test.ts b/packages/infra/test/workflow-engine-sqlite.test.ts index b7c6322020..ccdb6fa44f 100644 --- a/packages/infra/test/workflow-engine-sqlite.test.ts +++ b/packages/infra/test/workflow-engine-sqlite.test.ts @@ -9,8 +9,8 @@ import * as S from "effect-app/Schema" import * as Duration from "effect/Duration" import * as Exit from "effect/Exit" import * as Schema from "effect/Schema" -import { SqlClient } from "effect/unstable/sql" -import { Activity, DurableDeferred, Workflow } from "effect/unstable/workflow" +import { SqlClient } from "effect/sql" +import { Activity, DurableDeferred, Workflow } from "effect/workflow" import { layerSqlite } from "../src/WorkflowEngineSqlite.js" // --- Shared mutable counter service ----------------------------------- diff --git a/packages/vue-components/package.json b/packages/vue-components/package.json index a5445ed1a4..8d41006555 100644 --- a/packages/vue-components/package.json +++ b/packages/vue-components/package.json @@ -25,7 +25,7 @@ }, "peerDependencies": { "@mdi/js": "^7.4.47", - "effect": "^4.0.0-rc.115", + "effect": "^4.0.0", "intl-messageformat": "^11.2.7", "mdi-js": "^1.0.1", "primeflex": "^4.0.0", diff --git a/packages/vue-components/src/components/CommandButton.vue b/packages/vue-components/src/components/CommandButton.vue index b11d49d171..b56da0f525 100644 --- a/packages/vue-components/src/components/CommandButton.vue +++ b/packages/vue-components/src/components/CommandButton.vue @@ -5,7 +5,7 @@ > import type { CommandBase, Progress } from "@effect-app/vue/makeClient" import * as Option from "effect-app/Option" -import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +import type * as AsyncResult from "effect/reactivity/AsyncResult" import { computed } from "vue" import type { VBtn } from "vuetify/components" diff --git a/packages/vue-components/src/components/OmegaForm/meta/defaults.ts b/packages/vue-components/src/components/OmegaForm/meta/defaults.ts index 6c6cacd5ba..33e7f67fd8 100644 --- a/packages/vue-components/src/components/OmegaForm/meta/defaults.ts +++ b/packages/vue-components/src/components/OmegaForm/meta/defaults.ts @@ -2,13 +2,27 @@ import * as Effect from "effect-app/Effect" import * as Option from "effect-app/Option" import * as S from "effect-app/Schema" +import * as SchemaGetter from "effect/SchemaGetter" import { isNullableOrUndefined, unwrapDeclaration } from "./createMeta" -const extractDefaultFromLink = (link: any): unknown | undefined => { - if (!link?.transformation?.decode?.run) return undefined +const extractConstructorDefault = (property: S.AST.AST): unknown | undefined => { + const ctor = property.context?.constructorDefault + if (ctor === undefined) return undefined try { - // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- vue-tsc needs the decoded value narrowed at this boundary - const result = Effect.runSync(link.transformation.decode.run(Option.none())) as Option.Option + return Effect.runSync(ctor) + } catch { + return undefined + } +} + +const extractDecodingDefault = (property: S.AST.AST): unknown | undefined => { + const decode = property.encoding?.[0]?.transformation?.decode + if (decode === undefined || property.context?.isOptional !== true) return undefined + try { + const getter = decode as SchemaGetter.Getter + const result: Option.Option = Effect.runSync( + SchemaGetter.run(getter, Option.none(), {}) + ) return Option.isSome(result) ? result.value : undefined } catch { return undefined @@ -16,19 +30,9 @@ const extractDefaultFromLink = (link: any): unknown | undefined => { } const getDefaultFromAst = (property: S.AST.AST) => { - // 1. Check withConstructorDefault (context.constructorDefault is a single Link since beta.107; - // previously context.defaultValue was an Encoding tuple of Links) - const constructorLink = property.context?.constructorDefault - const constructorDefault = extractDefaultFromLink(constructorLink) + const constructorDefault = extractConstructorDefault(property) if (constructorDefault !== undefined) return constructorDefault - - // 2. Check withDecodingDefault (stored in encoding) - const encodingLink = property.encoding?.[0] - if (encodingLink && property.context?.isOptional) { - return extractDefaultFromLink(encodingLink) - } - - return undefined + return extractDecodingDefault(property) } type SchemaWithMembers = { diff --git a/packages/vue-components/stories/Commands/helpers.ts b/packages/vue-components/stories/Commands/helpers.ts index e989258e47..da1474c4c1 100644 --- a/packages/vue-components/stories/Commands/helpers.ts +++ b/packages/vue-components/stories/Commands/helpers.ts @@ -12,10 +12,10 @@ import * as Toast_ from "@effect-app/vue/toast" import { WithToast } from "@effect-app/vue/withToast" import { ApiClientFactory } from "effect-app/client" import * as Effect from "effect/Effect" +import * as FetchHttpClient from "effect/http/FetchHttpClient" import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" import * as Option from "effect/Option" -import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient" import { onUnmounted, ref } from "vue" import { useToast } from "vue-toastification" import { Router } from "./useEffectRouter" diff --git a/packages/vue/package.json b/packages/vue/package.json index 4aa3e06c9a..8a1d92d486 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -18,14 +18,14 @@ "query-string": "^9.4.0" }, "peerDependencies": { - "@effect/atom-vue": "^4.0.0-rc.115", - "@effect/platform-browser": "^4.0.0-rc.115", + "@effect/atom-vue": "^4.0.0", + "@effect/platform-browser": "^4.0.0", "@sentry/browser": "^10.55.0", - "effect": "^4.0.0-rc.115", + "effect": "^4.0.0", "vue": "^3.5.35" }, "devDependencies": { - "@effect/vitest": "4.0.0-rc.115", + "@effect/vitest": "4.0.0", "@formatjs/icu-messageformat-parser": "^3.5.10", "@types/node": "25.9.1", "@vitejs/plugin-vue": "^6.0.7", diff --git a/packages/vue/src/atomQuery.ts b/packages/vue/src/atomQuery.ts index e59460cc57..e31750fab5 100644 --- a/packages/vue/src/atomQuery.ts +++ b/packages/vue/src/atomQuery.ts @@ -24,14 +24,14 @@ import * as Cause from "effect/Cause" import * as Duration from "effect/Duration" import * as Equal from "effect/Equal" import * as Hash from "effect/Hash" +import { isHttpClientError } from "effect/http/HttpClientError" import type * as Layer from "effect/Layer" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import * as Ref from "effect/Ref" import * as Stream from "effect/Stream" import type * as Tracer from "effect/Tracer" -import { isHttpClientError } from "effect/unstable/http/HttpClientError" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" import { clearQueryReadDependencies, getQueryReadDependencies, type QueryInvalidationMode, registerQueryInvalidationMode, setQueryReadDependencies } from "./dependencyMetadata.ts" import { reportRuntimeError } from "./lib.ts" import { beginLiveQueryFetch, endLiveQueryFetch, type LiveQueryOptions, registerLiveQuery } from "./liveQueryInvalidation.ts" diff --git a/packages/vue/src/commander.ts b/packages/vue/src/commander.ts index 80c8140a9a..eec0378241 100644 --- a/packages/vue/src/commander.ts +++ b/packages/vue/src/commander.ts @@ -17,8 +17,8 @@ import { flow } from "effect/Function" import * as MutableHashMap from "effect/MutableHashMap" import * as Predicate from "effect/Predicate" import { type Refinement } from "effect/Predicate" +import * as AsyncResult from "effect/reactivity/AsyncResult" import * as Stream from "effect/Stream" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" import { type FormatXMLElementFn, type PrimitiveType } from "intl-messageformat" import { computed, type ComputedRef, reactive, ref, toRaw } from "vue" import { Confirm } from "./confirm.ts" diff --git a/packages/vue/src/internal/tanstackQuery.ts b/packages/vue/src/internal/tanstackQuery.ts index e829ffb1a2..f6e1a04704 100644 --- a/packages/vue/src/internal/tanstackQuery.ts +++ b/packages/vue/src/internal/tanstackQuery.ts @@ -8,11 +8,11 @@ import * as Effect from "effect-app/Effect" import * as Option from "effect-app/Option" import * as S from "effect-app/Schema" import * as Cause from "effect/Cause" +import { isHttpClientError } from "effect/http/HttpClientError" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" import * as Ref from "effect/Ref" import type * as Tracer from "effect/Tracer" -import { isHttpClientError } from "effect/unstable/http/HttpClientError" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" import { computed, type MaybeRefOrGetter, shallowRef, toValue, watch, type WatchSource } from "vue" import { replaceEqualDeep } from "../atomQuery.ts" import { clearQueryReadDependencies, registerQueryInvalidationMode, setQueryReadDependencies } from "../dependencyMetadata.ts" diff --git a/packages/vue/src/lib.ts b/packages/vue/src/lib.ts index 7cd58d8311..22150f5af4 100644 --- a/packages/vue/src/lib.ts +++ b/packages/vue/src/lib.ts @@ -3,11 +3,11 @@ import type { Req } from "effect-app/client" import type { RequestHandlerWithInput } from "effect-app/client/clientFor" import type * as Effect from "effect-app/Effect" import * as Cause from "effect/Cause" -import { isHttpClientError } from "effect/unstable/http/HttpClientError" +import { isHttpClientError } from "effect/http/HttpClientError" import { isProxy, isReactive, isRef, type MaybeRefOrGetter, toRaw } from "vue" import { reportError } from "./errorReporter.ts" -export * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +export * as AsyncResult from "effect/reactivity/AsyncResult" const reportRuntimeError_ = reportError("Runtime") diff --git a/packages/vue/src/makeClient.ts b/packages/vue/src/makeClient.ts index a9f96df8c3..d54036d49e 100644 --- a/packages/vue/src/makeClient.ts +++ b/packages/vue/src/makeClient.ts @@ -14,10 +14,10 @@ import * as Exit from "effect/Exit" import { type Fiber } from "effect/Fiber" import * as Hash from "effect/Hash" import type * as ManagedRuntime from "effect/ManagedRuntime" +import type * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Reactivity from "effect/reactivity/Reactivity" import type * as Stream from "effect/Stream" import * as Struct from "effect/Struct" -import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Reactivity from "effect/unstable/reactivity/Reactivity" import { type ComputedRef, effectScope, onBeforeUnmount, onScopeDispose, ref, type WatchSource } from "vue" import { type AtomClientRuntime, invalidateAndAwait, invalidateSoft, makeAtomClientRuntime } from "./atomQuery.ts" import { type Commander, CommanderStatic, type Progress } from "./commander.ts" diff --git a/packages/vue/src/mutate.ts b/packages/vue/src/mutate.ts index 4554a36b8f..b966954df4 100644 --- a/packages/vue/src/mutate.ts +++ b/packages/vue/src/mutate.ts @@ -8,10 +8,10 @@ import * as Option from "effect-app/Option" import { isReadonlyArrayNonEmpty } from "effect/Array" import type * as Cause from "effect/Cause" import * as Exit from "effect/Exit" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import type * as Reactivity from "effect/reactivity/Reactivity" import * as Ref from "effect/Ref" import * as Stream from "effect/Stream" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import type * as Reactivity from "effect/unstable/reactivity/Reactivity" import { computed, type ComputedRef, shallowRef } from "vue" import { invalidateAndAwait, invalidateSoft } from "./atomQuery.ts" import { getDerivedInvalidationKeys, partitionInvalidationKeys } from "./dependencyMetadata.ts" diff --git a/packages/vue/src/query.ts b/packages/vue/src/query.ts index 71dc9339aa..edb869ef8b 100644 --- a/packages/vue/src/query.ts +++ b/packages/vue/src/query.ts @@ -13,9 +13,9 @@ import * as Effect from "effect-app/Effect" import * as Option from "effect-app/Option" import type * as Cause from "effect/Cause" import * as Exit from "effect/Exit" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" import * as Stream from "effect/Stream" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" import { computed, type ComputedRef, effectScope, type MaybeRefOrGetter, onBeforeUnmount, onMounted, onScopeDispose, ref, toValue, type WatchSource } from "vue" import { type AtomClientRuntime, type AtomQueryOptions, awaitAtomResult, buildQueryFamily, buildStreamQueryFamily, disabledQueryAtom, isStaleResult, queryKeyForAtom, refreshAtomWithCurrentSpan, staleTimeMsOf, withQueryOptions } from "./atomQuery.ts" import type { QueryInvalidationMode } from "./dependencyMetadata.ts" diff --git a/packages/vue/src/queryLifetime.ts b/packages/vue/src/queryLifetime.ts index d5d7dce8da..0b8416f900 100644 --- a/packages/vue/src/queryLifetime.ts +++ b/packages/vue/src/queryLifetime.ts @@ -1,5 +1,5 @@ -import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" +import type * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" /** * Per-query observer count + invalidation flag. Lives on the family atom so diff --git a/packages/vue/src/suspense.ts b/packages/vue/src/suspense.ts index bde2eeb2e4..23e3602062 100644 --- a/packages/vue/src/suspense.ts +++ b/packages/vue/src/suspense.ts @@ -1,5 +1,5 @@ import { Effect } from "effect-app" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +import * as AsyncResult from "effect/reactivity/AsyncResult" import { computed, type ComputedRef, nextTick } from "vue" /** diff --git a/packages/vue/test/dependencyInvalidation.test.ts b/packages/vue/test/dependencyInvalidation.test.ts index f88acc12e2..0b2b8d3642 100644 --- a/packages/vue/test/dependencyInvalidation.test.ts +++ b/packages/vue/test/dependencyInvalidation.test.ts @@ -9,9 +9,9 @@ import * as Deferred from "effect/Deferred" import * as Fiber from "effect/Fiber" import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" +import * as Reactivity from "effect/reactivity/Reactivity" import * as Stream from "effect/Stream" import { TestClock } from "effect/testing" -import * as Reactivity from "effect/unstable/reactivity/Reactivity" import { createApp, effectScope, ref } from "vue" import { awaitAtomResult, buildQueryFamily, invalidateAndAwait, makeAtomClientRuntime } from "../src/atomQuery.js" import { clearQueryReadDependencies, getDerivedInvalidationKeys, partitionInvalidationKeys, registerQueryInvalidationMode, setQueryReadDependencies } from "../src/dependencyMetadata.js" diff --git a/packages/vue/test/inactiveInvalidation.test.ts b/packages/vue/test/inactiveInvalidation.test.ts index d6aea3fe2d..448bd3b644 100644 --- a/packages/vue/test/inactiveInvalidation.test.ts +++ b/packages/vue/test/inactiveInvalidation.test.ts @@ -6,8 +6,8 @@ import * as Option from "effect-app/Option" import * as Latch from "effect/Latch" import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Reactivity from "effect/unstable/reactivity/Reactivity" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Reactivity from "effect/reactivity/Reactivity" import { describe, expect, it } from "vitest" import { createApp, nextTick } from "vue" import { buildQueryFamily, invalidateAndAwait, makeAtomClientRuntime, withQueryOptions } from "../src/atomQuery.js" diff --git a/packages/vue/test/interrupt-refetch-repro.test.ts b/packages/vue/test/interrupt-refetch-repro.test.ts index 9fb2a744a8..cb07f0854f 100644 --- a/packages/vue/test/interrupt-refetch-repro.test.ts +++ b/packages/vue/test/interrupt-refetch-repro.test.ts @@ -27,8 +27,8 @@ import * as Fiber from "effect/Fiber" import * as Latch from "effect/Latch" import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Reactivity from "effect/unstable/reactivity/Reactivity" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Reactivity from "effect/reactivity/Reactivity" import { createApp, nextTick } from "vue" import { awaitAtomResult, buildQueryFamily, invalidateAndAwait, makeAtomClientRuntime, queryFetchStates, withQueryOptions } from "../src/atomQuery.js" import { invalidateQueries } from "../src/mutate.js" diff --git a/packages/vue/test/makeClient.test.ts b/packages/vue/test/makeClient.test.ts index b4384ddaa3..c909c19066 100644 --- a/packages/vue/test/makeClient.test.ts +++ b/packages/vue/test/makeClient.test.ts @@ -6,8 +6,8 @@ import * as Effect from "effect-app/Effect" import * as S from "effect-app/Schema" import * as Exit from "effect/Exit" import * as Fiber from "effect/Fiber" +import * as Atom from "effect/reactivity/Atom" import { TestClock } from "effect/testing" -import * as Atom from "effect/unstable/reactivity/Atom" import type { CommandFromRequest } from "../src/makeClient.js" import { combineQueryInvalidators, invalidateQueries } from "../src/mutate.js" import { useAtomQuery, useAtomSuspense } from "../src/query.js" @@ -352,7 +352,7 @@ it("additive atom query api type tests", () => { const mappedFamilyAtom = Atom.mapResult(familyAtom, (data) => data.toString()) expectTypeOf>().toEqualTypeOf() expectTypeOf(useAtomQuery(() => mappedFamilyAtom).data.value).toEqualTypeOf() - useAtomSuspense(() => mappedFamilyAtom).then((view) => { + void useAtomSuspense(() => mappedFamilyAtom).then((view) => { expectTypeOf(view[1].value).toEqualTypeOf() return view }) @@ -368,7 +368,7 @@ it("additive atom query api type tests", () => { const selected = client.GetSomething2.queryNew({ id: "a" }, { select: (data) => data.toString() }) expectTypeOf(selected.data.value).toEqualTypeOf() - client.GetSomething2.suspenseNew({ id: "a" }, { select: (data) => data.toString() }).then((view) => { + void client.GetSomething2.suspenseNew({ id: "a" }, { select: (data) => data.toString() }).then((view) => { expectTypeOf(view[1].value).toEqualTypeOf() return view }) @@ -379,7 +379,7 @@ it("additive atom query api type tests", () => { const projectedAtom = projected.atom({ id: "a" }) expectTypeOf>().toEqualTypeOf() expectTypeOf(projected.queryNew({ id: "a" }).data.value).toEqualTypeOf() - projected.suspenseNew({ id: "a" }).then((view) => { + void projected.suspenseNew({ id: "a" }).then((view) => { expectTypeOf(view[1].value).toEqualTypeOf() return view }) @@ -387,7 +387,7 @@ it("additive atom query api type tests", () => { const helperFamilyAtom = client.helpers.getSomething2QueryFamily({ id: "a" }) expectTypeOf>().toEqualTypeOf() expectTypeOf(client.helpers.getSomething2QueryNew({ id: "a" }).data.value).toEqualTypeOf() - client.helpers.getSomething2SuspenseQueryNew({ id: "a" }).then((view) => { + void client.helpers.getSomething2SuspenseQueryNew({ id: "a" }).then((view) => { expectTypeOf(view[1].value).toEqualTypeOf() return view }) @@ -423,9 +423,7 @@ it.skip("works", () => { // @ts-expect-error query request does not match Command.wrap mutation signature const e000 = Command.wrap(client.GetSomething2WithDependencies) const e00 = client.GetSomething2WithDependencies.request(null as any) - // @ts-expect-error dependencies required that are not provided const e1 = client.GetSomething2WithDependencies.suspense(null as any) - // @ts-expect-error dependencies required that are not provided const e2 = client.GetSomething2WithDependencies.query(null as any) // @ts-expect-error query requests no longer expose command helpers const f0 = client.GetSomething2WithDependencies.fn(null as any) diff --git a/packages/vue/test/query-span.test.ts b/packages/vue/test/query-span.test.ts index 7d7d768b53..4918f825d1 100644 --- a/packages/vue/test/query-span.test.ts +++ b/packages/vue/test/query-span.test.ts @@ -6,7 +6,7 @@ import * as Effect from "effect-app/Effect" import * as Layer from "effect-app/Layer" import * as Option from "effect-app/Option" import type * as Cause from "effect/Cause" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +import * as AsyncResult from "effect/reactivity/AsyncResult" import { createApp, effectScope, nextTick } from "vue" import { buildQueryFamily, makeAtomClientRuntime } from "../src/atomQuery.js" import { makeTanstackQuery } from "../src/internal/tanstackQuery.js" diff --git a/packages/vue/test/queryOptions.test.ts b/packages/vue/test/queryOptions.test.ts index ac1279e286..2d2c55ac8e 100644 --- a/packages/vue/test/queryOptions.test.ts +++ b/packages/vue/test/queryOptions.test.ts @@ -1,7 +1,7 @@ import { expect, it } from "@effect/vitest" import * as Cause from "effect/Cause" import * as Option from "effect/Option" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +import * as AsyncResult from "effect/reactivity/AsyncResult" import { shallowRef } from "vue" import { withDataFallback } from "../src/query.js" diff --git a/packages/vue/test/stubs.ts b/packages/vue/test/stubs.ts index 2b66419d3c..043ebf60b3 100644 --- a/packages/vue/test/stubs.ts +++ b/packages/vue/test/stubs.ts @@ -8,8 +8,8 @@ import * as Option from "effect-app/Option" import { RpcContextMap } from "effect-app/rpc" import * as S from "effect-app/Schema" import * as Exit from "effect/Exit" +import * as FetchHttpClient from "effect/http/FetchHttpClient" import * as ManagedRuntime from "effect/ManagedRuntime" -import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient" import { createApp, ref } from "vue" import { Commander } from "../src/commander.js" import { I18n } from "../src/intl.js" @@ -146,7 +146,7 @@ class SomethingGetSomething2WithDependencies id: S.String }, { // this is intentilally fake, to simulate a codec that requires a dependency - success: S.FiniteFromString as S.Codec, + success: S.FiniteFromString, error: S.String }) {} @@ -262,7 +262,7 @@ class SomethingElseGetSomething2WithDependencies extends SomethingElseQuery()("GetSomething2", { id: S.String }, { - success: S.FiniteFromString as S.Codec, + success: S.FiniteFromString, error: S.String }) {} diff --git a/packages/vue/test/suspense-regression.test.ts b/packages/vue/test/suspense-regression.test.ts index 6a400531c3..21dbd5d037 100644 --- a/packages/vue/test/suspense-regression.test.ts +++ b/packages/vue/test/suspense-regression.test.ts @@ -6,7 +6,7 @@ import * as Effect from "effect-app/Effect" import * as Option from "effect-app/Option" import * as Layer from "effect/Layer" import * as ManagedRuntime from "effect/ManagedRuntime" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +import * as AsyncResult from "effect/reactivity/AsyncResult" import { createApp, effectScope, nextTick, ref } from "vue" import { makeAtomClientRuntime } from "../src/atomQuery.js" import { makeTanstackQuery } from "../src/internal/tanstackQuery.js" @@ -37,9 +37,9 @@ it("makeClient .suspense(): observer re-pointed mid-flight -> resolves (seeded) const getRuntime = () => Context.empty() const qi = new QueryImpl( getRuntime, - (() => { + () => { throw new Error("atom runtime is unused by this legacy TanStack regression") - }) as any, + }, makeTanstackQuery(getRuntime, queryClient) ) @@ -115,7 +115,7 @@ it("atom engine .suspense(): reactive arg switched to an uncached input serves t app.provide(registryKey, defaultRegistry) app.mount(host) - const [resultRef, latestRef] = (await promise) as any + const [resultRef, latestRef] = await promise expect(latestRef.value).toBe("DATA_A") // Re-point to an input whose atom has never fetched -> Initial, no previousSuccess. diff --git a/packages/vue/test/suspense.test.ts b/packages/vue/test/suspense.test.ts index 16e834c40e..d6da2c0805 100644 --- a/packages/vue/test/suspense.test.ts +++ b/packages/vue/test/suspense.test.ts @@ -1,7 +1,7 @@ import { defaultRegistry, registryKey } from "@effect/atom-vue" import { Effect, Option } from "effect-app" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" import { computed, createApp, nextTick, ref } from "vue" import { withQueryOptions } from "../src/atomQuery.js" import { useAtomQuery, useAtomSuspense } from "../src/query.js" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d968a10dd8..e81f65faf5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,17 +42,17 @@ importers: specifier: workspace:* version: link:packages/infra "@effect/language-service": - specifier: 0.87.2 - version: 0.87.2 + specifier: 0.87.3 + version: 0.87.3 "@effect/platform-node": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/tsgo": - specifier: ^0.45.0 - version: 0.45.0 + specifier: ^0.47.2 + version: 0.47.2 "@effect/vitest": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@tsconfig/strictest": specifier: ^2.0.8 version: 2.0.8 @@ -72,8 +72,8 @@ importers: specifier: ^0.54.0 version: 0.54.0 effect: - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: 4.0.0 + version: 4.0.0 effect-app: specifier: workspace:* version: link:packages/effect-app @@ -117,11 +117,11 @@ importers: packages/cli: dependencies: "@effect/platform-node": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(redis@6.2.1(@opentelemetry/api@1.9.1)) effect: - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: 4.0.0 + version: 4.0.0 js-yaml: specifier: 4.2.0 version: 4.2.0 @@ -153,14 +153,14 @@ importers: version: link:../effect-app devDependencies: "@effect/atom-vue": - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) + specifier: ^4.0.0 + version: 4.0.0(effect@4.0.0)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) "@effect/platform-node": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/vitest": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@tanstack/vue-query": specifier: 5.96.2 version: 5.96.2(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) @@ -171,8 +171,8 @@ importers: specifier: ^6.0.7 version: 6.0.7(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) effect: - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: ^4.0.0 + version: 4.0.0 typescript: specifier: ~6.0.3 version: 6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e) @@ -192,8 +192,8 @@ importers: specifier: ^4.4.0 version: 4.4.0 effect: - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: ^4.0.0 + version: 4.0.0 nanoid: specifier: ^5.1.11 version: 5.1.11 @@ -331,8 +331,8 @@ importers: packages/infra: dependencies: "@effect/vitest": - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: ^4.0.0 + version: 4.0.0(effect@4.0.0)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@faker-js/faker": specifier: ^8.4.1 version: 8.4.1 @@ -346,8 +346,8 @@ importers: specifier: 10.55.0 version: 10.55.0(supports-color@8.1.1) effect: - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: ^4.0.0 + version: 4.0.0 effect-app: specifier: workspace:* version: link:../effect-app @@ -374,8 +374,8 @@ importers: specifier: ^7.9.5 version: 7.9.5(supports-color@8.1.1) "@effect/sql-sqlite-node": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0) "@sentry/opentelemetry": specifier: 10.55.0 version: 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) @@ -420,11 +420,11 @@ importers: packages/vue: dependencies: "@effect/atom-vue": - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) + specifier: ^4.0.0 + version: 4.0.0(effect@4.0.0)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) "@effect/platform-browser": - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115) + specifier: ^4.0.0 + version: 4.0.0(effect@4.0.0) "@formatjs/intl": specifier: ^4.1.12 version: 4.1.12 @@ -441,8 +441,8 @@ importers: specifier: ^5.4.4 version: 5.4.4 effect: - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: ^4.0.0 + version: 4.0.0 effect-app: specifier: workspace:* version: link:../effect-app @@ -454,8 +454,8 @@ importers: version: 3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)) devDependencies: "@effect/vitest": - specifier: 4.0.0-rc.115 - version: 4.0.0-rc.115(effect@4.0.0-rc.115)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) + specifier: 4.0.0 + version: 4.0.0(effect@4.0.0)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))) "@formatjs/icu-messageformat-parser": specifier: ^3.5.10 version: 3.5.10 @@ -494,8 +494,8 @@ importers: specifier: ^1.32.0 version: 1.32.0(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e))) effect: - specifier: ^4.0.0-rc.115 - version: 4.0.0-rc.115 + specifier: ^4.0.0 + version: 4.0.0 effect-app: specifier: workspace:* version: link:../effect-app @@ -1188,26 +1188,34 @@ packages: } hasBin: true - "@effect/atom-vue@4.0.0-rc.115": + "@effect/atom-vue@4.0.0": resolution: { - integrity: sha512-1750Ozq5zxliHP78hbw80ynant7IuB6V1MKS+6dnhATZ5I1oViM1Y10ZsSdU9/aGnbnMfpN3SG2URwqhzorXmA==, + integrity: sha512-ZlVU+qGJA/2C0ZsBS9ytSBdSM93aLecjXnMp/s0qvoMEf+kKRlrrYfD7dRpCrD56JqI0IcGJ9C8Y74Lu2Sz6AQ==, } peerDependencies: - effect: ^4.0.0-rc.115 + effect: ^4.0.0 vue: ^3.5.35 - "@effect/language-service@0.87.2": + "@effect/language-service@0.87.3": resolution: { - integrity: sha512-CfiSoaVQO8pZgaMZGw5VvMvRGybsJ2LaSDoLYaqiVGvo/YYPYVR2GzUKY0h1NtIweq/+SQ5XLrvtzPIttWQ0GQ==, + integrity: sha512-g1OmRBZF5chbW9QMNoirPo5Bb4zzU2HfP88tR7/VSZEUxHJ1/qVb8AQ0kUh2ggctLH1I715yCwP4gkw56PTjpQ==, } hasBin: true - "@effect/platform-browser@4.0.0-rc.115": + "@effect/platform-browser@4.0.0": resolution: { - integrity: sha512-RwpnxRYSBHBL1HYYlRwIrwz0+1hwmn5RvgqkKI0Tb+ChOIR9UCMpXsAhnwQcGP+mBidrC7dlloL42poGHZW4yw==, + integrity: sha512-OHo7EPpF3RVPiI+mjoU9yd05LdCdKPjzDXqs5wRgF9CsdVvwN76B0zgt54DxPUcvMYri1tVJOtLCVnK0tRkNQQ==, } peerDependencies: - effect: ^4.0.0-rc.115 + effect: ^4.0.0 + + "@effect/platform-node-shared@4.0.0": + resolution: { + integrity: sha512-VBXHJU9UXVZPdQGGDRjGXpo+TpOzRwZuMrdKAtIxi1qwcRftmacZ1zTgIrzBa0Z2RA2KVkoOecQUsTlt/nJZhw==, + } + engines: { node: ">=18.0.0" } + peerDependencies: + effect: ^4.0.0 "@effect/platform-node-shared@4.0.0-beta.86": resolution: { @@ -1217,13 +1225,14 @@ packages: peerDependencies: effect: ^4.0.0-beta.86 - "@effect/platform-node-shared@4.0.0-rc.115": + "@effect/platform-node@4.0.0": resolution: { - integrity: sha512-rcBwhDIfb82akJoTv7Vwh8K5x9vC/XfuePS7JjOr+z+dORXzsmKqI1BUQF27ARCN9fTAbDh7I6YvBlWhLjnqNg==, + integrity: sha512-/L+4MUbl8FejPKLHLROYSOebvHnfixkP8KiblSD/DE5uH5xENZBHaux4nH6XMnRP+lr8ajpEJFS8sUrmmxFJeQ==, } engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.0-rc.115 + effect: ^4.0.0 + redis: ">=5.0.0 <7.0.0" "@effect/platform-node@4.0.0-beta.86": resolution: { @@ -1234,83 +1243,74 @@ packages: effect: ^4.0.0-beta.86 ioredis: ^5.7.0 - "@effect/platform-node@4.0.0-rc.115": + "@effect/sql-sqlite-node@4.0.0": resolution: { - integrity: sha512-mW03Wjq2FiTL5oi9yOkaKpCyUNBaduKgTk7qMHNELvC21Jq2qOAfdsfH+p+s2zmpB9w2RSa+ApODv4WjO9wQfw==, + integrity: sha512-xyUSLY7v55iMgGxrjFrTo5ngixgQ3jLmOQ+AX8CCazO/caxuMM0oq8zb974FAcvQHRrpMEWU7Vnoo+rxct7PAQ==, } - engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.0-rc.115 - redis: ">=5.0.0 <7.0.0" + effect: ^4.0.0 - "@effect/sql-sqlite-node@4.0.0-rc.115": + "@effect/tsgo-darwin-arm64@0.47.2": resolution: { - integrity: sha512-53n3Kbwush7K/8/G1CDz638ClEoMvnUOT5BltCxMZ8TqcxeNoMv+nty08ZdrJ7P6m1q1RIeWlqdy5J9ZDICpbw==, - } - peerDependencies: - effect: ^4.0.0-rc.115 - - "@effect/tsgo-darwin-arm64@0.45.0": - resolution: { - integrity: sha512-WUoiEVcm8E19QK6fl63lsssM4dkT9IFnULbmqQ6LBfv7H9uRkpE9S3thj8ZtZ+vl/QY8atwfuaTf+3A1m/kGtQ==, + integrity: sha512-sLYKt201VdTjSHG3rbyIdpLMNGEMiArm0OgO7kYfnA0yRV0ux0blgcucf97bx90gIvDJFSSL17QCSDPGs9jsKQ==, } cpu: [arm64] os: [darwin] - "@effect/tsgo-darwin-x64@0.45.0": + "@effect/tsgo-darwin-x64@0.47.2": resolution: { - integrity: sha512-s3yz7ZfIzvObXJ/r5xd9sEPxHKC3L+D+yedS1BV2Kpn47Je2svYCNYKxzXIpqFLkNjK+RcOQrcfYbYEpKZZa6g==, + integrity: sha512-gW0fdFjSIacCDOjLa+vS/xWmyElbM0jX3cCTlKXGzNCyMKsP/T0MkvT2GZO/njB2jIt/XXG9qEQ5FE/Rcqt7Zg==, } cpu: [x64] os: [darwin] - "@effect/tsgo-linux-arm64@0.45.0": + "@effect/tsgo-linux-arm64@0.47.2": resolution: { - integrity: sha512-N+lyzixtylOOn49demDaqg0kb/U8ISxM10Gphs1pIMSGlqlfqC4inYEoFpYej+f4T/cRlkA11Ue7wJzNz83ePA==, + integrity: sha512-hA3SoKGuLd0HNqJ34QcYEWwpE6tvit3xpKupwj1TaPtHpLLGdtSF7czBIknp6kR8fOvnuYLjB6aEkxXmJsTVWg==, } cpu: [arm64] os: [linux] - "@effect/tsgo-linux-arm@0.45.0": + "@effect/tsgo-linux-arm@0.47.2": resolution: { - integrity: sha512-f6zL6YbWA9lXdfJ//KOjUz/U3CdGtajKi5NsR/lcQaKfZiJwHfeeLBB3yetF3qbPGSA149/kuMoDMWL13RUH3A==, + integrity: sha512-GT8fgQ3Pnzr0ax/AtYptztVquLAWeWoyLvQTpfI5APrs+Me+/rSwOLSA7//17ZVmNYbcGbKImX8laUYeDqByNQ==, } cpu: [arm] os: [linux] - "@effect/tsgo-linux-x64@0.45.0": + "@effect/tsgo-linux-x64@0.47.2": resolution: { - integrity: sha512-aWt0G8celAxJOis4+xiILkyMP/3J+vukimUhSoCmUAPt8zoT9qhZcN64XYZE7jfqp88ByRdgN9GK1/C7YfPtcQ==, + integrity: sha512-vELwVsUQ5VlM2jbvv7zaIPOkFsxmaS0nLqq+p0RodtkQiuScg27febnLClzKAv/Tl6GrXTBz4IBh78aBF2CvbQ==, } cpu: [x64] os: [linux] - "@effect/tsgo-win32-arm64@0.45.0": + "@effect/tsgo-win32-arm64@0.47.2": resolution: { - integrity: sha512-EAdeV366ja+M4XCx6xjXMv4Xkc0Fgzy4bapa0saDWcGhllNLqJDfOk4lk0BgAEjBUI8+0xC+PXKVB5SMz4EpVA==, + integrity: sha512-FSwpJp3XaJK9o7cOXtJcYIsvZWR/mrY4ZZyyTuRgphEZju7FwOzLeVhLr9ziyScfmisOpX2i+8v1H+PlMMZ1TQ==, } cpu: [arm64] os: [win32] - "@effect/tsgo-win32-x64@0.45.0": + "@effect/tsgo-win32-x64@0.47.2": resolution: { - integrity: sha512-1Hsr/sT5dmIafoY9L0ASJo4VqGyPNzM1nwTb4Wj1Lmjq6kGuwO/PVozFg3WZZ0piChFmgiU3nx1AKZfyqgjGUw==, + integrity: sha512-Lh/N7E2p6E0tBZM+uZ40Hv/AUG81WGtw76H4pLLPAG7wFANwGSx984NwKypXqGtOXBmKqb0R/cYpszDyzbNyYA==, } cpu: [x64] os: [win32] - "@effect/tsgo@0.45.0": + "@effect/tsgo@0.47.2": resolution: { - integrity: sha512-gPG0ixVHgtYBUe5DbKX9vzHlhqd9+ZEfs4m/XWy0808av0A2XqMzaG3F5rnPBN7EUJRPlo9TluIi8WHx/pBV+A==, + integrity: sha512-mGIelPZhTJg8g+RgiAe7wrB8NLw/lOzPGntAYBBqEJS0/I8ir1/ULKcpFQyuAfPjwkDYrg93KWv1PDGwgsgBIA==, } hasBin: true - "@effect/vitest@4.0.0-rc.115": + "@effect/vitest@4.0.0": resolution: { - integrity: sha512-DJR0hfdr3HNXAkzW8AzfvUz6VmrNFLrKk9tZGDURi33faMyxkc7ZewxELMMZwGpOTGN4mfqYVbX2CXgI+ZJBeQ==, + integrity: sha512-kGElCPFGtP+CgMCwV/HjJWqtE3zYo6I1+8MSA54l+EI24lPU9p2Efa+TBp1jfkg9j711KKzEqgSAy3VzTsb76Q==, } peerDependencies: - effect: ^4.0.0-rc.115 + effect: ^4.0.0 vitest: ">=5.0.0 <6.0.0" "@emnapi/core@1.10.0": @@ -5734,14 +5734,14 @@ packages: engines: { node: ">=14" } hasBin: true - effect@4.0.0-beta.86: + effect@4.0.0: resolution: { - integrity: sha512-+Y16hy3LhAju/FM3dz3Xp+aGi8dw9AtEzswcvwxREpeEPq+rL8TFS1PgWAO1+/3kKKH4bq9NdBGtfhHzM2K7Ow==, + integrity: sha512-ooc1TG5t+FfzgYnFz2ff6BBKyZ7EwBRVXC7c4RhQUAD6/TZ2gTXXMeb4WX7a19ozQo4J73/QW+S00YAIresoMQ==, } - effect@4.0.0-rc.115: + effect@4.0.0-beta.86: resolution: { - integrity: sha512-ogYulZ5ffeOzrJqQrG0XOkDO5lKn2s9KNHhoxJy/6wKUkF0i12dlVXWA9TgsgQ+zV/JzizyG0+XMPepZEQX6vw==, + integrity: sha512-+Y16hy3LhAju/FM3dz3Xp+aGi8dw9AtEzswcvwxREpeEPq+rL8TFS1PgWAO1+/3kKKH4bq9NdBGtfhHzM2K7Ow==, } electron-to-chromium@1.5.267: @@ -9109,9 +9109,9 @@ packages: } engines: { node: ">=20.18.1" } - undici@8.10.2: + undici@8.11.2: resolution: { - integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==, + integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==, } engines: { node: ">=22.19.0" } @@ -9670,6 +9670,20 @@ packages: utf-8-validate: optional: true + ws@8.22.0: + resolution: { + integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==, + } + engines: { node: ">=10.0.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wsl-utils@0.1.0: resolution: { integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==, @@ -10319,16 +10333,25 @@ snapshots: - ioredis - utf-8-validate - "@effect/atom-vue@4.0.0-rc.115(effect@4.0.0-rc.115)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)))": + "@effect/atom-vue@4.0.0(effect@4.0.0)(vue@3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)))": dependencies: - effect: 4.0.0-rc.115 + effect: 4.0.0 vue: 3.5.35(typescript@6.0.3(patch_hash=6a20b8df080eb8a9c34008e1802ecfd839f4e90e07f1ee97f78694dbd95a520e)) - "@effect/language-service@0.87.2": {} + "@effect/language-service@0.87.3": {} - "@effect/platform-browser@4.0.0-rc.115(effect@4.0.0-rc.115)": + "@effect/platform-browser@4.0.0(effect@4.0.0)": dependencies: - effect: 4.0.0-rc.115 + effect: 4.0.0 + + "@effect/platform-node-shared@4.0.0(effect@4.0.0)": + dependencies: + "@types/ws": 8.18.1 + effect: 4.0.0 + ws: 8.22.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate "@effect/platform-node-shared@4.0.0-beta.86(effect@4.0.0-beta.86)": dependencies: @@ -10339,11 +10362,12 @@ snapshots: - bufferutil - utf-8-validate - "@effect/platform-node-shared@4.0.0-rc.115(effect@4.0.0-rc.115)": + "@effect/platform-node@4.0.0(effect@4.0.0)(redis@6.2.1(@opentelemetry/api@1.9.1))": dependencies: - "@types/ws": 8.18.1 - effect: 4.0.0-rc.115 - ws: 8.21.3 + "@effect/platform-node-shared": 4.0.0(effect@4.0.0) + effect: 4.0.0 + redis: 6.2.1(@opentelemetry/api@1.9.1) + undici: 8.11.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -10359,54 +10383,44 @@ snapshots: - bufferutil - utf-8-validate - "@effect/platform-node@4.0.0-rc.115(effect@4.0.0-rc.115)(redis@6.2.1(@opentelemetry/api@1.9.1))": + "@effect/sql-sqlite-node@4.0.0(effect@4.0.0)": dependencies: - "@effect/platform-node-shared": 4.0.0-rc.115(effect@4.0.0-rc.115) - effect: 4.0.0-rc.115 - redis: 6.2.1(@opentelemetry/api@1.9.1) - undici: 8.10.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - "@effect/sql-sqlite-node@4.0.0-rc.115(effect@4.0.0-rc.115)": - dependencies: - effect: 4.0.0-rc.115 + effect: 4.0.0 - "@effect/tsgo-darwin-arm64@0.45.0": + "@effect/tsgo-darwin-arm64@0.47.2": optional: true - "@effect/tsgo-darwin-x64@0.45.0": + "@effect/tsgo-darwin-x64@0.47.2": optional: true - "@effect/tsgo-linux-arm64@0.45.0": + "@effect/tsgo-linux-arm64@0.47.2": optional: true - "@effect/tsgo-linux-arm@0.45.0": + "@effect/tsgo-linux-arm@0.47.2": optional: true - "@effect/tsgo-linux-x64@0.45.0": + "@effect/tsgo-linux-x64@0.47.2": optional: true - "@effect/tsgo-win32-arm64@0.45.0": + "@effect/tsgo-win32-arm64@0.47.2": optional: true - "@effect/tsgo-win32-x64@0.45.0": + "@effect/tsgo-win32-x64@0.47.2": optional: true - "@effect/tsgo@0.45.0": + "@effect/tsgo@0.47.2": optionalDependencies: - "@effect/tsgo-darwin-arm64": 0.45.0 - "@effect/tsgo-darwin-x64": 0.45.0 - "@effect/tsgo-linux-arm": 0.45.0 - "@effect/tsgo-linux-arm64": 0.45.0 - "@effect/tsgo-linux-x64": 0.45.0 - "@effect/tsgo-win32-arm64": 0.45.0 - "@effect/tsgo-win32-x64": 0.45.0 + "@effect/tsgo-darwin-arm64": 0.47.2 + "@effect/tsgo-darwin-x64": 0.47.2 + "@effect/tsgo-linux-arm": 0.47.2 + "@effect/tsgo-linux-arm64": 0.47.2 + "@effect/tsgo-linux-x64": 0.47.2 + "@effect/tsgo-win32-arm64": 0.47.2 + "@effect/tsgo-win32-x64": 0.47.2 - "@effect/vitest@4.0.0-rc.115(effect@4.0.0-rc.115)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)))": + "@effect/vitest@4.0.0(effect@4.0.0)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)))": dependencies: - effect: 4.0.0-rc.115 + effect: 4.0.0 vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@29.1.1)(vite@8.0.15(@types/node@25.9.1)(esbuild@0.28.0)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)) "@emnapi/core@1.10.0": @@ -13039,6 +13053,8 @@ snapshots: minimatch: 9.0.1 semver: 7.7.3 + effect@4.0.0: {} + effect@4.0.0-beta.86: dependencies: "@standard-schema/spec": 1.1.0 @@ -13052,8 +13068,6 @@ snapshots: uuid: 14.0.2 yaml: 2.9.0 - effect@4.0.0-rc.115: {} - electron-to-chromium@1.5.267: optional: true @@ -15688,7 +15702,7 @@ snapshots: undici@7.25.0: {} - undici@8.10.2: {} + undici@8.11.2: {} undici@8.3.0: {} @@ -16142,6 +16156,8 @@ snapshots: ws@8.21.3: {} + ws@8.22.0: {} + wsl-utils@0.1.0: dependencies: is-wsl: 3.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f6bfcf271a..13281d1925 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,21 +8,22 @@ allowBuilds: msgpackr-extract: false vue-demi: false minimumReleaseAgeExclude: - - "@effect/tsgo-darwin-arm64@0.45.0" - - "@effect/tsgo-darwin-x64@0.45.0" - - "@effect/tsgo-linux-arm64@0.45.0" - - "@effect/tsgo-linux-arm@0.45.0" - - "@effect/tsgo-linux-x64@0.45.0" - - "@effect/tsgo-win32-arm64@0.45.0" - - "@effect/tsgo-win32-x64@0.45.0" - - "@effect/tsgo@0.45.0" - - "@effect/atom-vue@4.0.0-rc.115" - - "@effect/platform-browser@4.0.0-rc.115" - - "@effect/platform-node-shared@4.0.0-rc.115" - - "@effect/platform-node@4.0.0-rc.115" - - "@effect/sql-sqlite-node@4.0.0-rc.115" - - "@effect/vitest@4.0.0-rc.115" - - effect@4.0.0-rc.115 + - "@effect/tsgo-darwin-arm64@0.47.2" + - "@effect/tsgo-darwin-x64@0.47.2" + - "@effect/tsgo-linux-arm64@0.47.2" + - "@effect/tsgo-linux-arm@0.47.2" + - "@effect/tsgo-linux-x64@0.47.2" + - "@effect/tsgo-win32-arm64@0.47.2" + - "@effect/tsgo-win32-x64@0.47.2" + - "@effect/tsgo@0.47.2" + - "@effect/language-service@0.87.3" + - "@effect/atom-vue@4.0.0" + - "@effect/platform-browser@4.0.0" + - "@effect/platform-node-shared@4.0.0" + - "@effect/platform-node@4.0.0" + - "@effect/sql-sqlite-node@4.0.0" + - "@effect/vitest@4.0.0" + - effect@4.0.0 overrides: date-fns: ^4.4.0 fast-check: ^4.9.0 diff --git a/repos/effect/.agents/AGENTS.md b/repos/effect/.agents/AGENTS.md index 1f33cf814c..e6450c28e1 100644 --- a/repos/effect/.agents/AGENTS.md +++ b/repos/effect/.agents/AGENTS.md @@ -16,14 +16,19 @@ This is the Effect TypeScript monorepo. The git base branch is `main`; use `pnpm Use the narrowest validation that covers the change: -| Change type | Validation | -| -------------------------------- | ---------------------------------------------------------------------------------- | -| Code changes | `pnpm lint-fix`, targeted `pnpm test --run `, `pnpm check` | -| Tests-only changes | `pnpm lint-fix`, targeted `pnpm test --run `, `pnpm check` | -| Type-level/API type changes | Targeted `pnpm test-types `, plus `pnpm check` when source types changed | -| JSDoc text/category/link changes | `pnpm jsdocs --check`, `pnpm lint` | -| JSDoc example changes | `pnpm jsdocs --check`, `pnpm lint`, root `pnpm doctest --run ` | -| Docs-only changes | `pnpm lint-fix`; no tests unless examples or code changed | +| Change type | Validation | +| ---------------------------------- | ---------------------------------------------------------------------------------- | +| Runtime source or test changes | `pnpm lint-fix`, targeted `pnpm test --run `, `pnpm check` | +| Type-system behavior or type tests | Targeted `pnpm test-types `, plus `pnpm check` when source types changed | +| JSDoc text/category/link changes | `pnpm jsdocs --check`, `pnpm lint` | +| JSDoc example changes | `pnpm jsdocs --check`, `pnpm lint`, root `pnpm doctest --run ` | +| Docs-only changes | `pnpm lint-fix`; no tests unless examples or code changed | + +Choose coverage from the contract being changed. Runtime tests cover executed +behavior. Tstyche tests cover intended compiler behavior such as inference, +assignability, rejection, or displayed public types. An ordinary source edit or +exported declaration is covered by `pnpm check` unless compiler behavior is part +of the task. Never run bare `pnpm test` or `pnpm doctest`; both start the full suite in watch mode. Always pass `--run` and the specific files covering the change. CI runs the full suite. diff --git a/repos/effect/.agents/skills/changesets/SKILL.md b/repos/effect/.agents/skills/changesets/SKILL.md index 0f4186cfc2..5b6b4d5f29 100644 --- a/repos/effect/.agents/skills/changesets/SKILL.md +++ b/repos/effect/.agents/skills/changesets/SKILL.md @@ -1,22 +1,38 @@ --- name: changesets -description: Changesets. Use after consumer-visible runtime, public API, entrypoint, lifecycle, or wire-format changes, when deciding whether a change is breaking, or when authoring changesets and consumer release notes. +description: Concise PR finalization changesets. Use when implementation scope is complete and focused validation has finished to classify release impact, consolidate the PR's existing changeset, or write its consumer release note. --- -Record what consumers need to know after implementation and focused validation. +Treat a changeset as the release note for the complete PR, not as a log of work +performed along the way. Finalize it after the current implementation scope and +focused validation are complete. ## Workflow -1. Inspect the complete diff and identify directly affected published packages. +1. Inspect the complete diff from the merge base, including changeset files + already introduced by the PR, and identify directly affected published + packages. 2. Classify impact across source types, runtime behavior, entrypoints, required services, lifecycle, and persisted or wire data. 3. Perform the breaking audit below. -4. Record either a reason no changeset is required or one coherent changeset. - When required, read [authoring.md](authoring.md). -5. Validate package names, frontmatter, bump policy, and consumer-facing text. +4. Record a reason no changeset is required, or update the PR's existing + changeset. Create one only when the PR does not have one. When required, read + [authoring.md](authoring.md). +5. Validate package names, frontmatter, bump policy, and a concise + consumer-facing description. Do not include unrelated worktree changes. +## PR-level consolidation + +The default is one changeset file per PR. One file can name several packages and +summarize several related fixes or additions. Successive prompts, commits, and +subtasks within the same PR update that file. If implementation resumes after a +changeset was drafted, revise it during the next finalization pass. + +Use separate files only when a maintainer requests the split or the PR contains +independent changes that need separate release notes. + ## Requirement Create a changeset for observable runtime behavior changes, including bug @@ -53,6 +69,7 @@ consumer-visible. Verify representative existing calls when overload ordering, structural assignability, or inference makes compatibility uncertain. API diff output is mechanical evidence, not a semantic-version decision. -The task is complete when every affected surface and published package is -accounted for and either the no-changeset decision is explicit or one coherent, -valid changeset describes the consumer impact and migration for every break. +The task is complete when every affected contract and published package is +accounted for and either the no-changeset decision is explicit or the PR has one +consolidated, valid changeset by default. Its release note describes the consumer +impact and migration for every break without repeating the PR description. diff --git a/repos/effect/.agents/skills/changesets/authoring.md b/repos/effect/.agents/skills/changesets/authoring.md index cb8fcb99c6..e1a1c22fe1 100644 --- a/repos/effect/.agents/skills/changesets/authoring.md +++ b/repos/effect/.agents/skills/changesets/authoring.md @@ -1,6 +1,7 @@ -# Authoring Changesets +# Authoring changesets -Create one `.changeset/.md` per coherent change: +Update the changeset already introduced by the PR. If there is none, create one +`.changeset/.md` for the complete PR: ```md --- @@ -11,22 +12,34 @@ Create one `.changeset/.md` per coherent change: Describe the consumer-visible change and why it matters. ``` +Default to one short paragraph of one to three sentences. State the changed +behavior or API and, for a break, the required migration. Leave motivation, +implementation details, benchmarks, test coverage, and the development history +in the PR description. + +When several related consumer-visible effects cannot be stated clearly in that +paragraph, use compact bullets with one outcome per bullet. Keep them in this +file under the PR's shared purpose. + List every directly affected published package. Do not list packages merely because they share the fixed release group in `.changeset/config.json`. Choose the bump from current release policy: -- On a stable line, use `patch` for compatible fixes, `minor` for compatible - additions, and `major` for breaks. -- In `.changeset/pre.json` `rc` mode, follow the current convention of recording - v4 release-candidate changes, including breaking cleanups, as `patch` unless a - maintainer requests another level. -- Ask when release mode or intent is ambiguous. - -Write for consumers. State the changed behavior or API and give concrete -migration guidance for every break. Use a `### Breaking changes` section when -several breaks need separate scanning. Include before/after examples only when -they materially clarify migration. Omit implementation and test details. +- Use `patch` for compatible fixes, `minor` for compatible additions, and + `major` for breaks. +- A break confined to APIs tagged `@stability unstable` is `minor`. A break + confined to APIs tagged `@stability experimental` is `patch`. APIs without a + stability tag follow strict semver. +- Use `major` only when a maintainer has approved a major release. +- The release queue retargets PRs with `minor` changesets to `v4/next-minor` + and PRs with `major` changesets to `v4/next-major`. Choose the level the + change requires, not the branch you want it to land on. +- Ask when release intent is ambiguous. + +Write for consumers. Use a `### Breaking changes` section when several breaks +need separate scanning. Include before/after examples only when they materially +clarify migration. Validate frontmatter against published package names and inspect nearby current changesets for wording and release convention. Never run `changeset-version` or diff --git a/repos/effect/.agents/skills/jsdocs/declarations.md b/repos/effect/.agents/skills/jsdocs/declarations.md index 62937bd366..9795b4c9a0 100644 --- a/repos/effect/.agents/skills/jsdocs/declarations.md +++ b/repos/effect/.agents/skills/jsdocs/declarations.md @@ -53,8 +53,9 @@ Declaration tags appear in this order: 1. `@deprecated` 2. `@default` 3. `@see` -4. `@category` -5. `@since` +4. `@stability unstable` or `@stability experimental` (when applicable) +5. `@category` +6. `@since` - Roots require stable-semver `@since` and no `@default`; category requirements live in [categories.md](categories.md). @@ -64,6 +65,16 @@ Declaration tags appear in this order: non-empty `@default`, rejects `@category`, and follows the prose contract. - Any declaration permits one non-empty `@deprecated` and repeated non-empty `@see` tags. +- Use `@stability unstable` when an API may receive breaking changes in minor + releases, or `@stability experimental` when it may receive breaking changes + across patch versions. Leave the tag out for APIs that follow strict semver. +- Tag any API that exposes a third-party dependency `@stability unstable`: an + accessor to the underlying client or instance, options typed as the + dependency's options, constructors that accept its instances, re-exports of + it, and signatures that mention its types. Tag the whole module when it exists + to wrap the dependency; otherwise tag only the exposing declarations or + members. Runtime built-ins (Node, Bun, Deno, DOM) and framework types inherent + to a binding (React, Solid, Vue) are exempt. Use canonical `**Example**` sections rather than `@example` tags or loose code fences. @@ -73,8 +84,9 @@ fences. When present, the first top-level JSDoc is the module block unless TypeScript attaches it to a non-import first declaration. An `@internal` module is omitted. Module prose does not use the declaration template. Its tags are optional -non-empty `@deprecated`, repeated non-empty `@see`, then required stable-semver -`@since`. Its examples and links follow the declaration contracts. +non-empty `@deprecated`, repeated non-empty `@see`, optional `@stability unstable` +or `@stability experimental`, then required stable-semver `@since`. Its examples +and links follow the declaration contracts. Inline `{@link Symbol}` targets must resolve to TypeScript symbols; use normal Markdown links for URLs. Prefer code formatting when navigation does not help a diff --git a/repos/effect/.agents/skills/test-development/SKILL.md b/repos/effect/.agents/skills/test-development/SKILL.md index 3c6082e7ca..64938f89cd 100644 --- a/repos/effect/.agents/skills/test-development/SKILL.md +++ b/repos/effect/.agents/skills/test-development/SKILL.md @@ -1,13 +1,21 @@ --- name: test-development -description: Test development. Use when adding or changing runtime Vitest coverage or Tstyche contracts for behavior, inference, assignability, or displayed public types. +description: Test development. Use when adding or changing Vitest or Tstyche coverage. Choose Tstyche only for intended compiler behavior such as inference, assignability, rejection, or displayed public types. --- -Select every applicable branch before editing: +Choose branches from the task's acceptance criteria before editing: -- **Runtime behavior:** Read [runtime.md](runtime.md). -- **Type contracts:** Read [types.md](types.md). +- **Runtime behavior:** The intended result is observable when code executes. + Read [runtime.md](runtime.md). +- **Compiler behavior:** The intended result is TypeScript accepting, rejecting, + or inferring a consumer expression in a particular way. Read + [types.md](types.md). -Some changes may require both branches. The task is complete when every -changed runtime behavior and type contract has focused coverage, targeted tests -pass, and applicable root checks pass or are reported as not runnable. +An exported signature alone is validated by `pnpm check`; it enters the compiler +branch only when its inference, assignability, rejection, overload selection, or +displayed type is part of the task. Some changes require both branches because +they independently change runtime and compiler behavior. + +The task is complete when each intended behavior in scope has the relevant +focused coverage, targeted tests pass, and applicable root checks pass or are +reported as not runnable. diff --git a/repos/effect/.agents/skills/test-development/types.md b/repos/effect/.agents/skills/test-development/types.md index 3bc747789e..03663d8468 100644 --- a/repos/effect/.agents/skills/test-development/types.md +++ b/repos/effect/.agents/skills/test-development/types.md @@ -1,4 +1,10 @@ -# Type Tests +# Type tests + +Enter this branch when the acceptance criterion is compiler behavior: inference, +assignability, rejection, overload selection, generic constraints, or a displayed +public type. Use `pnpm check` for ordinary declaration edits whose compiler +behavior is not independently under test. A runtime bug in generic code needs a +runtime regression test, not a type test, when its type contract is unchanged. Inspect nearby `.tst.ts` files and use their imports and assertion style. Use ordinary Tstyche assertions such as `toBe` for structural equality and choose a diff --git a/repos/effect/.changeset/config.json b/repos/effect/.changeset/config.json index 0a755e5f06..2b10fe8a6a 100644 --- a/repos/effect/.changeset/config.json +++ b/repos/effect/.changeset/config.json @@ -17,6 +17,7 @@ "effect", "@effect/ai-anthropic", "@effect/ai-openai", + "@effect/ai-typesafe", "@effect/ai-openai-compat", "@effect/ai-openrouter", "@effect/atom-react", diff --git a/repos/effect/.changeset/pre.json b/repos/effect/.changeset/pre.json deleted file mode 100644 index d1deaa4cf1..0000000000 --- a/repos/effect/.changeset/pre.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mode": "pre", - "tag": "rc" -} diff --git a/repos/effect/.changeset/pre/add-adaptive-rate-limiter-store.md b/repos/effect/.changeset/pre/add-adaptive-rate-limiter-store.md deleted file mode 100644 index c191add811..0000000000 --- a/repos/effect/.changeset/pre/add-adaptive-rate-limiter-store.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add adaptive consume and feedback operations to the unstable persistent RateLimiterStore API, including in-memory and Redis-backed bounded cooldown, learning, learned pacing, and expiry behavior for 429 Retry-After feedback. diff --git a/repos/effect/.changeset/pre/add-atom-equality.md b/repos/effect/.changeset/pre/add-atom-equality.md deleted file mode 100644 index c2600bfa50..0000000000 --- a/repos/effect/.changeset/pre/add-atom-equality.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/reactivity Atom: add `withEquality` combinator for customizing how the registry detects value changes diff --git a/repos/effect/.changeset/pre/add-bigdecimal-sumall-multiplyall.md b/repos/effect/.changeset/pre/add-bigdecimal-sumall-multiplyall.md deleted file mode 100644 index 3d3a695633..0000000000 --- a/repos/effect/.changeset/pre/add-bigdecimal-sumall-multiplyall.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Added `BigDecimal.sumAll` and `BigDecimal.multiplyAll` for feature parity with `Number` and `BigInt`, closes #1880. diff --git a/repos/effect/.changeset/pre/add-chunk-schema.md b/repos/effect/.changeset/pre/add-chunk-schema.md deleted file mode 100644 index 890e0fcbfb..0000000000 --- a/repos/effect/.changeset/pre/add-chunk-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `Chunk` schema, closes #1585. diff --git a/repos/effect/.changeset/pre/add-command-hidden.md b/repos/effect/.changeset/pre/add-command-hidden.md deleted file mode 100644 index 34754c51b4..0000000000 --- a/repos/effect/.changeset/pre/add-command-hidden.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"effect": patch ---- - -Add `Command.withHidden` to hide subcommands from `--help` output, shell completions, and "did you mean?" suggestions, while keeping them fully invocable by exact name. - -Useful for experimental or internal subcommands that should be accepted but not advertised on the public CLI surface. - -```ts -import { Command } from "effect/unstable/cli" - -const experimental = Command.make("experimental").pipe( - Command.withHidden -) - -const root = Command.make("mycli").pipe( - Command.withSubcommands([experimental]) -) -``` diff --git a/repos/effect/.changeset/pre/add-config-nested.md b/repos/effect/.changeset/pre/add-config-nested.md deleted file mode 100644 index 22f53da810..0000000000 --- a/repos/effect/.changeset/pre/add-config-nested.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Config.nested` combinator to scope a config under a named prefix, closes #1437. diff --git a/repos/effect/.changeset/pre/add-deno-file-system.md b/repos/effect/.changeset/pre/add-deno-file-system.md deleted file mode 100644 index 6bd6a73dff..0000000000 --- a/repos/effect/.changeset/pre/add-deno-file-system.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a Deno-backed FileSystem layer. diff --git a/repos/effect/.changeset/pre/add-deno-http-client.md b/repos/effect/.changeset/pre/add-deno-http-client.md deleted file mode 100644 index 2bf5753f8c..0000000000 --- a/repos/effect/.changeset/pre/add-deno-http-client.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add `DenoHttpClient`, re-exporting `effect/unstable/http/FetchHttpClient` - -Deno's `fetch` is spec-compliant, so the core fetch-based `HttpClient` works on Deno unmodified. This module mirrors `BunHttpClient` so the platform packages expose a consistent surface. diff --git a/repos/effect/.changeset/pre/add-deno-multipart.md b/repos/effect/.changeset/pre/add-deno-multipart.md deleted file mode 100644 index b396bfd8c4..0000000000 --- a/repos/effect/.changeset/pre/add-deno-multipart.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add web-standard multipart request parsing helpers for Deno. diff --git a/repos/effect/.changeset/pre/add-deno-socket-server.md b/repos/effect/.changeset/pre/add-deno-socket-server.md deleted file mode 100644 index d726624284..0000000000 --- a/repos/effect/.changeset/pre/add-deno-socket-server.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add native Deno TCP, Unix, and TLS socket server adapters. diff --git a/repos/effect/.changeset/pre/add-deno-socket.md b/repos/effect/.changeset/pre/add-deno-socket.md deleted file mode 100644 index da98c6486a..0000000000 --- a/repos/effect/.changeset/pre/add-deno-socket.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add native Deno TCP, Unix, and WebSocket integrations for Effect sockets. diff --git a/repos/effect/.changeset/pre/add-flag-hidden.md b/repos/effect/.changeset/pre/add-flag-hidden.md deleted file mode 100644 index 1c31e6cd8b..0000000000 --- a/repos/effect/.changeset/pre/add-flag-hidden.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"effect": patch ---- - -Add `Flag.withHidden` (and `Param.withHidden`) to hide flags from `--help` output and shell completions while keeping them fully parseable on the command line. - -Useful for experimental, internal, or deprecated flags that should be accepted but not advertised, e.g. `--experimental-foo`, debug toggles, or escape hatches that are not yet committed to the public CLI surface. - -```ts -import { Flag } from "effect/unstable/cli" - -const experimental = Flag.boolean("experimental-foo").pipe( - Flag.withHidden -) -``` diff --git a/repos/effect/.changeset/pre/add-from-string-schemas.md b/repos/effect/.changeset/pre/add-from-string-schemas.md deleted file mode 100644 index 5ee9f82fbc..0000000000 --- a/repos/effect/.changeset/pre/add-from-string-schemas.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `DateFromString`, `BigIntFromString`, `BigDecimalFromString`, `TimeZoneNamedFromString`, `TimeZoneFromString`, and `DateTimeZonedFromString` schemas, closes #1941. diff --git a/repos/effect/.changeset/pre/add-headers-remove-many.md b/repos/effect/.changeset/pre/add-headers-remove-many.md deleted file mode 100644 index 8d09bb5167..0000000000 --- a/repos/effect/.changeset/pre/add-headers-remove-many.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/http Headers: add `removeMany` combinator for removing multiple headers at once diff --git a/repos/effect/.changeset/pre/add-http-client-request-update-headers.md b/repos/effect/.changeset/pre/add-http-client-request-update-headers.md deleted file mode 100644 index 391894aa6a..0000000000 --- a/repos/effect/.changeset/pre/add-http-client-request-update-headers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/http HttpClientRequest: add `updateHeaders` and `removeHeader` combinators for transforming or removing request headers, closes #6271 diff --git a/repos/effect/.changeset/pre/add-http-client-tracer-header-filter.md b/repos/effect/.changeset/pre/add-http-client-tracer-header-filter.md deleted file mode 100644 index 44d8044679..0000000000 --- a/repos/effect/.changeset/pre/add-http-client-tracer-header-filter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a configurable filter for HTTP client request and response header span attributes. diff --git a/repos/effect/.changeset/pre/add-httpapi-with-headers.md b/repos/effect/.changeset/pre/add-httpapi-with-headers.md deleted file mode 100644 index e91cccacc5..0000000000 --- a/repos/effect/.changeset/pre/add-httpapi-with-headers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -httpapi: add typed response headers across handlers, generated clients (including `HttpApiTest`), streaming responses, and OpenAPI with `HttpApiSchema.WithHeaders`. Add `HttpApiSchema.encodeToWithHeaders` for folding response headers into domain types such as error classes. Explicit `content-type` and `content-length` values applied with `HttpServerResponse.setHeader` or `setHeaders` now override body-derived values. diff --git a/repos/effect/.changeset/pre/add-indexeddb-kvs-layer.md b/repos/effect/.changeset/pre/add-indexeddb-kvs-layer.md deleted file mode 100644 index bb6671bc7f..0000000000 --- a/repos/effect/.changeset/pre/add-indexeddb-kvs-layer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Adds an IndexedDB backed implementation of `KeyValueStore` as `BrowserKeyValueStore.layerIndexedDb`. This backend allows for non-blocking `KeyValueStore` operations, unlike the existing `Storage` api backed implementations. diff --git a/repos/effect/.changeset/pre/add-json-schema-draft-04.md b/repos/effect/.changeset/pre/add-json-schema-draft-04.md deleted file mode 100644 index 440b16628c..0000000000 --- a/repos/effect/.changeset/pre/add-json-schema-draft-04.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add support for converting JSON Schema documents to Draft-04, preserve literal `$ref` values, `$ref` sibling constraints, `not`, `readOnly`, and `writeOnly` in Draft-07 conversions, correct the Draft-07 meta-schema URI, and prevent OpenAPI component-key collisions during conversion. diff --git a/repos/effect/.changeset/pre/add-make-msgpack.md b/repos/effect/.changeset/pre/add-make-msgpack.md deleted file mode 100644 index 9abec1e9e9..0000000000 --- a/repos/effect/.changeset/pre/add-make-msgpack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `RpcSerialization.makeMsgPack` for creating MessagePack serialization with custom msgpackr options. On Cloudflare Workers with `allow_eval_during_startup` (default for `compatibility_date >= 2025-06-01`), pass `{ useRecords: false }` to prevent msgpackr's JIT code generation via `new Function()`, which is blocked during request handling. Also fixes silent error swallowing in the `msgPack` decode path — non-incomplete errors are now rethrown instead of returning `[]`. diff --git a/repos/effect/.changeset/pre/add-make-option.md b/repos/effect/.changeset/pre/add-make-option.md deleted file mode 100644 index 4984b63192..0000000000 --- a/repos/effect/.changeset/pre/add-make-option.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `SchemaParser.makeOption` and `Schema.makeOption` for constructing schema values as `Option`. diff --git a/repos/effect/.changeset/pre/add-mcp-2025-11-25-protocol.md b/repos/effect/.changeset/pre/add-mcp-2025-11-25-protocol.md deleted file mode 100644 index fbc935c0b9..0000000000 --- a/repos/effect/.changeset/pre/add-mcp-2025-11-25-protocol.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -MCP servers can now use the 2025-11-25 protocol, including sampling with tools and both form- and URL-based elicitation. - -Enable it by adding `McpProtocol.v2025_11_25` to the server's `protocols` option. diff --git a/repos/effect/.changeset/pre/add-mcp-icons.md b/repos/effect/.changeset/pre/add-mcp-icons.md deleted file mode 100644 index 8e2383a628..0000000000 --- a/repos/effect/.changeset/pre/add-mcp-icons.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -MCP servers can now provide icons for server information, resources, resource templates, prompts, and tools using `McpSchema.Icon`. - -Each icon can specify its source URI, MIME type, supported sizes, and light or dark theme. diff --git a/repos/effect/.changeset/pre/add-missing-tx-modules.md b/repos/effect/.changeset/pre/add-missing-tx-modules.md deleted file mode 100644 index aa7cc305cc..0000000000 --- a/repos/effect/.changeset/pre/add-missing-tx-modules.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Add transactional STM modules: TxDeferred, TxPriorityQueue, TxPubSub, TxReentrantLock, TxSubscriptionRef. - -Refactor transaction model: remove `Effect.atomic`/`Effect.atomicWith`. All Tx operations now return `Effect` requiring explicit `Effect.tx(...)` at boundaries. - -Expose `TxPubSub.acquireSubscriber`/`releaseSubscriber` for composable transaction boundaries. Fix `TxSubscriptionRef.changes` race condition ensuring current value is delivered first. - -Remove `TxRandom` module. diff --git a/repos/effect/.changeset/pre/add-newtype-module.md b/repos/effect/.changeset/pre/add-newtype-module.md deleted file mode 100644 index c407aeb54c..0000000000 --- a/repos/effect/.changeset/pre/add-newtype-module.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Newtype` module. diff --git a/repos/effect/.changeset/pre/add-otlp-manual-flush.md b/repos/effect/.changeset/pre/add-otlp-manual-flush.md deleted file mode 100644 index fbbd9b16aa..0000000000 --- a/repos/effect/.changeset/pre/add-otlp-manual-flush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add manual flushing to the OTLP exporters through a shared `Flusher` service exposed by each signal layer. The signal layer output types now include `Flusher`, and `OtlpExporter.make` requires it so custom exporters register unconditionally. diff --git a/repos/effect/.changeset/pre/add-platform-deno.md b/repos/effect/.changeset/pre/add-platform-deno.md deleted file mode 100644 index 9237eee680..0000000000 --- a/repos/effect/.changeset/pre/add-platform-deno.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add Deno platform integrations for paths, runtime execution, workers, and Web Storage. diff --git a/repos/effect/.changeset/pre/add-scalar-show-operation-id.md b/repos/effect/.changeset/pre/add-scalar-show-operation-id.md deleted file mode 100644 index 04d131e1db..0000000000 --- a/repos/effect/.changeset/pre/add-scalar-show-operation-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `showOperationId` to `HttpApiScalar.ScalarConfig`. diff --git a/repos/effect/.changeset/pre/add-schedule-tap.md b/repos/effect/.changeset/pre/add-schedule-tap.md deleted file mode 100644 index 0f4ccc3c5c..0000000000 --- a/repos/effect/.changeset/pre/add-schedule-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Added `Schedule.tap`, which allows observing full schedule metadata without altering schedule inputs or outputs. diff --git a/repos/effect/.changeset/pre/add-schema-annotate-encoded.md b/repos/effect/.changeset/pre/add-schema-annotate-encoded.md deleted file mode 100644 index 0e5b7e1152..0000000000 --- a/repos/effect/.changeset/pre/add-schema-annotate-encoded.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `annotateEncoded` function for annotating the encoded side of a schema. diff --git a/repos/effect/.changeset/pre/add-schema-array-ensure.md b/repos/effect/.changeset/pre/add-schema-array-ensure.md deleted file mode 100644 index 908baea884..0000000000 --- a/repos/effect/.changeset/pre/add-schema-array-ensure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.ArrayEnsure`. diff --git a/repos/effect/.changeset/pre/add-schema-bigdecimal.md b/repos/effect/.changeset/pre/add-schema-bigdecimal.md deleted file mode 100644 index 76d278fdb9..0000000000 --- a/repos/effect/.changeset/pre/add-schema-bigdecimal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `BigDecimal` schema with comparison checks (`isGreaterThanBigDecimal`, `isGreaterThanOrEqualToBigDecimal`, `isLessThanBigDecimal`, `isLessThanOrEqualToBigDecimal`, `isBetweenBigDecimal`). diff --git a/repos/effect/.changeset/pre/add-schema-datetime.md b/repos/effect/.changeset/pre/add-schema-datetime.md deleted file mode 100644 index c72f4608e3..0000000000 --- a/repos/effect/.changeset/pre/add-schema-datetime.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `DateTimeZoned`, `TimeZoneOffset`, `TimeZoneNamed`, and `TimeZone` schemas. diff --git a/repos/effect/.changeset/pre/add-schema-error-module.md b/repos/effect/.changeset/pre/add-schema-error-module.md deleted file mode 100644 index d28c2c29a7..0000000000 --- a/repos/effect/.changeset/pre/add-schema-error-module.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Expose `SchemaError` as a public module and re-export `Schema.isSchemaError`. - -This gives consumers a stable import path and guard for schema failures without -depending on the internal schema implementation, while preserving the existing -`Schema.SchemaError` surface. diff --git a/repos/effect/.changeset/pre/add-schema-option-from-optional-nullor.md b/repos/effect/.changeset/pre/add-schema-option-from-optional-nullor.md deleted file mode 100644 index d634eea40f..0000000000 --- a/repos/effect/.changeset/pre/add-schema-option-from-optional-nullor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `OptionFromOptionalNullOr` schema, closes #1707. diff --git a/repos/effect/.changeset/pre/add-schema-option-from-undefined-nullish.md b/repos/effect/.changeset/pre/add-schema-option-from-undefined-nullish.md deleted file mode 100644 index c2d569326c..0000000000 --- a/repos/effect/.changeset/pre/add-schema-option-from-undefined-nullish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `OptionFromUndefinedOr` and `OptionFromNullishOr` schemas. diff --git a/repos/effect/.changeset/pre/add-schema-string-encoding.md b/repos/effect/.changeset/pre/add-schema-string-encoding.md deleted file mode 100644 index 7910ae29f6..0000000000 --- a/repos/effect/.changeset/pre/add-schema-string-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `StringFromBase64`, `StringFromBase64Url`, `StringFromHex`, and `StringFromUriComponent` schemas for decoding encoded strings into UTF-8 strings, closes #1995. diff --git a/repos/effect/.changeset/pre/add-schema-tagged-union-discriminants.md b/repos/effect/.changeset/pre/add-schema-tagged-union-discriminants.md deleted file mode 100644 index 5930ed631f..0000000000 --- a/repos/effect/.changeset/pre/add-schema-tagged-union-discriminants.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Add a `discriminants` tuple to schemas augmented with `Schema.toTaggedUnion` and reject duplicate discriminant -property keys. diff --git a/repos/effect/.changeset/pre/add-semaphore-take-if-available.md b/repos/effect/.changeset/pre/add-semaphore-take-if-available.md deleted file mode 100644 index 8cb5182cc2..0000000000 --- a/repos/effect/.changeset/pre/add-semaphore-take-if-available.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": minor ---- - -Add `Semaphore.takeIfAvailable` for non-blocking manual permit acquisition. diff --git a/repos/effect/.changeset/pre/add-sql-pglite.md b/repos/effect/.changeset/pre/add-sql-pglite.md deleted file mode 100644 index e0eb64eaef..0000000000 --- a/repos/effect/.changeset/pre/add-sql-pglite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pglite": minor ---- - -Add `@effect/sql-pglite` package, wrapping `@electric-sql/pglite` with the Effect SQL client (Postgres dialect, Effect-managed transactions via savepoints, listen/notify, dumpDataDir/refreshArrayTypes, and a Migrator). diff --git a/repos/effect/.changeset/pre/add-standard-jsdoc-rule.md b/repos/effect/.changeset/pre/add-standard-jsdoc-rule.md deleted file mode 100644 index 317fe356f4..0000000000 --- a/repos/effect/.changeset/pre/add-standard-jsdoc-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/oxc": patch ---- - -Add an `effect/standard-jsdoc` oxlint rule for staged public API JSDoc standardization. diff --git a/repos/effect/.changeset/pre/add-stream-broadcastn.md b/repos/effect/.changeset/pre/add-stream-broadcastn.md deleted file mode 100644 index ca1fa612f2..0000000000 --- a/repos/effect/.changeset/pre/add-stream-broadcastn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add Stream.broadcastN for fixed-size stream broadcasts. diff --git a/repos/effect/.changeset/pre/add-tool-set-needs-approval.md b/repos/effect/.changeset/pre/add-tool-set-needs-approval.md deleted file mode 100644 index 959b435799..0000000000 --- a/repos/effect/.changeset/pre/add-tool-set-needs-approval.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Tool.setNeedsApproval` for replacing the approval policy of an existing tool. diff --git a/repos/effect/.changeset/pre/add-unstable-encoding-export.md b/repos/effect/.changeset/pre/add-unstable-encoding-export.md deleted file mode 100644 index 99b107249c..0000000000 --- a/repos/effect/.changeset/pre/add-unstable-encoding-export.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `unstable/encoding` subpath export. diff --git a/repos/effect/.changeset/pre/add-update-service-scoped.md b/repos/effect/.changeset/pre/add-update-service-scoped.md deleted file mode 100644 index 95f49061ac..0000000000 --- a/repos/effect/.changeset/pre/add-update-service-scoped.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.updateServiceScoped` for updating a context service until the current scope closes, with customizable reset behavior. diff --git a/repos/effect/.changeset/pre/add-values-unprepared.md b/repos/effect/.changeset/pre/add-values-unprepared.md deleted file mode 100644 index d0ac10cb97..0000000000 --- a/repos/effect/.changeset/pre/add-values-unprepared.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch -"@effect/sql-clickhouse": patch -"@effect/sql-d1": patch -"@effect/sql-libsql": patch -"@effect/sql-mssql": patch -"@effect/sql-mysql2": patch -"@effect/sql-pg": patch -"@effect/sql-pglite": patch -"@effect/sql-sqlite-bun": patch -"@effect/sql-sqlite-do": patch -"@effect/sql-sqlite-node": patch -"@effect/sql-sqlite-react-native": patch -"@effect/sql-sqlite-wasm": patch ---- - -Add `Statement.valuesUnprepared` for returning unprepared SQL statement rows as arrays. diff --git a/repos/effect/.changeset/pre/afraid-cobras-like.md b/repos/effect/.changeset/pre/afraid-cobras-like.md deleted file mode 100644 index c471ffa354..0000000000 --- a/repos/effect/.changeset/pre/afraid-cobras-like.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -default to endOnDone: false in NodeStdio diff --git a/repos/effect/.changeset/pre/ai-approved-tool-results.md b/repos/effect/.changeset/pre/ai-approved-tool-results.md deleted file mode 100644 index 203cf0284f..0000000000 --- a/repos/effect/.changeset/pre/ai-approved-tool-results.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Retain completed tool approval results in non-streaming responses so Chat records them and does not replay approved tools on later turns. diff --git a/repos/effect/.changeset/pre/ai-openai-config-field-leak.md b/repos/effect/.changeset/pre/ai-openai-config-field-leak.md deleted file mode 100644 index 58df093aa7..0000000000 --- a/repos/effect/.changeset/pre/ai-openai-config-field-leak.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Fix `OpenAiLanguageModel` leaking library-only config fields (`fileIdPrefixes`, `strictJsonSchema`) into request body, causing OpenAI 400 errors. diff --git a/repos/effect/.changeset/pre/ai-openai-file-nullable-fields.md b/repos/effect/.changeset/pre/ai-openai-file-nullable-fields.md deleted file mode 100644 index b59718e49c..0000000000 --- a/repos/effect/.changeset/pre/ai-openai-file-nullable-fields.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Fix `OpenAIFile` schema decode failure on responses where `expires_at` and `status_details` are returned as literal `null`. The OpenAI files endpoint returns `null` (not omitted) for these fields when no expiration / status detail applies (e.g. uploads with `purpose: "user_data"`), but the upstream OpenAPI spec marks them only as optional. Codegen patches widen both fields to allow `null`, which now decodes cleanly via `OpenAiClient.createFile`, `retrieveFile`, `listFiles`, and any other endpoint returning the `OpenAIFile` shape. diff --git a/repos/effect/.changeset/pre/align-type-id-paths.md b/repos/effect/.changeset/pre/align-type-id-paths.md deleted file mode 100644 index 932d2d2c02..0000000000 --- a/repos/effect/.changeset/pre/align-type-id-paths.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/opentelemetry": patch ---- - -Align runtime type IDs with their module paths. Effect markers now omit legacy grouping prefixes and the `unstable` path segment, while OpenTelemetry spans use the `OtelTracer` module path. Custom implementations that copy these marker strings must adopt the corrected IDs. diff --git a/repos/effect/.changeset/pre/all-union-record-channels.md b/repos/effect/.changeset/pre/all-union-record-channels.md deleted file mode 100644 index ddafd3ab60..0000000000 --- a/repos/effect/.changeset/pre/all-union-record-channels.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.all` to retain errors and required services from every branch of a union of record inputs. diff --git a/repos/effect/.changeset/pre/anthropic-4-6-structured-output.md b/repos/effect/.changeset/pre/anthropic-4-6-structured-output.md deleted file mode 100644 index 3900a93f6e..0000000000 --- a/repos/effect/.changeset/pre/anthropic-4-6-structured-output.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Mark the Claude 4-6 generation as supporting native structured output in `getModelCapabilities`. `claude-opus-4-6` and `claude-sonnet-4-6` support Anthropic's constrained-decoding structured output (verified against the live API), but were classified as `supportsStructuredOutput: false`, so `generateObject` fell back to a forced JSON tool instead of requesting `output_config.format` (`json_schema`). `claude-opus-4-7` / `claude-opus-4-8` are classified the same way for when the generated `Model` enum picks them up. diff --git a/repos/effect/.changeset/pre/anthropic-claude-5-capabilities.md b/repos/effect/.changeset/pre/anthropic-claude-5-capabilities.md deleted file mode 100644 index 785acd5357..0000000000 --- a/repos/effect/.changeset/pre/anthropic-claude-5-capabilities.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Default new Anthropic models to modern capabilities while preserving the limits of legacy Claude models. - -Unknown models now default to native structured outputs and 128K output tokens, so future model releases do not require capability-table updates. Use the new `structuredOutputs` model config option to override capability detection when needed. diff --git a/repos/effect/.changeset/pre/anthropic-image-strings.md b/repos/effect/.changeset/pre/anthropic-image-strings.md deleted file mode 100644 index 7cb1c1467c..0000000000 --- a/repos/effect/.changeset/pre/anthropic-image-strings.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Preserve base64 image strings in Anthropic requests. diff --git a/repos/effect/.changeset/pre/anthropic-open-model-enum.md b/repos/effect/.changeset/pre/anthropic-open-model-enum.md deleted file mode 100644 index b5ab0393d6..0000000000 --- a/repos/effect/.changeset/pre/anthropic-open-model-enum.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Widen the Anthropic `Model` schema to accept both known model identifiers as well -as any string to allow for newer models diff --git a/repos/effect/.changeset/pre/anthropic-stale-max-output-tokens.md b/repos/effect/.changeset/pre/anthropic-stale-max-output-tokens.md deleted file mode 100644 index f7e83ff6e0..0000000000 --- a/repos/effect/.changeset/pre/anthropic-stale-max-output-tokens.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Correct the maximum output tokens for Claude Opus 4.6, 4.7, 4.8 and Sonnet 4.6. - -These models were grouped with the 4.5 family at 64000 output tokens, half of the 128000 the API actually allows, so requests defaulted to a cap far below the model's real limit. The 4.5 models keep 64000, which is correct for them. diff --git a/repos/effect/.changeset/pre/anthropic-strict-json-schema.md b/repos/effect/.changeset/pre/anthropic-strict-json-schema.md deleted file mode 100644 index 9a1caae535..0000000000 --- a/repos/effect/.changeset/pre/anthropic-strict-json-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Exclude the provider-only `strictJsonSchema` option from Anthropic Messages request bodies while preserving its control over tool strictness. diff --git a/repos/effect/.changeset/pre/anthropic-structured-output-fallback.md b/repos/effect/.changeset/pre/anthropic-structured-output-fallback.md deleted file mode 100644 index 3ed06de122..0000000000 --- a/repos/effect/.changeset/pre/anthropic-structured-output-fallback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix non-native structured output generation by always requesting the response tool and excluding accompanying prose when decoding its JSON payload. diff --git a/repos/effect/.changeset/pre/apply-httpapi-endpoint-client-transform.md b/repos/effect/.changeset/pre/apply-httpapi-endpoint-client-transform.md deleted file mode 100644 index ce9d74cbab..0000000000 --- a/repos/effect/.changeset/pre/apply-httpapi-endpoint-client-transform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Apply `transformClient` when building an individual HttpApi endpoint client, preserving the supplied client's error and service channels. diff --git a/repos/effect/.changeset/pre/arbitrary-index-constraints.md b/repos/effect/.changeset/pre/arbitrary-index-constraints.md deleted file mode 100644 index 9cff5c7daf..0000000000 --- a/repos/effect/.changeset/pre/arbitrary-index-constraints.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `Arbitrary.schema` to respect applicable index signatures when generating and shrinking object properties, including fixed fields in `Schema.StructWithRest` and overlapping records. - -Combine compatible string, number, and bigint constraints during generation so cases such as a `String` field constrained by a `NonEmptyString` record remain productive at size zero. Other intersections are validated and may exhaust the discard budget. diff --git a/repos/effect/.changeset/pre/array-ensure-array-elements.md b/repos/effect/.changeset/pre/array-ensure-array-elements.md deleted file mode 100644 index 762cc5afa9..0000000000 --- a/repos/effect/.changeset/pre/array-ensure-array-elements.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.ArrayEnsure` to preserve array-valued element branches and outer-array encoding cardinality. diff --git a/repos/effect/.changeset/pre/asyncresult-exhaustive.md b/repos/effect/.changeset/pre/asyncresult-exhaustive.md deleted file mode 100644 index d448fd604d..0000000000 --- a/repos/effect/.changeset/pre/asyncresult-exhaustive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add an exhaustive finalizer to the AsyncResult builder. diff --git a/repos/effect/.changeset/pre/atom-rpc-query-requires.md b/repos/effect/.changeset/pre/atom-rpc-query-requires.md deleted file mode 100644 index 7169977a49..0000000000 --- a/repos/effect/.changeset/pre/atom-rpc-query-requires.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `AtomRpc.query` returning `never` for RPCs whose middleware declares service `requires`. The return-type conditional now infers all six `Rpc` type parameters, matching `mutation` and every utility in `Rpc`. diff --git a/repos/effect/.changeset/pre/atom-solid-idle-ttl.md b/repos/effect/.changeset/pre/atom-solid-idle-ttl.md deleted file mode 100644 index e7a9989e5e..0000000000 --- a/repos/effect/.changeset/pre/atom-solid-idle-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-solid": patch ---- - -Allow `RegistryProvider` to leave `defaultIdleTTL` undefined, matching the React binding and enabling immediate cleanup of unused atoms unless a TTL is explicitly configured. diff --git a/repos/effect/.changeset/pre/atom-stream-error-type.md b/repos/effect/.changeset/pre/atom-stream-error-type.md deleted file mode 100644 index a0809cae78..0000000000 --- a/repos/effect/.changeset/pre/atom-stream-error-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose `NoSuchElementError` in the error type of stream-based `Atom.make` overloads. diff --git a/repos/effect/.changeset/pre/atomic-optional-rc-map.md b/repos/effect/.changeset/pre/atomic-optional-rc-map.md deleted file mode 100644 index 2e6c8caa8e..0000000000 --- a/repos/effect/.changeset/pre/atomic-optional-rc-map.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Add `RcMap.getOption` and `LayerMap.contextEffectOption` for atomically retaining -entries only when they are already cached. diff --git a/repos/effect/.changeset/pre/authentication-error-description.md b/repos/effect/.changeset/pre/authentication-error-description.md deleted file mode 100644 index f5745934d5..0000000000 --- a/repos/effect/.changeset/pre/authentication-error-description.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch ---- - -Add an optional `description` to `AiError.AuthenticationError`, rendered after the kind-based suggestion, and pass the provider's own error text through it on HTTP 401 and 403, so authentication failures report what actually went wrong instead of only a category. diff --git a/repos/effect/.changeset/pre/autocomplete-jk-filter.md b/repos/effect/.changeset/pre/autocomplete-jk-filter.md deleted file mode 100644 index 00c1ec6689..0000000000 --- a/repos/effect/.changeset/pre/autocomplete-jk-filter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Prompt.autoComplete` swallowing `j` and `k` while typing a filter query. diff --git a/repos/effect/.changeset/pre/batch-persistence-expiration-cleanup.md b/repos/effect/.changeset/pre/batch-persistence-expiration-cleanup.md deleted file mode 100644 index 3409edfb68..0000000000 --- a/repos/effect/.changeset/pre/batch-persistence-expiration-cleanup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Run shared-table SQL persistence expiration cleanup in indexed, bounded background batches. diff --git a/repos/effect/.changeset/pre/beige-goats-sin.md b/repos/effect/.changeset/pre/beige-goats-sin.md deleted file mode 100644 index 22cef1f7fb..0000000000 --- a/repos/effect/.changeset/pre/beige-goats-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Fix dynamic tools defined with a raw JSON schema sending empty parameter schema to OpenRouter diff --git a/repos/effect/.changeset/pre/beige-paths-sort.md b/repos/effect/.changeset/pre/beige-paths-sort.md deleted file mode 100644 index 0cfad27ccb..0000000000 --- a/repos/effect/.changeset/pre/beige-paths-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -move ChildProcess apis into spawner service diff --git a/repos/effect/.changeset/pre/better-apples-nail.md b/repos/effect/.changeset/pre/better-apples-nail.md deleted file mode 100644 index 338b25b16e..0000000000 --- a/repos/effect/.changeset/pre/better-apples-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Effect.repeat now uses effect return value when using options diff --git a/repos/effect/.changeset/pre/better-rocks-arrive.md b/repos/effect/.changeset/pre/better-rocks-arrive.md deleted file mode 100644 index c703f179d4..0000000000 --- a/repos/effect/.changeset/pre/better-rocks-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -add back openai reasoning types diff --git a/repos/effect/.changeset/pre/better-shrimps-follow.md b/repos/effect/.changeset/pre/better-shrimps-follow.md deleted file mode 100644 index 95cb596409..0000000000 --- a/repos/effect/.changeset/pre/better-shrimps-follow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -prefix opentelemetry modules to avoid collisions diff --git a/repos/effect/.changeset/pre/big-masks-care.md b/repos/effect/.changeset/pre/big-masks-care.md deleted file mode 100644 index 8b3a17254e..0000000000 --- a/repos/effect/.changeset/pre/big-masks-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/doctest": patch ---- - -Support `.mdx` files diff --git a/repos/effect/.changeset/pre/big-pans-look.md b/repos/effect/.changeset/pre/big-pans-look.md deleted file mode 100644 index 0e4fd594ff..0000000000 --- a/repos/effect/.changeset/pre/big-pans-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Rpc.custom diff --git a/repos/effect/.changeset/pre/blue-dingos-greet.md b/repos/effect/.changeset/pre/blue-dingos-greet.md deleted file mode 100644 index 01e918bc6c..0000000000 --- a/repos/effect/.changeset/pre/blue-dingos-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.filter` support for synchronous `Filter.Filter` overloads and correctly handle non-effect `Result` return values at runtime. diff --git a/repos/effect/.changeset/pre/blue-ligers-cheat.md b/repos/effect/.changeset/pre/blue-ligers-cheat.md deleted file mode 100644 index 37a66d750c..0000000000 --- a/repos/effect/.changeset/pre/blue-ligers-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update unstable schema variant helpers to use array-based arguments for `FieldOnly`, `FieldExcept`, and `Union`, aligning `VariantSchema` and `Model` with other v4 API shapes. diff --git a/repos/effect/.changeset/pre/blue-onions-smile.md b/repos/effect/.changeset/pre/blue-onions-smile.md deleted file mode 100644 index 2ef4e625a1..0000000000 --- a/repos/effect/.changeset/pre/blue-onions-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Preserve streamed OpenAI compat tool call ids and names across fragmented chat completion chunks. diff --git a/repos/effect/.changeset/pre/blue-pigs-push.md b/repos/effect/.changeset/pre/blue-pigs-push.md deleted file mode 100644 index 7cb74e13f9..0000000000 --- a/repos/effect/.changeset/pre/blue-pigs-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Channel.mergeAll` to propagate outer failures promptly and interrupt active inner channels. diff --git a/repos/effect/.changeset/pre/blue-ravens-type.md b/repos/effect/.changeset/pre/blue-ravens-type.md deleted file mode 100644 index 0919ee483c..0000000000 --- a/repos/effect/.changeset/pre/blue-ravens-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure streamed tool results are emitted before the finish part so chat history includes tool outputs before stream termination. diff --git a/repos/effect/.changeset/pre/blue-trams-kiss.md b/repos/effect/.changeset/pre/blue-trams-kiss.md deleted file mode 100644 index 27fa6f36bb..0000000000 --- a/repos/effect/.changeset/pre/blue-trams-kiss.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix consolePretty ignoring explicit colors option in non-TTY environments. - -When colors is explicitly set to true, prettyLoggerTty was still gating it with processStdoutIsTTY check, making it impossible to enable colors in non-TTY environments like Vite dev server. diff --git a/repos/effect/.changeset/pre/bold-chairs-yawn.md b/repos/effect/.changeset/pre/bold-chairs-yawn.md deleted file mode 100644 index 1b14a10a82..0000000000 --- a/repos/effect/.changeset/pre/bold-chairs-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add HttpApiTest module diff --git a/repos/effect/.changeset/pre/bold-planets-shout.md b/repos/effect/.changeset/pre/bold-planets-shout.md deleted file mode 100644 index 001dbd01fb..0000000000 --- a/repos/effect/.changeset/pre/bold-planets-shout.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"effect": patch ---- - -add support for merging external events into `Prompt.custom` render loops via an optional `events` dequeue and `receive` handler. - -The prompt races user input against events from the dequeue, allowing background events to trigger re-renders without waiting for a keypress: - -```ts -const eventQueue = yield * Queue.make() - -const prompt = Prompt.custom( - { count: 0 }, - Queue.asDequeue(eventQueue), // <-- provide the event queue as a dequeue to the prompt - { - render: (state) => Effect.succeed(`Count: ${state.count}`), - process: (input, state) => - Effect.succeed( - Match.value(input).pipe( - // handle user input - Match.tag("Input", () => Action.Submit({ value: state.count })), - // handle external events from the queue - Match.tag("Event", (input) => Action.NextFrame({ state: { count: state.count + input.value } })), - Match.exhaustive - ) - ), - clear: () => Effect.succeed("") - } -) -``` diff --git a/repos/effect/.changeset/pre/brave-keys-commit.md b/repos/effect/.changeset/pre/brave-keys-commit.md deleted file mode 100644 index f9358594f9..0000000000 --- a/repos/effect/.changeset/pre/brave-keys-commit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix IndexedDB-backed key-value writes to wait for transaction commit before reporting success. diff --git a/repos/effect/.changeset/pre/brave-rings-update.md b/repos/effect/.changeset/pre/brave-rings-update.md deleted file mode 100644 index 21931bcb5e..0000000000 --- a/repos/effect/.changeset/pre/brave-rings-update.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update existing `HashRing` nodes when adding a value with the same primary key. diff --git a/repos/effect/.changeset/pre/breezy-meals-see.md b/repos/effect/.changeset/pre/breezy-meals-see.md deleted file mode 100644 index c4a8359c58..0000000000 --- a/repos/effect/.changeset/pre/breezy-meals-see.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -support tag unions in Effect.catchTag/Reason diff --git a/repos/effect/.changeset/pre/bright-bugs-bow.md b/repos/effect/.changeset/pre/bright-bugs-bow.md deleted file mode 100644 index ca6cb86e96..0000000000 --- a/repos/effect/.changeset/pre/bright-bugs-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Run request resolver batch fibers with request services by using `Effect.runForkWith`, so resolver delay effects and `runAll` execution see the request service map. diff --git a/repos/effect/.changeset/pre/bright-canyons-clean.md b/repos/effect/.changeset/pre/bright-canyons-clean.md deleted file mode 100644 index 3bddee36c5..0000000000 --- a/repos/effect/.changeset/pre/bright-canyons-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add support for deferred responses in rpc diff --git a/repos/effect/.changeset/pre/bright-clocks-count.md b/repos/effect/.changeset/pre/bright-clocks-count.md deleted file mode 100644 index 8f3df8ee01..0000000000 --- a/repos/effect/.changeset/pre/bright-clocks-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `DateTime.toEpochSeconds` and `DateTime.fromEpochSeconds` for converting date-time values to and from Unix epoch seconds. diff --git a/repos/effect/.changeset/pre/bright-docs-teach.md b/repos/effect/.changeset/pre/bright-docs-teach.md deleted file mode 100644 index 3e26d3391e..0000000000 --- a/repos/effect/.changeset/pre/bright-docs-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add SQL, HttpApi testing, and CLI schema examples to the published AI documentation. diff --git a/repos/effect/.changeset/pre/bright-dogs-fail.md b/repos/effect/.changeset/pre/bright-dogs-fail.md deleted file mode 100644 index f29db94184..0000000000 --- a/repos/effect/.changeset/pre/bright-dogs-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Model.BooleanSqlite`, a model field schema that uses `0 | 1` encoding for database variants and plain `boolean` encoding for JSON variants. diff --git a/repos/effect/.changeset/pre/bright-flags-stand.md b/repos/effect/.changeset/pre/bright-flags-stand.md deleted file mode 100644 index 131bcf4340..0000000000 --- a/repos/effect/.changeset/pre/bright-flags-stand.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix CLI parsing so command-local flags can override globals without breaking global flags before subcommands. diff --git a/repos/effect/.changeset/pre/bright-graphs-calculate.md b/repos/effect/.changeset/pre/bright-graphs-calculate.md deleted file mode 100644 index 86935a9957..0000000000 --- a/repos/effect/.changeset/pre/bright-graphs-calculate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject graph shortest-path calculations that overflow or underflow the finite number range. diff --git a/repos/effect/.changeset/pre/bright-graphs-trace.md b/repos/effect/.changeset/pre/bright-graphs-trace.md deleted file mode 100644 index 1f08e6a7f5..0000000000 --- a/repos/effect/.changeset/pre/bright-graphs-trace.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include traversed edge indexes in graph shortest-path results. diff --git a/repos/effect/.changeset/pre/bright-journals-commit.md b/repos/effect/.changeset/pre/bright-journals-commit.md deleted file mode 100644 index 5c312a18f5..0000000000 --- a/repos/effect/.changeset/pre/bright-journals-commit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Commit SQL event journal entries only after their write callback succeeds. diff --git a/repos/effect/.changeset/pre/bright-laws-teach.md b/repos/effect/.changeset/pre/bright-laws-teach.md deleted file mode 100644 index 12df791c78..0000000000 --- a/repos/effect/.changeset/pre/bright-laws-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Config.Success` type utility, closes #1783. diff --git a/repos/effect/.changeset/pre/bright-lemons-dance.md b/repos/effect/.changeset/pre/bright-lemons-dance.md deleted file mode 100644 index 0bae9332d9..0000000000 --- a/repos/effect/.changeset/pre/bright-lemons-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow `Effect.acquireRelease` release finalizers to depend on the surrounding environment. diff --git a/repos/effect/.changeset/pre/bright-mimes-leave.md b/repos/effect/.changeset/pre/bright-mimes-leave.md deleted file mode 100644 index 09ca9859ab..0000000000 --- a/repos/effect/.changeset/pre/bright-mimes-leave.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-node": patch -"effect": patch ---- - -Remove the `mime` runtime dependency. The new `effect/unstable/http/Mime` module provides top-level lookup functions -backed by a vendored standard MIME registry. diff --git a/repos/effect/.changeset/pre/bright-planes-smash.md b/repos/effect/.changeset/pre/bright-planes-smash.md deleted file mode 100644 index edf82b8b25..0000000000 --- a/repos/effect/.changeset/pre/bright-planes-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `responseText` to `AiError.StructuredOutputError` and populate it from `LanguageModel.generateObject` so failed structured output decodes include the full LLM text. diff --git a/repos/effect/.changeset/pre/bright-rats-attend.md b/repos/effect/.changeset/pre/bright-rats-attend.md deleted file mode 100644 index 071f37890d..0000000000 --- a/repos/effect/.changeset/pre/bright-rats-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -make fiber keepAlive setInterval evaluation lazy diff --git a/repos/effect/.changeset/pre/bright-sockets-connect.md b/repos/effect/.changeset/pre/bright-sockets-connect.md deleted file mode 100644 index ca176f3810..0000000000 --- a/repos/effect/.changeset/pre/bright-sockets-connect.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/platform-bun": patch -"@effect/platform-node": patch -"effect": patch ---- - -Allow sockets to use browser, Bun, and Node WebSocket implementations without consumer casts. Platform constructors -now support typed opening-handshake headers where available. diff --git a/repos/effect/.changeset/pre/bright-tags-recognize.md b/repos/effect/.changeset/pre/bright-tags-recognize.md deleted file mode 100644 index edead0f682..0000000000 --- a/repos/effect/.changeset/pre/bright-tags-recognize.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Recognize tagged Config and RPC errors across duplicated `effect` package copies. diff --git a/repos/effect/.changeset/pre/bright-toes-rush.md b/repos/effect/.changeset/pre/bright-toes-rush.md deleted file mode 100644 index 34bda7c435..0000000000 --- a/repos/effect/.changeset/pre/bright-toes-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Persist MCP HTTP session and protocol headers after initialize so follow-up JSON-RPC requests include `MCP-Protocol-Version`. diff --git a/repos/effect/.changeset/pre/brown-glasses-thank.md b/repos/effect/.changeset/pre/brown-glasses-thank.md deleted file mode 100644 index 0445b02de0..0000000000 --- a/repos/effect/.changeset/pre/brown-glasses-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Handle BigInt values safely and consistently across JSON diagnostics and logger formats. diff --git a/repos/effect/.changeset/pre/brown-peas-enter.md b/repos/effect/.changeset/pre/brown-peas-enter.md deleted file mode 100644 index 2779fa9efc..0000000000 --- a/repos/effect/.changeset/pre/brown-peas-enter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP tool handler defects now return a stable internal error without exposing defect details. diff --git a/repos/effect/.changeset/pre/browser-crypto-missing-subtle.md b/repos/effect/.changeset/pre/browser-crypto-missing-subtle.md deleted file mode 100644 index 0fedd8a2b0..0000000000 --- a/repos/effect/.changeset/pre/browser-crypto-missing-subtle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Return a typed `PlatformError` from `BrowserCrypto` digest operations when `crypto.subtle` is unavailable, instead of failing with a defect. diff --git a/repos/effect/.changeset/pre/browser-indexeddb-binary-key-existence.md b/repos/effect/.changeset/pre/browser-indexeddb-binary-key-existence.md deleted file mode 100644 index c5462e706a..0000000000 --- a/repos/effect/.changeset/pre/browser-indexeddb-binary-key-existence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix `BrowserKeyValueStore.layerIndexedDb` to report keys containing `Uint8Array` values from `has`. diff --git a/repos/effect/.changeset/pre/browser-runtime-custom-teardown.md b/repos/effect/.changeset/pre/browser-runtime-custom-teardown.md deleted file mode 100644 index 443d692f7d..0000000000 --- a/repos/effect/.changeset/pre/browser-runtime-custom-teardown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Invoke custom `BrowserRuntime.runMain` teardown callbacks when the main effect completes, and remove the `pagehide` listener once the main fiber finishes. diff --git a/repos/effect/.changeset/pre/bumpy-boxes-teach.md b/repos/effect/.changeset/pre/bumpy-boxes-teach.md deleted file mode 100644 index 31205424ef..0000000000 --- a/repos/effect/.changeset/pre/bumpy-boxes-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename Atom's `Context` type to `AtomContext` diff --git a/repos/effect/.changeset/pre/bun-http-server-unix-address.md b/repos/effect/.changeset/pre/bun-http-server-unix-address.md deleted file mode 100644 index b5abe92de5..0000000000 --- a/repos/effect/.changeset/pre/bun-http-server-unix-address.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Report Unix socket addresses from Bun HTTP servers. diff --git a/repos/effect/.changeset/pre/bun-stream-preserve-failure.md b/repos/effect/.changeset/pre/bun-stream-preserve-failure.md deleted file mode 100644 index c5756f3ee9..0000000000 --- a/repos/effect/.changeset/pre/bun-stream-preserve-failure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Preserve mapped errors from Bun readable streams. diff --git a/repos/effect/.changeset/pre/bun-websocket-outgoing-compression.md b/repos/effect/.changeset/pre/bun-websocket-outgoing-compression.md deleted file mode 100644 index 9be7aeaf63..0000000000 --- a/repos/effect/.changeset/pre/bun-websocket-outgoing-compression.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Compress outgoing Bun WebSocket messages when per-message deflate is configured and negotiated. Messages -smaller than 1 KiB are left uncompressed, matching the default threshold used by Node's `ws` server. -The threshold is configurable via the new `websocket.compressionThreshold` server option. diff --git a/repos/effect/.changeset/pre/busy-lions-sneeze.md b/repos/effect/.changeset/pre/busy-lions-sneeze.md deleted file mode 100644 index de2167c4a8..0000000000 --- a/repos/effect/.changeset/pre/busy-lions-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Fix HTTP Referer header name in the `OpenRouterClient` diff --git a/repos/effect/.changeset/pre/busy-maps-attend.md b/repos/effect/.changeset/pre/busy-maps-attend.md deleted file mode 100644 index 395698754a..0000000000 --- a/repos/effect/.changeset/pre/busy-maps-attend.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-bun": patch -"@effect/platform-node": patch ---- - -add rows to Terminal diff --git a/repos/effect/.changeset/pre/cache-invalidate-when-replacement.md b/repos/effect/.changeset/pre/cache-invalidate-when-replacement.md deleted file mode 100644 index b8b861c282..0000000000 --- a/repos/effect/.changeset/pre/cache-invalidate-when-replacement.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cache.invalidateWhen` and `ScopedCache.invalidateWhen` deleting a replacement entry while waiting for an earlier lookup. diff --git a/repos/effect/.changeset/pre/cache-refresh-cancellation-ownership.md b/repos/effect/.changeset/pre/cache-refresh-cancellation-ownership.md deleted file mode 100644 index 7ad98b91c6..0000000000 --- a/repos/effect/.changeset/pre/cache-refresh-cancellation-ownership.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix interruption of `Cache.refresh` for a missing key removing a newer value written by `Cache.set`. diff --git a/repos/effect/.changeset/pre/cache-refresh-capacity.md b/repos/effect/.changeset/pre/cache-refresh-capacity.md deleted file mode 100644 index d03ac1d50d..0000000000 --- a/repos/effect/.changeset/pre/cache-refresh-capacity.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cache.refresh` and `ScopedCache.refresh` exceeding capacity when an existing key is evicted while its refresh is in progress. Publishing the refreshed entry now evicts older entries as needed, releasing their resources in `ScopedCache`. diff --git a/repos/effect/.changeset/pre/cache-refresh-zero-ttl-ownership.md b/repos/effect/.changeset/pre/cache-refresh-zero-ttl-ownership.md deleted file mode 100644 index 06f9ee7f25..0000000000 --- a/repos/effect/.changeset/pre/cache-refresh-zero-ttl-ownership.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cache.refresh` for an initially missing key deleting a newer cached value when the refresh completes with zero time to live. diff --git a/repos/effect/.changeset/pre/cache-synchronous-interruption.md b/repos/effect/.changeset/pre/cache-synchronous-interruption.md deleted file mode 100644 index 23fde1c27d..0000000000 --- a/repos/effect/.changeset/pre/cache-synchronous-interruption.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent `Cache` from retaining synchronously interrupted lookups. diff --git a/repos/effect/.changeset/pre/cached-with-ttl-callback.md b/repos/effect/.changeset/pre/cached-with-ttl-callback.md deleted file mode 100644 index af20cb464f..0000000000 --- a/repos/effect/.changeset/pre/cached-with-ttl-callback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow `Effect.cachedWithTTL` to compute the TTL from each completed `Exit`, so successes and failures can use different cache durations. diff --git a/repos/effect/.changeset/pre/calm-atoms-relax.md b/repos/effect/.changeset/pre/calm-atoms-relax.md deleted file mode 100644 index a7464d5434..0000000000 --- a/repos/effect/.changeset/pre/calm-atoms-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-react": patch ---- - -Relax react peer dependency range diff --git a/repos/effect/.changeset/pre/calm-bash-completions.md b/repos/effect/.changeset/pre/calm-bash-completions.md deleted file mode 100644 index ca77256cef..0000000000 --- a/repos/effect/.changeset/pre/calm-bash-completions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Select Bash completions for the active positional argument. diff --git a/repos/effect/.changeset/pre/calm-buckets-own.md b/repos/effect/.changeset/pre/calm-buckets-own.md deleted file mode 100644 index 0610fbdbab..0000000000 --- a/repos/effect/.changeset/pre/calm-buckets-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -improve openai socket errors diff --git a/repos/effect/.changeset/pre/calm-buses-smile.md b/repos/effect/.changeset/pre/calm-buses-smile.md deleted file mode 100644 index e7fe600cea..0000000000 --- a/repos/effect/.changeset/pre/calm-buses-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix IndexedDB query range, ordering, streaming, and transaction semantics. diff --git a/repos/effect/.changeset/pre/calm-carrots-march.md b/repos/effect/.changeset/pre/calm-carrots-march.md deleted file mode 100644 index 75703506f1..0000000000 --- a/repos/effect/.changeset/pre/calm-carrots-march.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Allow unstable HttpApi middleware to declare multiple error schemas with arrays. - -Middleware errors now follow endpoint error behavior for response status resolution, client decoding, and generated API schemas. diff --git a/repos/effect/.changeset/pre/calm-cars-rest.md b/repos/effect/.changeset/pre/calm-cars-rest.md deleted file mode 100644 index a61b82e771..0000000000 --- a/repos/effect/.changeset/pre/calm-cars-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add grouped subcommand support to `Command.withSubcommands`, including help output sections for named groups while keeping ungrouped commands under `SUBCOMMANDS`. diff --git a/repos/effect/.changeset/pre/calm-coins-smile.md b/repos/effect/.changeset/pre/calm-coins-smile.md deleted file mode 100644 index 9737718554..0000000000 --- a/repos/effect/.changeset/pre/calm-coins-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Generate even and odd safe integers in Crypto random APIs. diff --git a/repos/effect/.changeset/pre/calm-contexts-rest.md b/repos/effect/.changeset/pre/calm-contexts-rest.md deleted file mode 100644 index 1dede3ad93..0000000000 --- a/repos/effect/.changeset/pre/calm-contexts-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve the `Context.mapUnsafe` accessor when code is compiled with loose object spread transforms. diff --git a/repos/effect/.changeset/pre/calm-dates-view.md b/repos/effect/.changeset/pre/calm-dates-view.md deleted file mode 100644 index b9e963edb8..0000000000 --- a/repos/effect/.changeset/pre/calm-dates-view.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Equal.equals` and `Hash.hash` to handle invalid dates and `DataView` values without throwing. diff --git a/repos/effect/.changeset/pre/calm-dragons-command.md b/repos/effect/.changeset/pre/calm-dragons-command.md deleted file mode 100644 index d98e1edd6e..0000000000 --- a/repos/effect/.changeset/pre/calm-dragons-command.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Correct the runtime tag spelling for `CliError.UnknownSubcommand`. diff --git a/repos/effect/.changeset/pre/calm-ducks-fail.md b/repos/effect/.changeset/pre/calm-ducks-fail.md deleted file mode 100644 index 7c801d3218..0000000000 --- a/repos/effect/.changeset/pre/calm-ducks-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Wake `NodeStream.pipeThroughDuplex` readers when the upstream fails so the original error is propagated instead of hanging. diff --git a/repos/effect/.changeset/pre/calm-graphs-span.md b/repos/effect/.changeset/pre/calm-graphs-span.md deleted file mode 100644 index c53042d543..0000000000 --- a/repos/effect/.changeset/pre/calm-graphs-span.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add deterministic, index-preserving `Graph.minimumSpanningForest`. diff --git a/repos/effect/.changeset/pre/calm-graphs-travel.md b/repos/effect/.changeset/pre/calm-graphs-travel.md deleted file mode 100644 index 85618505b7..0000000000 --- a/repos/effect/.changeset/pre/calm-graphs-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix graph index exhaustion, A* path consistency, snapshot validation, Mermaid line endings, and topological initials. diff --git a/repos/effect/.changeset/pre/calm-headers-hide.md b/repos/effect/.changeset/pre/calm-headers-hide.md deleted file mode 100644 index 0e3cff5d52..0000000000 --- a/repos/effect/.changeset/pre/calm-headers-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Respect custom HTTP header redaction when recording server span attributes. diff --git a/repos/effect/.changeset/pre/calm-heads-close.md b/repos/effect/.changeset/pre/calm-heads-close.md deleted file mode 100644 index c717945ada..0000000000 --- a/repos/effect/.changeset/pre/calm-heads-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Ensure aborted `HEAD` responses do not block `NodeHttpServer` disposal. diff --git a/repos/effect/.changeset/pre/calm-hounds-smile.md b/repos/effect/.changeset/pre/calm-hounds-smile.md deleted file mode 100644 index 8f7a1640a7..0000000000 --- a/repos/effect/.changeset/pre/calm-hounds-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-clickhouse": patch ---- - -Parameterize ClickHouse query IDs when cancelling queries and inserts. diff --git a/repos/effect/.changeset/pre/calm-keys-repeat.md b/repos/effect/.changeset/pre/calm-keys-repeat.md deleted file mode 100644 index 82f669b0eb..0000000000 --- a/repos/effect/.changeset/pre/calm-keys-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix SQL-backed Persistence `getMany` to preserve duplicate key positions. diff --git a/repos/effect/.changeset/pre/calm-masks-count.md b/repos/effect/.changeset/pre/calm-masks-count.md deleted file mode 100644 index b70fc9ae85..0000000000 --- a/repos/effect/.changeset/pre/calm-masks-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.DateFromMillis` and `SchemaTransformation.dateFromMillis` for decoding millisecond timestamps into `Date` values. diff --git a/repos/effect/.changeset/pre/calm-pages-hide.md b/repos/effect/.changeset/pre/calm-pages-hide.md deleted file mode 100644 index 22600772a2..0000000000 --- a/repos/effect/.changeset/pre/calm-pages-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Keep BrowserRuntime main programs running when navigation does not unload the document, while interrupting on non-bfcache pagehide events. diff --git a/repos/effect/.changeset/pre/calm-pandas-retry.md b/repos/effect/.changeset/pre/calm-pandas-retry.md deleted file mode 100644 index b2372c6bd4..0000000000 --- a/repos/effect/.changeset/pre/calm-pandas-retry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add bounded 429 retries and custom response header names to `HttpClient.withRateLimiter`. diff --git a/repos/effect/.changeset/pre/calm-panthers-nail.md b/repos/effect/.changeset/pre/calm-panthers-nail.md deleted file mode 100644 index 1f855e558e..0000000000 --- a/repos/effect/.changeset/pre/calm-panthers-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `disableFatalDefects` to `RpcServer.layerHttp`, `RpcServer.toHttpEffect`, and `RpcServer.toHttpEffectWebsocket` option types to match existing runtime support. diff --git a/repos/effect/.changeset/pre/calm-pears-smile.md b/repos/effect/.changeset/pre/calm-pears-smile.md deleted file mode 100644 index a83413eeae..0000000000 --- a/repos/effect/.changeset/pre/calm-pears-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Exclude disabled choices from multi-select prompt selection and submission. diff --git a/repos/effect/.changeset/pre/calm-queues-await.md b/repos/effect/.changeset/pre/calm-queues-await.md deleted file mode 100644 index 18ac80170c..0000000000 --- a/repos/effect/.changeset/pre/calm-queues-await.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Queue.await` failing with `Cause.Done` when registered before the queue ends. diff --git a/repos/effect/.changeset/pre/calm-ravens-reflect.md b/repos/effect/.changeset/pre/calm-ravens-reflect.md deleted file mode 100644 index ba31e47c2b..0000000000 --- a/repos/effect/.changeset/pre/calm-ravens-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve `__proto__` group and endpoint identifiers in HTTP APIs, generated clients, and URL builders. diff --git a/repos/effect/.changeset/pre/calm-readers-transact.md b/repos/effect/.changeset/pre/calm-readers-transact.md deleted file mode 100644 index 011717a3c4..0000000000 --- a/repos/effect/.changeset/pre/calm-readers-transact.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/sql-sqlite-bun": patch -"@effect/sql-sqlite-node": patch ---- - -Allow read-only SQLite clients to use `withTransaction` when `PRAGMA query_only` is enabled. Writable clients continue -to reserve the writer lock when a transaction starts. diff --git a/repos/effect/.changeset/pre/calm-redis-clear.md b/repos/effect/.changeset/pre/calm-redis-clear.md deleted file mode 100644 index a707eadd98..0000000000 --- a/repos/effect/.changeset/pre/calm-redis-clear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure clearing an empty Redis-backed persistence store succeeds. diff --git a/repos/effect/.changeset/pre/calm-results-align.md b/repos/effect/.changeset/pre/calm-results-align.md deleted file mode 100644 index 896040c70d..0000000000 --- a/repos/effect/.changeset/pre/calm-results-align.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep ordered SQL resolver results aligned when batched request encoding fails. diff --git a/repos/effect/.changeset/pre/calm-schemas-encode.md b/repos/effect/.changeset/pre/calm-schemas-encode.md deleted file mode 100644 index 6bec17848a..0000000000 --- a/repos/effect/.changeset/pre/calm-schemas-encode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the encoded output type of `TestSchema.Encoding.encodeUnknownEffect`. diff --git a/repos/effect/.changeset/pre/calm-seas-smile.md b/repos/effect/.changeset/pre/calm-seas-smile.md deleted file mode 100644 index bde18ad7fb..0000000000 --- a/repos/effect/.changeset/pre/calm-seas-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Align workflow tags with RPCs by changing `Workflow.make` to accept the tag as its first argument, exposing workflow tags as `_tag`, and supporting `class MyWorkflow extends Workflow.make(...) {}`. diff --git a/repos/effect/.changeset/pre/calm-servers-share.md b/repos/effect/.changeset/pre/calm-servers-share.md deleted file mode 100644 index 504a98ce55..0000000000 --- a/repos/effect/.changeset/pre/calm-servers-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update `McpServer.layerHttp` to return `405` for unsupported HTTP methods, reject unsupported `MCP-Protocol-Version` headers with `400`, and return an empty `202` for accepted notifications and responses. diff --git a/repos/effect/.changeset/pre/calm-services-rest.md b/repos/effect/.changeset/pre/calm-services-rest.md deleted file mode 100644 index 400dade634..0000000000 --- a/repos/effect/.changeset/pre/calm-services-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Stop capturing definition-location stack frames in `Context.Service`. diff --git a/repos/effect/.changeset/pre/calm-squids-hug.md b/repos/effect/.changeset/pre/calm-squids-hug.md deleted file mode 100644 index 3ac3c61ab9..0000000000 --- a/repos/effect/.changeset/pre/calm-squids-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Context.Key type, used a base for Context.Service and Context.Reference diff --git a/repos/effect/.changeset/pre/calm-tools-parse.md b/repos/effect/.changeset/pre/calm-tools-parse.md deleted file mode 100644 index 779bde415f..0000000000 --- a/repos/effect/.changeset/pre/calm-tools-parse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add lightweight INI, YAML, and TOML parsers under `effect/unstable/encoding` and remove their runtime dependencies. diff --git a/repos/effect/.changeset/pre/calm-tools-remember.md b/repos/effect/.changeset/pre/calm-tools-remember.md deleted file mode 100644 index 3fdf6410e0..0000000000 --- a/repos/effect/.changeset/pre/calm-tools-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve equals signs in inline CLI option values after the first separator. diff --git a/repos/effect/.changeset/pre/calm-tracers-sample.md b/repos/effect/.changeset/pre/calm-tracers-sample.md deleted file mode 100644 index 3bbb0ad84c..0000000000 --- a/repos/effect/.changeset/pre/calm-tracers-sample.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Preserve sampling decisions when adapting generic Effect external spans to OpenTelemetry. diff --git a/repos/effect/.changeset/pre/calm-trees-generate.md b/repos/effect/.changeset/pre/calm-trees-generate.md deleted file mode 100644 index 8179f0ba79..0000000000 --- a/repos/effect/.changeset/pre/calm-trees-generate.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -"@effect/vitest": patch -"effect": patch ---- - -Add the experimental Schema-first `effect/unstable/arbitrary/Arbitrary` module for native generation without -fast-check. `Arbitrary.schema` derives an opaque arbitrary from the decoded Schema `Type`, `Arbitrary.sampleEffect` -provides interruptible sampling with typed exhaustion, and `Arbitrary.checkEffect` returns structured property results. -The initial implementation supports bounded discards, shrinking, replay, and recursive and mutually recursive Schemas. -`SampleError` and `Exhausted` include the effective seed so discarded runs remain reproducible even when the caller did -not provide one. `Arbitrary.isArbitrary` identifies values through the module's nominal protocol. Numeric constraints -retain `NaN` when it is accepted by their supported `Order.Number` bounds. Union derivation validates `oneOf` -exclusivity and isolates lazy cross-member shrinking from unrelated random generation. Object derivation keeps -optional-property selection constructive when candidate fields have different recursive costs. -Struct, Record, JSON-object, and record-shaped `Arbitrary.all` outputs periodically use a null prototype as an edge -case, preserving that prototype throughout shrinking and replay without perturbing structural PRNG choices. The change -adds 0.01–0.03 KB gzip to representative Arbitrary fixtures and leaves production-only bundle sentinels unchanged. - -Add `Arbitrary.map`, `Arbitrary.flatMap`, `Arbitrary.filter`, `Arbitrary.filterMap`, and `Arbitrary.all` for composing -derived Arbitraries without exposing a second catalog of primitive constructors. Filtering remains bounded and -promotes valid shrink descendants through rejected nodes. `maxShrinks` bounds every inspected shrink candidate, -including candidates rejected before property evaluation, while retaining the best shrunk input found when the -budget is exhausted. `flatMap` provides deterministic dependent generation, source-first shrinking, post-source PRNG -checkpoints, and one shared residual recursion budget. `all` combines tuples, iterables, and records with a shared -budget, randomized internal generation order, stable output shape, and independent member shrinking. Arbitrary values -implement `Pipeable` for composition with data-last combinators. - -Add the experimental Schema `arbitraryConstraint` and `toCodecArbitrary` annotations and their -`Schema.Annotations.ToArbitrary` types. Declarations can provide a Schema Link optimized for generation, while filters -can contribute native semantic constraints. The callback receives decoded type parameters and normalized constraints. -The compiler owns efficient representations for common built-ins, including JSON, RegExp, URL, Date, byte arrays, -ReadonlyMap, and ReadonlySet. Effect-specific HashMap, HashSet, Chunk, Graph, BigDecimal, and date-time declarations keep -local generation Links, while declarations with productive canonical codecs require no arbitrary-specific annotation. -`Schema.isUniqueKey` provides key-based Map uniqueness for explicit array representations. - -The same ownership policy applies to formatter and equivalence derivation: implementations for common declarations -live in their compiler, while domain-specific and dynamically constructed declarations retain local annotations. -Declarations whose intrinsic `Equal` implementation already matches their Schema equivalence need no annotation or -compiler special case. This keeps unused common callbacks out of production Schema bundles. - -Against the previous layout, `schema-toArbitrary` decreases from 36.68 KB to 33.24 KB gzip and -`arbitrary-combinators` decreases from 37.16 KB to 33.70 KB. `schema-toFormatter` increases from 18.92 KB to 19.49 KB -and `schema-toEquivalence` increases from 19.05 KB to 19.39 KB because callers that explicitly derive these capabilities -now retain the common declaration handlers. Generic production fixtures remain unchanged; an equivalence-specific -production fixture using common declarations decreases from 20.75 KB to 20.48 KB, while declarations whose intrinsic -equality is sufficient decrease from 23.42 KB to 23.34 KB. An Arbitrary-specific production fixture using common -declarations decreases from 20.35 KB to 19.61 KB, while one using the locally annotated BigDecimal and date-time -declarations increases from 18.34 KB to 23.01 KB. -The complete 31-scenario native Arbitrary comparison reports no statistically classified runtime regression; the five -moved BigDecimal and date-time scenarios remain within measurement noise. - -Add `SchemaGetter.forbiddenEncoding`, a reusable getter for the encode side of decode-only Schema transformations. - -Remove the fast-check bridge from the `effect` package, including `Schema.toArbitrary` and -`effect/testing/FastCheck`. Replace the legacy `Schema.Annotations.ToArbitrary` callback contract with the native -Schema-first types. The `effect` package no longer depends on fast-check. - -Migrate `TestSchema.Asserts.verifyLosslessTransformation` and `TestSchema.Asserts.arbitrary().verifyGeneration` to the -native runner. Both methods now accept native check options directly, bound unsuccessful generation, and include the -shrunk input and replay token in property failures. - -Use the Arbitrary runner for all `@effect/vitest` property tests. Property inputs may combine Schemas and Arbitraries, -and are composed directly with `Arbitrary.all`; check options are available through `arbitrary`. Raw fast-check -arbitraries and the `fastCheck` options object are no longer supported. As with the previous fast-check adapter, thrown -exceptions, defects, and typed failures from a property are shrinkable falsifications; Effect interruption remains an -interruption. - -Optimize constructive regular-expression generation by caching feasible lengths on the compiled pattern, computing -sequence-suffix feasibility once, and precomputing character-class metadata. Seeded generation, shrinking, and replay -remain unchanged. - -Optimize `BigDecimal.Order` and `BigDecimal.Equivalence` with a shared hybrid comparator. Ordinary scale differences -use cached, bounded coefficient alignment, while large differences are compared without materializing their decimal -zeroes. `BigDecimal.make` now rejects scales that are not safe integers. - -Before its removal, the materialized fast-check bridge fixture -`schema-toArbitrary-materialized-fast-check.ts` measured 79.00 KB minified and gzipped. - -Representative runtime measurements against corresponding hand-written fast-check 4.9.0 arbitraries are shown below. -Values are median latency on Node 24.12.0 and Apple M3; lower is better. Both implementations validate the -same output domains, although their generation distributions are not identical. Native speedup is fast-check latency -divided by Native latency, so higher is better. - -| Scenario | fast-check | Native | Native speedup | -| ----------------------------------- | ---------: | ------: | -------------: | -| 32 recursive samples | 150 µs | 103 µs | 1.45x | -| 128 optional Struct samples | 244 µs | 86.0 µs | 2.84x | -| 128 constrained strings | 742 µs | 49.7 µs | 14.86x | -| RegExp derivation and first sample | 13.4 ms | 30.8 µs | 429.02x | -| 64 RegExp strings | 595 µs | 919 µs | 0.64x | -| RegExp failure and shrinking | 168 µs | 88.2 µs | 1.91x | -| 128 bounded numbers | 68.9 µs | 21.8 µs | 3.18x | -| 128 `Uint8Array` samples | 98.3 µs | 74.4 µs | 1.32x | -| 128 `BigDecimal` samples | 66.6 µs | 56.3 µs | 1.18x | -| 128 `DateTime.Utc` samples | 71.2 µs | 50.5 µs | 1.42x | -| 128 named time zones | 52.2 µs | 27.9 µs | 1.85x | -| 128 time zones | 63.7 µs | 33.8 µs | 1.89x | -| 128 zoned date-times | 130 µs | 112 µs | 1.16x | -| 32 samples through Schema filter | 65.9 µs | 49.4 µs | 1.33x | -| 32 unique arrays | 156 µs | 132 µs | 1.18x | -| 128 literal samples | 40.0 µs | 3.70 µs | 10.78x | -| 128 mapped samples | 59.0 µs | 14.1 µs | 4.21x | -| 128 samples through passing filter | 58.9 µs | 13.9 µs | 4.23x | -| 32 samples through selective filter | 66.1 µs | 42.9 µs | 1.54x | -| 128 `filterMap` samples | 75.7 µs | 31.5 µs | 2.40x | -| Filtered failure and shrinking | 12.7 µs | 7.71 µs | 1.66x | -| 128 `all` tuple samples | 43.5 µs | 18.5 µs | 2.35x | -| 128 `all` record samples | 81.0 µs | 30.4 µs | 2.66x | -| 128 dependent `flatMap` samples | 125 µs | 67.2 µs | 1.86x | -| `flatMap` failure and shrinking | 20.1 µs | 6.71 µs | 2.99x | -| Replay `flatMap` shrink path | 14.3 µs | 6.57 µs | 2.17x | -| Passing property, 100 runs | 42.3 µs | 27.1 µs | 1.56x | -| `TestSchema`, 100 generations | 44.5 µs | 35.9 µs | 1.24x | -| First failure plus one shrink | 8.77 µs | 1.30 µs | 6.75x | -| Replay recorded failure | 6.35 µs | 1.19 µs | 5.36x | - -Cold recursive derivation is not included because the native fixture constructs and compiles a Schema, while the -fast-check fixture constructs a hand-written arbitrary; it is not a like-for-like warm-generator comparison. - -Add a guide for the native module and a migration guide from the fast-check bridge published in `effect@4.0.0-rc.109`. diff --git a/repos/effect/.changeset/pre/calm-tuples-align.md b/repos/effect/.changeset/pre/calm-tuples-align.md deleted file mode 100644 index b62d5bdc4b..0000000000 --- a/repos/effect/.changeset/pre/calm-tuples-align.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix arbitrary generation for tuples with multiple optional elements. diff --git a/repos/effect/.changeset/pre/calm-tuples-pick.md b/repos/effect/.changeset/pre/calm-tuples-pick.md deleted file mode 100644 index 0422f5814a..0000000000 --- a/repos/effect/.changeset/pre/calm-tuples-pick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Tuple.pick` return types to preserve the requested index order and duplicate indices. diff --git a/repos/effect/.changeset/pre/calm-wolves-reduce.md b/repos/effect/.changeset/pre/calm-wolves-reduce.md deleted file mode 100644 index 1b25564566..0000000000 --- a/repos/effect/.changeset/pre/calm-wolves-reduce.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Sink.reduceWhileArray` applying its reducer more than once per input array. diff --git a/repos/effect/.changeset/pre/cancel-tedious-requests.md b/repos/effect/.changeset/pre/cancel-tedious-requests.md deleted file mode 100644 index 9b9ce459e9..0000000000 --- a/repos/effect/.changeset/pre/cancel-tedious-requests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Cancel in-flight Tedious requests when their Effects are interrupted. diff --git a/repos/effect/.changeset/pre/canonical-number-schemas.md b/repos/effect/.changeset/pre/canonical-number-schemas.md deleted file mode 100644 index 96fceebf42..0000000000 --- a/repos/effect/.changeset/pre/canonical-number-schemas.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch -"@effect/openapi-generator": patch ---- - -Add `Schema.Natural` for non-negative safe integers and use canonical `Schema.Int`, `Schema.Finite`, and `Schema.Natural` schemas for numeric domain values across Effect, AI protocols, and OpenAPI patches. - -Update the date, date-time, file, time-zone, cluster, event-log, persistence, socket, SQL, and DevTools schemas to reject invalid non-finite or non-integer values where appropriate. Correct the decoded schema of `Schema.NumberFromString`, and allow `Schema.DurationFromMillis` and `Schema.DurationFromNanos` to represent negative durations. diff --git a/repos/effect/.changeset/pre/cap-rpc-streaming-buffers.md b/repos/effect/.changeset/pre/cap-rpc-streaming-buffers.md deleted file mode 100644 index 4f67ff43b2..0000000000 --- a/repos/effect/.changeset/pre/cap-rpc-streaming-buffers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Cap incomplete RPC frames buffered by the NDJSON and MessagePack streaming decoders, and close socket transports when the limit is exceeded. diff --git a/repos/effect/.changeset/pre/catch-stream-channel-defects.md b/repos/effect/.changeset/pre/catch-stream-channel-defects.md deleted file mode 100644 index 6757799f40..0000000000 --- a/repos/effect/.changeset/pre/catch-stream-channel-defects.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Stream.catchDefect` and `Channel.catchDefect` for recovering from defects without catching typed failures or interruptions. diff --git a/repos/effect/.changeset/pre/channel-rundone-completion.md b/repos/effect/.changeset/pre/channel-rundone-completion.md deleted file mode 100644 index c0a74a290a..0000000000 --- a/repos/effect/.changeset/pre/channel-rundone-completion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Channel.runDone`; use `Channel.runDrain` to consume all output and return the completion value. diff --git a/repos/effect/.changeset/pre/chatty-poets-type.md b/repos/effect/.changeset/pre/chatty-poets-type.md deleted file mode 100644 index 623273cfbf..0000000000 --- a/repos/effect/.changeset/pre/chatty-poets-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -remove Effect.Yieldable diff --git a/repos/effect/.changeset/pre/child-process-astral-escape-arguments.md b/repos/effect/.changeset/pre/child-process-astral-escape-arguments.md deleted file mode 100644 index f68fd00493..0000000000 --- a/repos/effect/.changeset/pre/child-process-astral-escape-arguments.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve astral Unicode escapes and following arguments in `ChildProcess.make` and `ChildProcess.prefix` template literals. diff --git a/repos/effect/.changeset/pre/childprocess-process-group-wait.md b/repos/effect/.changeset/pre/childprocess-process-group-wait.md deleted file mode 100644 index 0a6f55a4e0..0000000000 --- a/repos/effect/.changeset/pre/childprocess-process-group-wait.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"effect": patch ---- - -Wait for Node child process groups to exit during scoped release and `kill`. - -After signalling a process group, both operations now wait for its leader and descendants. Without `forceKillAfter`, the wait is limited to one second and never escalates. With `forceKillAfter`, the group receives `SIGKILL` at the deadline, followed by a final wait of up to one second. Native timers keep escalation working under a `TestClock`, and cleanup no longer depends on stdio closing. - -`exitCode` and `isRunning` remain tied to the leader's exit, and a leader that already exited successfully still leaves its group untouched. Process group checks count zombies, so cleanup may wait for the full bound under a non-reaping PID 1. diff --git a/repos/effect/.changeset/pre/chilled-mice-wash.md b/repos/effect/.changeset/pre/chilled-mice-wash.md deleted file mode 100644 index 08e529a7e9..0000000000 --- a/repos/effect/.changeset/pre/chilled-mice-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpServerResponse.fromClientResponse` for directly converting client responses into server responses. diff --git a/repos/effect/.changeset/pre/chilly-pumas-rule.md b/repos/effect/.changeset/pre/chilly-pumas-rule.md deleted file mode 100644 index b53d3514c5..0000000000 --- a/repos/effect/.changeset/pre/chilly-pumas-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `Data.Class`, `Data.TaggedClass`, and `Cause.YieldableError` pipeable. diff --git a/repos/effect/.changeset/pre/chubby-buckets-feel.md b/repos/effect/.changeset/pre/chubby-buckets-feel.md deleted file mode 100644 index bf1cc12f94..0000000000 --- a/repos/effect/.changeset/pre/chubby-buckets-feel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix issues with metro bundler diff --git a/repos/effect/.changeset/pre/chubby-parents-flow.md b/repos/effect/.changeset/pre/chubby-parents-flow.md deleted file mode 100644 index 8c87cad403..0000000000 --- a/repos/effect/.changeset/pre/chubby-parents-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow undefined for VariantSchema.Overridable input diff --git a/repos/effect/.changeset/pre/chubby-planets-fall.md b/repos/effect/.changeset/pre/chubby-planets-fall.md deleted file mode 100644 index 15c60d5925..0000000000 --- a/repos/effect/.changeset/pre/chubby-planets-fall.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Remove duplicate `ToolApprovalResponsePartOptions` from Anthropic package diff --git a/repos/effect/.changeset/pre/chunk-slice-concatenation.md b/repos/effect/.changeset/pre/chunk-slice-concatenation.md deleted file mode 100644 index 593f5c2a66..0000000000 --- a/repos/effect/.changeset/pre/chunk-slice-concatenation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Chunk` concatenation to preserve sliced elements. diff --git a/repos/effect/.changeset/pre/clean-balloons-tan.md b/repos/effect/.changeset/pre/clean-balloons-tan.md deleted file mode 100644 index 1c4ff47182..0000000000 --- a/repos/effect/.changeset/pre/clean-balloons-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Proxy function arity from `Effect.fn` APIs so wrapped functions preserve the original `length` value. diff --git a/repos/effect/.changeset/pre/clean-bulldogs-care.md b/repos/effect/.changeset/pre/clean-bulldogs-care.md deleted file mode 100644 index 48f430f9f6..0000000000 --- a/repos/effect/.changeset/pre/clean-bulldogs-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Use `pg_notify` in `PgClient.notify` so channel and payload are sent through parameters instead of a `NOTIFY` statement string. diff --git a/repos/effect/.changeset/pre/clean-cats-document.md b/repos/effect/.changeset/pre/clean-cats-document.md deleted file mode 100644 index b2d26821d8..0000000000 --- a/repos/effect/.changeset/pre/clean-cats-document.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/docgen": major ---- - -Migrate `@effect/docgen` into the Effect monorepo and update it to Effect 4 while retaining existing behavior. diff --git a/repos/effect/.changeset/pre/clean-config-names.md b/repos/effect/.changeset/pre/clean-config-names.md deleted file mode 100644 index 0128f06601..0000000000 --- a/repos/effect/.changeset/pre/clean-config-names.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Rename the built-in `Config` constructors to PascalCase and rename `Config.mapOrFail` to `Config.mapEffect`. `Config.Array` and `Config.Record` now construct configs directly, with overloads for pathless options or a path followed by options, while their specialized schemas and the other built-in schemas are kept internal. - -This is a breaking naming cleanup for the Effect 4 release candidate. It makes casing consistently identify typed config constructors, aligns effectful mapping with the rest of the library, and prevents implementation schemas from expanding the public `Config` interface. diff --git a/repos/effect/.changeset/pre/clean-dryers-sneeze.md b/repos/effect/.changeset/pre/clean-dryers-sneeze.md deleted file mode 100644 index 96316e62f8..0000000000 --- a/repos/effect/.changeset/pre/clean-dryers-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Random.nextBoolean` for generating random boolean values. diff --git a/repos/effect/.changeset/pre/clean-formatters-agree.md b/repos/effect/.changeset/pre/clean-formatters-agree.md deleted file mode 100644 index 2fea77679a..0000000000 --- a/repos/effect/.changeset/pre/clean-formatters-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prioritize redacted representations in formatters and normalize text logger levels to uppercase. diff --git a/repos/effect/.changeset/pre/clean-geese-work.md b/repos/effect/.changeset/pre/clean-geese-work.md deleted file mode 100644 index e91346b785..0000000000 --- a/repos/effect/.changeset/pre/clean-geese-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Inspectable.stringifyCircular` and fix `Formatter.formatJson` so shared object references are preserved while only circular references are omitted. diff --git a/repos/effect/.changeset/pre/clean-goats-wave.md b/repos/effect/.changeset/pre/clean-goats-wave.md deleted file mode 100644 index 944fd84305..0000000000 --- a/repos/effect/.changeset/pre/clean-goats-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Ctrl-U` line clearing support to editable CLI prompts. diff --git a/repos/effect/.changeset/pre/clean-lions-cancel.md b/repos/effect/.changeset/pre/clean-lions-cancel.md deleted file mode 100644 index f725e3d199..0000000000 --- a/repos/effect/.changeset/pre/clean-lions-cancel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -RPC servers now suppress responses after a client cancels an in-flight request. diff --git a/repos/effect/.changeset/pre/clean-needles-shake.md b/repos/effect/.changeset/pre/clean-needles-shake.md deleted file mode 100644 index 17abeea6a6..0000000000 --- a/repos/effect/.changeset/pre/clean-needles-shake.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"effect": patch ---- - -Fix module-level side effects that defeated bundler tree-shaking. - -Bare top-level statements cannot be `#__PURE__`-annotated by the build, so -bundlers must retain them and everything they reference, even in bundles that -never use the code: - -- `Option`: the standalone `Object.defineProperty(SomeProto, "valueOrUndefined", ...)` - statement anchored the whole `Option` proto chain into every bundle. It is - now folded into the `SomeProto` initializer. -- `Headers`: same pattern with `Object.defineProperties(Proto, ...)`, folded - into the initializer. -- `Logger`: module-level `process.stdout.isTTY` property reads (potential - getters, never droppable) moved inside `consolePretty`. -- `Utils`: when `internalCall` was unused, its dropped binding left behind a - retained initializer tail (`standard`/`forced` probe with computed property - reads). The selection is now wrapped in a single pure-annotated call. - -A minimal `Effect.succeed(123).pipe(Effect.runFork)` bundle shrinks by ~1.3% -gzipped; bundles that don't use `Option` or `Headers` no longer pay for them. diff --git a/repos/effect/.changeset/pre/clean-tires-guess.md b/repos/effect/.changeset/pre/clean-tires-guess.md deleted file mode 100644 index 29463e91de..0000000000 --- a/repos/effect/.changeset/pre/clean-tires-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -clean up sql-pg constructors and layers diff --git a/repos/effect/.changeset/pre/clean-yaks-parse.md b/repos/effect/.changeset/pre/clean-yaks-parse.md deleted file mode 100644 index baf2d838cb..0000000000 --- a/repos/effect/.changeset/pre/clean-yaks-parse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Use Effect's YAML parser for OpenAPI patch files and remove the direct `yaml` dependency. diff --git a/repos/effect/.changeset/pre/cleanup-before-use-callbacks.md b/repos/effect/.changeset/pre/cleanup-before-use-callbacks.md deleted file mode 100644 index 414cb54e64..0000000000 --- a/repos/effect/.changeset/pre/cleanup-before-use-callbacks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `Effect.acquireUseRelease` releases an acquired resource and `Effect.useSpan` ends its span when the use callback throws before returning an effect. The thrown exception remains a defect, but no longer skips cleanup. diff --git a/repos/effect/.changeset/pre/clear-graphs-reduce.md b/repos/effect/.changeset/pre/clear-graphs-reduce.md deleted file mode 100644 index 0341ecbe6f..0000000000 --- a/repos/effect/.changeset/pre/clear-graphs-reduce.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add index-preserving transitive reduction for directed acyclic graphs. diff --git a/repos/effect/.changeset/pre/clear-hairs-pump.md b/repos/effect/.changeset/pre/clear-hairs-pump.md deleted file mode 100644 index ac42927cd3..0000000000 --- a/repos/effect/.changeset/pre/clear-hairs-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix activity retry policy diff --git a/repos/effect/.changeset/pre/clear-spies-boil.md b/repos/effect/.changeset/pre/clear-spies-boil.md deleted file mode 100644 index 44318f6f5d..0000000000 --- a/repos/effect/.changeset/pre/clear-spies-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -improve idb support for compound indexes diff --git a/repos/effect/.changeset/pre/clear-wings-relax.md b/repos/effect/.changeset/pre/clear-wings-relax.md deleted file mode 100644 index 526b3bc308..0000000000 --- a/repos/effect/.changeset/pre/clear-wings-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -use `new` instantiation for streams diff --git a/repos/effect/.changeset/pre/clever-maps-care.md b/repos/effect/.changeset/pre/clever-maps-care.md deleted file mode 100644 index 51de317462..0000000000 --- a/repos/effect/.changeset/pre/clever-maps-care.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/platform-deno": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"effect": patch ---- - -add platform literal to HttpPlatform diff --git a/repos/effect/.changeset/pre/cli-completion-command-aliases.md b/repos/effect/.changeset/pre/cli-completion-command-aliases.md deleted file mode 100644 index 00d824e98e..0000000000 --- a/repos/effect/.changeset/pre/cli-completion-command-aliases.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve flags and nested commands when completing a CLI subcommand through its alias. diff --git a/repos/effect/.changeset/pre/cli-completion-shared-flags.md b/repos/effect/.changeset/pre/cli-completion-shared-flags.md deleted file mode 100644 index 9e27bd98dd..0000000000 --- a/repos/effect/.changeset/pre/cli-completion-shared-flags.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include inherited shared flags in descendant CLI completions. diff --git a/repos/effect/.changeset/pre/cli-config-built-ins.md b/repos/effect/.changeset/pre/cli-config-built-ins.md deleted file mode 100644 index 6e0dbcd406..0000000000 --- a/repos/effect/.changeset/pre/cli-config-built-ins.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"effect": patch ---- - -Add a scoped `CliConfig` service for customizing the built-in global flags used by CLI command runners. - -For example, provide an explicit list that omits `GlobalFlag.LogLevel` to remove the built-in `--log-level` flag: - -```ts -import { Effect } from "effect" -import { CliConfig, Command, GlobalFlag } from "effect/unstable/cli" - -const program = Command.run(command, { version: "1.0.0" }).pipe( - Effect.provide( - CliConfig.layer({ - builtIns: [ - GlobalFlag.Help, - GlobalFlag.Version, - GlobalFlag.Completions - ] - }) - ) -) -``` diff --git a/repos/effect/.changeset/pre/cli-help-choices.md b/repos/effect/.changeset/pre/cli-help-choices.md deleted file mode 100644 index 379f75198b..0000000000 --- a/repos/effect/.changeset/pre/cli-help-choices.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Append concrete choice values to CLI flag help descriptions so generated help shows valid command-line inputs. diff --git a/repos/effect/.changeset/pre/cli-key-value-pair-first-separator.md b/repos/effect/.changeset/pre/cli-key-value-pair-first-separator.md deleted file mode 100644 index d8241fd5a5..0000000000 --- a/repos/effect/.changeset/pre/cli-key-value-pair-first-separator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow `=` in values parsed by `Primitive.keyValuePair`, `Flag.keyValuePair`, and `Param.keyValuePair` in `effect/unstable/cli`. diff --git a/repos/effect/.changeset/pre/cli-no-color-values.md b/repos/effect/.changeset/pre/cli-no-color-values.md deleted file mode 100644 index 084859b631..0000000000 --- a/repos/effect/.changeset/pre/cli-no-color-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Disable CLI formatter colors for every non-empty `NO_COLOR` value. diff --git a/repos/effect/.changeset/pre/cli-optional-alternative-flags.md b/repos/effect/.changeset/pre/cli-optional-alternative-flags.md deleted file mode 100644 index 743c995ff1..0000000000 --- a/repos/effect/.changeset/pre/cli-optional-alternative-flags.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow optional alternative CLI flags. diff --git a/repos/effect/.changeset/pre/cli-scalar-constructor-names.md b/repos/effect/.changeset/pre/cli-scalar-constructor-names.md deleted file mode 100644 index e5476bb8a2..0000000000 --- a/repos/effect/.changeset/pre/cli-scalar-constructor-names.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -"effect": patch ---- - -Rename CLI constructors to PascalCase, aligning scalar names with `Schema` and `Config`. This is a breaking change; parsing behavior is unchanged. - -In `Primitive`, `Param`, `Flag`, and `Argument`, capitalize existing constructor names, with these exceptions: - -| Previous | New | Modules | -| --------- | ---------- | --------------------- | -| `integer` | `Int` | All four | -| `float` | `Finite` | All four | -| `none` | `Never` | All four | -| `choice` | `Literals` | Param, Flag, Argument | - -`Primitive.choice` becomes `Primitive.Choice`; `choiceWithValue` becomes `ChoiceWithValue` where available. - -In `Prompt`, capitalize control constructors except `text` → `String`, `integer` → `Int`, and `float` → `Number`. Rename public types `IntegerOptions` → `IntOptions` and `FloatOptions` → `NumberOptions`. Shared `TextOptions` is unchanged. `Prompt.Number` retains its existing parser, without a finite-number restriction. - -In `GlobalFlag`, rename `action` → `Action` and `setting` → `Setting`. Factories and combinators, including `Command.make` and `Prompt.succeed`, keep their names. - -Update public `_tag` matches and completion descriptors: - -- `Primitive`: `"Integer"` → `"Int"`, `"Float"` → `"Finite"`, `"None"` → `"Never"`. -- `Completions.FlagType` and `Completions.ArgumentType`: `"Integer"` → `"Int"`, `"Float"` → `"Finite"`. - -Sentinels still always fail; their internal parameter name is now `"__never__"`. Help labels and completion scripts are unchanged. diff --git a/repos/effect/.changeset/pre/cli-variadic-absence-defaults.md b/repos/effect/.changeset/pre/cli-variadic-absence-defaults.md deleted file mode 100644 index 7f3f05e308..0000000000 --- a/repos/effect/.changeset/pre/cli-variadic-absence-defaults.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix defaulted variadic arguments when omitted. diff --git a/repos/effect/.changeset/pre/cli-wizard-mode.md b/repos/effect/.changeset/pre/cli-wizard-mode.md deleted file mode 100644 index 0b7c9f2d6e..0000000000 --- a/repos/effect/.changeset/pre/cli-wizard-mode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reintroduce interactive CLI wizard mode through the `--wizard` flag and `Command.wizard`. diff --git a/repos/effect/.changeset/pre/cli-wizard-option-looking-values.md b/repos/effect/.changeset/pre/cli-wizard-option-looking-values.md deleted file mode 100644 index 852a593825..0000000000 --- a/repos/effect/.changeset/pre/cli-wizard-option-looking-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix CLI wizard handling of negative numbers and other flag values beginning with `-`. diff --git a/repos/effect/.changeset/pre/close-failed-resource-map-scopes.md b/repos/effect/.changeset/pre/close-failed-resource-map-scopes.md deleted file mode 100644 index 2242910a88..0000000000 --- a/repos/effect/.changeset/pre/close-failed-resource-map-scopes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Close `ResourceMap` acquisition scopes when a lookup fails. diff --git a/repos/effect/.changeset/pre/cluster-active-teardowns.md b/repos/effect/.changeset/pre/cluster-active-teardowns.md deleted file mode 100644 index c57eceae0e..0000000000 --- a/repos/effect/.changeset/pre/cluster-active-teardowns.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Cluster no longer retains fiber ids for every local teardown. - -Transient persisted interrupts are now classified from live teardown state -(entity, shard, singleton, entity type, and node shutdown) instead of a -process-lifetime set of fiber ids. The registry is bounded by in-flight -teardowns and returns to baseline after entity reap storms. diff --git a/repos/effect/.changeset/pre/cluster-reply-codec-services.md b/repos/effect/.changeset/pre/cluster-reply-codec-services.md deleted file mode 100644 index 1ee7d1c8f2..0000000000 --- a/repos/effect/.changeset/pre/cluster-reply-codec-services.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Reply.Reply` codecs to require client services when decoding and server services when encoding. diff --git a/repos/effect/.changeset/pre/cold-knives-lie.md b/repos/effect/.changeset/pre/cold-knives-lie.md deleted file mode 100644 index 5e7051fd35..0000000000 --- a/repos/effect/.changeset/pre/cold-knives-lie.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -"effect": patch ---- - -Port `Pipeable.Class` from v3. - -```ts -class MyClass extends Pipeable.Class() { - constructor(public a: number) { - super() - } - methodA() { - return this.a - } -} -console.log(new MyClass(2).pipe((x) => x.methodA())) // 2 -``` - -```ts -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] -``` diff --git a/repos/effect/.changeset/pre/cold-rooms-show.md b/repos/effect/.changeset/pre/cold-rooms-show.md deleted file mode 100644 index ab2258a812..0000000000 --- a/repos/effect/.changeset/pre/cold-rooms-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -use Cause.NoSuchElementError for idb .first queries diff --git a/repos/effect/.changeset/pre/cold-shards-continue.md b/repos/effect/.changeset/pre/cold-shards-continue.md deleted file mode 100644 index e71308b84a..0000000000 --- a/repos/effect/.changeset/pre/cold-shards-continue.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"effect": patch ---- - -Transient routing states for persisted cluster messages no longer surface as errors. - -If an entity moves runners or is shut down before replying, the caller keeps -waiting for the reply via message storage while the entity moves. If the local -runner is shutting down while a caller is waiting, the call is interrupted -instead of failing with `EntityNotAssignedToRunner`: the request is already -durable and will be served under the next owner. - -Durable workflows treat such an interrupt as an abandoned run attempt: the run -stops with nothing persisted, without running compensations or resuming the -parent, ready to replay on the replacement runner. diff --git a/repos/effect/.changeset/pre/cold-sloths-wave.md b/repos/effect/.changeset/pre/cold-sloths-wave.md deleted file mode 100644 index 6e6a1f7fc9..0000000000 --- a/repos/effect/.changeset/pre/cold-sloths-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Add support for OpenAI `keepalive` response stream events. diff --git a/repos/effect/.changeset/pre/common-mammals-tickle.md b/repos/effect/.changeset/pre/common-mammals-tickle.md deleted file mode 100644 index 5108982921..0000000000 --- a/repos/effect/.changeset/pre/common-mammals-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Fix generation order for recursive schemas referenced by earlier recursive definitions, closes #6357. diff --git a/repos/effect/.changeset/pre/compact-json-schema-checks.md b/repos/effect/.changeset/pre/compact-json-schema-checks.md deleted file mode 100644 index 957c589976..0000000000 --- a/repos/effect/.changeset/pre/compact-json-schema-checks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Compact JSON Schema check constraints when they can be safely merged without keyword collisions. diff --git a/repos/effect/.changeset/pre/compact-json-schema-enum.md b/repos/effect/.changeset/pre/compact-json-schema-enum.md deleted file mode 100644 index f6b4e7633e..0000000000 --- a/repos/effect/.changeset/pre/compact-json-schema-enum.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -"effect": patch ---- - -Schema: collapse same-type literal branches in JSON Schema output into a single `enum` array, closes #1868. - -Before: - -```json -{ - "anyOf": [ - { "type": "string", "enum": ["A"] }, - { "type": "string", "enum": ["B"] } - ] -} -``` - -After: - -```json -{ - "type": "string", - "enum": ["A", "B"] -} -``` diff --git a/repos/effect/.changeset/pre/compatible-dependency-refresh.md b/repos/effect/.changeset/pre/compatible-dependency-refresh.md deleted file mode 100644 index a2d211be45..0000000000 --- a/repos/effect/.changeset/pre/compatible-dependency-refresh.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/platform-node": patch -"@effect/sql-libsql": patch -"@effect/sql-mysql2": patch -"@effect/doctest": patch ---- - -Update dependencies to their latest compatible versions. diff --git a/repos/effect/.changeset/pre/config-provider-option-lookup.md b/repos/effect/.changeset/pre/config-provider-option-lookup.md deleted file mode 100644 index a671ebb39f..0000000000 --- a/repos/effect/.changeset/pre/config-provider-option-lookup.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"effect": patch ---- - -Refine the `ConfigProvider` interface so lookup absence uses `undefined` and -path transformation is provider behavior. - -`ConfigProvider.load` and the lookup function accepted by -`ConfigProvider.make` now return `Node | undefined`. Use `undefined` when a path -does not exist and return the `Node` directly when it does. - -`ConfigProvider` now exposes `mapInput` as a capability. The exported -`ConfigProvider.mapInput` combinator delegates to it, preserving transformation -order and composition through `orElse` without requiring provider -representation state. diff --git a/repos/effect/.changeset/pre/config-withdefault-eager.md b/repos/effect/.changeset/pre/config-withdefault-eager.md deleted file mode 100644 index 107cd6fd7b..0000000000 --- a/repos/effect/.changeset/pre/config-withdefault-eager.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Revert `Config.withDefault` to v3 behavior, closes #1530. - -Make `Config.withDefault` accept an eager value instead of `LazyArg`, aligning with CLI module conventions. diff --git a/repos/effect/.changeset/pre/configurable-schema-reference-policy.md b/repos/effect/.changeset/pre/configurable-schema-reference-policy.md deleted file mode 100644 index c46a7a543b..0000000000 --- a/repos/effect/.changeset/pre/configurable-schema-reference-policy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add configurable schema representation reference policies and propagate them through JSON Schema and OpenAPI generation. By default, only schemas with resolved identifiers become references. Closes #7357. diff --git a/repos/effect/.changeset/pre/configure-cluster-rpc-buffer-limits.md b/repos/effect/.changeset/pre/configure-cluster-rpc-buffer-limits.md deleted file mode 100644 index 76c3172e7b..0000000000 --- a/repos/effect/.changeset/pre/configure-cluster-rpc-buffer-limits.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-node": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch ---- - -Allow configuring cluster RPC serialization buffer limits. diff --git a/repos/effect/.changeset/pre/consolidate-encoding.md b/repos/effect/.changeset/pre/consolidate-encoding.md deleted file mode 100644 index 8e0cf7c052..0000000000 --- a/repos/effect/.changeset/pre/consolidate-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Encoding: consolidate `effect/encoding` sub-modules (Base64, Base64Url, Hex, EncodingError) into a top-level `Encoding` module. Functions are now prefixed: `encodeBase64`, `decodeBase64`, `encodeHex`, `decodeHex`, etc. The `effect/encoding` sub-path export is removed. diff --git a/repos/effect/.changeset/pre/consolidate-sql-error.md b/repos/effect/.changeset/pre/consolidate-sql-error.md deleted file mode 100644 index fdefa5f8cb..0000000000 --- a/repos/effect/.changeset/pre/consolidate-sql-error.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect": patch -"@effect/sql-clickhouse": patch -"@effect/sql-d1": patch -"@effect/sql-libsql": patch -"@effect/sql-mssql": patch -"@effect/sql-mysql2": patch -"@effect/sql-pg": patch -"@effect/sql-sqlite-bun": patch -"@effect/sql-sqlite-do": patch -"@effect/sql-sqlite-node": patch -"@effect/sql-sqlite-react-native": patch -"@effect/sql-sqlite-wasm": patch ---- - -Consolidate the SqlError changes to the new reason-based shape across effect and the SQL drivers, classifying native failures into structured reasons with Unknown fallback where native codes are unavailable. diff --git a/repos/effect/.changeset/pre/context-saved-getter-inference.md b/repos/effect/.changeset/pre/context-saved-getter-inference.md deleted file mode 100644 index c3028f3605..0000000000 --- a/repos/effect/.changeset/pre/context-saved-getter-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix saved curried `Context.get` calls incorrectly inferring their required service as `unknown`. diff --git a/repos/effect/.changeset/pre/cookies-error-tag.md b/repos/effect/.changeset/pre/cookies-error-tag.md deleted file mode 100644 index c523bca088..0000000000 --- a/repos/effect/.changeset/pre/cookies-error-tag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the `CookiesError` tag in `effect/unstable/http` from `CookieError` to `CookiesError` to match the class name. diff --git a/repos/effect/.changeset/pre/cozy-geese-remain.md b/repos/effect/.changeset/pre/cozy-geese-remain.md deleted file mode 100644 index 52e90cd081..0000000000 --- a/repos/effect/.changeset/pre/cozy-geese-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix scoped reentrant lock finalizers releasing under the wrong fiber owner. diff --git a/repos/effect/.changeset/pre/crisp-seas-warn.md b/repos/effect/.changeset/pre/crisp-seas-warn.md deleted file mode 100644 index 31fac078f6..0000000000 --- a/repos/effect/.changeset/pre/crisp-seas-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure transformed Atom's don't extend idle ttl diff --git a/repos/effect/.changeset/pre/cron-locale-independent-aliases.md b/repos/effect/.changeset/pre/cron-locale-independent-aliases.md deleted file mode 100644 index 9b84a1ced5..0000000000 --- a/repos/effect/.changeset/pre/cron-locale-independent-aliases.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize cron month and weekday aliases independently of the host locale. diff --git a/repos/effect/.changeset/pre/cron-single-value-step.md b/repos/effect/.changeset/pre/cron-single-value-step.md deleted file mode 100644 index 02a9fff682..0000000000 --- a/repos/effect/.changeset/pre/cron-single-value-step.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow cron fields like `5/15` to expand from the starting value through the field maximum. diff --git a/repos/effect/.changeset/pre/cron-testclock-infinity.md b/repos/effect/.changeset/pre/cron-testclock-infinity.md deleted file mode 100644 index 3aea00e898..0000000000 --- a/repos/effect/.changeset/pre/cron-testclock-infinity.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.cron` when the test clock is adjusted to infinity. diff --git a/repos/effect/.changeset/pre/cuddly-rooms-bet.md b/repos/effect/.changeset/pre/cuddly-rooms-bet.md deleted file mode 100644 index 8d09b0ad84..0000000000 --- a/repos/effect/.changeset/pre/cuddly-rooms-bet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Stream.timeoutOrElse diff --git a/repos/effect/.changeset/pre/curly-files-range.md b/repos/effect/.changeset/pre/curly-files-range.md deleted file mode 100644 index aa2a20ade4..0000000000 --- a/repos/effect/.changeset/pre/curly-files-range.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Apply byte range and chunk size options to default Web file responses. diff --git a/repos/effect/.changeset/pre/curly-graphs-connect.md b/repos/effect/.changeset/pre/curly-graphs-connect.md deleted file mode 100644 index 37f344445b..0000000000 --- a/repos/effect/.changeset/pre/curly-graphs-connect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add graph snapshots, low-link connectivity analysis, bipartite matching, maximum flow, and minimum cut APIs. diff --git a/repos/effect/.changeset/pre/curly-graphs-snapshot.md b/repos/effect/.changeset/pre/curly-graphs-snapshot.md deleted file mode 100644 index 7c56a6daf2..0000000000 --- a/repos/effect/.changeset/pre/curly-graphs-snapshot.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Graph.Snapshot` and `Graph.fromSnapshot` for constructing immutable graphs with explicit node and edge indexes, and simplify `Graph.Edge` to a type-only structural interface. diff --git a/repos/effect/.changeset/pre/curly-poems-talk.md b/repos/effect/.changeset/pre/curly-poems-talk.md deleted file mode 100644 index 97fa70b92b..0000000000 --- a/repos/effect/.changeset/pre/curly-poems-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add `Schema.HashSet` for decoding and encoding `HashSet` values. diff --git a/repos/effect/.changeset/pre/curly-ravens-decode.md b/repos/effect/.changeset/pre/curly-ravens-decode.md deleted file mode 100644 index f8af5ea829..0000000000 --- a/repos/effect/.changeset/pre/curly-ravens-decode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Decode percent-encoded OTLP environment header values. diff --git a/repos/effect/.changeset/pre/curly-spies-relax.md b/repos/effect/.changeset/pre/curly-spies-relax.md deleted file mode 100644 index a495ef8bc9..0000000000 --- a/repos/effect/.changeset/pre/curly-spies-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -narrow types for Effect.retry/repeat while option diff --git a/repos/effect/.changeset/pre/curly-streams-stop.md b/repos/effect/.changeset/pre/curly-streams-stop.md deleted file mode 100644 index e2a08a9df1..0000000000 --- a/repos/effect/.changeset/pre/curly-streams-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Interrupt in-flight stream pulls when closing an async iterator. diff --git a/repos/effect/.changeset/pre/curvy-apples-float.md b/repos/effect/.changeset/pre/curvy-apples-float.md deleted file mode 100644 index fcdfac6342..0000000000 --- a/repos/effect/.changeset/pre/curvy-apples-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.validate` for validating collections while accumulating all failures, equivalent to the v3 `Effect.validateAll` behavior. diff --git a/repos/effect/.changeset/pre/curvy-birds-float.md b/repos/effect/.changeset/pre/curvy-birds-float.md deleted file mode 100644 index e0a68dddc2..0000000000 --- a/repos/effect/.changeset/pre/curvy-birds-float.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Update `Schema.Void` to model ignored `void` return values. - -Runtime parsing now accepts any present value and discards it as `undefined`. -This matches TypeScript `void` return values, where callers do not observe the -returned value. Use `Schema.Undefined` when the input must be exactly -`undefined`. diff --git a/repos/effect/.changeset/pre/curvy-graphs-code.md b/repos/effect/.changeset/pre/curvy-graphs-code.md deleted file mode 100644 index 758df43ffc..0000000000 --- a/repos/effect/.changeset/pre/curvy-graphs-code.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.Graph` for schema-based encoding and decoding of immutable directed and undirected graphs. diff --git a/repos/effect/.changeset/pre/curvy-melons-stare.md b/repos/effect/.changeset/pre/curvy-melons-stare.md deleted file mode 100644 index c9dd275767..0000000000 --- a/repos/effect/.changeset/pre/curvy-melons-stare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix MCP sampling metadata optionality and validate it as an object. diff --git a/repos/effect/.changeset/pre/curvy-otters-sync.md b/repos/effect/.changeset/pre/curvy-otters-sync.md deleted file mode 100644 index aa98e610d2..0000000000 --- a/repos/effect/.changeset/pre/curvy-otters-sync.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Retry transient EventLog remote write failures so pending local entries are synchronized after recovery. diff --git a/repos/effect/.changeset/pre/custom-cli-prompt-prefix.md b/repos/effect/.changeset/pre/custom-cli-prompt-prefix.md deleted file mode 100644 index e6c00a55a9..0000000000 --- a/repos/effect/.changeset/pre/custom-cli-prompt-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow customizing the prefix displayed by CLI prompts. diff --git a/repos/effect/.changeset/pre/custom-http-security-openapi-generator.md b/repos/effect/.changeset/pre/custom-http-security-openapi-generator.md deleted file mode 100644 index c338435df3..0000000000 --- a/repos/effect/.changeset/pre/custom-http-security-openapi-generator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Add HttpApi generation support for custom OpenAPI HTTP security schemes. diff --git a/repos/effect/.changeset/pre/cute-heads-thank.md b/repos/effect/.changeset/pre/cute-heads-thank.md deleted file mode 100644 index 25a2f17e3f..0000000000 --- a/repos/effect/.changeset/pre/cute-heads-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -catch defects when building Entity handlers diff --git a/repos/effect/.changeset/pre/cyan-loops-grow.md b/repos/effect/.changeset/pre/cyan-loops-grow.md deleted file mode 100644 index a807ca082a..0000000000 --- a/repos/effect/.changeset/pre/cyan-loops-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -improve idb transaction api diff --git a/repos/effect/.changeset/pre/cyan-radios-switch.md b/repos/effect/.changeset/pre/cyan-radios-switch.md deleted file mode 100644 index 6763e29536..0000000000 --- a/repos/effect/.changeset/pre/cyan-radios-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Broaden the `vitest` peer dependency range to support both v3 and v4. diff --git a/repos/effect/.changeset/pre/cyan-shirts-grin.md b/repos/effect/.changeset/pre/cyan-shirts-grin.md deleted file mode 100644 index 96d0ce0d7c..0000000000 --- a/repos/effect/.changeset/pre/cyan-shirts-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fork memo map on nested builds diff --git a/repos/effect/.changeset/pre/d1-batch-statements.md b/repos/effect/.changeset/pre/d1-batch-statements.md deleted file mode 100644 index 779a545b75..0000000000 --- a/repos/effect/.changeset/pre/d1-batch-statements.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-d1": minor ---- - -Add `D1Client.batch` for executing a collection of SQL statements as a single atomic D1 batch. diff --git a/repos/effect/.changeset/pre/d1-raw-native-results.md b/repos/effect/.changeset/pre/d1-raw-native-results.md deleted file mode 100644 index 5441b86d1c..0000000000 --- a/repos/effect/.changeset/pre/d1-raw-native-results.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/sql-d1": patch ---- - -Return the complete native `D1Result` from D1 statement `.raw`, preserving `success`, `meta`, and `results` instead of returning only the row array. - -Callers that treated `.raw` as an array should read `.results` or use an ordinary or `.unprepared` statement when only rows are needed. diff --git a/repos/effect/.changeset/pre/dark-dancers-rule.md b/repos/effect/.changeset/pre/dark-dancers-rule.md deleted file mode 100644 index e46ea884db..0000000000 --- a/repos/effect/.changeset/pre/dark-dancers-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use Context mapUnsafe in less call sites diff --git a/repos/effect/.changeset/pre/datetime-calendar-parts.md b/repos/effect/.changeset/pre/datetime-calendar-parts.md deleted file mode 100644 index 5529fb6e21..0000000000 --- a/repos/effect/.changeset/pre/datetime-calendar-parts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Apply DateTime calendar parts without intermediate overflow. diff --git a/repos/effect/.changeset/pre/deduplicate-json-schema-fallbacks.md b/repos/effect/.changeset/pre/deduplicate-json-schema-fallbacks.md deleted file mode 100644 index 1c5aa33498..0000000000 --- a/repos/effect/.changeset/pre/deduplicate-json-schema-fallbacks.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch -"@effect/openapi-generator": patch ---- - -Deduplicate equivalent fallback definitions when compiling JSON Schema, and reconstruct only definitions reachable from multi-document roots. - -Remove `SchemaMultiDocument` and `fromSchemaMultiDocument`; multi-document import and revival now return the ordered root schemas directly. - -Stop the OpenAPI generator from emitting component schemas that are not reachable from a generated root. diff --git a/repos/effect/.changeset/pre/deep-rivers-spend.md b/repos/effect/.changeset/pre/deep-rivers-spend.md deleted file mode 100644 index 2c89a0793b..0000000000 --- a/repos/effect/.changeset/pre/deep-rivers-spend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix Stream.withSpan options diff --git a/repos/effect/.changeset/pre/deferred-cleanup-after-completion.md b/repos/effect/.changeset/pre/deferred-cleanup-after-completion.md deleted file mode 100644 index 4434455d5e..0000000000 --- a/repos/effect/.changeset/pre/deferred-cleanup-after-completion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Deferred.await` dying with a `TypeError` when a waiter is interrupted after the `Deferred` has been completed. diff --git a/repos/effect/.changeset/pre/deferred-resume-starvation.md b/repos/effect/.changeset/pre/deferred-resume-starvation.md deleted file mode 100644 index 9a30bd76de..0000000000 --- a/repos/effect/.changeset/pre/deferred-resume-starvation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Deferred` completion skipping waiters when an earlier waiter dies during resume. Completing a `Deferred` with an interrupt cause kills a suspended waiter synchronously inside its resume; the dying waiter's `await` cleanup spliced the shared `resumes` array mid-iteration, so the next waiter was never resumed and hung forever. Completion now clears `resumes` before resuming waiters. diff --git a/repos/effect/.changeset/pre/deno-redis-url-credentials.md b/repos/effect/.changeset/pre/deno-redis-url-credentials.md deleted file mode 100644 index c150c6fefb..0000000000 --- a/repos/effect/.changeset/pre/deno-redis-url-credentials.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Decode percent-encoded Redis URL authority credentials before authentication. diff --git a/repos/effect/.changeset/pre/deno-write-copy-errors.md b/repos/effect/.changeset/pre/deno-write-copy-errors.md deleted file mode 100644 index 2b93ed38e6..0000000000 --- a/repos/effect/.changeset/pre/deno-write-copy-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Preserve high-level filesystem error context for `writeFile` and normalize Deno `AlreadyExists` errors from `copy`. diff --git a/repos/effect/.changeset/pre/deno-writefile-existing-mode.md b/repos/effect/.changeset/pre/deno-writefile-existing-mode.md deleted file mode 100644 index 17d44f9ed5..0000000000 --- a/repos/effect/.changeset/pre/deno-writefile-existing-mode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Fix `FileSystem.writeFile` on Deno to preserve permissions when writing an existing file with an explicit mode. diff --git a/repos/effect/.changeset/pre/dirty-lamps-trade.md b/repos/effect/.changeset/pre/dirty-lamps-trade.md deleted file mode 100644 index 26c515d515..0000000000 --- a/repos/effect/.changeset/pre/dirty-lamps-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Result.failVoid diff --git a/repos/effect/.changeset/pre/dirty-laws-wear.md b/repos/effect/.changeset/pre/dirty-laws-wear.md deleted file mode 100644 index 68ad0d5a8e..0000000000 --- a/repos/effect/.changeset/pre/dirty-laws-wear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -handle missing output array in openai responses diff --git a/repos/effect/.changeset/pre/docgen-alias-signature.md b/repos/effect/.changeset/pre/docgen-alias-signature.md deleted file mode 100644 index 58b06047fd..0000000000 --- a/repos/effect/.changeset/pre/docgen-alias-signature.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/docgen": patch ---- - -Preserve alias names, constraints, and defaults in generated type signatures. diff --git a/repos/effect/.changeset/pre/docgen-class-property-examples.md b/repos/effect/.changeset/pre/docgen-class-property-examples.md deleted file mode 100644 index 68bfef5408..0000000000 --- a/repos/effect/.changeset/pre/docgen-class-property-examples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/docgen": patch ---- - -Type-check parsed class property examples and execute them when `runExamples` is enabled. Previously unchecked property examples with type errors now cause docgen to fail. diff --git a/repos/effect/.changeset/pre/docgen-source-relative-module-pages.md b/repos/effect/.changeset/pre/docgen-source-relative-module-pages.md deleted file mode 100644 index 92efd26675..0000000000 --- a/repos/effect/.changeset/pre/docgen-source-relative-module-pages.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/docgen": patch ---- - -Fix module page paths for `.`, nested, and absolute source directories so modules with the same filename in different subdirectories produce distinct pages. diff --git a/repos/effect/.changeset/pre/docgen-unique-example-files.md b/repos/effect/.changeset/pre/docgen-unique-example-files.md deleted file mode 100644 index 15a55b1362..0000000000 --- a/repos/effect/.changeset/pre/docgen-unique-example-files.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/docgen": patch ---- - -Prevent examples from modules with colliding flattened paths from overwriting each other, so all examples are checked and optionally executed. Temporary example filenames and diagnostic paths now include a numeric prefix. diff --git a/repos/effect/.changeset/pre/doctest-console-output.md b/repos/effect/.changeset/pre/doctest-console-output.md deleted file mode 100644 index cd06cfddb1..0000000000 --- a/repos/effect/.changeset/pre/doctest-console-output.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/doctest": patch ---- - -Add convention-based `// =>` assertions that compare documentation example values using Effect equality. diff --git a/repos/effect/.changeset/pre/doctest-markdown-typescript.md b/repos/effect/.changeset/pre/doctest-markdown-typescript.md deleted file mode 100644 index a427190238..0000000000 --- a/repos/effect/.changeset/pre/doctest-markdown-typescript.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/doctest": patch ---- - -Fix spurious TypeScript syntax errors when running marked code fences in Markdown and MDX documents. diff --git a/repos/effect/.changeset/pre/doctest-statement-boundary.md b/repos/effect/.changeset/pre/doctest-statement-boundary.md deleted file mode 100644 index 9e69e68ff4..0000000000 --- a/repos/effect/.changeset/pre/doctest-statement-boundary.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/doctest": patch ---- - -Preserve statement boundaries after generated doctest assertions. diff --git a/repos/effect/.changeset/pre/document-child-process-env.md b/repos/effect/.changeset/pre/document-child-process-env.md deleted file mode 100644 index f28d728816..0000000000 --- a/repos/effect/.changeset/pre/document-child-process-env.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Document that `CommandOptions.extendEnv` defaults to `false` and that providing `env` without enabling it replaces the inherited child environment. diff --git a/repos/effect/.changeset/pre/dotenv-literal-substitution.md b/repos/effect/.changeset/pre/dotenv-literal-substitution.md deleted file mode 100644 index 0e351277d9..0000000000 --- a/repos/effect/.changeset/pre/dotenv-literal-substitution.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `ConfigProvider.fromDotEnvContents` variable expansion to preserve replacement tokens such as `$&` in referenced values. diff --git a/repos/effect/.changeset/pre/dry-bugs-hug.md b/repos/effect/.changeset/pre/dry-bugs-hug.md deleted file mode 100644 index 723f24935a..0000000000 --- a/repos/effect/.changeset/pre/dry-bugs-hug.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@effect/atom-react": patch -"@effect/atom-solid": patch -"@effect/atom-vue": patch -"@effect/docgen": patch -"@effect/doctest": patch -"@effect/opentelemetry": patch -"@effect/platform-node": patch -"@effect/sql-pg": patch -"@effect/sql-sqlite-react-native": patch -"@effect/sql-sqlite-wasm": patch -"@effect/vitest": patch ---- - -Update peer dependencies diff --git a/repos/effect/.changeset/pre/durable-clock-zero-threshold.md b/repos/effect/.changeset/pre/durable-clock-zero-threshold.md deleted file mode 100644 index 3a23956d33..0000000000 --- a/repos/effect/.changeset/pre/durable-clock-zero-threshold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `DurableClock.sleep` to preserve explicit `0` and `0n` in-memory thresholds. diff --git a/repos/effect/.changeset/pre/durable-deferred-into-encoding-services.md b/repos/effect/.changeset/pre/durable-deferred-into-encoding-services.md deleted file mode 100644 index 6a823609a3..0000000000 --- a/repos/effect/.changeset/pre/durable-deferred-into-encoding-services.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Require schema encoding services when `DurableDeferred.into` records an exit. diff --git a/repos/effect/.changeset/pre/duration-temporal-object-input.md b/repos/effect/.changeset/pre/duration-temporal-object-input.md deleted file mode 100644 index faa02f6718..0000000000 --- a/repos/effect/.changeset/pre/duration-temporal-object-input.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Add `DurationObject` to `Duration.Input` to support Temporal-style object input. - -Durations can now be created from objects with named unit properties like `{ hours: 1, minutes: 30 }`, similar to `Temporal.Duration.from()`. Supported fields: `weeks`, `days`, `hours`, `minutes`, `seconds`, `millis`, `micros`, `nanos`. diff --git a/repos/effect/.changeset/pre/dynamic-tool-parameter-schema.md b/repos/effect/.changeset/pre/dynamic-tool-parameter-schema.md deleted file mode 100644 index 7d16a999d2..0000000000 --- a/repos/effect/.changeset/pre/dynamic-tool-parameter-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update dynamic tools to advertise replacement parameter schemas after `setParameters`. diff --git a/repos/effect/.changeset/pre/eager-coats-cheat.md b/repos/effect/.changeset/pre/eager-coats-cheat.md deleted file mode 100644 index d75b97ae8c..0000000000 --- a/repos/effect/.changeset/pre/eager-coats-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -rename SqlSchema.findOne\* apis diff --git a/repos/effect/.changeset/pre/early-birds-dream.md b/repos/effect/.changeset/pre/early-birds-dream.md deleted file mode 100644 index a6de037331..0000000000 --- a/repos/effect/.changeset/pre/early-birds-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add trait for customizing exit codes diff --git a/repos/effect/.changeset/pre/early-donuts-argue.md b/repos/effect/.changeset/pre/early-donuts-argue.md deleted file mode 100644 index fe1b701771..0000000000 --- a/repos/effect/.changeset/pre/early-donuts-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix cache constructor inference by moving the lookup option diff --git a/repos/effect/.changeset/pre/early-jobs-bow.md b/repos/effect/.changeset/pre/early-jobs-bow.md deleted file mode 100644 index f62abe0151..0000000000 --- a/repos/effect/.changeset/pre/early-jobs-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Defer memoized Layer state installation until Effect execution. diff --git a/repos/effect/.changeset/pre/early-peaches-check.md b/repos/effect/.changeset/pre/early-peaches-check.md deleted file mode 100644 index 0782936ad4..0000000000 --- a/repos/effect/.changeset/pre/early-peaches-check.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"effect": patch ---- - -generate binary arrays from streams with less copying diff --git a/repos/effect/.changeset/pre/eff-1004-cli-display-width.md b/repos/effect/.changeset/pre/eff-1004-cli-display-width.md deleted file mode 100644 index 6c0fdf5938..0000000000 --- a/repos/effect/.changeset/pre/eff-1004-cli-display-width.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Align CLI help tables by terminal display width for wide, emoji, combining, and zero-width graphemes. diff --git a/repos/effect/.changeset/pre/eff-1008-tool-param-failure-mode.md b/repos/effect/.changeset/pre/eff-1008-tool-param-failure-mode.md deleted file mode 100644 index 8939684f6c..0000000000 --- a/repos/effect/.changeset/pre/eff-1008-tool-param-failure-mode.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch ---- - -Route tool call parameter validation failures through the tool's `failureMode` and drop `ToolParameterValidationError.toolParams`. diff --git a/repos/effect/.changeset/pre/eff-1036-fiber-allocations.md b/repos/effect/.changeset/pre/eff-1036-fiber-allocations.md deleted file mode 100644 index ddd8bbc1c1..0000000000 --- a/repos/effect/.changeset/pre/eff-1036-fiber-allocations.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch -"@effect/opentelemetry": patch ---- - -Reduce memory usage in Effect primitives and fibers. - -Breaking: context-derived `Fiber` fields now live under `fiber.cache`. The -`currentScheduler`, `currentSpan`, `currentLogLevel`, `currentStackFrame`, and -`currentPreventYield` fields are now `scheduler`, `span`, `logLevel`, -`stackFrame`, and `preventYield`. Access `minimumLogLevel` and -`maxOpsBeforeYield` through `cache` as well. diff --git a/repos/effect/.changeset/pre/eff-1038-http-server-allocations.md b/repos/effect/.changeset/pre/eff-1038-http-server-allocations.md deleted file mode 100644 index 26a8ea83bd..0000000000 --- a/repos/effect/.changeset/pre/eff-1038-http-server-allocations.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch ---- - -Reduce HTTP server allocation churn when tracing is not configured and for requests that complete synchronously. diff --git a/repos/effect/.changeset/pre/eff-1039-rpc-server-allocations.md b/repos/effect/.changeset/pre/eff-1039-rpc-server-allocations.md deleted file mode 100644 index 5973fa3cf2..0000000000 --- a/repos/effect/.changeset/pre/eff-1039-rpc-server-allocations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reduce per-request RPC server allocations. diff --git a/repos/effect/.changeset/pre/eff-1042-http-server-performance.md b/repos/effect/.changeset/pre/eff-1042-http-server-performance.md deleted file mode 100644 index bbdd463230..0000000000 --- a/repos/effect/.changeset/pre/eff-1042-http-server-performance.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch ---- - -Improve HTTP server throughput by reducing routing, request handling, response -construction, and body encoding overhead. Add `Effect.withFiberSucceed` for -synchronously computing successful values from the current fiber. Copy pooled -byte views by their exact range when exposing `ArrayBuffer` values. diff --git a/repos/effect/.changeset/pre/eff-115-sync-scheduler-microtask.md b/repos/effect/.changeset/pre/eff-115-sync-scheduler-microtask.md deleted file mode 100644 index f0d120781b..0000000000 --- a/repos/effect/.changeset/pre/eff-115-sync-scheduler-microtask.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use cancellable microtasks when dispatching yielded work from synchronous Effect runs. diff --git a/repos/effect/.changeset/pre/eff-117-hydration-reactivity.md b/repos/effect/.changeset/pre/eff-117-hydration-reactivity.md deleted file mode 100644 index 9480039381..0000000000 --- a/repos/effect/.changeset/pre/eff-117-hydration-reactivity.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix hydrated atoms with `Atom.withReactivity` to refresh after reactive mutations. diff --git a/repos/effect/.changeset/pre/eff-1201-http-overhead.md b/repos/effect/.changeset/pre/eff-1201-http-overhead.md deleted file mode 100644 index 84500451c0..0000000000 --- a/repos/effect/.changeset/pre/eff-1201-http-overhead.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Reduce HTTP server overhead: complete freshly created header maps in place in -`HttpServerResponse.setHeader` and `setHeaders`, compare static route prefixes -with a prepared `startsWith`, map `HttpApi` schema errors eagerly for completed -decoder results, and implement `Effect.cached` as a dedicated one-time memo -without time-to-live machinery. diff --git a/repos/effect/.changeset/pre/eff-121-http-router-web-handler.md b/repos/effect/.changeset/pre/eff-121-http-router-web-handler.md deleted file mode 100644 index 4576cfec24..0000000000 --- a/repos/effect/.changeset/pre/eff-121-http-router-web-handler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpRouter.toWebHandler` context inference for services provided by the application layer. diff --git a/repos/effect/.changeset/pre/eff-123-openai-compat-unknown-events.md b/repos/effect/.changeset/pre/eff-123-openai-compat-unknown-events.md deleted file mode 100644 index 0d587a2b83..0000000000 --- a/repos/effect/.changeset/pre/eff-123-openai-compat-unknown-events.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Surface parsed chat completion stream events that do not match the expected schema as `UnknownChatCompletionEvent`. diff --git a/repos/effect/.changeset/pre/eff-137-web-stream-interop.md b/repos/effect/.changeset/pre/eff-137-web-stream-interop.md deleted file mode 100644 index d3f164a895..0000000000 --- a/repos/effect/.changeset/pre/eff-137-web-stream-interop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add Web Stream interoperability for `Channel` and `Sink`, plus byte limiting and `ArrayBuffer` collection for `Stream`. diff --git a/repos/effect/.changeset/pre/eff-140-deno-crypto.md b/repos/effect/.changeset/pre/eff-140-deno-crypto.md deleted file mode 100644 index 1ea8d267af..0000000000 --- a/repos/effect/.changeset/pre/eff-140-deno-crypto.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a Deno Web Crypto implementation of the `Crypto` service. diff --git a/repos/effect/.changeset/pre/eff-141-deno-child-process.md b/repos/effect/.changeset/pre/eff-141-deno-child-process.md deleted file mode 100644 index 746c3ff424..0000000000 --- a/repos/effect/.changeset/pre/eff-141-deno-child-process.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a native Deno `ChildProcessSpawner` implementation and shared process conformance coverage. diff --git a/repos/effect/.changeset/pre/eff-142-deno-terminal.md b/repos/effect/.changeset/pre/eff-142-deno-terminal.md deleted file mode 100644 index b7eb5a35ad..0000000000 --- a/repos/effect/.changeset/pre/eff-142-deno-terminal.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/platform-deno": patch -"@effect/platform-node-shared": patch ---- - -Add a Deno `Terminal` implementation and keep `NodeTerminal` input readers alive until stdin ends under Deno. diff --git a/repos/effect/.changeset/pre/eff-143-deno-stdio.md b/repos/effect/.changeset/pre/eff-143-deno-stdio.md deleted file mode 100644 index c46576d615..0000000000 --- a/repos/effect/.changeset/pre/eff-143-deno-stdio.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a native Deno implementation of the `Stdio` service. diff --git a/repos/effect/.changeset/pre/eff-145-deno-services.md b/repos/effect/.changeset/pre/eff-145-deno-services.md deleted file mode 100644 index c9ef160845..0000000000 --- a/repos/effect/.changeset/pre/eff-145-deno-services.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add the aggregate Deno platform services layer. diff --git a/repos/effect/.changeset/pre/eff-148-deno-http-platform.md b/repos/effect/.changeset/pre/eff-148-deno-http-platform.md deleted file mode 100644 index 62766839ff..0000000000 --- a/repos/effect/.changeset/pre/eff-148-deno-http-platform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a native Deno `HttpPlatform` layer with resource-backed file responses. diff --git a/repos/effect/.changeset/pre/eff-151-deno-redis.md b/repos/effect/.changeset/pre/eff-151-deno-redis.md deleted file mode 100644 index 292d3fed17..0000000000 --- a/repos/effect/.changeset/pre/eff-151-deno-redis.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a native Deno Redis integration backed by `@db/redis`. diff --git a/repos/effect/.changeset/pre/eff-153-deno-http-server.md b/repos/effect/.changeset/pre/eff-153-deno-http-server.md deleted file mode 100644 index a2e2611642..0000000000 --- a/repos/effect/.changeset/pre/eff-153-deno-http-server.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add a native Deno HTTP server with multipart requests, file responses, and WebSocket upgrades. diff --git a/repos/effect/.changeset/pre/eff-153-websocket-initial-frames.md b/repos/effect/.changeset/pre/eff-153-websocket-initial-frames.md deleted file mode 100644 index 1f1142a068..0000000000 --- a/repos/effect/.changeset/pre/eff-153-websocket-initial-frames.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support replaying initial WebSocket messages and normalize `ArrayBuffer` frames to `Uint8Array`. diff --git a/repos/effect/.changeset/pre/eff-154-deno-cluster-http.md b/repos/effect/.changeset/pre/eff-154-deno-cluster-http.md deleted file mode 100644 index 36d0328958..0000000000 --- a/repos/effect/.changeset/pre/eff-154-deno-cluster-http.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add native Deno HTTP and WebSocket layers for Effect Cluster runners. diff --git a/repos/effect/.changeset/pre/eff-155-deno-cluster-socket.md b/repos/effect/.changeset/pre/eff-155-deno-cluster-socket.md deleted file mode 100644 index b079d9319f..0000000000 --- a/repos/effect/.changeset/pre/eff-155-deno-cluster-socket.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Add native Deno socket layers for Effect Cluster runners. diff --git a/repos/effect/.changeset/pre/eff-162-browser-crypto-chunks.md b/repos/effect/.changeset/pre/eff-162-browser-crypto-chunks.md deleted file mode 100644 index fb93845c99..0000000000 --- a/repos/effect/.changeset/pre/eff-162-browser-crypto-chunks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix `BrowserCrypto.randomBytes` for requests larger than the Web Crypto per-call limit. diff --git a/repos/effect/.changeset/pre/eff-170-bun-multipart-stream.md b/repos/effect/.changeset/pre/eff-170-bun-multipart-stream.md deleted file mode 100644 index ccc6b55a9f..0000000000 --- a/repos/effect/.changeset/pre/eff-170-bun-multipart-stream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Construct an empty multipart stream for each bodiless Bun request. diff --git a/repos/effect/.changeset/pre/eff-210-cookie-validation.md b/repos/effect/.changeset/pre/eff-210-cookie-validation.md deleted file mode 100644 index 7050ceb33a..0000000000 --- a/repos/effect/.changeset/pre/eff-210-cookie-validation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Validate cookie names, domains, and paths before constructing or serializing cookies. diff --git a/repos/effect/.changeset/pre/eff-212-secure-mssql-transport.md b/repos/effect/.changeset/pre/eff-212-secure-mssql-transport.md deleted file mode 100644 index f662266f92..0000000000 --- a/repos/effect/.changeset/pre/eff-212-secure-mssql-transport.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -**Breaking:** Secure Microsoft SQL Server connections by default by enabling encryption and validating server certificates. - -Users connecting to SQL Server instances without TLS must now explicitly set `encrypt: false`. Users connecting with untrusted or self-signed certificates must explicitly set `trustServer: true`. diff --git a/repos/effect/.changeset/pre/eff-216-secure-http-redirects.md b/repos/effect/.changeset/pre/eff-216-secure-http-redirects.md deleted file mode 100644 index fbfe9fde49..0000000000 --- a/repos/effect/.changeset/pre/eff-216-secure-http-redirects.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Strip credential headers on cross-origin HTTP redirects and align redirected request methods with fetch. diff --git a/repos/effect/.changeset/pre/eff-218-bound-sse-pending-state.md b/repos/effect/.changeset/pre/eff-218-bound-sse-pending-state.md deleted file mode 100644 index be71140d55..0000000000 --- a/repos/effect/.changeset/pre/eff-218-bound-sse-pending-state.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch -"@effect/openapi-generator": patch ---- - -Bound pending SSE decoder state with a configurable maximum event size. diff --git a/repos/effect/.changeset/pre/eff-219-key-value-store-file-keys.md b/repos/effect/.changeset/pre/eff-219-key-value-store-file-keys.md deleted file mode 100644 index ea7b27d883..0000000000 --- a/repos/effect/.changeset/pre/eff-219-key-value-store-file-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject empty, `.` and `..` keys in file-backed key-value stores. diff --git a/repos/effect/.changeset/pre/eff-220-cli-control-characters.md b/repos/effect/.changeset/pre/eff-220-cli-control-characters.md deleted file mode 100644 index 7073b7cb3b..0000000000 --- a/repos/effect/.changeset/pre/eff-220-cli-control-characters.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Escape terminal control characters in unstable CLI error output. diff --git a/repos/effect/.changeset/pre/eff-332-http-response-compression.md b/repos/effect/.changeset/pre/eff-332-http-response-compression.md deleted file mode 100644 index 831d4f07b7..0000000000 --- a/repos/effect/.changeset/pre/eff-332-http-response-compression.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch ---- - -Add HTTP response compression support. Node.js, Bun, and Deno use asynchronous -`node:zlib` one-shot compression for byte-array bodies, preserving an exact -`Content-Length`; stream and raw bodies remain streaming transforms. diff --git a/repos/effect/.changeset/pre/eff-337-preserve-mssql-parameters.md b/repos/effect/.changeset/pre/eff-337-preserve-mssql-parameters.md deleted file mode 100644 index 903a8ed19b..0000000000 --- a/repos/effect/.changeset/pre/eff-337-preserve-mssql-parameters.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Preserve fractional numbers and Unicode strings in default Microsoft SQL Server parameters. diff --git a/repos/effect/.changeset/pre/eff-342-clickhouse-number-binding.md b/repos/effect/.changeset/pre/eff-342-clickhouse-number-binding.md deleted file mode 100644 index 847d5b566c..0000000000 --- a/repos/effect/.changeset/pre/eff-342-clickhouse-number-binding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-clickhouse": patch ---- - -Preserve fractional JavaScript numbers in inferred ClickHouse parameters. diff --git a/repos/effect/.changeset/pre/eff-389-execution-plan-attempts.md b/repos/effect/.changeset/pre/eff-389-execution-plan-attempts.md deleted file mode 100644 index dc4262d0cf..0000000000 --- a/repos/effect/.changeset/pre/eff-389-execution-plan-attempts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject zero execution attempts in `ExecutionPlan` steps. diff --git a/repos/effect/.changeset/pre/eff-428-pg-transaction-permit.md b/repos/effect/.changeset/pre/eff-428-pg-transaction-permit.md deleted file mode 100644 index be6a9a20e9..0000000000 --- a/repos/effect/.changeset/pre/eff-428-pg-transaction-permit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Hold the shared PostgreSQL client permit for the full transaction lifetime. diff --git a/repos/effect/.changeset/pre/eff-467-execution-plan-events.md b/repos/effect/.changeset/pre/eff-467-execution-plan-events.md deleted file mode 100644 index eae1e3bec8..0000000000 --- a/repos/effect/.changeset/pre/eff-467-execution-plan-events.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch ---- - -Add execution-plan lifecycle events via an optional `onEvent` handler on `Effect.withExecutionPlan` and `Stream.withExecutionPlan`. - -The handler receives an `ExecutionPlan.Event`, a tagged union of `AttemptStart`, `AttemptSuccess`, and `AttemptFailure`, allowing attempt outcomes to be observed from outside the effect for logging and metrics: - -```ts -import { Effect } from "effect" - -Effect.withExecutionPlan(program, plan, { - onEvent: (event) => Effect.log("execution plan event", event) -}) -``` - -Every `AttemptStart` is followed by exactly one terminal event. `AttemptFailure` carries the full failure `Cause`, so defects and interruption are reported as well as expected errors, and terminal events run like finalizers so they are emitted even when the attempt is interrupted. Event numbering matches `ExecutionPlan.CurrentMetadata`: `attempt` is cumulative across steps, while `stepAttempt` is 1-based within the current step. diff --git a/repos/effect/.changeset/pre/eff-477-schedule-concat.md b/repos/effect/.changeset/pre/eff-477-schedule-concat.md deleted file mode 100644 index a9bbb3d9cf..0000000000 --- a/repos/effect/.changeset/pre/eff-477-schedule-concat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `Schedule.andThen` and `Schedule.andThenResult` to `Schedule.concat` and `Schedule.concatResult`. diff --git a/repos/effect/.changeset/pre/eff-487-web-tracer-shutdown.md b/repos/effect/.changeset/pre/eff-487-web-tracer-shutdown.md deleted file mode 100644 index bc9dba25a6..0000000000 --- a/repos/effect/.changeset/pre/eff-487-web-tracer-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Ensure Web and Node tracer providers shut down when flushing fails during layer release. diff --git a/repos/effect/.changeset/pre/eff-51-partitioned-semaphore-interruption.md b/repos/effect/.changeset/pre/eff-51-partitioned-semaphore-interruption.md deleted file mode 100644 index 3d36a756c9..0000000000 --- a/repos/effect/.changeset/pre/eff-51-partitioned-semaphore-interruption.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `PartitionedSemaphore.take` leaking partially acquired permits when interrupted. diff --git a/repos/effect/.changeset/pre/eff-523-registry-scoped-atom-runtime.md b/repos/effect/.changeset/pre/eff-523-registry-scoped-atom-runtime.md deleted file mode 100644 index 2b7a28424f..0000000000 --- a/repos/effect/.changeset/pre/eff-523-registry-scoped-atom-runtime.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Scope `Atom.runtime` layer memoization to each `AtomRegistry` by default. Process-wide sharing is still available by passing a concrete `Layer.MemoMap` to `Atom.context`; the `Atom.defaultMemoMap` export has been removed. diff --git a/repos/effect/.changeset/pre/eff-532-stdio-terminal.md b/repos/effect/.changeset/pre/eff-532-stdio-terminal.md deleted file mode 100644 index 992ca220f2..0000000000 --- a/repos/effect/.changeset/pre/eff-532-stdio-terminal.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch -"@effect/platform-deno": patch ---- - -Expose `stdinIsTerminal` and `stdoutIsTerminal` effects through the `Stdio` service. diff --git a/repos/effect/.changeset/pre/eff-537-preserve-response-metadata.md b/repos/effect/.changeset/pre/eff-537-preserve-response-metadata.md deleted file mode 100644 index 606ead5b46..0000000000 --- a/repos/effect/.changeset/pre/eff-537-preserve-response-metadata.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve provider metadata when converting AI response parts into prompts. OpenAI chats using `store: true` now reuse restored item IDs as item references, while conversation-mode chats omit items already present in the conversation instead of inlining them. diff --git a/repos/effect/.changeset/pre/eff-542-rc-ref-generation.md b/repos/effect/.changeset/pre/eff-542-rc-ref-generation.md deleted file mode 100644 index a7e8aeb364..0000000000 --- a/repos/effect/.changeset/pre/eff-542-rc-ref-generation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure concurrent first `RcRef` borrowers share the same resource generation. diff --git a/repos/effect/.changeset/pre/eff-547-node-tracer-timeout.md b/repos/effect/.changeset/pre/eff-547-node-tracer-timeout.md deleted file mode 100644 index 67198957ee..0000000000 --- a/repos/effect/.changeset/pre/eff-547-node-tracer-timeout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Bound Node tracer provider shutdown by the configured `shutdownTimeout`. diff --git a/repos/effect/.changeset/pre/eff-548-bun-serve-scope.md b/repos/effect/.changeset/pre/eff-548-bun-serve-scope.md deleted file mode 100644 index a2ae20dd51..0000000000 --- a/repos/effect/.changeset/pre/eff-548-bun-serve-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Fix Bun HTTP server handler restoration and defer shutdown while serve scopes remain active. diff --git a/repos/effect/.changeset/pre/eff-549-worker-send-error.md b/repos/effect/.changeset/pre/eff-549-worker-send-error.md deleted file mode 100644 index 5bab011f5f..0000000000 --- a/repos/effect/.changeset/pre/eff-549-worker-send-error.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Report buffered worker send failures as `WorkerError` values. diff --git a/repos/effect/.changeset/pre/eff-552-txqueue-shutdown.md b/repos/effect/.changeset/pre/eff-552-txqueue-shutdown.md deleted file mode 100644 index 8e02adb773..0000000000 --- a/repos/effect/.changeset/pre/eff-552-txqueue-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `TxQueue.shutdown` safe to call after a queue has already been interrupted. diff --git a/repos/effect/.changeset/pre/eff-554-sql-resolver.md b/repos/effect/.changeset/pre/eff-554-sql-resolver.md deleted file mode 100644 index a7581b87b2..0000000000 --- a/repos/effect/.changeset/pre/eff-554-sql-resolver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent SQL resolvers from invoking non-empty batch callbacks when every request fails encoding. diff --git a/repos/effect/.changeset/pre/eff-558-deno-file-web-range.md b/repos/effect/.changeset/pre/eff-558-deno-file-web-range.md deleted file mode 100644 index e4027e6b4a..0000000000 --- a/repos/effect/.changeset/pre/eff-558-deno-file-web-range.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Honor `offset` and `bytesToRead` when creating Deno Web file responses. diff --git a/repos/effect/.changeset/pre/eff-691-default-logger-ordering.md b/repos/effect/.changeset/pre/eff-691-default-logger-ordering.md deleted file mode 100644 index 91fe0547d0..0000000000 --- a/repos/effect/.changeset/pre/eff-691-default-logger-ordering.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve message item ordering in the default logger when logging a `Cause` with message values. diff --git a/repos/effect/.changeset/pre/eff-693-rpcgroup-handler-deps.md b/repos/effect/.changeset/pre/eff-693-rpcgroup-handler-deps.md deleted file mode 100644 index e812b194a9..0000000000 --- a/repos/effect/.changeset/pre/eff-693-rpcgroup-handler-deps.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RpcGroup.toLayer` and `RpcGroup.toLayerHandler` service requirement inference so handler dependencies are preserved for non-stream RPC handlers. diff --git a/repos/effect/.changeset/pre/eff-694-cli-completions-module.md b/repos/effect/.changeset/pre/eff-694-cli-completions-module.md deleted file mode 100644 index fcce2923db..0000000000 --- a/repos/effect/.changeset/pre/eff-694-cli-completions-module.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose CLI completions as a public unstable module at `effect/unstable/cli/Completions`. diff --git a/repos/effect/.changeset/pre/eff-695-layer-mock-dual-api.md b/repos/effect/.changeset/pre/eff-695-layer-mock-dual-api.md deleted file mode 100644 index b4766bb3fa..0000000000 --- a/repos/effect/.changeset/pre/eff-695-layer-mock-dual-api.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `Layer.mock` a dual API so it supports both `Layer.mock(Service)(impl)` and `Layer.mock(Service, impl)`. diff --git a/repos/effect/.changeset/pre/eff-697-rpcserialization-json-array-decode.md b/repos/effect/.changeset/pre/eff-697-rpcserialization-json-array-decode.md deleted file mode 100644 index 062fcf8637..0000000000 --- a/repos/effect/.changeset/pre/eff-697-rpcserialization-json-array-decode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RpcSerialization.json` decode so JSON array payloads are not wrapped in an extra outer array. diff --git a/repos/effect/.changeset/pre/eff-698-rpcserialization-unreachable-branch.md b/repos/effect/.changeset/pre/eff-698-rpcserialization-unreachable-branch.md deleted file mode 100644 index cb1b1de996..0000000000 --- a/repos/effect/.changeset/pre/eff-698-rpcserialization-unreachable-branch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove an unreachable array branch in `decodeJsonRpcRaw` to simplify JSON-RPC decode logic without changing behavior. diff --git a/repos/effect/.changeset/pre/eff-700-httpapi-middleware-errors.md b/repos/effect/.changeset/pre/eff-700-httpapi-middleware-errors.md deleted file mode 100644 index ef46ad705f..0000000000 --- a/repos/effect/.changeset/pre/eff-700-httpapi-middleware-errors.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch ---- - -Improve unstable HttpApi runtime failures for missing server middleware and missing group implementations. - -- HttpApiBuilder.applyMiddleware now resolves middleware services via Context.getUnsafe, so missing middleware fails with a clear "Service not found: " error instead of an opaque is not a function TypeError. -- HttpApiBuilder.layer now reports missing groups with actionable context (group identifier, service key, suggested HttpApiBuilder.group(...) call, and available group keys). -- Added regression tests in packages/platform/node/test/HttpApi.test.ts covering: - - addHttpApi + API-level middleware applied across merged groups - - missing middleware service diagnostics - - missing addHttpApi group layer diagnostics diff --git a/repos/effect/.changeset/pre/eff-701-httpapierror-respondable.md b/repos/effect/.changeset/pre/eff-701-httpapierror-respondable.md deleted file mode 100644 index 9b78e505ee..0000000000 --- a/repos/effect/.changeset/pre/eff-701-httpapierror-respondable.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make all built-in `HttpApiError` classes implement `HttpServerRespondable`, so they can be returned directly from plain HTTP server handlers outside of `HttpApi`. diff --git a/repos/effect/.changeset/pre/eff-704-stream-merge-predicate.md b/repos/effect/.changeset/pre/eff-704-stream-merge-predicate.md deleted file mode 100644 index a312260891..0000000000 --- a/repos/effect/.changeset/pre/eff-704-stream-merge-predicate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use predicate-based `dual` dispatch for `Stream.merge` so data-last calls with optional `options` are handled correctly. diff --git a/repos/effect/.changeset/pre/eff-705-layer-tap-apis.md b/repos/effect/.changeset/pre/eff-705-layer-tap-apis.md deleted file mode 100644 index c6b96bc0dd..0000000000 --- a/repos/effect/.changeset/pre/eff-705-layer-tap-apis.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Layer.tap`, `Layer.tapError`, and `Layer.tapCause` APIs for effectful observation of layer success and failure without changing layer outputs. diff --git a/repos/effect/.changeset/pre/eff-706-servicemap-mutate.md b/repos/effect/.changeset/pre/eff-706-servicemap-mutate.md deleted file mode 100644 index 6ce63c443a..0000000000 --- a/repos/effect/.changeset/pre/eff-706-servicemap-mutate.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/opentelemetry": patch ---- - -Refactor call sites with multiple `Context` mutations to use `Context.mutate` for batched updates. diff --git a/repos/effect/.changeset/pre/eff-716-response-id-tracker-map.md b/repos/effect/.changeset/pre/eff-716-response-id-tracker-map.md deleted file mode 100644 index 8605f1bc89..0000000000 --- a/repos/effect/.changeset/pre/eff-716-response-id-tracker-map.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use a normal Map in ResponseIdTracker and clear it on divergence / reset instead of reallocating a WeakMap. diff --git a/repos/effect/.changeset/pre/eff-717-openai-socket-cancel.md b/repos/effect/.changeset/pre/eff-717-openai-socket-cancel.md deleted file mode 100644 index d4adf58a72..0000000000 --- a/repos/effect/.changeset/pre/eff-717-openai-socket-cancel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Ensure OpenAiSocket sends a `{"type":"response.cancel"}` websocket event when a response stream is interrupted. diff --git a/repos/effect/.changeset/pre/eff-718-embedding-model-surface.md b/repos/effect/.changeset/pre/eff-718-embedding-model-surface.md deleted file mode 100644 index e8754bd3e9..0000000000 --- a/repos/effect/.changeset/pre/eff-718-embedding-model-surface.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Add unstable EmbeddingModel support across core and OpenAI providers. - -- Add the unstable EmbeddingModel module API surface in `effect`, including service, request, response, and provider types. -- Implement the unstable EmbeddingModel runtime constructor in `effect`, with `RequestResolver` batching, `embed` / `embedMany` spans, provider error propagation, deterministic ordering, and empty-input `embedMany` fast-path behavior. -- Add and align EmbeddingModel behavior tests in `effect` for embedding usage, batching, ordering, and error handling. -- Add `OpenAiEmbeddingModel` in `@effect/ai-openai`, including model / make / layer constructors, config overrides, and provider output index validation with deterministic reordering. -- Add OpenAI-compatible EmbeddingModel provider support in `@effect/ai-openai-compat`, including config overrides, layer constructors, and output index validation. diff --git a/repos/effect/.changeset/pre/eff-725-fix-catch-jsdoc.md b/repos/effect/.changeset/pre/eff-725-fix-catch-jsdoc.md deleted file mode 100644 index a5ca6b5222..0000000000 --- a/repos/effect/.changeset/pre/eff-725-fix-catch-jsdoc.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix JSDoc wording for `Effect.catch` to consistently reference the current API name. diff --git a/repos/effect/.changeset/pre/eff-726-model-dimensions.md b/repos/effect/.changeset/pre/eff-726-model-dimensions.md deleted file mode 100644 index 1a6022bcff..0000000000 --- a/repos/effect/.changeset/pre/eff-726-model-dimensions.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Add `EmbeddingModel.ModelDimensions` and require dimensions in embedding provider `model` constructors. diff --git a/repos/effect/.changeset/pre/eff-727-cli-help-alignment.md b/repos/effect/.changeset/pre/eff-727-cli-help-alignment.md deleted file mode 100644 index 32e9ada50d..0000000000 --- a/repos/effect/.changeset/pre/eff-727-cli-help-alignment.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Align CLI help flag and global flag descriptions to a single column even when some flag names are very long. diff --git a/repos/effect/.changeset/pre/eff-730-language-model-incremental-fallback.md b/repos/effect/.changeset/pre/eff-730-language-model-incremental-fallback.md deleted file mode 100644 index 55fa6a0a2d..0000000000 --- a/repos/effect/.changeset/pre/eff-730-language-model-incremental-fallback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `LanguageModel` incremental prompt fallback to reliably retry with the full prompt when an incremental request fails with `InvalidRequestError`. diff --git a/repos/effect/.changeset/pre/eff-736-cached-with-ttl.md b/repos/effect/.changeset/pre/eff-736-cached-with-ttl.md deleted file mode 100644 index 8ef05290bf..0000000000 --- a/repos/effect/.changeset/pre/eff-736-cached-with-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.cachedWithTTL` and `Effect.cachedInvalidateWithTTL` to start TTL expiration when the cached value is produced instead of when computation starts. diff --git a/repos/effect/.changeset/pre/eff-738-cron-prev.md b/repos/effect/.changeset/pre/eff-738-cron-prev.md deleted file mode 100644 index b9109d4b71..0000000000 --- a/repos/effect/.changeset/pre/eff-738-cron-prev.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Backport `Cron.prev` with reverse lookup tables and cron stepping logic, including DST-aware reverse traversal. diff --git a/repos/effect/.changeset/pre/eff-739-openai-function-call-done.md b/repos/effect/.changeset/pre/eff-739-openai-function-call-done.md deleted file mode 100644 index 9069313777..0000000000 --- a/repos/effect/.changeset/pre/eff-739-openai-function-call-done.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Handle streamed OpenAI function calls from `response.function_call_arguments.done` so tool calls are emitted even when `response.output_item.done` is missing. diff --git a/repos/effect/.changeset/pre/eff-740-missing-summary-parts.md b/repos/effect/.changeset/pre/eff-740-missing-summary-parts.md deleted file mode 100644 index c481d830ed..0000000000 --- a/repos/effect/.changeset/pre/eff-740-missing-summary-parts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Fix OpenAI reasoning stream state handling so out-of-order reasoning summary events do not crash when prior reasoning item state is missing. diff --git a/repos/effect/.changeset/pre/eff-742-http-client-request-web.md b/repos/effect/.changeset/pre/eff-742-http-client-request-web.md deleted file mode 100644 index 47c201686e..0000000000 --- a/repos/effect/.changeset/pre/eff-742-http-client-request-web.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/http HttpClientRequest: add toWeb and fromWeb conversions for web Request objects diff --git a/repos/effect/.changeset/pre/eff-744-sqlite-migrator-lock.md b/repos/effect/.changeset/pre/eff-744-sqlite-migrator-lock.md deleted file mode 100644 index badee2a510..0000000000 --- a/repos/effect/.changeset/pre/eff-744-sqlite-migrator-lock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix sql migrator lock handling to only treat duplicate migration-row inserts as a concurrent migration lock. diff --git a/repos/effect/.changeset/pre/eff-746-fixed-iteration-catchup.md b/repos/effect/.changeset/pre/eff-746-fixed-iteration-catchup.md deleted file mode 100644 index db267d594c..0000000000 --- a/repos/effect/.changeset/pre/eff-746-fixed-iteration-catchup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.fixed` to run the next iteration immediately when the previous action takes longer than the configured interval. diff --git a/repos/effect/.changeset/pre/eff-747-unify-effect.md b/repos/effect/.changeset/pre/eff-747-unify-effect.md deleted file mode 100644 index 251737c054..0000000000 --- a/repos/effect/.changeset/pre/eff-747-unify-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Unify.unify` so unions of `Effect` values collapse to a single unified `Effect` type again. diff --git a/repos/effect/.changeset/pre/eff-754-url-builder-any.md b/repos/effect/.changeset/pre/eff-754-url-builder-any.md deleted file mode 100644 index 9dbd7bbc30..0000000000 --- a/repos/effect/.changeset/pre/eff-754-url-builder-any.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Relax `HttpApiClient.urlBuilder` to accept `HttpApi.Any` instead of requiring `HttpApi.AnyWithProps`. -This allows use in helpers generic over `HttpApi.Any` while preserving inferred URL builder types. diff --git a/repos/effect/.changeset/pre/eff-755-references-core.md b/repos/effect/.changeset/pre/eff-755-references-core.md deleted file mode 100644 index 76715f96ab..0000000000 --- a/repos/effect/.changeset/pre/eff-755-references-core.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Re-export additional core runtime references from `effect/References`, including logger and error reporter references. diff --git a/repos/effect/.changeset/pre/eff-769-select-text-highlight.md b/repos/effect/.changeset/pre/eff-769-select-text-highlight.md deleted file mode 100644 index f69988f18c..0000000000 --- a/repos/effect/.changeset/pre/eff-769-select-text-highlight.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Highlight active option labels in `Prompt.select` and `Prompt.multiSelect` using cyan text so selection state is visible beyond the pointer / checkbox icon. diff --git a/repos/effect/.changeset/pre/eff-774-mutable-list-append-all-empty-array.md b/repos/effect/.changeset/pre/eff-774-mutable-list-append-all-empty-array.md deleted file mode 100644 index 7a3ee8cf09..0000000000 --- a/repos/effect/.changeset/pre/eff-774-mutable-list-append-all-empty-array.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `MutableList.appendAll` / `appendAllUnsafe` so empty arrays are treated as a no-op instead of leaving behind an empty internal bucket. diff --git a/repos/effect/.changeset/pre/eff-777-schema-make-effect.md b/repos/effect/.changeset/pre/eff-777-schema-make-effect.md deleted file mode 100644 index d80d6151e7..0000000000 --- a/repos/effect/.changeset/pre/eff-777-schema-make-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `schema.makeEffect(input, options?)` to `Schema.Bottom` and schema-backed classes, matching the existing constructor behavior exposed by `makeUnsafe` / `makeOption` while returning an `Effect` failure with `Schema.SchemaError`. diff --git a/repos/effect/.changeset/pre/eff-778-http-middleware-path-logger.md b/repos/effect/.changeset/pre/eff-778-http-middleware-path-logger.md deleted file mode 100644 index 716bc47334..0000000000 --- a/repos/effect/.changeset/pre/eff-778-http-middleware-path-logger.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update the unstable HTTP middleware logger to annotate only the request path in `http.url` instead of including the full URL (query / fragment), and add a regression test. diff --git a/repos/effect/.changeset/pre/eff-779-keyvaluestore-layer-sql.md b/repos/effect/.changeset/pre/eff-779-keyvaluestore-layer-sql.md deleted file mode 100644 index ce80278ff0..0000000000 --- a/repos/effect/.changeset/pre/eff-779-keyvaluestore-layer-sql.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `KeyValueStore.layerSql` to back key-value storage with a SQL database via `SqlClient`. diff --git a/repos/effect/.changeset/pre/eff-780-layer-unify.md b/repos/effect/.changeset/pre/eff-780-layer-unify.md deleted file mode 100644 index 59bc0c2a45..0000000000 --- a/repos/effect/.changeset/pre/eff-780-layer-unify.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Unify.unify` so Layer unions merge correctly, and add type tests covering Layer unification. diff --git a/repos/effect/.changeset/pre/eff-781-fix-stream-toqueue-types.md b/repos/effect/.changeset/pre/eff-781-fix-stream-toqueue-types.md deleted file mode 100644 index 3f3ff719f8..0000000000 --- a/repos/effect/.changeset/pre/eff-781-fix-stream-toqueue-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.toQueue` types and implementation to return a `Queue.Dequeue` in both overloads and delegate to `Channel.toQueueArray`. diff --git a/repos/effect/.changeset/pre/eff-782-httpapi-status-literals.md b/repos/effect/.changeset/pre/eff-782-httpapi-status-literals.md deleted file mode 100644 index 6eb4c1ec9f..0000000000 --- a/repos/effect/.changeset/pre/eff-782-httpapi-status-literals.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add support for common HTTP status string literals in `HttpApiSchema.status` (for example, `HttpApiSchema.status("Created")` resolves to status code `201`). diff --git a/repos/effect/.changeset/pre/eff-783-atom-http-api-errors.md b/repos/effect/.changeset/pre/eff-783-atom-http-api-errors.md deleted file mode 100644 index f62f2ae8f1..0000000000 --- a/repos/effect/.changeset/pre/eff-783-atom-http-api-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `AtomHttpApi` query and mutation error inference to include endpoint middleware and client middleware errors, matching `HttpApiClient` behavior (including response-only mutation mode). diff --git a/repos/effect/.changeset/pre/eff-798-schema-parser.md b/repos/effect/.changeset/pre/eff-798-schema-parser.md deleted file mode 100644 index e7ca179c97..0000000000 --- a/repos/effect/.changeset/pre/eff-798-schema-parser.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve synchronous Schema decode and encode performance by preserving completed parser exits and using a direct loop for common struct parsers. diff --git a/repos/effect/.changeset/pre/eff-802-tagged-union-match-or-else.md b/repos/effect/.changeset/pre/eff-802-tagged-union-match-or-else.md deleted file mode 100644 index 0336f28d21..0000000000 --- a/repos/effect/.changeset/pre/eff-802-tagged-union-match-or-else.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.TaggedUnion.matchOrElse` for partial case matching with a typed fallback. diff --git a/repos/effect/.changeset/pre/eff-806-schema-error-stack.md b/repos/effect/.changeset/pre/eff-806-schema-error-stack.md deleted file mode 100644 index 339f7f3676..0000000000 --- a/repos/effect/.changeset/pre/eff-806-schema-error-stack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve `SchemaError` construction performance by skipping stack frame capture. diff --git a/repos/effect/.changeset/pre/eff-819-cluster-workflow-shard-groups.md b/repos/effect/.changeset/pre/eff-819-cluster-workflow-shard-groups.md deleted file mode 100644 index 852c583398..0000000000 --- a/repos/effect/.changeset/pre/eff-819-cluster-workflow-shard-groups.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure ClusterWorkflowEngine routes durable clock wakeups and registered workflow deferred completions through the owning workflow's shard group. diff --git a/repos/effect/.changeset/pre/eff-825-pool-performance.md b/repos/effect/.changeset/pre/eff-825-pool-performance.md deleted file mode 100644 index 8f9d71792b..0000000000 --- a/repos/effect/.changeset/pre/eff-825-pool-performance.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch ---- - -Improve Pool acquisition and release performance. Pool now tracks usage -incrementally, stores available items in an intrusive FIFO, and skips work for -fixed and empty pools. This changes the public `Pool.State` and `Pool.PoolItem` -interfaces. diff --git a/repos/effect/.changeset/pre/eff-825-pool-use.md b/repos/effect/.changeset/pre/eff-825-pool-use.md deleted file mode 100644 index ddea9d1b05..0000000000 --- a/repos/effect/.changeset/pre/eff-825-pool-use.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Add `Pool.use`, which borrows an item while an effect runs and returns it on any -exit. Unlike `Effect.scoped(Pool.get(pool))`, it does not require a `Scope`. diff --git a/repos/effect/.changeset/pre/eff-825-scope-single-finalizer.md b/repos/effect/.changeset/pre/eff-825-scope-single-finalizer.md deleted file mode 100644 index fb77cd78c1..0000000000 --- a/repos/effect/.changeset/pre/eff-825-scope-single-finalizer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Reduce scoped resource acquisition allocations by storing the first Scope -finalizer inline and allocating a Map only when a second is added. This changes -the public `Scope.State.Open` interface. diff --git a/repos/effect/.changeset/pre/eff-837-dependency-updates.md b/repos/effect/.changeset/pre/eff-837-dependency-updates.md deleted file mode 100644 index 2d249ad2c9..0000000000 --- a/repos/effect/.changeset/pre/eff-837-dependency-updates.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/doctest": patch -"@effect/sql-d1": patch -"@effect/sql-mysql2": patch -"@effect/sql-pglite": patch ---- - -Update production dependencies to their latest releases. diff --git a/repos/effect/.changeset/pre/eff-849-transpose-option.md b/repos/effect/.changeset/pre/eff-849-transpose-option.md deleted file mode 100644 index ccdbd42810..0000000000 --- a/repos/effect/.changeset/pre/eff-849-transpose-option.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.transposeOption` for converting an `Option>` into an `Effect, E, R>`. diff --git a/repos/effect/.changeset/pre/eff-851-skip-empty-remote-write.md b/repos/effect/.changeset/pre/eff-851-skip-empty-remote-write.md deleted file mode 100644 index 95de0f05e0..0000000000 --- a/repos/effect/.changeset/pre/eff-851-skip-empty-remote-write.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Skip remote event journal write callbacks when there are no uncommitted entries and return an `Option` indicating -whether the callback ran. diff --git a/repos/effect/.changeset/pre/eff-854-pg-connection-startup.md b/repos/effect/.changeset/pre/eff-854-pg-connection-startup.md deleted file mode 100644 index f9b351dd96..0000000000 --- a/repos/effect/.changeset/pre/eff-854-pg-connection-startup.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@effect/sql-pg": patch -"effect": patch ---- - -Replace `@effect/sql-pg`'s `pg` runtime with a native PostgreSQL client. `PgConnection` and `PgPool` now handle connection setup, binary queries, prepared statements, pipelining, streaming, notifications, cancellation, and custom codecs. `PgConnection.listen` and `PgClient.listen` return scoped notification dequeues after PostgreSQL confirms the subscription. `PgClient` uses the native stack, and the legacy `fromPool`, `fromClient`, and `makeWith` constructors are removed. - -### Breaking changes - -- `fromPool`, `fromClient`, and `makeWith` are removed. Use `make` for a pool or `makeClient` for one connection. -- `PgClient.listen` returns a scoped `Effect, SqlError, Scope>` instead of a `Stream`. Acquisition completes after PostgreSQL confirms `LISTEN`, so notifications sent after it returns cannot be missed. -- `PgClientConfig.types` now accepts a `PgTypes.Registry` instead of `pg.CustomTypesConfig`. Plain object parameters are no longer inferred as JSON; wrap them with `sql.json`. -- Query strings must contain one statement. PostgreSQL's extended protocol rejects multi-statement strings. -- Results use the native binary codecs. In particular, `int8` decodes to `bigint`, `date` to a string, timestamps to Unix epoch milliseconds, and `bytea` or unknown OIDs to `Uint8Array`. `executeRaw` returns the native `PgConnection.Result` shape rather than `pg.Result`. -- Named prepared statements are enabled by default. Set `prepare: false` when using a pooler that cannot preserve prepared statements between queries. `Statement.unprepared` and `Statement.valuesUnprepared` use unnamed extended queries without adding entries to the prepared-statement cache. - -Inferred parameters stay permissive: strings bind untyped so the backend derives the type from the statement, and safe integers beyond the `int4` range bind as `int8`. - -Add `Pool.reserve` for exclusive access to a concurrent pool item, and fix waiter wakeups and capacity replacement after invalidation. diff --git a/repos/effect/.changeset/pre/eff-861-indexeddb-equals.md b/repos/effect/.changeset/pre/eff-861-indexeddb-equals.md deleted file mode 100644 index c3f17f1c8b..0000000000 --- a/repos/effect/.changeset/pre/eff-861-indexeddb-equals.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix IndexedDB `.equals` queries on tables without key paths to accept out-of-line `IDBValidKey` values. diff --git a/repos/effect/.changeset/pre/eff-946-concurrent-traversal-cleanup.md b/repos/effect/.changeset/pre/eff-946-concurrent-traversal-cleanup.md deleted file mode 100644 index 1ae7fabb1e..0000000000 --- a/repos/effect/.changeset/pre/eff-946-concurrent-traversal-cleanup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Interrupt and await concurrent traversal workers when mapper or refill callbacks throw. diff --git a/repos/effect/.changeset/pre/eff-952-terminal-failure-stack.md b/repos/effect/.changeset/pre/eff-952-terminal-failure-stack.md deleted file mode 100644 index 5c72f8d56a..0000000000 --- a/repos/effect/.changeset/pre/eff-952-terminal-failure-stack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve current stack frame annotations on terminal root failures. diff --git a/repos/effect/.changeset/pre/eff-953-interruptor-stack-trace.md b/repos/effect/.changeset/pre/eff-953-interruptor-stack-trace.md deleted file mode 100644 index aeca222b2f..0000000000 --- a/repos/effect/.changeset/pre/eff-953-interruptor-stack-trace.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Store interrupting fiber stack frames separately from interrupted target stack frames. diff --git a/repos/effect/.changeset/pre/eff-955-run-sync-dispatcher.md b/repos/effect/.changeset/pre/eff-955-run-sync-dispatcher.md deleted file mode 100644 index 22865608b6..0000000000 --- a/repos/effect/.changeset/pre/eff-955-run-sync-dispatcher.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Avoid allocating a scheduler dispatcher when `runSyncExit` completes without yielding. diff --git a/repos/effect/.changeset/pre/eff-956-await-all-children.md b/repos/effect/.changeset/pre/eff-956-await-all-children.md deleted file mode 100644 index 8f892c153c..0000000000 --- a/repos/effect/.changeset/pre/eff-956-await-all-children.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make awaitAllChildren child selection linear in the number of fibers. diff --git a/repos/effect/.changeset/pre/eff-961-node-socket-tls.md b/repos/effect/.changeset/pre/eff-961-node-socket-tls.md deleted file mode 100644 index f00fe3a71a..0000000000 --- a/repos/effect/.changeset/pre/eff-961-node-socket-tls.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch ---- - -Add `NodeSocket.makeTls`, `NodeSocket.makeTlsChannel`, and `NodeSocket.layerTls` for TLS client connections. - -These mirror the existing `makeNet` family but dial `tls.connect`, so they take the full `tls.ConnectionOptions` set: -trust anchors (`ca`), client certificates (`cert` / `key`), ALPN protocols, and `servername`. The socket opens once the -handshake completes; a failed handshake fails with a `SocketOpenError`. diff --git a/repos/effect/.changeset/pre/eff-963-node-socket-server-tls.md b/repos/effect/.changeset/pre/eff-963-node-socket-server-tls.md deleted file mode 100644 index dfac557a2b..0000000000 --- a/repos/effect/.changeset/pre/eff-963-node-socket-server-tls.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch ---- - -Add `NodeSocketServer.makeTls` and `NodeSocketServer.layerTls` for TLS socket servers. diff --git a/repos/effect/.changeset/pre/eff-965-socket-upgrade.md b/repos/effect/.changeset/pre/eff-965-socket-upgrade.md deleted file mode 100644 index 019a3c48cf..0000000000 --- a/repos/effect/.changeset/pre/eff-965-socket-upgrade.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch ---- - -Add Socket.upgrade, for upgrading tcp sockets using STARTTLS diff --git a/repos/effect/.changeset/pre/eff-969-dependency-updates.md b/repos/effect/.changeset/pre/eff-969-dependency-updates.md deleted file mode 100644 index 2d249ad2c9..0000000000 --- a/repos/effect/.changeset/pre/eff-969-dependency-updates.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/doctest": patch -"@effect/sql-d1": patch -"@effect/sql-mysql2": patch -"@effect/sql-pglite": patch ---- - -Update production dependencies to their latest releases. diff --git a/repos/effect/.changeset/pre/eff-972-deno-socket-server.md b/repos/effect/.changeset/pre/eff-972-deno-socket-server.md deleted file mode 100644 index 87e1b20d5c..0000000000 --- a/repos/effect/.changeset/pre/eff-972-deno-socket-server.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect/platform-deno": patch -"@effect/platform-node-shared": patch ---- - -Use the node-shared socket server on Deno so accepted TCP connections support reader-scoped server TLS upgrades. - -### Breaking changes - -`DenoSocketServer.make` and `layer` now accept Node listen options. Use `host` instead of `hostname` for TCP and -`{ path }` instead of `{ transport: "unix", path }` for Unix sockets. Deno 2.8.3 or newer is now required. diff --git a/repos/effect/.changeset/pre/eff-997-service-keys.md b/repos/effect/.changeset/pre/eff-997-service-keys.md deleted file mode 100644 index 8ba1ef8c6c..0000000000 --- a/repos/effect/.changeset/pre/eff-997-service-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize core service and runtime identities under their owning module namespaces. diff --git a/repos/effect/.changeset/pre/effect-eager-transform-arguments.md b/repos/effect/.changeset/pre/effect-eager-transform-arguments.md deleted file mode 100644 index 05be690d22..0000000000 --- a/repos/effect/.changeset/pre/effect-eager-transform-arguments.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.fnUntracedEager` to pass the original function arguments to each transform after the current effect, matching `Effect.fn` and `Effect.fnUntraced`. diff --git a/repos/effect/.changeset/pre/effect-scoped-service-restoration.md b/repos/effect/.changeset/pre/effect-scoped-service-restoration.md deleted file mode 100644 index ffc7b79abf..0000000000 --- a/repos/effect/.changeset/pre/effect-scoped-service-restoration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.updateServiceScoped` cleanup when an inner service provider has already completed. Closing the scope now preserves the service's absence instead of failing with a missing-service defect. diff --git a/repos/effect/.changeset/pre/effect-unmatched-reason-preservation.md b/repos/effect/.changeset/pre/effect-unmatched-reason-preservation.md deleted file mode 100644 index 38f3af546a..0000000000 --- a/repos/effect/.changeset/pre/effect-unmatched-reason-preservation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve the original cause in `Effect.catchReason` and `Effect.catchReasons` when no nested reason matches and no fallback is provided. diff --git a/repos/effect/.changeset/pre/effectable-class-override-delegation.md b/repos/effect/.changeset/pre/effectable-class-override-delegation.md deleted file mode 100644 index 9a99f8e882..0000000000 --- a/repos/effect/.changeset/pre/effectable-class-override-delegation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effectable.Class` evaluation by delegating to its abstract `asEffect()` method. The method is called on the instance for each execution, preserving current receiver state and provided services. diff --git a/repos/effect/.changeset/pre/effectable-mixin.md b/repos/effect/.changeset/pre/effectable-mixin.md deleted file mode 100644 index b087c5cde8..0000000000 --- a/repos/effect/.changeset/pre/effectable-mixin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effectable.Mixin` to insert the Effect prototype into an existing class inheritance chain. The returned abstract class requires an `asEffect` method and derives its Effect type from that method through polymorphic `this`. diff --git a/repos/effect/.changeset/pre/effectify-mapper-input-tuples.md b/repos/effect/.changeset/pre/effectify-mapper-input-tuples.md deleted file mode 100644 index 9f64953080..0000000000 --- a/repos/effect/.changeset/pre/effectify-mapper-input-tuples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the `onError` and `onSyncError` argument tuple types in `Effect.effectify` to include only caller inputs, excluding the synthesized callback. Mapper annotations that expected a callback slot must use the caller-input tuple instead. Runtime behavior is unchanged. diff --git a/repos/effect/.changeset/pre/eight-turkeys-own.md b/repos/effect/.changeset/pre/eight-turkeys-own.md deleted file mode 100644 index 1665cd0a44..0000000000 --- a/repos/effect/.changeset/pre/eight-turkeys-own.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-node": patch -"@effect/platform-bun": patch -"effect": patch ---- - -use cause annotations for detecting client aborts diff --git a/repos/effect/.changeset/pre/eighty-lies-deny.md b/repos/effect/.changeset/pre/eighty-lies-deny.md deleted file mode 100644 index 3b6e333ce9..0000000000 --- a/repos/effect/.changeset/pre/eighty-lies-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -change default ErrorReporter severity to Info diff --git a/repos/effect/.changeset/pre/eighty-poets-draw.md b/repos/effect/.changeset/pre/eighty-poets-draw.md deleted file mode 100644 index 27a23ed375..0000000000 --- a/repos/effect/.changeset/pre/eighty-poets-draw.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch ---- - -Add dedicated AiError metadata interfaces per reason so provider packages can safely augment metadata without conflicting module declarations. diff --git a/repos/effect/.changeset/pre/eighty-swans-scream.md b/repos/effect/.changeset/pre/eighty-swans-scream.md deleted file mode 100644 index eff5092037..0000000000 --- a/repos/effect/.changeset/pre/eighty-swans-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add HttpApiSchemaError for determining where a schema error originates from diff --git a/repos/effect/.changeset/pre/eighty-teeth-sniff.md b/repos/effect/.changeset/pre/eighty-teeth-sniff.md deleted file mode 100644 index 4ce98bc8c9..0000000000 --- a/repos/effect/.changeset/pre/eighty-teeth-sniff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -simplify Filter by removing Args type parameter diff --git a/repos/effect/.changeset/pre/eleven-apes-share.md b/repos/effect/.changeset/pre/eleven-apes-share.md deleted file mode 100644 index 4e63ebc137..0000000000 --- a/repos/effect/.changeset/pre/eleven-apes-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Add a new `OpenAiClientGenerated` module that exposes the generated OpenAI client as a dedicated context service with `make`, `layer`, and `layerConfig` constructors. This provides a compatibility path for direct generated-client access while preserving existing auth, base URL, header, and `OpenAiConfig.transformClient` wiring. diff --git a/repos/effect/.changeset/pre/eleven-numbers-bake.md b/repos/effect/.changeset/pre/eleven-numbers-bake.md deleted file mode 100644 index 7966ef89b9..0000000000 --- a/repos/effect/.changeset/pre/eleven-numbers-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -pass workflow parent on discard diff --git a/repos/effect/.changeset/pre/empty-env-values-missing.md b/repos/effect/.changeset/pre/empty-env-values-missing.md deleted file mode 100644 index cd894f95d7..0000000000 --- a/repos/effect/.changeset/pre/empty-env-values-missing.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Treat empty strings as missing values in built-in `ConfigProvider`s by default. - -`ConfigProvider.fromEnv`, `ConfigProvider.fromDotEnvContents`, `ConfigProvider.fromDotEnv`, `ConfigProvider.fromUnknown`, and `ConfigProvider.fromDir` now treat literal empty strings as absent values when loaded as values, allowing `Config.withDefault` and `Config.option` to recover. Container discovery still reflects the source structure. Pass `preserveEmptyStrings: true` to restore the previous behavior. - -`ConfigProvider.fromDotEnv({ expandVariables: true })` now expands variables consistently with `ConfigProvider.fromDotEnvContents`. diff --git a/repos/effect/.changeset/pre/empty-geckos-dispatch.md b/repos/effect/.changeset/pre/empty-geckos-dispatch.md deleted file mode 100644 index e082b11e56..0000000000 --- a/repos/effect/.changeset/pre/empty-geckos-dispatch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Encode SSE events with empty data as dispatchable events. diff --git a/repos/effect/.changeset/pre/empty-gifts-beg.md b/repos/effect/.changeset/pre/empty-gifts-beg.md deleted file mode 100644 index c7d39823c2..0000000000 --- a/repos/effect/.changeset/pre/empty-gifts-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -process schema properties / elements concurrently diff --git a/repos/effect/.changeset/pre/empty-http-rpc-client.md b/repos/effect/.changeset/pre/empty-http-rpc-client.md deleted file mode 100644 index 36685f7eec..0000000000 --- a/repos/effect/.changeset/pre/empty-http-rpc-client.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fail RpcClient HTTP requests when the server response contains no RPC messages instead of leaving requests pending. diff --git a/repos/effect/.changeset/pre/empty-snakes-return.md b/repos/effect/.changeset/pre/empty-snakes-return.md deleted file mode 100644 index 94ae6a96ff..0000000000 --- a/repos/effect/.changeset/pre/empty-snakes-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `String.snakeToCamel` and `String.snakeToPascal` to return an empty string for empty input. diff --git a/repos/effect/.changeset/pre/entity-proxy-client-codec-services.md b/repos/effect/.changeset/pre/entity-proxy-client-codec-services.md deleted file mode 100644 index a7e883600c..0000000000 --- a/repos/effect/.changeset/pre/entity-proxy-client-codec-services.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `EntityProxyServer` handler layers to include client-side codec service requirements. diff --git a/repos/effect/.changeset/pre/entity-test-client-fatal-defect-option.md b/repos/effect/.changeset/pre/entity-test-client-fatal-defect-option.md deleted file mode 100644 index 403acc21d2..0000000000 --- a/repos/effect/.changeset/pre/entity-test-client-fatal-defect-option.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor the entity layer's `disableFatalDefects` option in `Entity.makeTestClient`. When enabled, a handler defect no longer fails other pending calls to the same entity ID. The failing call still reports its defect; omitted or false options retain fatal-defect behavior. diff --git a/repos/effect/.changeset/pre/escape-completion-choice-values.md b/repos/effect/.changeset/pre/escape-completion-choice-values.md deleted file mode 100644 index 2b3433363c..0000000000 --- a/repos/effect/.changeset/pre/escape-completion-choice-values.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix shell completion for choice values containing quotes, spaces, word-break characters, Unicode, and shell metacharacters. - -Bash now quotes candidates for readline, keeps choice values intact when reconstructing words, and supports Bash 3.2 without associative arrays. Fish and Zsh escape choices across both parsing rounds, and Fish hides value-taking flags after use without suppressing their value completions. diff --git a/repos/effect/.changeset/pre/eventlog-authentication-forbidden-retry.md b/repos/effect/.changeset/pre/eventlog-authentication-forbidden-retry.md deleted file mode 100644 index 44b83c11b0..0000000000 --- a/repos/effect/.changeset/pre/eventlog-authentication-forbidden-retry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Retry `EventLogRemote` writes and change streams when authentication returns `Forbidden`. diff --git a/repos/effect/.changeset/pre/eventlog-empty-chunk-framing.md b/repos/effect/.changeset/pre/eventlog-empty-chunk-framing.md deleted file mode 100644 index c289354641..0000000000 --- a/repos/effect/.changeset/pre/eventlog-empty-chunk-framing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Return one empty chunk when `ChunkedMessage.split` receives an empty `Uint8Array`. diff --git a/repos/effect/.changeset/pre/eventlog-unencrypted.md b/repos/effect/.changeset/pre/eventlog-unencrypted.md deleted file mode 100644 index 2a3e8a8e88..0000000000 --- a/repos/effect/.changeset/pre/eventlog-unencrypted.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add EventLogServerUnencrypted module diff --git a/repos/effect/.changeset/pre/every-olives-burn.md b/repos/effect/.changeset/pre/every-olives-burn.md deleted file mode 100644 index 87e34c2c83..0000000000 --- a/repos/effect/.changeset/pre/every-olives-burn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -change default operation in redis from LPUSH TO RPUSH diff --git a/repos/effect/.changeset/pre/exact-byte-size.md b/repos/effect/.changeset/pre/exact-byte-size.md deleted file mode 100644 index d3f07145d4..0000000000 --- a/repos/effect/.changeset/pre/exact-byte-size.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-node-shared": patch -"@effect/platform-node": patch ---- - -Add `ByteSize` module and use it across the ecosystem diff --git a/repos/effect/.changeset/pre/exact-http-file-body-length.md b/repos/effect/.changeset/pre/exact-http-file-body-length.md deleted file mode 100644 index 72c8cd6b8d..0000000000 --- a/repos/effect/.changeset/pre/exact-http-file-body-length.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Calculate `HttpBody.file`, `HttpBody.fileFromInfo`, and `HttpClientRequest.bodyFile` content lengths with exact bigint arithmetic and EOF clamping. diff --git a/repos/effect/.changeset/pre/execution-plan-captured-predicate.md b/repos/effect/.changeset/pre/execution-plan-captured-predicate.md deleted file mode 100644 index 5dbd29881c..0000000000 --- a/repos/effect/.changeset/pre/execution-plan-captured-predicate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `ExecutionPlan.captureRequirements` provides captured services to effectful `while` predicates. diff --git a/repos/effect/.changeset/pre/expand-schema-filter-output.md b/repos/effect/.changeset/pre/expand-schema-filter-output.md deleted file mode 100644 index c269804bd0..0000000000 --- a/repos/effect/.changeset/pre/expand-schema-filter-output.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"effect": patch ---- - -Schema: expand `FilterOutput` and add `FilterIssue` for richer filter failures. - -The return type of a `Schema.makeFilter` predicate now supports two additional shapes: - -- `{ path, issue }` where `issue` is `string | SchemaIssue.Issue` (previously only `{ path, message: string }` was accepted). The `issue` arm lets you attach a fully-formed `Issue` at a nested path without manually constructing a `Pointer`. -- `ReadonlyArray` to report several failures at once. An empty array is success, a single-element array is equivalent to returning that element, and multi-entry arrays are grouped into an `Issue.Composite`. This removes the need to import `SchemaIssue` and hand-build a `Composite` for multi-field validators. - -The single-failure shapes (`undefined`, `true`, `false`, `string`, `SchemaIssue.Issue`) are unchanged. - -**Breaking**: the object shape renamed from `{ path, message }` to `{ path, issue }`. Call sites that used the old shape must rename the field; the migration is mechanical. - -```ts -// before -Schema.makeFilter((o) => ({ path: ["a"], message: "bad" })) - -// after -Schema.makeFilter((o) => ({ path: ["a"], issue: "bad" })) -``` - -Also renamed `{ path, message }` to `{ path, issue }` in the accepted return type of `SchemaGetter.checkEffect`. diff --git a/repos/effect/.changeset/pre/explicit-env-record.md b/repos/effect/.changeset/pre/explicit-env-record.md deleted file mode 100644 index 2045bbd205..0000000000 --- a/repos/effect/.changeset/pre/explicit-env-record.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `ConfigProvider.fromEnvRecord` for building a provider from an explicit environment record. diff --git a/repos/effect/.changeset/pre/explicit-otel-service-identity.md b/repos/effect/.changeset/pre/explicit-otel-service-identity.md deleted file mode 100644 index a34030e336..0000000000 --- a/repos/effect/.changeset/pre/explicit-otel-service-identity.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prefer explicit OTLP resource configuration over environment configuration. diff --git a/repos/effect/.changeset/pre/export-schema-encode-keys-interface.md b/repos/effect/.changeset/pre/export-schema-encode-keys-interface.md deleted file mode 100644 index 2290cd6884..0000000000 --- a/repos/effect/.changeset/pre/export-schema-encode-keys-interface.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Export the `Schema.encodeKeys` interface, closes #2070. - -Previously the interface was internal, so exporting a value whose inferred type referenced it triggered TypeScript error `TS4023: Exported variable has or is using name 'encodeKeys' from external module ... but cannot be named`, e.g.: diff --git a/repos/effect/.changeset/pre/expose-ai-prompt-part-schemas.md b/repos/effect/.changeset/pre/expose-ai-prompt-part-schemas.md deleted file mode 100644 index b29af5e941..0000000000 --- a/repos/effect/.changeset/pre/expose-ai-prompt-part-schemas.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose runtime schemas for AI prompt parts and message-specific part unions. diff --git a/repos/effect/.changeset/pre/extract-semaphore-latch.md b/repos/effect/.changeset/pre/extract-semaphore-latch.md deleted file mode 100644 index 755512bcc4..0000000000 --- a/repos/effect/.changeset/pre/extract-semaphore-latch.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Extract `Semaphore` and `Latch` into their own modules. - -`Semaphore.make` / `Semaphore.makeUnsafe` replace `Effect.makeSemaphore` / `Effect.makeSemaphoreUnsafe`. -`Latch.make` / `Latch.makeUnsafe` replace `Effect.makeLatch` / `Effect.makeLatchUnsafe`. - -Merge `PartitionedSemaphore` into `Semaphore` as `Semaphore.Partitioned`, `Semaphore.makePartitioned`, `Semaphore.makePartitionedUnsafe`. diff --git a/repos/effect/.changeset/pre/failed-otlp-checkpoints.md b/repos/effect/.changeset/pre/failed-otlp-checkpoints.md deleted file mode 100644 index 8e806105d7..0000000000 --- a/repos/effect/.changeset/pre/failed-otlp-checkpoints.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve OTLP metric delta checkpoints when an export fails. diff --git a/repos/effect/.changeset/pre/fair-bees-relax.md b/repos/effect/.changeset/pre/fair-bees-relax.md deleted file mode 100644 index 4e711ae3ee..0000000000 --- a/repos/effect/.changeset/pre/fair-bees-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Fix missing `yield*` in `OpenAiLanguageModel.prepareResponseFormat` diff --git a/repos/effect/.changeset/pre/fair-birds-limit.md b/repos/effect/.changeset/pre/fair-birds-limit.md deleted file mode 100644 index c72957459c..0000000000 --- a/repos/effect/.changeset/pre/fair-birds-limit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add Schedule.upTo options for limiting schedules by duration and/or recurrence count. diff --git a/repos/effect/.changeset/pre/fair-buttons-share.md b/repos/effect/.changeset/pre/fair-buttons-share.md deleted file mode 100644 index 397f1293ef..0000000000 --- a/repos/effect/.changeset/pre/fair-buttons-share.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Make `AtomRpc.query` and `AtomHttpApi.query` return serializable atoms by default when query results are schema-backed. - -The atom serialization key now uses each API's built-in request schemas so dehydrated state can be keyed consistently across server and client. diff --git a/repos/effect/.changeset/pre/fair-citations-stream.md b/repos/effect/.changeset/pre/fair-citations-stream.md deleted file mode 100644 index 49f8e0ab12..0000000000 --- a/repos/effect/.changeset/pre/fair-citations-stream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Preserve start and end offsets for streamed OpenRouter citations. diff --git a/repos/effect/.changeset/pre/fair-cooks-stop.md b/repos/effect/.changeset/pre/fair-cooks-stop.md deleted file mode 100644 index 9aabf7f8fe..0000000000 --- a/repos/effect/.changeset/pre/fair-cooks-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schedule.jittered` to randomize schedule delays between 80% and 120% of the original delay. diff --git a/repos/effect/.changeset/pre/fair-cups-train.md b/repos/effect/.changeset/pre/fair-cups-train.md deleted file mode 100644 index d72aa2f9f8..0000000000 --- a/repos/effect/.changeset/pre/fair-cups-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `TxPubSub.publish` and `TxPubSub.publishAll` overloads to require `Effect.Transaction` in their return environment. diff --git a/repos/effect/.changeset/pre/fair-dryers-speak.md b/repos/effect/.changeset/pre/fair-dryers-speak.md deleted file mode 100644 index cef623e1fc..0000000000 --- a/repos/effect/.changeset/pre/fair-dryers-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpClientRequest.bodyFormDataRecord` and `HttpBody.makeFormDataRecord` helpers for creating multipart form bodies from plain records. diff --git a/repos/effect/.changeset/pre/fair-forks-shake.md b/repos/effect/.changeset/pre/fair-forks-shake.md deleted file mode 100644 index 395d1ec434..0000000000 --- a/repos/effect/.changeset/pre/fair-forks-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix unstable CLI boolean flags so `Flag.optional(Flag.boolean(...))` returns `Option.none()` when omitted, and support canonical `--no-` negation for boolean flags. diff --git a/repos/effect/.changeset/pre/fair-jobs-like.md b/repos/effect/.changeset/pre/fair-jobs-like.md deleted file mode 100644 index e985873345..0000000000 --- a/repos/effect/.changeset/pre/fair-jobs-like.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add a `radius` option to `Graph` search configuration, allowing `dfs`, `bfs`, and `dfsPostOrder` traversals to limit returned nodes by edge distance from the configured start nodes. Traversals can also use `direction: "undirected"` to follow edges in either direction. diff --git a/repos/effect/.changeset/pre/fair-logs-correlate.md b/repos/effect/.changeset/pre/fair-logs-correlate.md deleted file mode 100644 index dacdd7b02d..0000000000 --- a/repos/effect/.changeset/pre/fair-logs-correlate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Prevent log annotations from overwriting active span correlation identifiers. diff --git a/repos/effect/.changeset/pre/fair-logs-listen.md b/repos/effect/.changeset/pre/fair-logs-listen.md deleted file mode 100644 index 6608c22367..0000000000 --- a/repos/effect/.changeset/pre/fair-logs-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now advertise logging and honor each client's selected log level when sending log notifications. diff --git a/repos/effect/.changeset/pre/fair-matches-infer.md b/repos/effect/.changeset/pre/fair-matches-infer.md deleted file mode 100644 index 8f7c4ab308..0000000000 --- a/repos/effect/.changeset/pre/fair-matches-infer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix contextual typing for `Match` tag and discriminator handler maps when handlers use `Effect.fn` or `Effect.fnUntraced`. diff --git a/repos/effect/.changeset/pre/fair-pandas-prove.md b/repos/effect/.changeset/pre/fair-pandas-prove.md deleted file mode 100644 index d88f5fbc4f..0000000000 --- a/repos/effect/.changeset/pre/fair-pandas-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Changed socket close handling so all close codes are treated as errors by default unless `closeCodeIsError` is overridden. diff --git a/repos/effect/.changeset/pre/fair-pants-float.md b/repos/effect/.changeset/pre/fair-pants-float.md deleted file mode 100644 index 020c76aff9..0000000000 --- a/repos/effect/.changeset/pre/fair-pants-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix a race in `Semaphore.take` where interruption could leak permits after a waiter was resumed. diff --git a/repos/effect/.changeset/pre/fair-poems-visit.md b/repos/effect/.changeset/pre/fair-poems-visit.md deleted file mode 100644 index 3aa7a731d1..0000000000 --- a/repos/effect/.changeset/pre/fair-poems-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpServerRequest.toClientRequest` for direct server-to-client request conversion. diff --git a/repos/effect/.changeset/pre/fair-sampling-content.md b/repos/effect/.changeset/pre/fair-sampling-content.md deleted file mode 100644 index d88205d8f6..0000000000 --- a/repos/effect/.changeset/pre/fair-sampling-content.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve MCP sampling request preferences and response content. diff --git a/repos/effect/.changeset/pre/fair-sinks-catch.md b/repos/effect/.changeset/pre/fair-sinks-catch.md deleted file mode 100644 index 6ea07ee3e4..0000000000 --- a/repos/effect/.changeset/pre/fair-sinks-catch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the error type exposed by the curried `Sink.catch` overload. diff --git a/repos/effect/.changeset/pre/fair-workflows-suspend.md b/repos/effect/.changeset/pre/fair-workflows-suspend.md deleted file mode 100644 index 0c9810dea0..0000000000 --- a/repos/effect/.changeset/pre/fair-workflows-suspend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix parallel child workflows inside activities to dispatch before suspending, release activity resources during durable waits, and resume reliably when children complete during cleanup. diff --git a/repos/effect/.changeset/pre/famous-loops-flow.md b/repos/effect/.changeset/pre/famous-loops-flow.md deleted file mode 100644 index ac6581223f..0000000000 --- a/repos/effect/.changeset/pre/famous-loops-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Check symbol-keyed properties in Match object patterns. diff --git a/repos/effect/.changeset/pre/famous-wolves-lead.md b/repos/effect/.changeset/pre/famous-wolves-lead.md deleted file mode 100644 index 19f2bbad4e..0000000000 --- a/repos/effect/.changeset/pre/famous-wolves-lead.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -don't omit reasoning from openai config diff --git a/repos/effect/.changeset/pre/fancy-glasses-grow.md b/repos/effect/.changeset/pre/fancy-glasses-grow.md deleted file mode 100644 index 1f55db96aa..0000000000 --- a/repos/effect/.changeset/pre/fancy-glasses-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -add IndexedDb modules diff --git a/repos/effect/.changeset/pre/fast-graph-path-queues.md b/repos/effect/.changeset/pre/fast-graph-path-queues.md deleted file mode 100644 index 66d188167e..0000000000 --- a/repos/effect/.changeset/pre/fast-graph-path-queues.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve `Graph.dijkstra` and `Graph.astar` priority queue performance. diff --git a/repos/effect/.changeset/pre/fast-times-camp.md b/repos/effect/.changeset/pre/fast-times-camp.md deleted file mode 100644 index f44c6cb8b1..0000000000 --- a/repos/effect/.changeset/pre/fast-times-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allocate less effects when reading a file diff --git a/repos/effect/.changeset/pre/faster-schema-construction.md b/repos/effect/.changeset/pre/faster-schema-construction.md deleted file mode 100644 index 3310a7c53e..0000000000 --- a/repos/effect/.changeset/pre/faster-schema-construction.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Optimize schema initialization while preserving custom constructor options. diff --git a/repos/effect/.changeset/pre/fetch-raw-stream-duplex.md b/repos/effect/.changeset/pre/fetch-raw-stream-duplex.md deleted file mode 100644 index ebf4b44150..0000000000 --- a/repos/effect/.changeset/pre/fetch-raw-stream-duplex.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Set `duplex` for raw Web stream request bodies in `FetchHttpClient`. diff --git a/repos/effect/.changeset/pre/few-birds-matter.md b/repos/effect/.changeset/pre/few-birds-matter.md deleted file mode 100644 index 66fdca809b..0000000000 --- a/repos/effect/.changeset/pre/few-birds-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Allow custom request properties in openai-compat model config and chat request types, and forward model-level custom properties to chat-completions payloads. diff --git a/repos/effect/.changeset/pre/few-cougars-dig.md b/repos/effect/.changeset/pre/few-cougars-dig.md deleted file mode 100644 index 86055e698b..0000000000 --- a/repos/effect/.changeset/pre/few-cougars-dig.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Effect.abortSignal diff --git a/repos/effect/.changeset/pre/few-foxes-grin.md b/repos/effect/.changeset/pre/few-foxes-grin.md deleted file mode 100644 index 8d7631f5c6..0000000000 --- a/repos/effect/.changeset/pre/few-foxes-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Command.withExamples` to attach concrete usage examples to CLI commands, expose them through `HelpDoc.examples`, and render them in the default help formatter. diff --git a/repos/effect/.changeset/pre/few-mirrors-pull.md b/repos/effect/.changeset/pre/few-mirrors-pull.md deleted file mode 100644 index dd1bee2979..0000000000 --- a/repos/effect/.changeset/pre/few-mirrors-pull.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect": patch ---- - -Make CLI global settings directly yieldable and simplify built-in names. - -`GlobalFlag.setting` now takes `{ flag, defaultValue }` and returns a setting that is a `Context.Reference`, so handlers and `Command.provide*` effects can `yield*` global setting values directly. - -Built-in settings keep internal behavior in `runWith` (for example, `--log-level` still configures `References.MinimumLogLevel`) while also being readable as values. - -Also renamed built-in globals: - -- `GlobalFlag.CompletionsFlag` -> `GlobalFlag.Completions` -- `GlobalFlag.LogLevelFlag` -> `GlobalFlag.LogLevel` diff --git a/repos/effect/.changeset/pre/few-socks-poke.md b/repos/effect/.changeset/pre/few-socks-poke.md deleted file mode 100644 index cdc249ea7f..0000000000 --- a/repos/effect/.changeset/pre/few-socks-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Accept both `in-memory` and `in_memory` for OpenAI `prompt_cache_retention` schema fields. diff --git a/repos/effect/.changeset/pre/fiber-join-all-errors.md b/repos/effect/.changeset/pre/fiber-join-all-errors.md deleted file mode 100644 index ac883aedce..0000000000 --- a/repos/effect/.changeset/pre/fiber-join-all-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve input fiber error types in `Fiber.joinAll`. diff --git a/repos/effect/.changeset/pre/fiber-map-reentrant-replacement.md b/repos/effect/.changeset/pre/fiber-map-reentrant-replacement.md deleted file mode 100644 index 6153f0ac2f..0000000000 --- a/repos/effect/.changeset/pre/fiber-map-reentrant-replacement.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `FiberMap` losing track of fibers started under the same key by a replaced fiber's synchronous finalizer, ensuring they are interrupted when the map's scope closes. diff --git a/repos/effect/.changeset/pre/fiber-registration-same-fiber.md b/repos/effect/.changeset/pre/fiber-registration-same-fiber.md deleted file mode 100644 index ee7c0ef2cd..0000000000 --- a/repos/effect/.changeset/pre/fiber-registration-same-fiber.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve an already registered fiber when `FiberHandle` or `FiberMap` registers it again with `onlyIfMissing: true`, instead of interrupting it and clearing the entry. diff --git a/repos/effect/.changeset/pre/fiber-runtime-start-metrics.md b/repos/effect/.changeset/pre/fiber-runtime-start-metrics.md deleted file mode 100644 index 212fc64936..0000000000 --- a/repos/effect/.changeset/pre/fiber-runtime-start-metrics.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Record fiber runtime start metrics when fibers are constructed so yielded fibers are only counted once. diff --git a/repos/effect/.changeset/pre/fiery-jokes-care.md b/repos/effect/.changeset/pre/fiery-jokes-care.md deleted file mode 100644 index 0093f23187..0000000000 --- a/repos/effect/.changeset/pre/fiery-jokes-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix Entity.keepAlive diff --git a/repos/effect/.changeset/pre/fiery-mammals-call.md b/repos/effect/.changeset/pre/fiery-mammals-call.md deleted file mode 100644 index 2fce3b1f48..0000000000 --- a/repos/effect/.changeset/pre/fiery-mammals-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure tagged enum \_tag is correctly set diff --git a/repos/effect/.changeset/pre/fifty-carrots-punch.md b/repos/effect/.changeset/pre/fifty-carrots-punch.md deleted file mode 100644 index 4ba43f52dc..0000000000 --- a/repos/effect/.changeset/pre/fifty-carrots-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Exposed the platform specific pretty loggers separately. diff --git a/repos/effect/.changeset/pre/file-prompt-jk-filter.md b/repos/effect/.changeset/pre/file-prompt-jk-filter.md deleted file mode 100644 index d4d204e6b3..0000000000 --- a/repos/effect/.changeset/pre/file-prompt-jk-filter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Prompt.file` swallowing `j` and `k` while typing a filter query. diff --git a/repos/effect/.changeset/pre/filesystem-sink-undefined-flag.md b/repos/effect/.changeset/pre/filesystem-sink-undefined-flag.md deleted file mode 100644 index 4c26793e98..0000000000 --- a/repos/effect/.changeset/pre/filesystem-sink-undefined-flag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `FileSystem.sink` to retain its default write flag when `flag` is undefined. diff --git a/repos/effect/.changeset/pre/filesystem-stat-optional-inode.md b/repos/effect/.changeset/pre/filesystem-stat-optional-inode.md deleted file mode 100644 index 6fa54f6f06..0000000000 --- a/repos/effect/.changeset/pre/filesystem-stat-optional-inode.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch ---- - -Keep Node and Bun file stats usable when optional numeric metadata exceeds the safe integer range by returning `Option.none()` for those fields. diff --git a/repos/effect/.changeset/pre/fine-walls-decide.md b/repos/effect/.changeset/pre/fine-walls-decide.md deleted file mode 100644 index 7f6e67dfc4..0000000000 --- a/repos/effect/.changeset/pre/fine-walls-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -don't remove SIGINT listener until fiber exit diff --git a/repos/effect/.changeset/pre/first-success-of.md b/repos/effect/.changeset/pre/first-success-of.md deleted file mode 100644 index e31e6792a7..0000000000 --- a/repos/effect/.changeset/pre/first-success-of.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Port `Effect.firstSuccessOf` from Effect v3. diff --git a/repos/effect/.changeset/pre/five-parents-relax.md b/repos/effect/.changeset/pre/five-parents-relax.md deleted file mode 100644 index 15192e8dc9..0000000000 --- a/repos/effect/.changeset/pre/five-parents-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -allow customizing idb durability diff --git a/repos/effect/.changeset/pre/five-spoons-visit.md b/repos/effect/.changeset/pre/five-spoons-visit.md deleted file mode 100644 index f3d0b9e51d..0000000000 --- a/repos/effect/.changeset/pre/five-spoons-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Removed unused stderr option from Logger.consolePretty signature diff --git a/repos/effect/.changeset/pre/five-worms-rhyme.md b/repos/effect/.changeset/pre/five-worms-rhyme.md deleted file mode 100644 index 01707d165d..0000000000 --- a/repos/effect/.changeset/pre/five-worms-rhyme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix eventlog skipping entries diff --git a/repos/effect/.changeset/pre/fix-1332.md b/repos/effect/.changeset/pre/fix-1332.md deleted file mode 100644 index 4ab4fa1237..0000000000 --- a/repos/effect/.changeset/pre/fix-1332.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: avoid eager resolution for type-level helpers, closes #1332 diff --git a/repos/effect/.changeset/pre/fix-1917.md b/repos/effect/.changeset/pre/fix-1917.md deleted file mode 100644 index f7cf342ee1..0000000000 --- a/repos/effect/.changeset/pre/fix-1917.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Simplify and align the default-value APIs. - -`Schema.withConstructorDefault` now accepts an `Effect` instead of `(o: Option) => Option | Effect>`. - -`Schema.withDecodingDefault` / `Schema.withDecodingDefaultKey` now accept an `Effect` instead of `() => T`, enabling effectful defaults. - -`SchemaGetter.withDefault` follows the same change, accepting `Effect` instead of `() => T`. diff --git a/repos/effect/.changeset/pre/fix-1927.md b/repos/effect/.changeset/pre/fix-1927.md deleted file mode 100644 index 7882a2191c..0000000000 --- a/repos/effect/.changeset/pre/fix-1927.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reuse existing references when duplicate identifiers have the same representation, closes #1927. diff --git a/repos/effect/.changeset/pre/fix-1940.md b/repos/effect/.changeset/pre/fix-1940.md deleted file mode 100644 index be9ef1a1f5..0000000000 --- a/repos/effect/.changeset/pre/fix-1940.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `ErrorClass` and `TaggedErrorClass` `toString` to match native `Error` output format (e.g. `E: my message` instead of `E({"message":"my message"})`), closes #1940. - -Also fix prototype properties (e.g. `name`) being lost after `.extend()`. diff --git a/repos/effect/.changeset/pre/fix-1947.md b/repos/effect/.changeset/pre/fix-1947.md deleted file mode 100644 index f5cd72ff76..0000000000 --- a/repos/effect/.changeset/pre/fix-1947.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.resolveAnnotationsKey` API to retrieve the context (key-level) annotations from a schema, closes #1947. - -Also rename `Schema.resolveInto` to `Schema.resolveAnnotations`. diff --git a/repos/effect/.changeset/pre/fix-2002.md b/repos/effect/.changeset/pre/fix-2002.md deleted file mode 100644 index a2de203181..0000000000 --- a/repos/effect/.changeset/pre/fix-2002.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.splitLines` to correctly handle standalone `\r` as a line terminator and flush the final unterminated line when the stream ends, closes #2002. diff --git a/repos/effect/.changeset/pre/fix-2012.md b/repos/effect/.changeset/pre/fix-2012.md deleted file mode 100644 index 6b7ae930af..0000000000 --- a/repos/effect/.changeset/pre/fix-2012.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add withDecodingDefaultTypeKey / withDecodingDefaultType, closes #2012 diff --git a/repos/effect/.changeset/pre/fix-2015.md b/repos/effect/.changeset/pre/fix-2015.md deleted file mode 100644 index 067530685a..0000000000 --- a/repos/effect/.changeset/pre/fix-2015.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: allow `Class` constructors to accept `void` when all fields are optional, closes #2015. diff --git a/repos/effect/.changeset/pre/fix-2260.md b/repos/effect/.changeset/pre/fix-2260.md deleted file mode 100644 index dddb2c1e49..0000000000 --- a/repos/effect/.changeset/pre/fix-2260.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add JSON Schema custom annotation passthrough option, closes #2260 diff --git a/repos/effect/.changeset/pre/fix-2268.md b/repos/effect/.changeset/pre/fix-2268.md deleted file mode 100644 index b0977d4a1e..0000000000 --- a/repos/effect/.changeset/pre/fix-2268.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: reintroduce `.value` on `Schema.Array` and `Schema.NonEmptyArray` for consistency with other collection wrappers (`Chunk`, `HashSet`, etc.), closes #2268. diff --git a/repos/effect/.changeset/pre/fix-2271.md b/repos/effect/.changeset/pre/fix-2271.md deleted file mode 100644 index 1be94c36b7..0000000000 --- a/repos/effect/.changeset/pre/fix-2271.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Clarify that `Data.$is(tag)` only checks the `_tag` field, not the full structure, closes #2271. diff --git a/repos/effect/.changeset/pre/fix-2384.md b/repos/effect/.changeset/pre/fix-2384.md deleted file mode 100644 index 6494902952..0000000000 --- a/repos/effect/.changeset/pre/fix-2384.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -`Config.withDefault` now only recovers from missing data for literal/union -schemas. Invalid present values now propagate validation errors instead of -using the default, closes #2384. diff --git a/repos/effect/.changeset/pre/fix-2414.md b/repos/effect/.changeset/pre/fix-2414.md deleted file mode 100644 index 984fba98ab..0000000000 --- a/repos/effect/.changeset/pre/fix-2414.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Derive template literal arbitraries from encoded parts, closes #2414. diff --git a/repos/effect/.changeset/pre/fix-2419.md b/repos/effect/.changeset/pre/fix-2419.md deleted file mode 100644 index ec9b13b858..0000000000 --- a/repos/effect/.changeset/pre/fix-2419.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow schema class `.extend` to accept a `Struct` and preserve checks from the extension schema, closes #2419. diff --git a/repos/effect/.changeset/pre/fix-2497.md b/repos/effect/.changeset/pre/fix-2497.md deleted file mode 100644 index 4425db712b..0000000000 --- a/repos/effect/.changeset/pre/fix-2497.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.andThenResult` to emit `self` outputs as `Failure` and `other` outputs as `Success`, closes #2497. diff --git a/repos/effect/.changeset/pre/fix-2499.md b/repos/effect/.changeset/pre/fix-2499.md deleted file mode 100644 index 9e26f847e8..0000000000 --- a/repos/effect/.changeset/pre/fix-2499.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix excess property handling in schema-backed class constructors, closes #2499. diff --git a/repos/effect/.changeset/pre/fix-6464.md b/repos/effect/.changeset/pre/fix-6464.md deleted file mode 100644 index 13d7b52d21..0000000000 --- a/repos/effect/.changeset/pre/fix-6464.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Distribute `HttpApiBuilder` handler requirements per service so request middleware layers can provide them, closes #6464. diff --git a/repos/effect/.changeset/pre/fix-6491.md b/repos/effect/.changeset/pre/fix-6491.md deleted file mode 100644 index a37e2d0cc7..0000000000 --- a/repos/effect/.changeset/pre/fix-6491.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve nested class construction when applying constructor defaults, closes #6491. diff --git a/repos/effect/.changeset/pre/fix-6521.md b/repos/effect/.changeset/pre/fix-6521.md deleted file mode 100644 index ca7c85ad32..0000000000 --- a/repos/effect/.changeset/pre/fix-6521.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Simplify the displayed `Type`, `Encoded`, and `Iso` types of required readonly `Schema.Struct` fields, closes #6521. diff --git a/repos/effect/.changeset/pre/fix-ai-empty-params-structured-output.md b/repos/effect/.changeset/pre/fix-ai-empty-params-structured-output.md deleted file mode 100644 index aa3806870b..0000000000 --- a/repos/effect/.changeset/pre/fix-ai-empty-params-structured-output.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve unstable AI structured output handling for empty tool params and add `Tool.EmptyParams`, closes #1749. diff --git a/repos/effect/.changeset/pre/fix-ai-text-toolkit-typing.md b/repos/effect/.changeset/pre/fix-ai-text-toolkit-typing.md deleted file mode 100644 index da37eea4c2..0000000000 --- a/repos/effect/.changeset/pre/fix-ai-text-toolkit-typing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix AI text method toolkit typing to support generic handler toolkits, preserve toolkit union inference, and keep response part narrowing by tool name. diff --git a/repos/effect/.changeset/pre/fix-ai-tool-call-id.md b/repos/effect/.changeset/pre/fix-ai-tool-call-id.md deleted file mode 100644 index 42d87284ad..0000000000 --- a/repos/effect/.changeset/pre/fix-ai-tool-call-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose the tool call ID to AI tool handlers and `Toolkit.WithHandler.handle` wrappers. diff --git a/repos/effect/.changeset/pre/fix-ansi-cursor-to.md b/repos/effect/.changeset/pre/fix-ansi-cursor-to.md deleted file mode 100644 index 36309f8325..0000000000 --- a/repos/effect/.changeset/pre/fix-ansi-cursor-to.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Emit valid CSI sequences from the unstable CLI `cursorTo` helper. diff --git a/repos/effect/.changeset/pre/fix-anthropic-caller-toolid.md b/repos/effect/.changeset/pre/fix-anthropic-caller-toolid.md deleted file mode 100644 index 866bc4dc83..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-caller-toolid.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix non-streaming Anthropic responses throwing when a tool call carries `caller` metadata. The mapper emitted `caller.toolId: undefined`, but `ProviderMetadata` is `Record(String, NullOr(Json))` and `undefined` is not a valid Json value, so decoding the model's own response threw `Expected JSON value`. Emit `null` instead, matching the streaming mappers. diff --git a/repos/effect/.changeset/pre/fix-anthropic-code-execution-deltas.md b/repos/effect/.changeset/pre/fix-anthropic-code-execution-deltas.md deleted file mode 100644 index a50a451a25..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-code-execution-deltas.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix malformed JSON in streamed Anthropic code-execution tool parameters. diff --git a/repos/effect/.changeset/pre/fix-anthropic-header-redaction.md b/repos/effect/.changeset/pre/fix-anthropic-header-redaction.md deleted file mode 100644 index 3a8bd80b72..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-header-redaction.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Redact the Anthropic API key from client error context. diff --git a/repos/effect/.changeset/pre/fix-anthropic-memory-tool-requires-handler.md b/repos/effect/.changeset/pre/fix-anthropic-memory-tool-requires-handler.md deleted file mode 100644 index f9bd9843fa..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-memory-tool-requires-handler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix `Memory_20250818` provider-defined tool missing `requiresHandler: true`. Like the other client-executed tools (`TextEditor_20250728`, `Bash_2025*`, `ComputerUse_2025*`), the memory tool requires the application to implement its execution (view/create/str_replace/insert/delete/rename over `/memories/*`). Without this flag, `Tool.HandlersFor` excluded it from the required handlers, making it impossible to type-check a handler for `Memory_20250818` in `Toolkit.toLayer`. diff --git a/repos/effect/.changeset/pre/fix-anthropic-memory-tool.md b/repos/effect/.changeset/pre/fix-anthropic-memory-tool.md deleted file mode 100644 index 3a9a9cdbc8..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-memory-tool.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix client-executed provider tools (Memory, Text Editor, Computer Use, Bash) which were unusable on the wire. - -- `makeResponse` (and the streaming equivalents) now map a provider `tool_use` wire name (e.g. `"memory"`) back to the tool's custom name (e.g. `"AnthropicMemory"`) that the toolkit is keyed by, instead of raising `ToolNotFoundError`. -- `AnthropicTool.MemoryCreateCommand` now includes the required `file_text` field, so a `create` command no longer drops the file body. -- Optional parameters on client-executed provider tools now use `Schema.optionalKey` instead of `Schema.optional`, which the Anthropic codec rejected with "Unsupported AST Undefined": `Memory`/`TextEditor` `view_range`, `ComputerUse` `coordinate`, and `Bash` `restart`. - -Closes #2615. diff --git a/repos/effect/.changeset/pre/fix-anthropic-plaintext-bytes.md b/repos/effect/.changeset/pre/fix-anthropic-plaintext-bytes.md deleted file mode 100644 index 5d6c5740e1..0000000000 --- a/repos/effect/.changeset/pre/fix-anthropic-plaintext-bytes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Decode byte-backed plain-text attachments as UTF-8 text in Anthropic requests. diff --git a/repos/effect/.changeset/pre/fix-array-non-finite-indexes.md b/repos/effect/.changeset/pre/fix-array-non-finite-indexes.md deleted file mode 100644 index dcfd9db0fd..0000000000 --- a/repos/effect/.changeset/pre/fix-array-non-finite-indexes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Array index operations handling `NaN` and fractional indexes. diff --git a/repos/effect/.changeset/pre/fix-atom-batch-dependencies.md b/repos/effect/.changeset/pre/fix-atom-batch-dependencies.md deleted file mode 100644 index 5c1ad70711..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-batch-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Atom dependency tracking and re-entrant invalidation during batch rebuilds. diff --git a/repos/effect/.changeset/pre/fix-atom-http-stream-success-types.md b/repos/effect/.changeset/pre/fix-atom-http-stream-success-types.md deleted file mode 100644 index 186f81b120..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-http-stream-success-types.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Match `AtomHttpApi` query and mutation success types to the generated HTTP client, -including SSE, binary streams, and header-wrapped responses. Stream transport, -decoding, and SSE errors now appear in the stream's error channel instead of -`never`, so code that assumed a failure-free stream may need to handle them. -Runtime and serialization behavior are unchanged. diff --git a/repos/effect/.changeset/pre/fix-atom-http-top-level-dispatch.md b/repos/effect/.changeset/pre/fix-atom-http-top-level-dispatch.md deleted file mode 100644 index d38505b77d..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-http-top-level-dispatch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `AtomHttpApi` query and mutation dispatch for top-level API groups. diff --git a/repos/effect/.changeset/pre/fix-atom-kvs-async-write.md b/repos/effect/.changeset/pre/fix-atom-kvs-async-write.md deleted file mode 100644 index 2227e7aed3..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-kvs-async-write.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Atom.kvs` async mode to retain its `AsyncResult` value shape after writes. diff --git a/repos/effect/.changeset/pre/fix-atom-query-zero-ttl.md b/repos/effect/.changeset/pre/fix-atom-query-zero-ttl.md deleted file mode 100644 index f29a0e9951..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-query-zero-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor explicit `timeToLive: 0` and `timeToLive: 0n` in `AtomRpc` and `AtomHttpApi` queries. Zero now opts out of the registry's default idle retention, matching other zero-duration inputs, so an unmounted query can be disposed and fetched again on remount. Omitting `timeToLive` still uses the registry default. diff --git a/repos/effect/.changeset/pre/fix-atom-rpc-client-middleware-errors.md b/repos/effect/.changeset/pre/fix-atom-rpc-client-middleware-errors.md deleted file mode 100644 index 882cdf455a..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-rpc-client-middleware-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `AtomRpc` mutation and query atoms to include client middleware errors in their result error types. diff --git a/repos/effect/.changeset/pre/fix-atom-suspense-registry-cache.md b/repos/effect/.changeset/pre/fix-atom-suspense-registry-cache.md deleted file mode 100644 index 220407192d..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-suspense-registry-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-react": patch ---- - -Scope `useAtomSuspense` promises to their atom registry so concurrent registries resolve independently. diff --git a/repos/effect/.changeset/pre/fix-atom-writable-fallback.md b/repos/effect/.changeset/pre/fix-atom-writable-fallback.md deleted file mode 100644 index 711e5279ac..0000000000 --- a/repos/effect/.changeset/pre/fix-atom-writable-fallback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Forward `Atom.withFallback` writes to the primary atom. diff --git a/repos/effect/.changeset/pre/fix-bash-subcommand-dispatch.md b/repos/effect/.changeset/pre/fix-bash-subcommand-dispatch.md deleted file mode 100644 index c43cfc220c..0000000000 --- a/repos/effect/.changeset/pre/fix-bash-subcommand-dispatch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent Bash completions from treating flag values as subcommands. diff --git a/repos/effect/.changeset/pre/fix-bigint-gcd-lcm.md b/repos/effect/.changeset/pre/fix-bigint-gcd-lcm.md deleted file mode 100644 index bfffa04847..0000000000 --- a/repos/effect/.changeset/pre/fix-bigint-gcd-lcm.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `BigInt.gcd` and `BigInt.lcm` return non-negative values and handle zero operands in `BigInt.lcm`. diff --git a/repos/effect/.changeset/pre/fix-bun-http-default-hostname.md b/repos/effect/.changeset/pre/fix-bun-http-default-hostname.md deleted file mode 100644 index 79e580325f..0000000000 --- a/repos/effect/.changeset/pre/fix-bun-http-default-hostname.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Fix `BunHttpServer` startup when `hostname` is omitted by explicitly listening on `::`, preserving IPv4 and IPv6 connectivity and avoiding a `ServeError` when parsing Bun's default hostname. - -Preserve the configured listen address in `BunClusterHttp`, including loopback addresses, when applying the HTTP server hostname default. diff --git a/repos/effect/.changeset/pre/fix-cache-set-race.md b/repos/effect/.changeset/pre/fix-cache-set-race.md deleted file mode 100644 index be4cf636d8..0000000000 --- a/repos/effect/.changeset/pre/fix-cache-set-race.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent an interrupted cache lookup from removing a newer value written with `Cache.set`. diff --git a/repos/effect/.changeset/pre/fix-catch-orelse-error-erasure.md b/repos/effect/.changeset/pre/fix-catch-orelse-error-erasure.md deleted file mode 100644 index 0f4846bf3a..0000000000 --- a/repos/effect/.changeset/pre/fix-catch-orelse-error-erasure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fixed the `catch*` combinators silently dropping unhandled error types diff --git a/repos/effect/.changeset/pre/fix-cause-map-annotations.md b/repos/effect/.changeset/pre/fix-cause-map-annotations.md deleted file mode 100644 index efc7e791fe..0000000000 --- a/repos/effect/.changeset/pre/fix-cause-map-annotations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve failure annotations when mapping errors with `Cause.map`. diff --git a/repos/effect/.changeset/pre/fix-channel-effects-finalizer.md b/repos/effect/.changeset/pre/fix-channel-effects-finalizer.md deleted file mode 100644 index 4c470c7fbd..0000000000 --- a/repos/effect/.changeset/pre/fix-channel-effects-finalizer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Shut down the internal effects queue when ordered concurrent channel mapping closes. diff --git a/repos/effect/.changeset/pre/fix-channel-schema-decode-unknown.md b/repos/effect/.changeset/pre/fix-channel-schema-decode-unknown.md deleted file mode 100644 index c7ee9a2ba3..0000000000 --- a/repos/effect/.changeset/pre/fix-channel-schema-decode-unknown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `ChannelSchema.decodeUnknown` to accept unknown input chunks while keeping `ChannelSchema.decode` typed to the schema's encoded input. diff --git a/repos/effect/.changeset/pre/fix-chunk-fractional-counts.md b/repos/effect/.changeset/pre/fix-chunk-fractional-counts.md deleted file mode 100644 index 0fae65c5a1..0000000000 --- a/repos/effect/.changeset/pre/fix-chunk-fractional-counts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `Chunk.take` and `Chunk.drop` produce valid chunks for fractional counts. diff --git a/repos/effect/.changeset/pre/fix-class-constructor-defaults.md b/repos/effect/.changeset/pre/fix-class-constructor-defaults.md deleted file mode 100644 index 8e9e824b1d..0000000000 --- a/repos/effect/.changeset/pre/fix-class-constructor-defaults.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: rename `MakeOptions.disableValidation` to `disableChecks`. Apply constructor defaults when `disableChecks` is true, closes #1841. diff --git a/repos/effect/.changeset/pre/fix-cli-help-table-spacing.md b/repos/effect/.changeset/pre/fix-cli-help-table-spacing.md deleted file mode 100644 index e8b5ef3024..0000000000 --- a/repos/effect/.changeset/pre/fix-cli-help-table-spacing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep long CLI subcommand and argument names separated from their descriptions in help output. diff --git a/repos/effect/.changeset/pre/fix-cli-missing-flag-values.md b/repos/effect/.changeset/pre/fix-cli-missing-flag-values.md deleted file mode 100644 index 0dfb373f9a..0000000000 --- a/repos/effect/.changeset/pre/fix-cli-missing-flag-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Report an error when a CLI flag, including `--completions`, is provided without its required value. diff --git a/repos/effect/.changeset/pre/fix-cli-mixed-global-flag-context.md b/repos/effect/.changeset/pre/fix-cli-mixed-global-flag-context.md deleted file mode 100644 index 73ab0d1e40..0000000000 --- a/repos/effect/.changeset/pre/fix-cli-mixed-global-flag-context.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `Command.withGlobalFlags` type inference when mixing `GlobalFlag.action` and `GlobalFlag.setting`. - -`Setting` service identifiers are now correctly removed from command requirements in mixed global flag arrays. diff --git a/repos/effect/.changeset/pre/fix-cli-subcommands-requirements.md b/repos/effect/.changeset/pre/fix-cli-subcommands-requirements.md deleted file mode 100644 index 563526731f..0000000000 --- a/repos/effect/.changeset/pre/fix-cli-subcommands-requirements.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -- Fix `Command.withSubcommands` collapsing the inferred requirements type to `never` when given more than one subcommand -- Export a `Command.Services` utility type to extract the required services from a `Command` diff --git a/repos/effect/.changeset/pre/fix-cli-unexpected-arguments.md b/repos/effect/.changeset/pre/fix-cli-unexpected-arguments.md deleted file mode 100644 index 2f6b775cd9..0000000000 --- a/repos/effect/.changeset/pre/fix-cli-unexpected-arguments.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject unexpected positional arguments left after command parsing, including values exceeding `Argument.variadic` maximum bounds. diff --git a/repos/effect/.changeset/pre/fix-clickhouse-connect-timeout.md b/repos/effect/.changeset/pre/fix-clickhouse-connect-timeout.md deleted file mode 100644 index 41a80e3690..0000000000 --- a/repos/effect/.changeset/pre/fix-clickhouse-connect-timeout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-clickhouse": patch ---- - -Close the ClickHouse client when the startup connection check times out. diff --git a/repos/effect/.changeset/pre/fix-cluster-entity-context-bleed.md b/repos/effect/.changeset/pre/fix-cluster-entity-context-bleed.md deleted file mode 100644 index c9037d3d5c..0000000000 --- a/repos/effect/.changeset/pre/fix-cluster-entity-context-bleed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use registration context for cluster entities diff --git a/repos/effect/.changeset/pre/fix-cluster-reply-defect-isolation.md b/repos/effect/.changeset/pre/fix-cluster-reply-defect-isolation.md deleted file mode 100644 index 62f0d84727..0000000000 --- a/repos/effect/.changeset/pre/fix-cluster-reply-defect-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Scope cluster reply serialization failures and peer-delivered defects to their own request instead of the whole runner connection diff --git a/repos/effect/.changeset/pre/fix-cluster-shutdown-deadlock.md b/repos/effect/.changeset/pre/fix-cluster-shutdown-deadlock.md deleted file mode 100644 index 50649c4a0c..0000000000 --- a/repos/effect/.changeset/pre/fix-cluster-shutdown-deadlock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix a `@effect/cluster` shutdown deadlock on single-runner topologies (e.g. single-node deployments and `TestRunner`), where `Sharding.sendOutgoing` retried `EntityNotAssignedToRunner` forever during teardown. diff --git a/repos/effect/.changeset/pre/fix-cluster-strand-request-shutdown.md b/repos/effect/.changeset/pre/fix-cluster-strand-request-shutdown.md deleted file mode 100644 index 886d22f7e4..0000000000 --- a/repos/effect/.changeset/pre/fix-cluster-strand-request-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix cluster shutdown hangs by failing abandoned non-discard requests and stream chunk acknowledgements with `EntityNotAssignedToRunner`, including persisted requests sent after runner unregistration. This adds `EntityNotAssignedToRunner` to the typed error channel of entity clients and request-only `EntityProxy` RPC/HTTP endpoints; discard endpoints remain unchanged. diff --git a/repos/effect/.changeset/pre/fix-cluster-stream-recovery.md b/repos/effect/.changeset/pre/fix-cluster-stream-recovery.md deleted file mode 100644 index 7c54c63815..0000000000 --- a/repos/effect/.changeset/pre/fix-cluster-stream-recovery.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix persisted cluster stream recovery when SQL drivers return a null reply kind. diff --git a/repos/effect/.changeset/pre/fix-config-array-default.md b/repos/effect/.changeset/pre/fix-config-array-default.md deleted file mode 100644 index 129e3d1823..0000000000 --- a/repos/effect/.changeset/pre/fix-config-array-default.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Config.schema` so missing array values are treated as missing data, allowing `Config.withDefault` to apply. diff --git a/repos/effect/.changeset/pre/fix-config-or-else-evidence.md b/repos/effect/.changeset/pre/fix-config-or-else-evidence.md deleted file mode 100644 index ced02896e0..0000000000 --- a/repos/effect/.changeset/pre/fix-config-or-else-evidence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve provider input evidence when `Config.orElse` recovers a configuration failure. diff --git a/repos/effect/.changeset/pre/fix-config-withDefault.md b/repos/effect/.changeset/pre/fix-config-withDefault.md deleted file mode 100644 index a0ce253d75..0000000000 --- a/repos/effect/.changeset/pre/fix-config-withDefault.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Config.withDefault` type inference, closes #1530. diff --git a/repos/effect/.changeset/pre/fix-config-withdefault-filter.md b/repos/effect/.changeset/pre/fix-config-withdefault-filter.md deleted file mode 100644 index ddddc9442a..0000000000 --- a/repos/effect/.changeset/pre/fix-config-withdefault-filter.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -`Config.withDefault` no longer recovers from schema filter failures. A filter -failure means a present value reached refinement checks, so using the default -could hide invalid configuration values. diff --git a/repos/effect/.changeset/pre/fix-context-add-or-omit-types.md b/repos/effect/.changeset/pre/fix-context-add-or-omit-types.md deleted file mode 100644 index f05f8bb404..0000000000 --- a/repos/effect/.changeset/pre/fix-context-add-or-omit-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Omit services removed by `Context.addOrOmit` from the returned context type. diff --git a/repos/effect/.changeset/pre/fix-cors-vary.md b/repos/effect/.changeset/pre/fix-cors-vary.md deleted file mode 100644 index b5c0fe58b4..0000000000 --- a/repos/effect/.changeset/pre/fix-cors-vary.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpMiddleware.cors` to preserve `Origin` and other required `Vary` dimensions. diff --git a/repos/effect/.changeset/pre/fix-cron-and-representations.md b/repos/effect/.changeset/pre/fix-cron-and-representations.md deleted file mode 100644 index 064eeb3eec..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-and-representations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Cron` day and weekday intersection semantics in inspection representations. diff --git a/repos/effect/.changeset/pre/fix-cron-make-validation.md b/repos/effect/.changeset/pre/fix-cron-make-validation.md deleted file mode 100644 index 5f9e350f02..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-make-validation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Validate `Cron.make` field constraints and treat weekday `7` as Sunday consistently with cron parsing. diff --git a/repos/effect/.changeset/pre/fix-cron-next-missing-day-overflow.md b/repos/effect/.changeset/pre/fix-cron-next-missing-day-overflow.md deleted file mode 100644 index 6b392d177f..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-next-missing-day-overflow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cron.next` skipping earlier matching days when the upcoming day-of-month does not exist in the current month. diff --git a/repos/effect/.changeset/pre/fix-cron-parser-semantics.md b/repos/effect/.changeset/pre/fix-cron-parser-semantics.md deleted file mode 100644 index 840d6f06b7..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-parser-semantics.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -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. diff --git a/repos/effect/.changeset/pre/fix-cron-prev-month-rollover.md b/repos/effect/.changeset/pre/fix-cron-prev-month-rollover.md deleted file mode 100644 index 8085ddc1df..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-prev-month-rollover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cron.prev` day-of-month rollover across shorter months and non-leap years. diff --git a/repos/effect/.changeset/pre/fix-cron-prev-weekday-wrap.md b/repos/effect/.changeset/pre/fix-cron-prev-weekday-wrap.md deleted file mode 100644 index e4b8307497..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-prev-weekday-wrap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cron.prev` weekday wrapping to always return a matching instant before the input. diff --git a/repos/effect/.changeset/pre/fix-cron-timezone-hash.md b/repos/effect/.changeset/pre/fix-cron-timezone-hash.md deleted file mode 100644 index 6ee572d046..0000000000 --- a/repos/effect/.changeset/pre/fix-cron-timezone-hash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make Cron equality and hashing include the optional timezone consistently. diff --git a/repos/effect/.changeset/pre/fix-dangling-internal-declarations.md b/repos/effect/.changeset/pre/fix-dangling-internal-declarations.md deleted file mode 100644 index 5fb1a0e7c1..0000000000 --- a/repos/effect/.changeset/pre/fix-dangling-internal-declarations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix published declarations referencing symbols stripped as `@internal`, which broke consumers compiling with `skipLibCheck: false`. `Effectable.d.ts` now uses the public `Effect.TypeId`, `Match.d.ts` no longer aliases an internal `Contextual` type, `Schema.d.ts` ships the `AnnotationSchemaConstraint` alias it references, and the CLI's `toFlagDoc` helper is marked internal so it no longer leaks `Param.getParamMetadata`. diff --git a/repos/effect/.changeset/pre/fix-datetime-gmt.md b/repos/effect/.changeset/pre/fix-datetime-gmt.md deleted file mode 100644 index 02baaa85d8..0000000000 --- a/repos/effect/.changeset/pre/fix-datetime-gmt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `DateTime.makeUnsafe` incorrectly appending "Z" to date strings containing "GMT" diff --git a/repos/effect/.changeset/pre/fix-deep-mutable-opaque-types.md b/repos/effect/.changeset/pre/fix-deep-mutable-opaque-types.md deleted file mode 100644 index 0e0efdbac7..0000000000 --- a/repos/effect/.changeset/pre/fix-deep-mutable-opaque-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Types.DeepMutable` to preserve built-in objects, Effect data types, and other objects with methods or symbol-keyed properties while recursively making arrays, tuples, maps, sets, and plain records mutable. diff --git a/repos/effect/.changeset/pre/fix-devtools-flush-on-teardown.md b/repos/effect/.changeset/pre/fix-devtools-flush-on-teardown.md deleted file mode 100644 index d5c46f9e12..0000000000 --- a/repos/effect/.changeset/pre/fix-devtools-flush-on-teardown.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Fix DevToolsClient not flushing final span events on teardown. - -The stream consumer was `forkScoped`, causing it to be interrupted before -it could drain remaining queue items. Replaced with `forkChild` and -`Fiber.await` in the finalizer so the stream drains naturally after the -queue is failed. diff --git a/repos/effect/.changeset/pre/fix-devtools-span-snapshot.md b/repos/effect/.changeset/pre/fix-devtools-span-snapshot.md deleted file mode 100644 index ca381c8d5b..0000000000 --- a/repos/effect/.changeset/pre/fix-devtools-span-snapshot.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix DevTools span requests to preserve their state when queued for sending. diff --git a/repos/effect/.changeset/pre/fix-durable-clock-fractional-wakeup.md b/repos/effect/.changeset/pre/fix-durable-clock-fractional-wakeup.md deleted file mode 100644 index d0a7390c84..0000000000 --- a/repos/effect/.changeset/pre/fix-durable-clock-fractional-wakeup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize cluster durable clock wake-up timestamps to whole milliseconds. diff --git a/repos/effect/.changeset/pre/fix-durable-deferred-race.md b/repos/effect/.changeset/pre/fix-durable-deferred-race.md deleted file mode 100644 index 21b43f3601..0000000000 --- a/repos/effect/.changeset/pre/fix-durable-deferred-race.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `DurableDeferred.raceAll` so a completed deferred can wake an active workflow without changing success-biased race semantics diff --git a/repos/effect/.changeset/pre/fix-durable-race-replay.md b/repos/effect/.changeset/pre/fix-durable-race-replay.md deleted file mode 100644 index 2b0e3b242b..0000000000 --- a/repos/effect/.changeset/pre/fix-durable-race-replay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix replay of persisted `DurableDeferred.raceAll` results. diff --git a/repos/effect/.changeset/pre/fix-duration-decimal-precision.md b/repos/effect/.changeset/pre/fix-duration-decimal-precision.md deleted file mode 100644 index 2afc2db772..0000000000 --- a/repos/effect/.changeset/pre/fix-duration-decimal-precision.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve integral precision when parsing decimal nano and micro duration inputs diff --git a/repos/effect/.changeset/pre/fix-duration-symmetric-rounding.md b/repos/effect/.changeset/pre/fix-duration-symmetric-rounding.md deleted file mode 100644 index 2fdb282e4b..0000000000 --- a/repos/effect/.changeset/pre/fix-duration-symmetric-rounding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Round fractional durations symmetrically when normalizing to nanoseconds. diff --git a/repos/effect/.changeset/pre/fix-effect-fn-self.md b/repos/effect/.changeset/pre/fix-effect-fn-self.md deleted file mode 100644 index 80eda7033f..0000000000 --- a/repos/effect/.changeset/pre/fix-effect-fn-self.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.fn` binding the final transform as the generator body when using the `{ self }` overload. diff --git a/repos/effect/.changeset/pre/fix-effect-schedule-errors.md b/repos/effect/.changeset/pre/fix-effect-schedule-errors.md deleted file mode 100644 index 8a8910026e..0000000000 --- a/repos/effect/.changeset/pre/fix-effect-schedule-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include schedule errors in the error channel of `Effect.schedule` and `Effect.scheduleFrom`. diff --git a/repos/effect/.changeset/pre/fix-encoded-header-metadata.md b/repos/effect/.changeset/pre/fix-encoded-header-metadata.md deleted file mode 100644 index d443806281..0000000000 --- a/repos/effect/.changeset/pre/fix-encoded-header-metadata.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use body status and encoding defaults in `HttpApiSchema.encodeToWithHeaders`. diff --git a/repos/effect/.changeset/pre/fix-entity-manager-defect-replay.md b/repos/effect/.changeset/pre/fix-entity-manager-defect-replay.md deleted file mode 100644 index 5428fc6d4a..0000000000 --- a/repos/effect/.changeset/pre/fix-entity-manager-defect-replay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix EntityManager defect restarts so in-flight requests are replayed instead of being dropped when the old entity scope is interrupted. diff --git a/repos/effect/.changeset/pre/fix-entity-proxy-rpc-handler-context.md b/repos/effect/.changeset/pre/fix-entity-proxy-rpc-handler-context.md deleted file mode 100644 index 9b4e1c3c1b..0000000000 --- a/repos/effect/.changeset/pre/fix-entity-proxy-rpc-handler-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix entity proxy RPC handlers to provide the context expected by RpcServer. diff --git a/repos/effect/.changeset/pre/fix-entity-proxy-server-path-params.md b/repos/effect/.changeset/pre/fix-entity-proxy-server-path-params.md deleted file mode 100644 index 2cbf13f11d..0000000000 --- a/repos/effect/.changeset/pre/fix-entity-proxy-server-path-params.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `EntityProxyServer.layerHttpApi` using `path.entityId` instead of `params.entityId` diff --git a/repos/effect/.changeset/pre/fix-eventlog-duplicate-chunks.md b/repos/effect/.changeset/pre/fix-eventlog-duplicate-chunks.md deleted file mode 100644 index dc215a8b37..0000000000 --- a/repos/effect/.changeset/pre/fix-eventlog-duplicate-chunks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore duplicate chunk indexes when joining event log messages. diff --git a/repos/effect/.changeset/pre/fix-fiber-exit-observers.md b/repos/effect/.changeset/pre/fix-fiber-exit-observers.md deleted file mode 100644 index 50aebaee14..0000000000 --- a/repos/effect/.changeset/pre/fix-fiber-exit-observers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure fiber observer cancellation during exit does not skip remaining observers. diff --git a/repos/effect/.changeset/pre/fix-fiberhandle-clear-race.md b/repos/effect/.changeset/pre/fix-fiberhandle-clear-race.md deleted file mode 100644 index 567ba19e04..0000000000 --- a/repos/effect/.changeset/pre/fix-fiberhandle-clear-race.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix a race where FiberHandle.clear could remove a newer fiber installed while the previous fiber was still interrupting. diff --git a/repos/effect/.changeset/pre/fix-fiberset-json-id.md b/repos/effect/.changeset/pre/fix-fiberset-json-id.md deleted file mode 100644 index 158b963c14..0000000000 --- a/repos/effect/.changeset/pre/fix-fiberset-json-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the inspectable JSON identity of `FiberSet`. diff --git a/repos/effect/.changeset/pre/fix-fiberset-runtime-interruption.md b/repos/effect/.changeset/pre/fix-fiberset-runtime-interruption.md deleted file mode 100644 index 0d550e711d..0000000000 --- a/repos/effect/.changeset/pre/fix-fiberset-runtime-interruption.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Propagate the `FiberSet.runtime` interruption option when registering managed fibers. diff --git a/repos/effect/.changeset/pre/fix-filesystem-integer-precision.md b/repos/effect/.changeset/pre/fix-filesystem-integer-precision.md deleted file mode 100644 index 70e684da55..0000000000 --- a/repos/effect/.changeset/pre/fix-filesystem-integer-precision.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"effect": patch ---- - -Prevent precision loss in Node/Bun filesystem operations and `HttpPlatform` file responses. diff --git a/repos/effect/.changeset/pre/fix-fish-command-path.md b/repos/effect/.changeset/pre/fix-fish-command-path.md deleted file mode 100644 index 15053389d9..0000000000 --- a/repos/effect/.changeset/pre/fix-fish-command-path.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Match Fish completions against the full nested command path. diff --git a/repos/effect/.changeset/pre/fix-formatter-output-contracts.md b/repos/effect/.changeset/pre/fix-formatter-output-contracts.md deleted file mode 100644 index 5312f9d8e2..0000000000 --- a/repos/effect/.changeset/pre/fix-formatter-output-contracts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Formatter.format` handling of shared references and ensure `Formatter.formatJson` always returns valid JSON. diff --git a/repos/effect/.changeset/pre/fix-from-json-string-identifier.md b/repos/effect/.changeset/pre/fix-from-json-string-identifier.md deleted file mode 100644 index b2e478fe7e..0000000000 --- a/repos/effect/.changeset/pre/fix-from-json-string-identifier.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Preserve content schema identifiers when emitting JSON Schema for `Schema.fromJsonString`. - -This keeps user-defined identifiers attached to the decoded JSON payload while giving the generated JSON string wrapper its own derived name, avoiding client codegen outputs where the payload type is renamed behind the transport wrapper. diff --git a/repos/effect/.changeset/pre/fix-from-option-inline-inference.md b/repos/effect/.changeset/pre/fix-from-option-inline-inference.md deleted file mode 100644 index 8be078dddb..0000000000 --- a/repos/effect/.changeset/pre/fix-from-option-inline-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.fromOption` data-first inference for inline `Option` expressions. diff --git a/repos/effect/.changeset/pre/fix-from-readable-stream-cancel-defect.md b/repos/effect/.changeset/pre/fix-from-readable-stream-cancel-defect.md deleted file mode 100644 index 4c70506cf2..0000000000 --- a/repos/effect/.changeset/pre/fix-from-readable-stream-cancel-defect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Stream.fromReadableStream: swallow the `reader.cancel()` rejection in the finalizer. Cancelling the reader of an already-errored ReadableStream rejects with the stored error, which turned the typed `onError` failure into a defect. diff --git a/repos/effect/.changeset/pre/fix-graph-allocator-equality.md b/repos/effect/.changeset/pre/fix-graph-allocator-equality.md deleted file mode 100644 index d796cad079..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-allocator-equality.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix immutable Graph equality and hashing to include future node and edge identifier allocation. diff --git a/repos/effect/.changeset/pre/fix-graph-bellman-ford-self-cycle.md b/repos/effect/.changeset/pre/fix-graph-bellman-ford-self-cycle.md deleted file mode 100644 index 07dcc32a6a..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-bellman-ford-self-cycle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.bellmanFord` to detect reachable negative cycles when the source and target are the same node. diff --git a/repos/effect/.changeset/pre/fix-graph-curried-getters.md b/repos/effect/.changeset/pre/fix-graph-curried-getters.md deleted file mode 100644 index 4d79479201..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-curried-getters.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix standalone data-last `Graph.getNode` and `Graph.getEdge` inference. diff --git a/repos/effect/.changeset/pre/fix-graph-dfs-astar-edge-cases.md b/repos/effect/.changeset/pre/fix-graph-dfs-astar-edge-cases.md deleted file mode 100644 index 894c65d013..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-dfs-astar-edge-cases.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve depth-first traversal order with finite radii and validate A* heuristics for trivial paths. diff --git a/repos/effect/.changeset/pre/fix-graph-edge-transforms.md b/repos/effect/.changeset/pre/fix-graph-edge-transforms.md deleted file mode 100644 index e0c410ee86..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-edge-transforms.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.mapEdges` and `Graph.filterMapEdges` to preserve `Graph.Edge` instances when transforming edge data. diff --git a/repos/effect/.changeset/pre/fix-graph-finite-edge-weights.md b/repos/effect/.changeset/pre/fix-graph-finite-edge-weights.md deleted file mode 100644 index 171304d070..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-finite-edge-weights.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject `NaN` and `-Infinity` edge weights in Graph shortest-path algorithms. diff --git a/repos/effect/.changeset/pre/fix-graph-mutable-hash.md b/repos/effect/.changeset/pre/fix-graph-mutable-hash.md deleted file mode 100644 index 62463090cf..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-mutable-hash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix mutable Graph equality and hashing to use reference identity while preserving structural semantics for immutable graphs. diff --git a/repos/effect/.changeset/pre/fix-graph-mutable-topo.md b/repos/effect/.changeset/pre/fix-graph-mutable-topo.md deleted file mode 100644 index 8a929bde8b..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-mutable-topo.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix topological walkers silently completing with an incomplete order when a mutable graph becomes cyclic after walker creation. diff --git a/repos/effect/.changeset/pre/fix-graph-mutation-errors.md b/repos/effect/.changeset/pre/fix-graph-mutation-errors.md deleted file mode 100644 index 476d8c4019..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-mutation-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve scoped Graph mutation callback errors when the callback manually finalizes its mutable handle. diff --git a/repos/effect/.changeset/pre/fix-graph-topo-types.md b/repos/effect/.changeset/pre/fix-graph-topo-types.md deleted file mode 100644 index adab4eb464..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-topo-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Restrict `Graph.topo` to directed graphs at the type level while retaining runtime validation for unsafe undirected inputs. diff --git a/repos/effect/.changeset/pre/fix-graph-undirected-traversal.md b/repos/effect/.changeset/pre/fix-graph-undirected-traversal.md deleted file mode 100644 index e80e04680b..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-undirected-traversal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Graph traversal and shortest-path algorithms to traverse undirected edges independently of their stored source/target orientation. diff --git a/repos/effect/.changeset/pre/fix-graph-walker-repeatability.md b/repos/effect/.changeset/pre/fix-graph-walker-repeatability.md deleted file mode 100644 index 25c1ccd982..0000000000 --- a/repos/effect/.changeset/pre/fix-graph-walker-repeatability.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.Walker` to create a fresh iterable for each direct iteration. diff --git a/repos/effect/.changeset/pre/fix-graphviz-dot-escaping.md b/repos/effect/.changeset/pre/fix-graphviz-dot-escaping.md deleted file mode 100644 index 35bd254d67..0000000000 --- a/repos/effect/.changeset/pre/fix-graphviz-dot-escaping.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.toGraphViz` to quote DOT graph names and escape labels as literal text. diff --git a/repos/effect/.changeset/pre/fix-group-by-key-types.md b/repos/effect/.changeset/pre/fix-group-by-key-types.md deleted file mode 100644 index de711323af..0000000000 --- a/repos/effect/.changeset/pre/fix-group-by-key-types.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Preserve finite string and unique symbol key unions in the return types of `Array.groupBy` and `Iterable.groupBy`. - -Previously, grouping widened finite keys to `string` or `symbol`, which lost known-key autocomplete and allowed access to keys that the selector could never produce. The new `Record.ReadonlyRecord.GroupByResult` keeps finite keys and marks their properties optional because any group may be absent at runtime, while open `string` and `symbol` selectors retain their existing record index signatures. diff --git a/repos/effect/.changeset/pre/fix-has-interrupts-only-empty.md b/repos/effect/.changeset/pre/fix-has-interrupts-only-empty.md deleted file mode 100644 index 5434d7a384..0000000000 --- a/repos/effect/.changeset/pre/fix-has-interrupts-only-empty.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Cause.hasInterruptsOnly` to return `false` for empty causes. diff --git a/repos/effect/.changeset/pre/fix-hashmap-bit31-ordering.md b/repos/effect/.changeset/pre/fix-hashmap-bit31-ordering.md deleted file mode 100644 index b56bac4739..0000000000 --- a/repos/effect/.changeset/pre/fix-hashmap-bit31-ordering.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -HashMap: compare HAMT bit positions as unsigned to preserve entry lookup when bit 31 is set diff --git a/repos/effect/.changeset/pre/fix-hashmap-modify-hash.md b/repos/effect/.changeset/pre/fix-hashmap-modify-hash.md deleted file mode 100644 index ca36cabf55..0000000000 --- a/repos/effect/.changeset/pre/fix-hashmap-modify-hash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use the supplied hash for `HashMap.modifyHash` insertions, updates, and removals. diff --git a/repos/effect/.changeset/pre/fix-headers-proto-enumerability.md b/repos/effect/.changeset/pre/fix-headers-proto-enumerability.md deleted file mode 100644 index 686c038ff2..0000000000 --- a/repos/effect/.changeset/pre/fix-headers-proto-enumerability.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/http Headers: hide inspectable prototype methods from for..in iteration to avoid invalid header names in runtime fetch polyfills diff --git a/repos/effect/.changeset/pre/fix-http-client-request-content-length.md b/repos/effect/.changeset/pre/fix-http-client-request-content-length.md deleted file mode 100644 index 12f66418c1..0000000000 --- a/repos/effect/.changeset/pre/fix-http-client-request-content-length.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove stale `content-length` headers when replacing an HTTP client request body with one of unknown length. diff --git a/repos/effect/.changeset/pre/fix-http-incoming-message-parse-options.md b/repos/effect/.changeset/pre/fix-http-incoming-message-parse-options.md deleted file mode 100644 index 1f71f78c82..0000000000 --- a/repos/effect/.changeset/pre/fix-http-incoming-message-parse-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpIncomingMessage.schemaBodyJson` to forward parse options via the `parseOptions` annotation key. diff --git a/repos/effect/.changeset/pre/fix-http-platform-file-ranges.md b/repos/effect/.changeset/pre/fix-http-platform-file-ranges.md deleted file mode 100644 index 87b522ab5f..0000000000 --- a/repos/effect/.changeset/pre/fix-http-platform-file-ranges.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Clamp HttpPlatform file response ranges to the file size so Content-Length matches the bytes available. Oversized reads stop at EOF, and offsets at or past EOF return an empty body with Content-Length 0. Apply the same clamping to the default Web file response implementation. diff --git a/repos/effect/.changeset/pre/fix-http-pre-response-handler-types.md b/repos/effect/.changeset/pre/fix-http-pre-response-handler-types.md deleted file mode 100644 index 66eeb8b702..0000000000 --- a/repos/effect/.changeset/pre/fix-http-pre-response-handler-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the published declaration for `HttpEffect.appendPreResponseHandlerUnsafe`. diff --git a/repos/effect/.changeset/pre/fix-http-server-request-raw-body.md b/repos/effect/.changeset/pre/fix-http-server-request-raw-body.md deleted file mode 100644 index 148559e4a2..0000000000 --- a/repos/effect/.changeset/pre/fix-http-server-request-raw-body.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support standard `BodyInit` values when reading converted client request bodies through `HttpServerRequest`. diff --git a/repos/effect/.changeset/pre/fix-http-server-response-body-headers.md b/repos/effect/.changeset/pre/fix-http-server-response-body-headers.md deleted file mode 100644 index 90f2278179..0000000000 --- a/repos/effect/.changeset/pre/fix-http-server-response-body-headers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Synchronize HTTP server response content headers when replacing the body. diff --git a/repos/effect/.changeset/pre/fix-http-toweb-null-body-status.md b/repos/effect/.changeset/pre/fix-http-toweb-null-body-status.md deleted file mode 100644 index dccd823377..0000000000 --- a/repos/effect/.changeset/pre/fix-http-toweb-null-body-status.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch ---- - -Omit response bodies for statuses 204, 205, and 304 in `HttpServerResponse.toWeb` and the Bun/Deno HTTP adapters, preventing invalid Web responses and hung requests. Cancel omitted raw `ReadableStream` bodies, and finalize request resources without starting omitted Effect streams. diff --git a/repos/effect/.changeset/pre/fix-http-tracer-response-cause.md b/repos/effect/.changeset/pre/fix-http-tracer-response-cause.md deleted file mode 100644 index 30a5589df7..0000000000 --- a/repos/effect/.changeset/pre/fix-http-tracer-response-cause.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Exclude response metadata from HTTP server span failures after response headers have been sent. diff --git a/repos/effect/.changeset/pre/fix-httpapi-authorization-decoding.md b/repos/effect/.changeset/pre/fix-httpapi-authorization-decoding.md deleted file mode 100644 index b41cb0662e..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-authorization-decoding.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Fix HttpApi authorization decoding. - -Previously, `HttpApiBuilder.securityDecode` removed the expected scheme length and one following character from the `Authorization` header without verifying either value. A Bearer decoder could therefore pass credentials from a different scheme such as `Basic`, accept a malformed header without a separating space, or retain leading spaces when more than one separator was present. - -The decoder now validates the declared scheme before returning credentials, matches it case-insensitively as required by [RFC 9110 section 11.1](https://www.rfc-editor.org/rfc/rfc9110.html#section-11.1), and consumes one or more separating spaces. Missing, malformed, or mismatched headers produce the existing empty credential value so security middleware can reject them consistently. - -Basic authentication previously split the decoded `user-pass` value at every colon, causing otherwise valid passwords containing `:` to be discarded. It now uses only the first colon as the separator and preserves the rest of the password, following [RFC 7617 section 2](https://www.rfc-editor.org/rfc/rfc7617.html#section-2). diff --git a/repos/effect/.changeset/pre/fix-httpapi-client-error-content-type.md b/repos/effect/.changeset/pre/fix-httpapi-client-error-content-type.md deleted file mode 100644 index 2de77be2c6..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-client-error-content-type.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Fix HttpApi client error decoding. - -Generated clients previously combined every error schema for a status into one union decoder. When schemas used different encodings, their declaration order could determine the decoded error instead of the response `Content-Type`; for example, a text decoder could accept a JSON response before the JSON decoder was tried. - -Error responses are now grouped and selected by normalized content type, matching buffered success responses. Normalization happens before grouping, so declarations that differ only by casing or parameters such as `charset` share one union decoder instead of making later schemas unreachable. - -No-content schemas are represented by a headerless alternative, allowing empty error responses without a `Content-Type` header to decode correctly. Unsupported content types preserve the existing combination of `StatusCodeError` and the response decoding failure. diff --git a/repos/effect/.changeset/pre/fix-httpapi-endpoint-error-inference.md b/repos/effect/.changeset/pre/fix-httpapi-endpoint-error-inference.md deleted file mode 100644 index c476f25a21..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-endpoint-error-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix HttpApiEndpoint endpoint error inference when success schemas include streams. diff --git a/repos/effect/.changeset/pre/fix-httpapi-form-responses.md b/repos/effect/.changeset/pre/fix-httpapi-form-responses.md deleted file mode 100644 index 9e9f6f3337..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-form-responses.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiClient` decoding form-urlencoded responses. diff --git a/repos/effect/.changeset/pre/fix-httpapi-malformed-json-400.md b/repos/effect/.changeset/pre/fix-httpapi-malformed-json-400.md deleted file mode 100644 index 2f704e0e85..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-malformed-json-400.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Map HttpApi json defects to SchemaError diff --git a/repos/effect/.changeset/pre/fix-httpapi-runtime-shape.md b/repos/effect/.changeset/pre/fix-httpapi-runtime-shape.md deleted file mode 100644 index a8c9bd8690..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-runtime-shape.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApi.make` so it stores the API identifier and starts with an empty `groups` object instead of a `Map`. This makes empty APIs match the shape they have after groups are added. diff --git a/repos/effect/.changeset/pre/fix-httpapi-schema-types.md b/repos/effect/.changeset/pre/fix-httpapi-schema-types.md deleted file mode 100644 index d5dd3394b1..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-schema-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix published HttpApi declaration files by exporting schema metadata types referenced by public declarations. diff --git a/repos/effect/.changeset/pre/fix-httpapi-security-middleware-cache.md b/repos/effect/.changeset/pre/fix-httpapi-security-middleware-cache.md deleted file mode 100644 index cf9e68fbb7..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-security-middleware-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiBuilder` security middleware caching so separate handler builds do not reuse the first provided middleware implementation. diff --git a/repos/effect/.changeset/pre/fix-httpapi-single-array-query.md b/repos/effect/.changeset/pre/fix-httpapi-single-array-query.md deleted file mode 100644 index 6a8a671ebe..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-single-array-query.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApi` query decoding for array parameters with a single value. diff --git a/repos/effect/.changeset/pre/fix-httpapi-test-pre-response-handlers.md b/repos/effect/.changeset/pre/fix-httpapi-test-pre-response-handlers.md deleted file mode 100644 index d1b3998517..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-test-pre-response-handlers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Run registered pre-response handlers before `HttpApiTest` returns responses. diff --git a/repos/effect/.changeset/pre/fix-httpapi-url-builder-base-path.md b/repos/effect/.changeset/pre/fix-httpapi-url-builder-base-path.md deleted file mode 100644 index 9733d4db0f..0000000000 --- a/repos/effect/.changeset/pre/fix-httpapi-url-builder-base-path.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiClient.urlBuilder` dropping base URL pathnames. diff --git a/repos/effect/.changeset/pre/fix-invalid-openapi-examples.md b/repos/effect/.changeset/pre/fix-invalid-openapi-examples.md deleted file mode 100644 index 3e05529532..0000000000 --- a/repos/effect/.changeset/pre/fix-invalid-openapi-examples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Drop invalid OpenAPI schema examples from generated Effect Schema annotations. diff --git a/repos/effect/.changeset/pre/fix-invalid-value-doubled-expected.md b/repos/effect/.changeset/pre/fix-invalid-value-doubled-expected.md deleted file mode 100644 index a8e9deaeed..0000000000 --- a/repos/effect/.changeset/pre/fix-invalid-value-doubled-expected.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix doubled `Expected: Expected ...` prefixes in CLI `InvalidValue` error messages, closes #6312. diff --git a/repos/effect/.changeset/pre/fix-is-json-dag.md b/repos/effect/.changeset/pre/fix-is-json-dag.md deleted file mode 100644 index 68809e071f..0000000000 --- a/repos/effect/.changeset/pre/fix-is-json-dag.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `SchemaAST.isJson` rejecting DAGs as cycles, closes #2021. - -The previous implementation marked every visited object in a single `seen` set and never removed it, so any value that referenced the same object through two different paths (a DAG, e.g. `{ x: shared, y: shared }`) was treated as a cycle and returned `false`. Cycle detection now tracks only the current recursion path (popping on exit) and memoizes fully validated subtrees, so DAGs are accepted while true cycles are still rejected. diff --git a/repos/effect/.changeset/pre/fix-iterable-flatten-stack-safety.md b/repos/effect/.changeset/pre/fix-iterable-flatten-stack-safety.md deleted file mode 100644 index 54894d0c6b..0000000000 --- a/repos/effect/.changeset/pre/fix-iterable-flatten-stack-safety.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `Iterable.flatten` stack safe across empty iterables. diff --git a/repos/effect/.changeset/pre/fix-json-schema-anyof-oneof-siblings.md b/repos/effect/.changeset/pre/fix-json-schema-anyof-oneof-siblings.md deleted file mode 100644 index 9f0840d1e3..0000000000 --- a/repos/effect/.changeset/pre/fix-json-schema-anyof-oneof-siblings.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -SchemaRepresentation: support `anyOf`/`oneOf` with sibling keywords in `fromJsonSchemaMultiDocument` diff --git a/repos/effect/.changeset/pre/fix-json-schema-import-json.md b/repos/effect/.changeset/pre/fix-json-schema-import-json.md deleted file mode 100644 index 048a0d7989..0000000000 --- a/repos/effect/.changeset/pre/fix-json-schema-import-json.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Import unconstrained JSON Schema nodes as `Schema.Json` instead of `Schema.Unknown`. diff --git a/repos/effect/.changeset/pre/fix-json-schema-percent-references.md b/repos/effect/.changeset/pre/fix-json-schema-percent-references.md deleted file mode 100644 index 01c6a3c470..0000000000 --- a/repos/effect/.changeset/pre/fix-json-schema-percent-references.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `JsonPointer.parseUriFragment` and `JsonPointer.formatUriFragment` for converting RFC 6901 URI fragments, and use them to preserve percent-encoded definition names in exported JSON Schema references. JSON Schema compilation now rejects malformed local definition references returned by `toJsonSchema` hooks. Such hooks must percent-encode characters that URI fragments do not permit, for example `%` as `%25` and `#` as `%23`. diff --git a/repos/effect/.changeset/pre/fix-json-schema-reference-resolution.md b/repos/effect/.changeset/pre/fix-json-schema-reference-resolution.md deleted file mode 100644 index 52c87639a8..0000000000 --- a/repos/effect/.changeset/pre/fix-json-schema-reference-resolution.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject unsupported JSON Schema references instead of resolving them by their final path segment, closes #7409. diff --git a/repos/effect/.changeset/pre/fix-json-schema-unique-items-false.md b/repos/effect/.changeset/pre/fix-json-schema-unique-items-false.md deleted file mode 100644 index 0365d9dc50..0000000000 --- a/repos/effect/.changeset/pre/fix-json-schema-unique-items-false.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore `uniqueItems` when set to `false` while importing JSON Schema documents. diff --git a/repos/effect/.changeset/pre/fix-keepalive-blocked-timers.md b/repos/effect/.changeset/pre/fix-keepalive-blocked-timers.md deleted file mode 100644 index 8cdb6fa16c..0000000000 --- a/repos/effect/.changeset/pre/fix-keepalive-blocked-timers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -runtime: guard keepAlive setInterval / clearInterval so Effect.runPromise works in runtimes that block timer APIs diff --git a/repos/effect/.changeset/pre/fix-language-model-stream-concurrency.md b/repos/effect/.changeset/pre/fix-language-model-stream-concurrency.md deleted file mode 100644 index 491b4fdcd6..0000000000 --- a/repos/effect/.changeset/pre/fix-language-model-stream-concurrency.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `LanguageModel.streamText` to apply the configured concurrency limit to tool call resolution, including approval checks. diff --git a/repos/effect/.changeset/pre/fix-latch-stale-flush.md b/repos/effect/.changeset/pre/fix-latch-stale-flush.md deleted file mode 100644 index c36527840c..0000000000 --- a/repos/effect/.changeset/pre/fix-latch-stale-flush.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Fix Latch open/release resuming waiters that registered after a subsequent close. - -`Latch.open` and `Latch.release` schedule the waiter flush on the fiber's -dispatcher. Previously the flush drained whatever waiters existed at flush -time, so a waiter that registered after the latch was closed again could be -resumed by the stale flush. The waiters are now snapshotted at schedule time, -so only waiters covered by an `open`/`release` call are resumed. diff --git a/repos/effect/.changeset/pre/fix-layermap-preload.md b/repos/effect/.changeset/pre/fix-layermap-preload.md deleted file mode 100644 index 6b72607345..0000000000 --- a/repos/effect/.changeset/pre/fix-layermap-preload.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `LayerMap` preload options so configured entries are acquired during construction. diff --git a/repos/effect/.changeset/pre/fix-mcp-call-tool-arguments.md b/repos/effect/.changeset/pre/fix-mcp-call-tool-arguments.md deleted file mode 100644 index 952f6d0da9..0000000000 --- a/repos/effect/.changeset/pre/fix-mcp-call-tool-arguments.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow MCP tool calls to omit optional arguments. diff --git a/repos/effect/.changeset/pre/fix-mcp-completion-context.md b/repos/effect/.changeset/pre/fix-mcp-completion-context.md deleted file mode 100644 index 82c30fc894..0000000000 --- a/repos/effect/.changeset/pre/fix-mcp-completion-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP completion handlers now receive resolved argument context, and completion responses are limited to one hundred values. diff --git a/repos/effect/.changeset/pre/fix-mcp-param-name-resolution.md b/repos/effect/.changeset/pre/fix-mcp-param-name-resolution.md deleted file mode 100644 index 2ec5b38416..0000000000 --- a/repos/effect/.changeset/pre/fix-mcp-param-name-resolution.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix MCP resource template parameter names resolving as `param0`, `param1` instead of actual names by checking `isParam` on the original schema before `toCodecStringTree` transformation. diff --git a/repos/effect/.changeset/pre/fix-mcp-request-errors.md b/repos/effect/.changeset/pre/fix-mcp-request-errors.md deleted file mode 100644 index 7da53c303c..0000000000 --- a/repos/effect/.changeset/pre/fix-mcp-request-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now return protocol errors for invalid tool, prompt, completion, resource, and logging requests. diff --git a/repos/effect/.changeset/pre/fix-memory-journal-conflicts.md b/repos/effect/.changeset/pre/fix-memory-journal-conflicts.md deleted file mode 100644 index ae2c2ddd57..0000000000 --- a/repos/effect/.changeset/pre/fix-memory-journal-conflicts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix memory journal conflict detection skipping the first newer entry. diff --git a/repos/effect/.changeset/pre/fix-memory-journal-next-sequence.md b/repos/effect/.changeset/pre/fix-memory-journal-next-sequence.md deleted file mode 100644 index f515118f08..0000000000 --- a/repos/effect/.changeset/pre/fix-memory-journal-next-sequence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Return the first unused remote sequence from the in-memory event journal. diff --git a/repos/effect/.changeset/pre/fix-memory-journal-relay.md b/repos/effect/.changeset/pre/fix-memory-journal-relay.md deleted file mode 100644 index cee740fdd8..0000000000 --- a/repos/effect/.changeset/pre/fix-memory-journal-relay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Relay entries imported into an in-memory event journal to other remotes. diff --git a/repos/effect/.changeset/pre/fix-memory-runner-health.md b/repos/effect/.changeset/pre/fix-memory-runner-health.md deleted file mode 100644 index 77a0e9ce88..0000000000 --- a/repos/effect/.changeset/pre/fix-memory-runner-health.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve and update runner health in the in-memory cluster runner storage. diff --git a/repos/effect/.changeset/pre/fix-memory-workflow-interrupt.md b/repos/effect/.changeset/pre/fix-memory-workflow-interrupt.md deleted file mode 100644 index 625013f2b1..0000000000 --- a/repos/effect/.changeset/pre/fix-memory-workflow-interrupt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Align in-memory workflow interrupt finalization with the cluster workflow engine. diff --git a/repos/effect/.changeset/pre/fix-mermaid-escape-special-chars.md b/repos/effect/.changeset/pre/fix-mermaid-escape-special-chars.md deleted file mode 100644 index 873f71b920..0000000000 --- a/repos/effect/.changeset/pre/fix-mermaid-escape-special-chars.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.toMermaid` to escape special characters using HTML entity codes per the Mermaid specification. diff --git a/repos/effect/.changeset/pre/fix-message-storage-clear-address-dedup.md b/repos/effect/.changeset/pre/fix-message-storage-clear-address-dedup.md deleted file mode 100644 index f8d75f32a7..0000000000 --- a/repos/effect/.changeset/pre/fix-message-storage-clear-address-dedup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Clear in-memory message primary-key indexes when clearing an entity address. diff --git a/repos/effect/.changeset/pre/fix-metric-attribute-key-collisions.md b/repos/effect/.changeset/pre/fix-metric-attribute-key-collisions.md deleted file mode 100644 index 3d00cc8e7c..0000000000 --- a/repos/effect/.changeset/pre/fix-metric-attribute-key-collisions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent distinct metric attribute sets from sharing registry state. diff --git a/repos/effect/.changeset/pre/fix-metric-is-metric.md b/repos/effect/.changeset/pre/fix-metric-is-metric.md deleted file mode 100644 index 00e1854300..0000000000 --- a/repos/effect/.changeset/pre/fix-metric-is-metric.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Metric.isMetric` to recognize metrics using their current runtime brand. diff --git a/repos/effect/.changeset/pre/fix-metric-linear-boundaries.md b/repos/effect/.changeset/pre/fix-metric-linear-boundaries.md deleted file mode 100644 index d5a0219a45..0000000000 --- a/repos/effect/.changeset/pre/fix-metric-linear-boundaries.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Metric.linearBoundaries` to space boundaries by the configured width. diff --git a/repos/effect/.changeset/pre/fix-metric-negative-max.md b/repos/effect/.changeset/pre/fix-metric-negative-max.md deleted file mode 100644 index a2f1d2450c..0000000000 --- a/repos/effect/.changeset/pre/fix-metric-negative-max.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix histogram and summary maximum values for negative-only observations. diff --git a/repos/effect/.changeset/pre/fix-mime-parameter-normalization.md b/repos/effect/.changeset/pre/fix-mime-parameter-normalization.md deleted file mode 100644 index 545607aac6..0000000000 --- a/repos/effect/.changeset/pre/fix-mime-parameter-normalization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize MIME type parameters and whitespace in `Mime.getAllExtensions`. diff --git a/repos/effect/.changeset/pre/fix-mssql-multitable-persistence-upsert.md b/repos/effect/.changeset/pre/fix-mssql-multitable-persistence-upsert.md deleted file mode 100644 index 8c870fa224..0000000000 --- a/repos/effect/.changeset/pre/fix-mssql-multitable-persistence-upsert.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Generate valid MSSQL upserts for multi-table persistence. diff --git a/repos/effect/.changeset/pre/fix-multipart-file-stream-limits.md b/repos/effect/.changeset/pre/fix-multipart-file-stream-limits.md deleted file mode 100644 index fd4d227f45..0000000000 --- a/repos/effect/.changeset/pre/fix-multipart-file-stream-limits.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Terminate active multipart file streams when a parser limit is exceeded or the body ends unexpectedly, so file parts fail instead of hanging. diff --git a/repos/effect/.changeset/pre/fix-mutable-list-bounds.md b/repos/effect/.changeset/pre/fix-mutable-list-bounds.md deleted file mode 100644 index d481f7a747..0000000000 --- a/repos/effect/.changeset/pre/fix-mutable-list-bounds.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `MutableList.prepend` on empty lists and handle non-positive `toArrayN` bounds. diff --git a/repos/effect/.changeset/pre/fix-mutable-list-empty-filter.md b/repos/effect/.changeset/pre/fix-mutable-list-empty-filter.md deleted file mode 100644 index 4352c73143..0000000000 --- a/repos/effect/.changeset/pre/fix-mutable-list-empty-filter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `MutableList.filter` leaving an invalid empty bucket when no values match. diff --git a/repos/effect/.changeset/pre/fix-mutable-list-filter-length.md b/repos/effect/.changeset/pre/fix-mutable-list-filter-length.md deleted file mode 100644 index c6a6a84fdf..0000000000 --- a/repos/effect/.changeset/pre/fix-mutable-list-filter-length.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `MutableList.filter` and `MutableList.remove` length updates. diff --git a/repos/effect/.changeset/pre/fix-ndjson-split-utf8.md b/repos/effect/.changeset/pre/fix-ndjson-split-utf8.md deleted file mode 100644 index 4474398b48..0000000000 --- a/repos/effect/.changeset/pre/fix-ndjson-split-utf8.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Decode split UTF-8 sequences correctly in NDJSON streams. diff --git a/repos/effect/.changeset/pre/fix-negative-file-seek.md b/repos/effect/.changeset/pre/fix-negative-file-seek.md deleted file mode 100644 index 50227499bf..0000000000 --- a/repos/effect/.changeset/pre/fix-negative-file-seek.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch -"@effect/platform-deno": patch ---- - -On Node and Deno, `FileSystem.File.seek` now rejects negative resulting positions with a `BadArgument` platform error, leaving the cursor unchanged. Its return type is now `Effect`. diff --git a/repos/effect/.changeset/pre/fix-node-compression-content-type.md b/repos/effect/.changeset/pre/fix-node-compression-content-type.md deleted file mode 100644 index eef388ec4b..0000000000 --- a/repos/effect/.changeset/pre/fix-node-compression-content-type.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch -"@effect/platform-node-shared": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch ---- - -Preserve the response Content-Type when compressing file, raw, stream, and byte-array responses on Node, Bun, Deno, and the web platform, including headers overridden after body construction. diff --git a/repos/effect/.changeset/pre/fix-node-http-stream-failure.md b/repos/effect/.changeset/pre/fix-node-http-stream-failure.md deleted file mode 100644 index b1049b4753..0000000000 --- a/repos/effect/.changeset/pre/fix-node-http-stream-failure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Fix Node HTTP client requests hanging when a streamed request body fails. diff --git a/repos/effect/.changeset/pre/fix-node-path-file-url-flavor.md b/repos/effect/.changeset/pre/fix-node-path-file-url-flavor.md deleted file mode 100644 index d1cb74f621..0000000000 --- a/repos/effect/.changeset/pre/fix-node-path-file-url-flavor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -NodePath: `layerPosix` and `layerWin32` now convert between paths and `file:` URLs using their own platform flavor instead of the host's. diff --git a/repos/effect/.changeset/pre/fix-node-pipeline-kill.md b/repos/effect/.changeset/pre/fix-node-pipeline-kill.md deleted file mode 100644 index b551d51dad..0000000000 --- a/repos/effect/.changeset/pre/fix-node-pipeline-kill.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Kill every process in a Node child process pipeline when killing its aggregate handle. diff --git a/repos/effect/.changeset/pre/fix-number-remainder-scientific-notation.md b/repos/effect/.changeset/pre/fix-number-remainder-scientific-notation.md deleted file mode 100644 index 9b43310d21..0000000000 --- a/repos/effect/.changeset/pre/fix-number-remainder-scientific-notation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Number.remainder` for very small and large values formatted in scientific notation. diff --git a/repos/effect/.changeset/pre/fix-object-keyword-json-schema.md b/repos/effect/.changeset/pre/fix-object-keyword-json-schema.md deleted file mode 100644 index 411cc653e5..0000000000 --- a/repos/effect/.changeset/pre/fix-object-keyword-json-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Emit `Schema.ObjectKeyword` as an object-or-array JSON Schema union. diff --git a/repos/effect/.changeset/pre/fix-one-shot-iterables.md b/repos/effect/.changeset/pre/fix-one-shot-iterables.md deleted file mode 100644 index 9908168c1b..0000000000 --- a/repos/effect/.changeset/pre/fix-one-shot-iterables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix one-shot iterable handling in Array.rotate, Iterable.cartesian, and in-memory RunnerStorage acquisition diff --git a/repos/effect/.changeset/pre/fix-openai-header-redaction.md b/repos/effect/.changeset/pre/fix-openai-header-redaction.md deleted file mode 100644 index 1357ea76b8..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-header-redaction.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Redact OpenAI organization and project headers from client errors. diff --git a/repos/effect/.changeset/pre/fix-openai-mcp-tool-names.md b/repos/effect/.changeset/pre/fix-openai-mcp-tool-names.md deleted file mode 100644 index c6009ba2b8..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-mcp-tool-names.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Fix OpenAI MCP tool call handling to keep the canonical `OpenAiMcp` tool name across response and stream paths, including approval flows. diff --git a/repos/effect/.changeset/pre/fix-openai-response-failure.md b/repos/effect/.changeset/pre/fix-openai-response-failure.md deleted file mode 100644 index dbe8abb998..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-response-failure.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Preserve OpenAI provider errors from failed response stream events. diff --git a/repos/effect/.changeset/pre/fix-openai-specialized-tool-output.md b/repos/effect/.changeset/pre/fix-openai-specialized-tool-output.md deleted file mode 100644 index ac215a2691..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-specialized-tool-output.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Emit specialized OpenAI tool results only once. diff --git a/repos/effect/.changeset/pre/fix-openai-system-input-text.md b/repos/effect/.changeset/pre/fix-openai-system-input-text.md deleted file mode 100644 index 830f6d00e1..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-system-input-text.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Encode OpenAI Responses API system messages as typed input text content. diff --git a/repos/effect/.changeset/pre/fix-openai-telemetry-response-namespace.md b/repos/effect/.changeset/pre/fix-openai-telemetry-response-namespace.md deleted file mode 100644 index d0488f9d6b..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-telemetry-response-namespace.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Fix OpenAI response telemetry attribute types to use the emitted response namespace. diff --git a/repos/effect/.changeset/pre/fix-openai-web-search-action.md b/repos/effect/.changeset/pre/fix-openai-web-search-action.md deleted file mode 100644 index ac95aedf19..0000000000 --- a/repos/effect/.changeset/pre/fix-openai-web-search-action.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Fix OpenAI stable web search response decoding by preserving the provider action in tool call parameters. diff --git a/repos/effect/.changeset/pre/fix-openapi-client-form-url-encoding.md b/repos/effect/.changeset/pre/fix-openapi-client-form-url-encoding.md deleted file mode 100644 index 99c7fd0343..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-client-form-url-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Encode form-urlencoded request bodies in generated HTTP clients. diff --git a/repos/effect/.changeset/pre/fix-openapi-client-multipart-records.md b/repos/effect/.changeset/pre/fix-openapi-client-multipart-records.md deleted file mode 100644 index c4c36d038d..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-client-multipart-records.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Encode multipart record payloads as form data in generated schema-backed clients. diff --git a/repos/effect/.changeset/pre/fix-openapi-client-paths.md b/repos/effect/.changeset/pre/fix-openapi-client-paths.md deleted file mode 100644 index 04b3c50ec8..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-client-paths.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Quote static paths and percent-encode path parameters in generated HTTP clients, including SSE and binary stream methods. Reject empty parameters and dot segments with an HTTP client error before sending a request. diff --git a/repos/effect/.changeset/pre/fix-openapi-endpoint-transform-order.md b/repos/effect/.changeset/pre/fix-openapi-endpoint-transform-order.md deleted file mode 100644 index ff1335312d..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-endpoint-transform-order.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Apply endpoint OpenAPI overrides and transforms after schema generation. diff --git a/repos/effect/.changeset/pre/fix-openapi-from-api-cache-copy.md b/repos/effect/.changeset/pre/fix-openapi-from-api-cache-copy.md deleted file mode 100644 index 68a5c04f44..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-from-api-cache-copy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Return fresh OpenAPI specs from cached `OpenApi.fromApi` calls. diff --git a/repos/effect/.changeset/pre/fix-openapi-generator-form-urlencoded.md b/repos/effect/.changeset/pre/fix-openapi-generator-form-urlencoded.md deleted file mode 100644 index 0b37c34bdd..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-generator-form-urlencoded.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Support `application/x-www-form-urlencoded` request bodies in `httpclient` output format. Previously, form-urlencoded request bodies were silently dropped, producing operations with no payload parameter. The generator now emits `HttpClientRequest.bodyUrlParams` for these endpoints, matching the existing pattern for `multipart/form-data` (`bodyFormData`) and `application/json` (`bodyJsonUnsafe`). The `httpapi` format was already handling this content type correctly. diff --git a/repos/effect/.changeset/pre/fix-openapi-generator-swagger2openapi.md b/repos/effect/.changeset/pre/fix-openapi-generator-swagger2openapi.md deleted file mode 100644 index 38ac06dc2a..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-generator-swagger2openapi.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Declare `swagger2openapi` as a runtime dependency so published `openapigen` installs can resolve the converter imported by `OpenApiGenerator`. diff --git a/repos/effect/.changeset/pre/fix-openapi-preserve-multiple-response-content-types.md b/repos/effect/.changeset/pre/fix-openapi-preserve-multiple-response-content-types.md deleted file mode 100644 index 7074326ffe..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-preserve-multiple-response-content-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `OpenApi.fromApi` preserving multiple response content types for one status code, closes #1485. diff --git a/repos/effect/.changeset/pre/fix-openapi-stream-transform-client.md b/repos/effect/.changeset/pre/fix-openapi-stream-transform-client.md deleted file mode 100644 index 13f781b601..0000000000 --- a/repos/effect/.changeset/pre/fix-openapi-stream-transform-client.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Apply `transformClient` when generated binary and SSE streams are consumed, preserving lazy stream construction. diff --git a/repos/effect/.changeset/pre/fix-openrouter-sparse-array.md b/repos/effect/.changeset/pre/fix-openrouter-sparse-array.md deleted file mode 100644 index 74f87f92f2..0000000000 --- a/repos/effect/.changeset/pre/fix-openrouter-sparse-array.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Fix sparse array crash in `streamText` tool call handling. diff --git a/repos/effect/.changeset/pre/fix-otel-logger-clock-skew.md b/repos/effect/.changeset/pre/fix-otel-logger-clock-skew.md deleted file mode 100644 index 5783219d71..0000000000 --- a/repos/effect/.changeset/pre/fix-otel-logger-clock-skew.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Use the Effect wall clock for log timestamps to match span timestamps. - -The Logger used `Date.now()` directly for log `timestamp` while the Tracer used `clock.currentTimeNanosUnsafe()` for span `startTime`. These could diverge when the high-resolution wall-clock origin drifted, causing logs to appear before their parent span. Both now use the same Effect wall clock via `nanosToHrTime(clock.currentTimeNanosUnsafe())`. diff --git a/repos/effect/.changeset/pre/fix-otel-logger-severity-number.md b/repos/effect/.changeset/pre/fix-otel-logger-severity-number.md deleted file mode 100644 index b411ad4d70..0000000000 --- a/repos/effect/.changeset/pre/fix-otel-logger-severity-number.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Logger: emit OTel-spec `SeverityNumber` (1-24) instead of Effect's internal log-level ordinal. - -`OtelLogger.make` previously passed `LogLevel.getOrdinal(level)` (e.g. Info=20000, Error=40000) as `severityNumber`, which falls outside the OpenTelemetry logs data model spec range (1-24). Backends that validate the field (Honeycomb, Datadog, etc.) bucket such values as `UNSPECIFIED`. - -The mapping now follows the spec: - -| Effect LogLevel | OTel SeverityNumber | -| --------------- | ------------------- | -| Trace | TRACE (1) | -| Debug | DEBUG (5) | -| Info | INFO (9) | -| Warn | WARN (13) | -| Error | ERROR (17) | -| Fatal | FATAL (21) | - -Also exports the helper `logLevelToSeverityNumber` for downstream use. diff --git a/repos/effect/.changeset/pre/fix-otel-logger-shutdown.md b/repos/effect/.changeset/pre/fix-otel-logger-shutdown.md deleted file mode 100644 index d811098636..0000000000 --- a/repos/effect/.changeset/pre/fix-otel-logger-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Ensure logger providers shut down when flushing fails. diff --git a/repos/effect/.changeset/pre/fix-otlp-exporter-shutdown.md b/repos/effect/.changeset/pre/fix-otlp-exporter-shutdown.md deleted file mode 100644 index 444ba3ce49..0000000000 --- a/repos/effect/.changeset/pre/fix-otlp-exporter-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix OTLP exporter shutdown to await in-flight and final buffered exports up to the configured shutdown timeout. diff --git a/repos/effect/.changeset/pre/fix-otlp-resource-attributes.md b/repos/effect/.changeset/pre/fix-otlp-resource-attributes.md deleted file mode 100644 index a461bfa335..0000000000 --- a/repos/effect/.changeset/pre/fix-otlp-resource-attributes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `OtlpResource` to decode percent-encoded environment attributes and preserve bigint precision. diff --git a/repos/effect/.changeset/pre/fix-pending-interruptible-mask.md b/repos/effect/.changeset/pre/fix-pending-interruptible-mask.md deleted file mode 100644 index e0a8494e48..0000000000 --- a/repos/effect/.changeset/pre/fix-pending-interruptible-mask.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Deliver pending interrupts when interruptibleMask restores fiber interruptibility. diff --git a/repos/effect/.changeset/pre/fix-persisted-cluster-reply-hang.md b/repos/effect/.changeset/pre/fix-persisted-cluster-reply-hang.md deleted file mode 100644 index 2a1b8732fb..0000000000 --- a/repos/effect/.changeset/pre/fix-persisted-cluster-reply-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Persist a serializable defect when a cluster reply cannot be encoded, preventing persisted entity callers from hanging. diff --git a/repos/effect/.changeset/pre/fix-persisted-queue-attempt-accounting.md b/repos/effect/.changeset/pre/fix-persisted-queue-attempt-accounting.md deleted file mode 100644 index 8737be085d..0000000000 --- a/repos/effect/.changeset/pre/fix-persisted-queue-attempt-accounting.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `PersistedQueue` to count schema decoding and malformed SQL payload failures as processing attempts. diff --git a/repos/effect/.changeset/pre/fix-pg-tls-servername.md b/repos/effect/.changeset/pre/fix-pg-tls-servername.md deleted file mode 100644 index 258137c259..0000000000 --- a/repos/effect/.changeset/pre/fix-pg-tls-servername.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Default TLS servername to the hostname for non-IP hosts during connection startup and cancellation, restoring connections to Postgres endpoints that require SNI. Preserve explicit SSL servername overrides. diff --git a/repos/effect/.changeset/pre/fix-prompt-all-iterables.md b/repos/effect/.changeset/pre/fix-prompt-all-iterables.md deleted file mode 100644 index 070620a8c1..0000000000 --- a/repos/effect/.changeset/pre/fix-prompt-all-iterables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support empty records and non-array iterables in `Prompt.all`. diff --git a/repos/effect/.changeset/pre/fix-prompt-date-tab-buffer.md b/repos/effect/.changeset/pre/fix-prompt-date-tab-buffer.md deleted file mode 100644 index 43f07df7a5..0000000000 --- a/repos/effect/.changeset/pre/fix-prompt-date-tab-buffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Prompt.date` carrying typed digits into the next field when pressing Tab, including when navigation wraps. diff --git a/repos/effect/.changeset/pre/fix-proto-record-assignment.md b/repos/effect/.changeset/pre/fix-proto-record-assignment.md deleted file mode 100644 index 1d960ffa4a..0000000000 --- a/repos/effect/.changeset/pre/fix-proto-record-assignment.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch -"@effect/docgen": patch -"@effect/openapi-generator": patch -"@effect/opentelemetry": patch -"@effect/sql-mssql": patch -"@effect/sql-sqlite-do": patch -"@effect/sql-sqlite-wasm": patch -"@effect/vitest": patch ---- - -Add `Record.assignProperty` and safely handle dynamic record keys such as `__proto__` and inherited property names. diff --git a/repos/effect/.changeset/pre/fix-pubsub-replay-retention.md b/repos/effect/.changeset/pre/fix-pubsub-replay-retention.md deleted file mode 100644 index 632765fdcf..0000000000 --- a/repos/effect/.changeset/pre/fix-pubsub-replay-retention.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent replay-enabled PubSubs from retaining values beyond each subscription's replay window. diff --git a/repos/effect/.changeset/pre/fix-queue-collect-duplication.md b/repos/effect/.changeset/pre/fix-queue-collect-duplication.md deleted file mode 100644 index bc29d2ee2d..0000000000 --- a/repos/effect/.changeset/pre/fix-queue-collect-duplication.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Queue.collect: stop duplicating drained messages by appending each batch once diff --git a/repos/effect/.changeset/pre/fix-random-string-seeds.md b/repos/effect/.changeset/pre/fix-random-string-seeds.md deleted file mode 100644 index 201923e1de..0000000000 --- a/repos/effect/.changeset/pre/fix-random-string-seeds.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix string seed encoding in Random.withSeed so short, trailing, and astral UTF-8 bytes affect deterministic streams. diff --git a/repos/effect/.changeset/pre/fix-rate-limiter-sleep.md b/repos/effect/.changeset/pre/fix-rate-limiter-sleep.md deleted file mode 100644 index 73633920be..0000000000 --- a/repos/effect/.changeset/pre/fix-rate-limiter-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `RateLimiter.makeSleep` to `RateLimiter.sleep` and support self-first partially applied and uncurried usage. diff --git a/repos/effect/.changeset/pre/fix-ratelimiter-tokenbucket-redis-ttl.md b/repos/effect/.changeset/pre/fix-ratelimiter-tokenbucket-redis-ttl.md deleted file mode 100644 index 874cd2ae34..0000000000 --- a/repos/effect/.changeset/pre/fix-ratelimiter-tokenbucket-redis-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the Redis `RateLimiterStore` token-bucket failing with opaque errors under memory pressure: it now writes its keys with a TTL and guards against a missing refill timestamp. diff --git a/repos/effect/.changeset/pre/fix-react-native-sqlite-values.md b/repos/effect/.changeset/pre/fix-react-native-sqlite-values.md deleted file mode 100644 index 97070d9b0c..0000000000 --- a/repos/effect/.changeset/pre/fix-react-native-sqlite-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-react-native": patch ---- - -Return selected rows from synchronous and asynchronous value queries. diff --git a/repos/effect/.changeset/pre/fix-react-ref-switch.md b/repos/effect/.changeset/pre/fix-react-ref-switch.md deleted file mode 100644 index 0044f52272..0000000000 --- a/repos/effect/.changeset/pre/fix-react-ref-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-react": patch ---- - -Ensure `useAtomRef` updates after switching refs when the new value matches the previous ref's value. diff --git a/repos/effect/.changeset/pre/fix-reactive-query-metadata.md b/repos/effect/.changeset/pre/fix-reactive-query-metadata.md deleted file mode 100644 index 6617c533bd..0000000000 --- a/repos/effect/.changeset/pre/fix-reactive-query-metadata.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve serialization and retention metadata on reactive `AtomRpc` and `AtomHttpApi` queries. diff --git a/repos/effect/.changeset/pre/fix-reactivity-duplicate-keys.md b/repos/effect/.changeset/pre/fix-reactivity-duplicate-keys.md deleted file mode 100644 index 2f01110879..0000000000 --- a/repos/effect/.changeset/pre/fix-reactivity-duplicate-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent `Reactivity.query` cleanup from failing when keys are repeated. diff --git a/repos/effect/.changeset/pre/fix-read-alloc-invalid-sizes.md b/repos/effect/.changeset/pre/fix-read-alloc-invalid-sizes.md deleted file mode 100644 index b656ca94c0..0000000000 --- a/repos/effect/.changeset/pre/fix-read-alloc-invalid-sizes.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"@effect/platform-deno": patch ---- - -Fix `File.readAlloc` on Node and Deno to fail with `PlatformError` (`BadArgument`) for negative, fractional, non-finite, or unallocatable sizes without moving the cursor. Zero-size reads continue to return `Option.none()` without moving the cursor. diff --git a/repos/effect/.changeset/pre/fix-redis-persisted-queue.md b/repos/effect/.changeset/pre/fix-redis-persisted-queue.md deleted file mode 100644 index 8ec5249674..0000000000 --- a/repos/effect/.changeset/pre/fix-redis-persisted-queue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Redis-backed `PersistedQueue` reset and failed-item handling. diff --git a/repos/effect/.changeset/pre/fix-remainder-scientific-notation.md b/repos/effect/.changeset/pre/fix-remainder-scientific-notation.md deleted file mode 100644 index 1a0b109d97..0000000000 --- a/repos/effect/.changeset/pre/fix-remainder-scientific-notation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -`Number.remainder`: fix incorrect results for small floats in scientific notation (e.g. `1e-7`). diff --git a/repos/effect/.changeset/pre/fix-request-resolver-pending-batches-leak.md b/repos/effect/.changeset/pre/fix-request-resolver-pending-batches-leak.md deleted file mode 100644 index 9721cd1755..0000000000 --- a/repos/effect/.changeset/pre/fix-request-resolver-pending-batches-leak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use `WeakMap` for `pendingBatches` instead of `Map`, to allow GC to collect resolvers diff --git a/repos/effect/.changeset/pre/fix-retry-transient-autocomplete.md b/repos/effect/.changeset/pre/fix-retry-transient-autocomplete.md deleted file mode 100644 index 0cdb6b2af3..0000000000 --- a/repos/effect/.changeset/pre/fix-retry-transient-autocomplete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpClient.retryTransient` autocomplete leaking `Schedule` internals by splitting the `{...} | Schedule` union into separate overloads. diff --git a/repos/effect/.changeset/pre/fix-rpc-http-requestids-finalizer.md b/repos/effect/.changeset/pre/fix-rpc-http-requestids-finalizer.md deleted file mode 100644 index 8c295f8995..0000000000 --- a/repos/effect/.changeset/pre/fix-rpc-http-requestids-finalizer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix request ID tracking in the RPC server HTTP protocol finalizer. diff --git a/repos/effect/.changeset/pre/fix-rpc-json-id-edges.md b/repos/effect/.changeset/pre/fix-rpc-json-id-edges.md deleted file mode 100644 index 6b915e0d03..0000000000 --- a/repos/effect/.changeset/pre/fix-rpc-json-id-edges.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix JSON-RPC serialization for `id` values that are falsey but valid, including `0` and `""`, while still mapping `null` to Effect's internal notification sentinel. diff --git a/repos/effect/.changeset/pre/fix-rpc-unknown-tag-isolation.md b/repos/effect/.changeset/pre/fix-rpc-unknown-tag-isolation.md deleted file mode 100644 index f8eb1b6bf3..0000000000 --- a/repos/effect/.changeset/pre/fix-rpc-unknown-tag-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Localize missing rpc method errors to the provided request id diff --git a/repos/effect/.changeset/pre/fix-runner-stream-completion.md b/repos/effect/.changeset/pre/fix-runner-stream-completion.md deleted file mode 100644 index 414fefe90d..0000000000 --- a/repos/effect/.changeset/pre/fix-runner-stream-completion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -End runner streams after emitting their terminal replies. diff --git a/repos/effect/.changeset/pre/fix-schedule-during.md b/repos/effect/.changeset/pre/fix-schedule-during.md deleted file mode 100644 index c881221afe..0000000000 --- a/repos/effect/.changeset/pre/fix-schedule-during.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.during` to recur until the configured duration has elapsed. diff --git a/repos/effect/.changeset/pre/fix-schedule-fixed-double-exec.md b/repos/effect/.changeset/pre/fix-schedule-fixed-double-exec.md deleted file mode 100644 index 1909b59a16..0000000000 --- a/repos/effect/.changeset/pre/fix-schedule-fixed-double-exec.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.fixed` double-executing the effect due to clock jitter. - -The `elapsedSincePrevious > window` check included sleep time from the -previous step, so any timer imprecision (e.g. 1001ms for a 1000ms sleep) -triggered an immediate zero-delay re-execution. diff --git a/repos/effect/.changeset/pre/fix-schedule-reduce-sync-state.md b/repos/effect/.changeset/pre/fix-schedule-reduce-sync-state.md deleted file mode 100644 index 9c1d929351..0000000000 --- a/repos/effect/.changeset/pre/fix-schedule-reduce-sync-state.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.reduce` to persist state updates when the combine function returns a synchronous value. diff --git a/repos/effect/.changeset/pre/fix-schema-arbitrary-exclusive-bounds.md b/repos/effect/.changeset/pre/fix-schema-arbitrary-exclusive-bounds.md deleted file mode 100644 index b14291c8da..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-arbitrary-exclusive-bounds.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix schema arbitrary constraints for exclusive BigInt, Date, and integer number bounds. diff --git a/repos/effect/.changeset/pre/fix-schema-bracket-prototype-pollution.md b/repos/effect/.changeset/pre/fix-schema-bracket-prototype-pollution.md deleted file mode 100644 index f570f34ca0..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-bracket-prototype-pollution.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix a bug where decoding bracket paths from FormData or URLSearchParams could mutate inherited object prototypes. diff --git a/repos/effect/.changeset/pre/fix-schema-codegen-optional-tuples.md b/repos/effect/.changeset/pre/fix-schema-codegen-optional-tuples.md deleted file mode 100644 index 6a39c5f2c1..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-codegen-optional-tuples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SchemaRepresentation.toCodeDocument` generating invalid TypeScript for optional tuple elements containing unions or nested readonly tuples. Optional element types are now parenthesized, for example `readonly [(string | number)?]` instead of `readonly [string | number?]`. Generated runtime schemas are unchanged. diff --git a/repos/effect/.changeset/pre/fix-schema-codegen-proto.md b/repos/effect/.changeset/pre/fix-schema-codegen-proto.md deleted file mode 100644 index 38f773bc38..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-codegen-proto.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SchemaRepresentation.toCodeDocument` dropping Struct fields named `__proto__` from generated schemas. These fields now use computed keys, such as `Schema.Struct({ ["__proto__"]: Schema.String })`, so the generated schema validates them correctly. diff --git a/repos/effect/.changeset/pre/fix-schema-defect-message.md b/repos/effect/.changeset/pre/fix-schema-defect-message.md deleted file mode 100644 index 15722dd3e0..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-defect-message.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Schema.Defect JSON encoding for Error values whose message property is not a string. diff --git a/repos/effect/.changeset/pre/fix-schema-encode-keys-property-keys.md b/repos/effect/.changeset/pre/fix-schema-encode-keys-property-keys.md deleted file mode 100644 index 8e72551528..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-encode-keys-property-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Struct` key renaming and `Schema.encodeKeys` to support symbol keys, and reject duplicate encoded keys. diff --git a/repos/effect/.changeset/pre/fix-schema-encodekeys-class.md b/repos/effect/.changeset/pre/fix-schema-encodekeys-class.md deleted file mode 100644 index ebf61c22ef..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-encodekeys-class.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema.encodeKeys: relax input constraint from Struct to schemas with fields so Schema.Class works, closes #1412. diff --git a/repos/effect/.changeset/pre/fix-schema-encodekeys-struct.md b/repos/effect/.changeset/pre/fix-schema-encodekeys-struct.md deleted file mode 100644 index fd884d1efd..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-encodekeys-struct.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.encodeKeys` to encode non-remapped struct fields during encoding. diff --git a/repos/effect/.changeset/pre/fix-schema-encoding-checks.md b/repos/effect/.changeset/pre/fix-schema-encoding-checks.md deleted file mode 100644 index ba56790cbb..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-encoding-checks.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch ---- - -Fix Schema handling of encoded-side checks for container ASTs. - -Checks added after `flip` are now preserved as `encodingChecks` across -`Declaration`, `Arrays`, `Objects`, and `Union`, even when rebuilding the AST -does not change child nodes. `toType` now projects those checks consistently, -and parsing applies encoded-side checks to the local encoded value when an -encoding chain is present without allowing encoded-side `parseOptions` -annotations to affect the current parser side. diff --git a/repos/effect/.changeset/pre/fix-schema-identifier-expected-message.md b/repos/effect/.changeset/pre/fix-schema-identifier-expected-message.md deleted file mode 100644 index 8db6496770..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-identifier-expected-message.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use the `identifier` annotation as the expected message when available, closes #1458. diff --git a/repos/effect/.changeset/pre/fix-schema-is-json-records.md b/repos/effect/.changeset/pre/fix-schema-is-json-records.md deleted file mode 100644 index 5a71fc1cc9..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-is-json-records.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SchemaAST.isJson` to reject class instances and other non-record objects. diff --git a/repos/effect/.changeset/pre/fix-schema-is-uuid.md b/repos/effect/.changeset/pre/fix-schema-is-uuid.md deleted file mode 100644 index ecd2eb12f2..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-is-uuid.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.isUUID` so the `version` parameter is optional in its public signature. diff --git a/repos/effect/.changeset/pre/fix-schema-json-tuple-allof.md b/repos/effect/.changeset/pre/fix-schema-json-tuple-allof.md deleted file mode 100644 index cbf16877bd..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-json-tuple-allof.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix JSON Schema `allOf` imports for tuple intersections and preserve primitive refinements when combining literal constraints. diff --git a/repos/effect/.changeset/pre/fix-schema-make-nested-class-union.md b/repos/effect/.changeset/pre/fix-schema-make-nested-class-union.md deleted file mode 100644 index cc53b35a44..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-make-nested-class-union.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.make` to preserve existing nested `Schema.Class` instances, including in array fields, while recursively constructing plain class inputs provided at runtime inside unions. Constructor defaults remain scoped to structural field and element occurrences, with `SchemaAST.Context.constructorDefault` representing the single default link for each occurrence. - -Optimize `Function.memoize` to use a single `WeakMap` lookup for cached values. Its callback no longer accepts `undefined` as a return type because `undefined` represents a cache miss. - -The performance of the two array paths can be reproduced by saving the following program as -`scratchpad/schema-make-6890-benchmark.ts` and running `node scratchpad/schema-make-6890-benchmark.ts` from the repository -root: - -```ts -import { Schema } from "effect" -import { performance } from "node:perf_hooks" - -class Row extends Schema.Class("Row")({ value: Schema.String }) {} -class DirectTable extends Schema.Class("DirectTable")({ rows: Schema.Array(Row) }) {} -class UnionTable extends Schema.Class("UnionTable")({ rows: Schema.Array(Schema.Union([Row])) }) {} - -const rows = Array.from({ length: 30_000 }, (_, value) => Row.make({ value: String(value) })) - -function benchmark(label: string, make: () => { readonly rows: ReadonlyArray }) { - const samples: Array = [] - for (let i = 0; i < 6; i++) { - const start = performance.now() - const result = make() - samples.push(performance.now() - start) - if (result.rows[0] !== rows[0] || result.rows.at(-1) !== rows.at(-1)) { - throw new Error(`${label} did not preserve Row identity`) - } - } - console.log(`${label}: ${samples.slice(1).map((n) => n.toFixed(3)).join(", ")} ms`) -} - -benchmark("Array(Class)", () => DirectTable.make({ rows })) -benchmark("Array(Union([Class]))", () => UnionTable.make({ rows })) -``` - -Representative local results on Node 24.12.0 (six runs, with the first discarded): - -```text -Array(Class): 0.639, 0.498, 0.447, 0.448, 0.451 ms -Array(Union([Class])): 3.141, 2.195, 2.126, 2.108, 2.057 ms -``` diff --git a/repos/effect/.changeset/pre/fix-schema-option-non-schema-failures.md b/repos/effect/.changeset/pre/fix-schema-option-non-schema-failures.md deleted file mode 100644 index 516ec89fa1..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-option-non-schema-failures.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch ---- - -Normalize error behavior for Schema and SchemaParser boundary APIs. - -`SchemaError` now extends `Data.TaggedError`, so it is also a native `Error`. -SchemaParser Promise APIs now reject an `Error` whose cause is the -`SchemaIssue.Issue` for schema failures. - -Schema and SchemaParser `Effect` and `Exit` adapters now preserve full causes -while mapping schema issue failures to their public error type. The `is`, -`asserts`, `Promise`, `Sync`, `Result`, `Option`, `make`, and `makeOption` -adapters now distinguish schema issues from non-schema causes. Schema-only -failures are converted to the adapter's normal representation (`false`, -rejected or thrown schema error, `Result.fail`, or `None`), while non-schema -causes throw or reject with an `Error` whose cause is the underlying `Cause`. diff --git a/repos/effect/.changeset/pre/fix-schema-parser-checks.md b/repos/effect/.changeset/pre/fix-schema-parser-checks.md deleted file mode 100644 index fd93a6c5ba..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-parser-checks.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema` encoding so container-level checks are validated against the decoded value instead of the encoded output. - -Disallow adding checks directly to `Schema.suspend(...)`; add the checks to the suspended schema instead. - -Fix `StructWithRest` so index signatures do not re-parse or overwrite fixed properties. diff --git a/repos/effect/.changeset/pre/fix-schema-pattern-state.md b/repos/effect/.changeset/pre/fix-schema-pattern-state.md deleted file mode 100644 index 2a321daada..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-pattern-state.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `Schema.isPattern` deterministic for regular expressions with global or sticky flags. diff --git a/repos/effect/.changeset/pre/fix-schema-representation-identifiers.md b/repos/effect/.changeset/pre/fix-schema-representation-identifiers.md deleted file mode 100644 index f4a8a2391d..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-representation-identifiers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -SchemaRepresentation: generate references from encoded AST identity, suffix colliding identifiers instead of throwing, and preserve sharing across property-key context. This avoids false-positive duplicate identifier errors while keeping referentially distinct schemas addressable; generated fallback definitions now use the clearer `Encoded` suffix. diff --git a/repos/effect/.changeset/pre/fix-schema-sentinel-declaration.md b/repos/effect/.changeset/pre/fix-schema-sentinel-declaration.md deleted file mode 100644 index f042aef646..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-sentinel-declaration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Mark the internal `~sentinels` Schema annotation as `@internal` so release declaration stripping removes it together with `SchemaAST.Sentinel`. This keeps the published declarations self-consistent for consumers that type-check dependencies with `skipLibCheck: false`. diff --git a/repos/effect/.changeset/pre/fix-schema-tuple-post-rest-indexing.md b/repos/effect/.changeset/pre/fix-schema-tuple-post-rest-indexing.md deleted file mode 100644 index 8546cc8be5..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-tuple-post-rest-indexing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.toFormatter` and `Schema.toEquivalence` indexing for tuples with multiple post-rest elements. diff --git a/repos/effect/.changeset/pre/fix-schema-union-dispatch-order.md b/repos/effect/.changeset/pre/fix-schema-union-dispatch-order.md deleted file mode 100644 index 61876f469f..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-union-dispatch-order.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch ---- - -Fix union candidate selection and decoding order so that unions now: - -- consider matches from every sentinel key instead of dropping valid members after the first match; -- reject ambiguous `oneOf` inputs when members with different sentinel keys both match; -- preserve declared member order when combining discriminated members with non-discriminated fallbacks; -- commit concurrent decoding results in declaration order instead of completion order. - -Reserved SSE failure event names with non-`Cause` data are now emitted as application events instead of producing a runtime defect. diff --git a/repos/effect/.changeset/pre/fix-schema-union-pruning.md b/repos/effect/.changeset/pre/fix-schema-union-pruning.md deleted file mode 100644 index c1029ae56f..0000000000 --- a/repos/effect/.changeset/pre/fix-schema-union-pruning.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Union candidate selection for recovering middleware and suspended members. diff --git a/repos/effect/.changeset/pre/fix-scoped-ref-failed-replacement.md b/repos/effect/.changeset/pre/fix-scoped-ref-failed-replacement.md deleted file mode 100644 index 5caffbbcaa..0000000000 --- a/repos/effect/.changeset/pre/fix-scoped-ref-failed-replacement.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep the current `ScopedRef` resource alive when acquiring its replacement fails. diff --git a/repos/effect/.changeset/pre/fix-searchparam-initial-decode.md b/repos/effect/.changeset/pre/fix-searchparam-initial-decode.md deleted file mode 100644 index f78d96a463..0000000000 --- a/repos/effect/.changeset/pre/fix-searchparam-initial-decode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Atom.searchParam: decode initial URL values correctly when a schema is provided diff --git a/repos/effect/.changeset/pre/fix-semaphore-with-permits-interrupt-leak.md b/repos/effect/.changeset/pre/fix-semaphore-with-permits-interrupt-leak.md deleted file mode 100644 index aba537cb54..0000000000 --- a/repos/effect/.changeset/pre/fix-semaphore-with-permits-interrupt-leak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Semaphore.withPermits` leaking permits when interrupted between acquiring them and installing their release. diff --git a/repos/effect/.changeset/pre/fix-serializable-wire-transfer.md b/repos/effect/.changeset/pre/fix-serializable-wire-transfer.md deleted file mode 100644 index 64c580d878..0000000000 --- a/repos/effect/.changeset/pre/fix-serializable-wire-transfer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `Atom.serializable` encode/decode for wire transfer. - -Use `Schema.toCodecJson` instead of `Schema.encodeSync`/`Schema.decodeSync` directly, so that encoded values are plain JSON objects that survive serialization roundtrips (JSON, seroval, etc.). Previously, `AsyncResult.Schema` encode produced instances with custom prototypes that were lost after wire transfer, causing decode to fail with "Expected AsyncResult" errors during SSR hydration. diff --git a/repos/effect/.changeset/pre/fix-sink-flatmap-leftovers.md b/repos/effect/.changeset/pre/fix-sink-flatmap-leftovers.md deleted file mode 100644 index 0afe46e741..0000000000 --- a/repos/effect/.changeset/pre/fix-sink-flatmap-leftovers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve pending leftovers when a `Sink.flatMap` continuation completes without consuming input. diff --git a/repos/effect/.changeset/pre/fix-sliding-size-chunks.md b/repos/effect/.changeset/pre/fix-sliding-size-chunks.md deleted file mode 100644 index 9d06bc18df..0000000000 --- a/repos/effect/.changeset/pre/fix-sliding-size-chunks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.slidingSize` to produce the same windows regardless of upstream chunk boundaries. diff --git a/repos/effect/.changeset/pre/fix-sql-persisted-queue-lock-refresh.md b/repos/effect/.changeset/pre/fix-sql-persisted-queue-lock-refresh.md deleted file mode 100644 index 19a98949be..0000000000 --- a/repos/effect/.changeset/pre/fix-sql-persisted-queue-lock-refresh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix SQL-backed persisted queues to refresh locks for actively acquired elements. diff --git a/repos/effect/.changeset/pre/fix-sqlite-persisted-queue.md b/repos/effect/.changeset/pre/fix-sqlite-persisted-queue.md deleted file mode 100644 index bb6bcc1b16..0000000000 --- a/repos/effect/.changeset/pre/fix-sqlite-persisted-queue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix SQL persisted queue delivery on SQLite builds without `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`. diff --git a/repos/effect/.changeset/pre/fix-sse-last-event-id.md b/repos/effect/.changeset/pre/fix-sse-last-event-id.md deleted file mode 100644 index 69109fa35f..0000000000 --- a/repos/effect/.changeset/pre/fix-sse-last-event-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Retain the last SSE event ID across dispatched events. diff --git a/repos/effect/.changeset/pre/fix-sse-leading-bom.md b/repos/effect/.changeset/pre/fix-sse-leading-bom.md deleted file mode 100644 index c1b7716c88..0000000000 --- a/repos/effect/.changeset/pre/fix-sse-leading-bom.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Recognize and ignore a leading UTF-8 byte order mark in server-sent event streams. diff --git a/repos/effect/.changeset/pre/fix-sse-mixed-line-endings.md b/repos/effect/.changeset/pre/fix-sse-mixed-line-endings.md deleted file mode 100644 index c1b2727290..0000000000 --- a/repos/effect/.changeset/pre/fix-sse-mixed-line-endings.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve SSE events with mixed line endings. diff --git a/repos/effect/.changeset/pre/fix-sse-retry-directives.md b/repos/effect/.changeset/pre/fix-sse-retry-directives.md deleted file mode 100644 index e80c35962f..0000000000 --- a/repos/effect/.changeset/pre/fix-sse-retry-directives.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore malformed retry directives when parsing server-sent event streams. diff --git a/repos/effect/.changeset/pre/fix-static-head-range.md b/repos/effect/.changeset/pre/fix-static-head-range.md deleted file mode 100644 index 915c8deb81..0000000000 --- a/repos/effect/.changeset/pre/fix-static-head-range.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore Range headers on non-GET requests in HttpStaticServer. diff --git a/repos/effect/.changeset/pre/fix-static-oversized-ranges.md b/repos/effect/.changeset/pre/fix-static-oversized-ranges.md deleted file mode 100644 index 5838914794..0000000000 --- a/repos/effect/.changeset/pre/fix-static-oversized-ranges.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Parse HttpStaticServer byte range integers exactly, including values above Number.MAX_SAFE_INTEGER. Oversized starts now return 416 with Content-Range instead of falling back to 200. Oversized ends clamp to the last byte, and oversized suffixes return the whole file as 206. diff --git a/repos/effect/.changeset/pre/fix-stream-aggregate-within-idle.md b/repos/effect/.changeset/pre/fix-stream-aggregate-within-idle.md deleted file mode 100644 index 96744190cf..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-aggregate-within-idle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.aggregateWithin` and `Stream.groupedWithin` retaining fiber continuations on every schedule tick while upstream is idle. diff --git a/repos/effect/.changeset/pre/fix-stream-execution-plan-retries.md b/repos/effect/.changeset/pre/fix-stream-execution-plan-retries.md deleted file mode 100644 index 0174fbabec..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-execution-plan-retries.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.withExecutionPlan` retry limits resetting after partial stream emissions. diff --git a/repos/effect/.changeset/pre/fix-stream-grouped-within-flush.md b/repos/effect/.changeset/pre/fix-stream-grouped-within-flush.md deleted file mode 100644 index fff052fc77..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-grouped-within-flush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.groupedWithin` dropping partial batches when the upstream ends or goes idle. diff --git a/repos/effect/.changeset/pre/fix-stream-haltwhen.md b/repos/effect/.changeset/pre/fix-stream-haltwhen.md deleted file mode 100644 index 8cb383c063..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-haltwhen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.haltWhen` to observe halt effects at pull boundaries for synchronous streams. diff --git a/repos/effect/.changeset/pre/fix-stream-map-accum-array-effect.md b/repos/effect/.changeset/pre/fix-stream-map-accum-array-effect.md deleted file mode 100644 index c67a81799c..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-map-accum-array-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix data-first dispatch for `Stream.mapAccumArrayEffect`. diff --git a/repos/effect/.changeset/pre/fix-stream-range-zero-chunk.md b/repos/effect/.changeset/pre/fix-stream-range-zero-chunk.md deleted file mode 100644 index cc28d66a7b..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-range-zero-chunk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `Stream.range` emits the full range when the chunk size is zero. diff --git a/repos/effect/.changeset/pre/fix-stream-run-for-each-while.md b/repos/effect/.changeset/pre/fix-stream-run-for-each-while.md deleted file mode 100644 index 91b9e1d2ca..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-run-for-each-while.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Stream.runForEachWhile so it continues across chunk boundaries while the predicate returns true and stops when the predicate returns false. diff --git a/repos/effect/.changeset/pre/fix-stream-scan-effect.md b/repos/effect/.changeset/pre/fix-stream-scan-effect.md deleted file mode 100644 index a1d821f93c..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-scan-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.scanEffect` hanging and repeatedly emitting the initial state. diff --git a/repos/effect/.changeset/pre/fix-stream-scoped-scope.md b/repos/effect/.changeset/pre/fix-stream-scoped-scope.md deleted file mode 100644 index c4437e6561..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-scoped-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.scoped` and `Channel.scoped` so pull effects run with the scoped resource scope. diff --git a/repos/effect/.changeset/pre/fix-stream-wrapper-status.md b/repos/effect/.changeset/pre/fix-stream-wrapper-status.md deleted file mode 100644 index 9568a5cfdc..0000000000 --- a/repos/effect/.changeset/pre/fix-stream-wrapper-status.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiBuilder` ignoring the status annotation on a `HttpApiSchema.WithHeaders` wrapper around a streaming success, which defected when the wrapper and inner statuses differed. diff --git a/repos/effect/.changeset/pre/fix-string-case-digits.md b/repos/effect/.changeset/pre/fix-string-case-digits.md deleted file mode 100644 index 5aaa2cd158..0000000000 --- a/repos/effect/.changeset/pre/fix-string-case-digits.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `String.camelCase` and `String.pascalCase` handling of numeric word segments, and add `String.configCase` for configuration key casing. diff --git a/repos/effect/.changeset/pre/fix-strip-approval-artifacts-multi-round.md b/repos/effect/.changeset/pre/fix-strip-approval-artifacts-multi-round.md deleted file mode 100644 index c091d0313d..0000000000 --- a/repos/effect/.changeset/pre/fix-strip-approval-artifacts-multi-round.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect": patch ---- - -Fix LanguageModel stripping of resolved approval artifacts across multi-round conversations. - -Previously, `stripResolvedApprovals` only ran when there were pending approvals -in the current round. Stale artifacts from earlier rounds would leak to the -provider, causing errors. The stripping now runs unconditionally. - -In streaming mode, pre-resolved tool results are also emitted as stream parts -so `Chat.streamText` persists them to history, preventing re-resolution on -subsequent rounds. diff --git a/repos/effect/.changeset/pre/fix-struct-utility-types-simplify.md b/repos/effect/.changeset/pre/fix-struct-utility-types-simplify.md deleted file mode 100644 index 4607703276..0000000000 --- a/repos/effect/.changeset/pre/fix-struct-utility-types-simplify.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Struct` utility return types (for example `pick`) to preserve the previous simplified shape instead of exposing raw utility types like `Pick`, closes #1855. diff --git a/repos/effect/.changeset/pre/fix-structural-proto-equality.md b/repos/effect/.changeset/pre/fix-structural-proto-equality.md deleted file mode 100644 index eae88bf89a..0000000000 --- a/repos/effect/.changeset/pre/fix-structural-proto-equality.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix structural equality for request-style values when structural hashes collide. diff --git a/repos/effect/.changeset/pre/fix-structwithrest-index-signatures.md b/repos/effect/.changeset/pre/fix-structwithrest-index-signatures.md deleted file mode 100644 index beee64ebe1..0000000000 --- a/repos/effect/.changeset/pre/fix-structwithrest-index-signatures.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/ai-openai": patch ---- - -Validate `Schema.StructWithRest` fixed fields against rest index signatures at the type level so schemas cannot be constructed with incompatible decoded, encoded, or make shapes. This keeps `StructWithRest` types sound and updates the generated OpenAI conversation-items request schema to keep accepting arbitrary additional fields under the stricter validation. diff --git a/repos/effect/.changeset/pre/fix-subscription-ref-get-and-update-some.md b/repos/effect/.changeset/pre/fix-subscription-ref-get-and-update-some.md deleted file mode 100644 index d231f9c6c9..0000000000 --- a/repos/effect/.changeset/pre/fix-subscription-ref-get-and-update-some.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SubscriptionRef.getAndUpdateSome` to return the current value when no update is selected. diff --git a/repos/effect/.changeset/pre/fix-subscriptionref-getandupdateeffect.md b/repos/effect/.changeset/pre/fix-subscriptionref-getandupdateeffect.md deleted file mode 100644 index 4983ed0b0e..0000000000 --- a/repos/effect/.changeset/pre/fix-subscriptionref-getandupdateeffect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SubscriptionRef.getAndUpdateEffect` to execute the effectful update. diff --git a/repos/effect/.changeset/pre/fix-tagged-union-class-sentinels.md b/repos/effect/.changeset/pre/fix-tagged-union-class-sentinels.md deleted file mode 100644 index f79912a971..0000000000 --- a/repos/effect/.changeset/pre/fix-tagged-union-class-sentinels.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.toTaggedUnion` discriminant detection for class-based schemas, including unique symbol tags, closes #1584. diff --git a/repos/effect/.changeset/pre/fix-tagged-union-match-unify.md b/repos/effect/.changeset/pre/fix-tagged-union-match-unify.md deleted file mode 100644 index 987abe5e62..0000000000 --- a/repos/effect/.changeset/pre/fix-tagged-union-match-unify.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `TaggedUnion.match` to use `Unify` for return types, allowing -branches to return distinct Effect types that are properly merged. diff --git a/repos/effect/.changeset/pre/fix-to-tagged-union-isanyof-custom-tags.md b/repos/effect/.changeset/pre/fix-to-tagged-union-isanyof-custom-tags.md deleted file mode 100644 index 17f0ec54cf..0000000000 --- a/repos/effect/.changeset/pre/fix-to-tagged-union-isanyof-custom-tags.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.toTaggedUnion(...).isAnyOf` narrowing for custom discriminant keys, closes #2386. - -Previously, the type predicate always extracted union members by `_tag`, even -when `toTaggedUnion` was created with a different discriminant key. Runtime -behavior already used the supplied key, so this aligns the type-level narrowing -with the existing runtime behavior. diff --git a/repos/effect/.changeset/pre/fix-toml-array-subtables.md b/repos/effect/.changeset/pre/fix-toml-array-subtables.md deleted file mode 100644 index 07d3edbae2..0000000000 --- a/repos/effect/.changeset/pre/fix-toml-array-subtables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Toml.parse` rejecting child tables in separate array-of-tables entries. diff --git a/repos/effect/.changeset/pre/fix-tool-provider-defined-clone.md b/repos/effect/.changeset/pre/fix-tool-provider-defined-clone.md deleted file mode 100644 index 6306c054d1..0000000000 --- a/repos/effect/.changeset/pre/fix-tool-provider-defined-clone.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Tool: preserve the tool kind when cloning provider-defined and dynamic tools. - -`Tool.addDependency`, `setParameters`, `setSuccess`, `setFailure`, `annotate`, and `annotateMerge` previously rebuilt the tool as a user-defined tool, which flipped `Tool.isProviderDefined` to `false`, corrupted the provider `id` (e.g. `anthropic.memory_20250818`), and crashed `Tool.getStrictMode`. These operations now clone the tool while preserving its prototype, `id`, and kind. Provider-defined tools also now carry an empty annotations context so `Tool.getStrictMode`/`annotate` work on them. Closes #2615. diff --git a/repos/effect/.changeset/pre/fix-tool-result-serialization.md b/repos/effect/.changeset/pre/fix-tool-result-serialization.md deleted file mode 100644 index 65accd0f1b..0000000000 --- a/repos/effect/.changeset/pre/fix-tool-result-serialization.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect": patch ---- - -Fix tool result serialization to select the codec using `isFailure` and preserve `encodedResult` through `Response.AllParts` round trips. - -Add `Tool.failureResultSchema(tool)` and `Tool.ExecutionFailure` to handle user failures, `AiError`, and denied or interrupted calls consistently. Also export `HttpRequestDetails` and `HttpResponseDetails` from `AiError`; the `Response` exports remain available. - -### Breaking changes - -- Stored results must match the selected schema. With success `Schema.Number` and failure `Schema.NumberFromString`, migrate failed results from `404` to `"404"`. -- `Response.ToolResultPart` returns `Schema.Codec` instead of `Schema.decodeTo`. Update annotations that depend on the old type. -- `Tool.FailureResult` and `Tool.Result`, including their encoded variants, now include `Tool.ExecutionFailure` in both failure modes. Handle it when narrowing failed results. diff --git a/repos/effect/.changeset/pre/fix-trie-key-replacement.md b/repos/effect/.changeset/pre/fix-trie-key-replacement.md deleted file mode 100644 index 00d3707328..0000000000 --- a/repos/effect/.changeset/pre/fix-trie-key-replacement.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Trie.insert` to replace existing values without mutating the original trie or increasing its size. diff --git a/repos/effect/.changeset/pre/fix-trie-longest-prefix.md b/repos/effect/.changeset/pre/fix-trie-longest-prefix.md deleted file mode 100644 index bf422ac6d4..0000000000 --- a/repos/effect/.changeset/pre/fix-trie-longest-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Trie.longestPrefixOf` returning a valued sibling that does not match the input key. diff --git a/repos/effect/.changeset/pre/fix-trie-undefined-values.md b/repos/effect/.changeset/pre/fix-trie-undefined-values.md deleted file mode 100644 index 12a9e3f138..0000000000 --- a/repos/effect/.changeset/pre/fix-trie-undefined-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Trie` to preserve entries whose value is `undefined`. diff --git a/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-index-drift.md b/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-index-drift.md deleted file mode 100644 index 155671bec3..0000000000 --- a/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-index-drift.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix TupleWithRest post-rest validation to check each tail index sequentially. diff --git a/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-validation.md b/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-validation.md deleted file mode 100644 index 60e4272f53..0000000000 --- a/repos/effect/.changeset/pre/fix-tuple-with-rest-post-rest-validation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.TupleWithRest` incorrectly accepting inputs with missing post-rest elements, closes #1410. diff --git a/repos/effect/.changeset/pre/fix-txpubsub-publish-all-iterables.md b/repos/effect/.changeset/pre/fix-txpubsub-publish-all-iterables.md deleted file mode 100644 index 390063b2ba..0000000000 --- a/repos/effect/.changeset/pre/fix-txpubsub-publish-all-iterables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `TxPubSub.publishAll` dropping values from one-shot iterables when a transaction retries. diff --git a/repos/effect/.changeset/pre/fix-txqueue-closing-drain.md b/repos/effect/.changeset/pre/fix-txqueue-closing-drain.md deleted file mode 100644 index 41b0208f88..0000000000 --- a/repos/effect/.changeset/pre/fix-txqueue-closing-drain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `TxQueue.poll` and `TxQueue.clear` complete a closing queue after draining its buffered items. diff --git a/repos/effect/.changeset/pre/fix-txqueue-offer-all-iterables.md b/repos/effect/.changeset/pre/fix-txqueue-offer-all-iterables.md deleted file mode 100644 index e6f159cf19..0000000000 --- a/repos/effect/.changeset/pre/fix-txqueue-offer-all-iterables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `TxQueue.offerAll` to preserve one-shot iterables across transaction retries and repeated runs. diff --git a/repos/effect/.changeset/pre/fix-types-voidifempty.md b/repos/effect/.changeset/pre/fix-types-voidifempty.md deleted file mode 100644 index f481e77957..0000000000 --- a/repos/effect/.changeset/pre/fix-types-voidifempty.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Types.VoidIfEmpty` to correctly detect empty object types. Remove deprecated `Types.MatchRecord` in favor of the simplified implementation, closes #1647. diff --git a/repos/effect/.changeset/pre/fix-variant-schema-default-cache.md b/repos/effect/.changeset/pre/fix-variant-schema-default-cache.md deleted file mode 100644 index 4449cb1754..0000000000 --- a/repos/effect/.changeset/pre/fix-variant-schema-default-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Separate the default `VariantSchema` cache from named variant entries. diff --git a/repos/effect/.changeset/pre/fix-vitest-proxy-chained-helpers.md b/repos/effect/.changeset/pre/fix-vitest-proxy-chained-helpers.md deleted file mode 100644 index a6b5ecbf22..0000000000 --- a/repos/effect/.changeset/pre/fix-vitest-proxy-chained-helpers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Preserve chained vitest helpers like `it.describe.each` and `it.skip.each` when accessed through the `it` proxy. Previously the proxy returned bound copies of vitest's functions, which stripped their static helper properties and caused `TypeError: it.describe.each is not a function`. diff --git a/repos/effect/.changeset/pre/fix-vitest-record-schema-arbitrary.md b/repos/effect/.changeset/pre/fix-vitest-record-schema-arbitrary.md deleted file mode 100644 index 840708d753..0000000000 --- a/repos/effect/.changeset/pre/fix-vitest-record-schema-arbitrary.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Fix record-form property tests to convert Schema values to FastCheck arbitraries. diff --git a/repos/effect/.changeset/pre/fix-vitest-runner-import.md b/repos/effect/.changeset/pre/fix-vitest-runner-import.md deleted file mode 100644 index 1f714c770e..0000000000 --- a/repos/effect/.changeset/pre/fix-vitest-runner-import.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": minor ---- - -Require Vitest 4.1 or later and read suite state from `TestRunner`, removing the direct `@vitest/runner` import and support for Vitest 3 and 4.0. diff --git a/repos/effect/.changeset/pre/fix-vitest-throws-assertions.md b/repos/effect/.changeset/pre/fix-vitest-throws-assertions.md deleted file mode 100644 index 307bb38379..0000000000 --- a/repos/effect/.changeset/pre/fix-vitest-throws-assertions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Ensure `throws` and `throwsAsync` fail when the supplied operation returns or resolves without throwing. diff --git a/repos/effect/.changeset/pre/fix-void-mcp-tool-results.md b/repos/effect/.changeset/pre/fix-void-mcp-tool-results.md deleted file mode 100644 index 5febf7769d..0000000000 --- a/repos/effect/.changeset/pre/fix-void-mcp-tool-results.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep MCP tool calls that return void successful. diff --git a/repos/effect/.changeset/pre/fix-void-response-encoding.md b/repos/effect/.changeset/pre/fix-void-response-encoding.md deleted file mode 100644 index bb84b57ff3..0000000000 --- a/repos/effect/.changeset/pre/fix-void-response-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -unstable/httpapi HttpApiBuilder: fix void responses producing a non-empty body instead of `Response.empty`, closes #1628. diff --git a/repos/effect/.changeset/pre/fix-vue-ref-switch.md b/repos/effect/.changeset/pre/fix-vue-ref-switch.md deleted file mode 100644 index b804de834a..0000000000 --- a/repos/effect/.changeset/pre/fix-vue-ref-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-vue": patch ---- - -Fix `useAtomRef` returning a stale value after switching refs. diff --git a/repos/effect/.changeset/pre/fix-worker-runner-cleanup.md b/repos/effect/.changeset/pre/fix-worker-runner-cleanup.md deleted file mode 100644 index ea8d118e06..0000000000 --- a/repos/effect/.changeset/pre/fix-worker-runner-cleanup.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-browser": patch -"@effect/platform-bun": patch -"@effect/platform-node": patch ---- - -Fix worker runner disconnect notifications and event listener cleanup. diff --git a/repos/effect/.changeset/pre/fix-workflow-activity-interruption.md b/repos/effect/.changeset/pre/fix-workflow-activity-interruption.md deleted file mode 100644 index 2ce3cda656..0000000000 --- a/repos/effect/.changeset/pre/fix-workflow-activity-interruption.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix activity count leaks when workflow activity acquisition is interrupted, which could block later workflow suspension. diff --git a/repos/effect/.changeset/pre/fix-workflow-defect-reply-serialization.md b/repos/effect/.changeset/pre/fix-workflow-defect-reply-serialization.md deleted file mode 100644 index 461124a653..0000000000 --- a/repos/effect/.changeset/pre/fix-workflow-defect-reply-serialization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix cluster workflow activity defect hydration diff --git a/repos/effect/.changeset/pre/fix-workflow-entity-client-collision.md b/repos/effect/.changeset/pre/fix-workflow-entity-client-collision.md deleted file mode 100644 index b682dc9217..0000000000 --- a/repos/effect/.changeset/pre/fix-workflow-entity-client-collision.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix ClusterWorkflowEngine partial workflow clients colliding with full workflow clients. diff --git a/repos/effect/.changeset/pre/fix-workflow-proxy-rpc-handler-context.md b/repos/effect/.changeset/pre/fix-workflow-proxy-rpc-handler-context.md deleted file mode 100644 index cc1541b66b..0000000000 --- a/repos/effect/.changeset/pre/fix-workflow-proxy-rpc-handler-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix workflow proxy RPC handlers to provide the context expected by RpcServer. diff --git a/repos/effect/.changeset/pre/fix-workflow-trace-context.md b/repos/effect/.changeset/pre/fix-workflow-trace-context.md deleted file mode 100644 index 5ede2477bb..0000000000 --- a/repos/effect/.changeset/pre/fix-workflow-trace-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Propagate trace context through persisted cluster workflow requests. diff --git a/repos/effect/.changeset/pre/fix-xhr-form-data.md b/repos/effect/.changeset/pre/fix-xhr-form-data.md deleted file mode 100644 index 28bc21691e..0000000000 --- a/repos/effect/.changeset/pre/fix-xhr-form-data.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix form data decoding for XMLHttpRequest client responses. diff --git a/repos/effect/.changeset/pre/fix-yaml-folded-scalars.md b/repos/effect/.changeset/pre/fix-yaml-folded-scalars.md deleted file mode 100644 index eeb3dfef25..0000000000 --- a/repos/effect/.changeset/pre/fix-yaml-folded-scalars.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix folded YAML scalars to preserve paragraph and indentation breaks. diff --git a/repos/effect/.changeset/pre/fix-zero-max-bytes.md b/repos/effect/.changeset/pre/fix-zero-max-bytes.md deleted file mode 100644 index c38fd6e0b6..0000000000 --- a/repos/effect/.changeset/pre/fix-zero-max-bytes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Enforce zero-byte limits when consuming Node readable streams. diff --git a/repos/effect/.changeset/pre/fix-zsh-mixed-command-completions.md b/repos/effect/.changeset/pre/fix-zsh-mixed-command-completions.md deleted file mode 100644 index 67978c2bd0..0000000000 --- a/repos/effect/.changeset/pre/fix-zsh-mixed-command-completions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Zsh completions for CLI commands with both positional arguments and subcommands. diff --git a/repos/effect/.changeset/pre/flat-chicken-remain.md b/repos/effect/.changeset/pre/flat-chicken-remain.md deleted file mode 100644 index 17b0598595..0000000000 --- a/repos/effect/.changeset/pre/flat-chicken-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix AI structured output schema generation for `Schema.Class` and `Schema.ErrorClass` by resolving top-level `$ref` entries before passing JSON Schema to providers and default codec transformers. diff --git a/repos/effect/.changeset/pre/flat-graphs-agree.md b/repos/effect/.changeset/pre/flat-graphs-agree.md deleted file mode 100644 index 8873ed9b48..0000000000 --- a/repos/effect/.changeset/pre/flat-graphs-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore removed allocator history when comparing and hashing immutable Graph values with the same active indexed structure. diff --git a/repos/effect/.changeset/pre/flat-pipelines-share.md b/repos/effect/.changeset/pre/flat-pipelines-share.md deleted file mode 100644 index 2716994723..0000000000 --- a/repos/effect/.changeset/pre/flat-pipelines-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Set the default `multiplexConcurrency` to 32 when PostgreSQL connection multiplexing is enabled. Set a lower value explicitly to limit how many statements share each connection. diff --git a/repos/effect/.changeset/pre/floppy-cows-spend.md b/repos/effect/.changeset/pre/floppy-cows-spend.md deleted file mode 100644 index 895423806a..0000000000 --- a/repos/effect/.changeset/pre/floppy-cows-spend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow encoding Redacted by default, and add option to disallow encoding diff --git a/repos/effect/.changeset/pre/floppy-frogs-juggle.md b/repos/effect/.changeset/pre/floppy-frogs-juggle.md deleted file mode 100644 index 4f4f40cd4c..0000000000 --- a/repos/effect/.changeset/pre/floppy-frogs-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Duration`'s `Hash.symbol` implementation to hash a canonical nanoseconds form instead of the raw internal `Millis`/`Nanos` representation. Two durations that `Duration.equals`/`Equal.equals` consider equal (e.g. `Duration.seconds(5)` and `Duration.nanos(5_000_000_000n)`) previously hashed differently, violating the Hash/Equal contract and silently breaking `HashSet`/`HashMap` lookups keyed by `Duration`. diff --git a/repos/effect/.changeset/pre/floppy-items-admire.md b/repos/effect/.changeset/pre/floppy-items-admire.md deleted file mode 100644 index 40c3132130..0000000000 --- a/repos/effect/.changeset/pre/floppy-items-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Change `Type_<>` implementation, from using `Exclude` type util to `keyof F as xx`, this implementation keeps IDE provenance link. This enables clicking "Go to definition (F12)" in VSCode on an object made from Schema Struct jumps to the correct Struct field definition. diff --git a/repos/effect/.changeset/pre/floppy-pigs-kiss.md b/repos/effect/.changeset/pre/floppy-pigs-kiss.md deleted file mode 100644 index 0dfba7bb7e..0000000000 --- a/repos/effect/.changeset/pre/floppy-pigs-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -use Option instead of undefined | A diff --git a/repos/effect/.changeset/pre/floppy-rats-leave.md b/repos/effect/.changeset/pre/floppy-rats-leave.md deleted file mode 100644 index cb520a1fdb..0000000000 --- a/repos/effect/.changeset/pre/floppy-rats-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fixed Clock.sleep handling of large durations diff --git a/repos/effect/.changeset/pre/floyd-warshall-null-edge-data.md b/repos/effect/.changeset/pre/floyd-warshall-null-edge-data.md deleted file mode 100644 index 3687163633..0000000000 --- a/repos/effect/.changeset/pre/floyd-warshall-null-edge-data.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve null edge data in Graph.floydWarshall costs. diff --git a/repos/effect/.changeset/pre/fluffy-meals-matter.md b/repos/effect/.changeset/pre/fluffy-meals-matter.md deleted file mode 100644 index fa656127ba..0000000000 --- a/repos/effect/.changeset/pre/fluffy-meals-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -add OpenAiClient.withWebSocketMode diff --git a/repos/effect/.changeset/pre/fluffy-pumas-push.md b/repos/effect/.changeset/pre/fluffy-pumas-push.md deleted file mode 100644 index 64e2b1e01e..0000000000 --- a/repos/effect/.changeset/pre/fluffy-pumas-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -disable sql traces for EventLog, RunnerStorage diff --git a/repos/effect/.changeset/pre/forked-memo-maps.md b/repos/effect/.changeset/pre/forked-memo-maps.md deleted file mode 100644 index 37dd43fabc..0000000000 --- a/repos/effect/.changeset/pre/forked-memo-maps.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/vitest": patch ---- - -Add forked memo maps so nested layer scopes can reuse parent allocations without leaking sibling-local layers. Update `@effect/vitest` to fork memo maps for nested `it.layer` suites, isolating sibling setup while preserving parent sharing. diff --git a/repos/effect/.changeset/pre/formatter-defined-error-causes.md b/repos/effect/.changeset/pre/formatter-defined-error-causes.md deleted file mode 100644 index 51f66c0f44..0000000000 --- a/repos/effect/.changeset/pre/formatter-defined-error-causes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve defined falsy Error causes (`0`, `false`, `""`, `null`, `0n`, and `NaN`) in `Formatter.format` output. Missing and explicitly `undefined` causes remain omitted. diff --git a/repos/effect/.changeset/pre/forty-hounds-cheer.md b/repos/effect/.changeset/pre/forty-hounds-cheer.md deleted file mode 100644 index ffbf6d29d2..0000000000 --- a/repos/effect/.changeset/pre/forty-hounds-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore unsupported Ctrl key combinations in interactive CLI prompts to avoid rendering control characters such as Ctrl+L form feed into prompt input. diff --git a/repos/effect/.changeset/pre/forty-otters-cry.md b/repos/effect/.changeset/pre/forty-otters-cry.md deleted file mode 100644 index e95732ebb7..0000000000 --- a/repos/effect/.changeset/pre/forty-otters-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Port `SqlSchema.findOne` from effect v3 to return `Option` on empty results and add `SqlSchema.single` for the fail-on-empty behavior. diff --git a/repos/effect/.changeset/pre/forty-rings-film.md b/repos/effect/.changeset/pre/forty-rings-film.md deleted file mode 100644 index 7c66c965d4..0000000000 --- a/repos/effect/.changeset/pre/forty-rings-film.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Refactor `OpenAiClient` to the handwritten minimal-schema path so `client` now exposes the configured `HttpClient`, `createResponse` / `createResponseStream` / `createEmbedding` use `OpenAiSchema` request-response types, and websocket mode no longer depends on generated-client internals. - -Also migrate OpenAI language and embedding model request-response typing to `OpenAiSchema` and make embedding decoding explicitly reject non-vector (string/base64) payloads with `InvalidOutputError`. diff --git a/repos/effect/.changeset/pre/forty-signs-stay.md b/repos/effect/.changeset/pre/forty-signs-stay.md deleted file mode 100644 index 089873c775..0000000000 --- a/repos/effect/.changeset/pre/forty-signs-stay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add rpc ConnectionHooks diff --git a/repos/effect/.changeset/pre/forty-swans-divide.md b/repos/effect/.changeset/pre/forty-swans-divide.md deleted file mode 100644 index 98e79f33d5..0000000000 --- a/repos/effect/.changeset/pre/forty-swans-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -improve Schema.TaggedUnion .match auto completion diff --git a/repos/effect/.changeset/pre/forty-trees-pay.md b/repos/effect/.changeset/pre/forty-trees-pay.md deleted file mode 100644 index 5e659393e5..0000000000 --- a/repos/effect/.changeset/pre/forty-trees-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add `availableShardGroups` to ShardingConfig, to ensure advisory locks do not conflict diff --git a/repos/effect/.changeset/pre/four-papayas-bow.md b/repos/effect/.changeset/pre/four-papayas-bow.md deleted file mode 100644 index a925f5442c..0000000000 --- a/repos/effect/.changeset/pre/four-papayas-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support toolkit unions in `LanguageModel` options. diff --git a/repos/effect/.changeset/pre/four-points-repeat.md b/repos/effect/.changeset/pre/four-points-repeat.md deleted file mode 100644 index 62f6b4b56e..0000000000 --- a/repos/effect/.changeset/pre/four-points-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add "Previously Known As" JSDoc migration notes for the `Semaphore` and `Latch` APIs extracted from `Effect`. diff --git a/repos/effect/.changeset/pre/frank-apes-vanish.md b/repos/effect/.changeset/pre/frank-apes-vanish.md deleted file mode 100644 index 603a16fd71..0000000000 --- a/repos/effect/.changeset/pre/frank-apes-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Close suspended workflow scopes after resumed completion. diff --git a/repos/effect/.changeset/pre/fresh-cats-smash.md b/repos/effect/.changeset/pre/fresh-cats-smash.md deleted file mode 100644 index d563083f4c..0000000000 --- a/repos/effect/.changeset/pre/fresh-cats-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix stream requests in Entity.toLayerQueue diff --git a/repos/effect/.changeset/pre/fresh-cycles-wait.md b/repos/effect/.changeset/pre/fresh-cycles-wait.md deleted file mode 100644 index f588122cd9..0000000000 --- a/repos/effect/.changeset/pre/fresh-cycles-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the `Schedule.both` APIs and add `Schedule.max` for combining schedules by their slowest delay. diff --git a/repos/effect/.changeset/pre/fresh-deps-update.md b/repos/effect/.changeset/pre/fresh-deps-update.md deleted file mode 100644 index c6701bb517..0000000000 --- a/repos/effect/.changeset/pre/fresh-deps-update.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update dependencies across the Effect workspace. diff --git a/repos/effect/.changeset/pre/fresh-emus-cheat.md b/repos/effect/.changeset/pre/fresh-emus-cheat.md deleted file mode 100644 index 3ee2e11d0b..0000000000 --- a/repos/effect/.changeset/pre/fresh-emus-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Export `Effect` do notation APIs (`Do`, `bindTo`, `bind`, and `let`) from `effect/Effect` and add runtime and type-level coverage. diff --git a/repos/effect/.changeset/pre/fresh-files-seek.md b/repos/effect/.changeset/pre/fresh-files-seek.md deleted file mode 100644 index 51fb444455..0000000000 --- a/repos/effect/.changeset/pre/fresh-files-seek.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": minor -"@effect/platform-node-shared": minor -"@effect/platform-deno": minor ---- - -Return the new file offset as a `Size` from `File.seek`. diff --git a/repos/effect/.changeset/pre/fresh-forms-travel.md b/repos/effect/.changeset/pre/fresh-forms-travel.md deleted file mode 100644 index 8b64d908d4..0000000000 --- a/repos/effect/.changeset/pre/fresh-forms-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve FormData bodies when converting client requests through HttpServerRequest. diff --git a/repos/effect/.changeset/pre/fresh-graphs-cycle.md b/repos/effect/.changeset/pre/fresh-graphs-cycle.md deleted file mode 100644 index d57f637496..0000000000 --- a/repos/effect/.changeset/pre/fresh-graphs-cycle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Graph.findCycle` with exact node and edge witnesses. diff --git a/repos/effect/.changeset/pre/fresh-images-generate.md b/repos/effect/.changeset/pre/fresh-images-generate.md deleted file mode 100644 index e8914d5f06..0000000000 --- a/repos/effect/.changeset/pre/fresh-images-generate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Accept image generation-specific lifecycle statuses and nullable results in OpenAI response items. diff --git a/repos/effect/.changeset/pre/fresh-lines-wait.md b/repos/effect/.changeset/pre/fresh-lines-wait.md deleted file mode 100644 index d064c998e6..0000000000 --- a/repos/effect/.changeset/pre/fresh-lines-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -NodeTerminal: preserve buffered input across sequential `readLine` calls diff --git a/repos/effect/.changeset/pre/fresh-monkeys-smoke.md b/repos/effect/.changeset/pre/fresh-monkeys-smoke.md deleted file mode 100644 index 523ab35adf..0000000000 --- a/repos/effect/.changeset/pre/fresh-monkeys-smoke.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch ---- - -Add `Model.ModelName` and provide it from AI model constructors. diff --git a/repos/effect/.changeset/pre/fresh-pools-rotate.md b/repos/effect/.changeset/pre/fresh-pools-rotate.md deleted file mode 100644 index 492f3ef413..0000000000 --- a/repos/effect/.changeset/pre/fresh-pools-rotate.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Allow each PostgreSQL pool connection to complete its first checkout before applying `connectionTTL`, so a zero TTL -disables connection reuse without entering an invalidate/reconnect loop. diff --git a/repos/effect/.changeset/pre/fresh-rivers-report.md b/repos/effect/.changeset/pre/fresh-rivers-report.md deleted file mode 100644 index 121cea53ea..0000000000 --- a/repos/effect/.changeset/pre/fresh-rivers-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Isolate delta metric baselines for each registered metric reader diff --git a/repos/effect/.changeset/pre/frozen-intrinsics-stack-trace-limit.md b/repos/effect/.changeset/pre/frozen-intrinsics-stack-trace-limit.md deleted file mode 100644 index 04aa3ce467..0000000000 --- a/repos/effect/.changeset/pre/frozen-intrinsics-stack-trace-limit.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Avoid throwing when `Error.stackTraceLimit` is non-writable (frozen intrinsics / SES / deterministic sandboxes such as Temporal). - -Effect manipulates `Error.stackTraceLimit` in several internal spots to capture short or empty stack traces cheaply. In hardened environments where `Error` is frozen and `stackTraceLimit` is read-only, assigning to it throws, which broke Effect entirely. Stack-trace-limit manipulation is now best-effort and silently no-ops when the property cannot be modified, mirroring Node's own internal guard. Behavior in normal (writable) environments is unchanged. diff --git a/repos/effect/.changeset/pre/fruity-houses-learn.md b/repos/effect/.changeset/pre/fruity-houses-learn.md deleted file mode 100644 index 3e843ad2af..0000000000 --- a/repos/effect/.changeset/pre/fruity-houses-learn.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/sql-mysql2": patch -"effect": patch ---- - -return resolvers directly from SqlModel.makeResolvers diff --git a/repos/effect/.changeset/pre/fruity-sloths-walk.md b/repos/effect/.changeset/pre/fruity-sloths-walk.md deleted file mode 100644 index 2e9ef2b1d7..0000000000 --- a/repos/effect/.changeset/pre/fruity-sloths-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now return standard JSON-RPC errors for malformed requests, unknown methods, and invalid parameters. diff --git a/repos/effect/.changeset/pre/full-adults-double.md b/repos/effect/.changeset/pre/full-adults-double.md deleted file mode 100644 index d1d236aff3..0000000000 --- a/repos/effect/.changeset/pre/full-adults-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add RpcGroup.omit diff --git a/repos/effect/.changeset/pre/funny-crabs-hang.md b/repos/effect/.changeset/pre/funny-crabs-hang.md deleted file mode 100644 index 2fd2a4cb78..0000000000 --- a/repos/effect/.changeset/pre/funny-crabs-hang.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"effect": patch ---- - -Ensure that OpenAI JSON schemas for tool calls and structured outputs are properly transformed diff --git a/repos/effect/.changeset/pre/funny-ears-beam.md b/repos/effect/.changeset/pre/funny-ears-beam.md deleted file mode 100644 index 00b7994c7d..0000000000 --- a/repos/effect/.changeset/pre/funny-ears-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent Effect.updateService and Effect.updateServiceScoped supertype widening diff --git a/repos/effect/.changeset/pre/funny-forks-move.md b/repos/effect/.changeset/pre/funny-forks-move.md deleted file mode 100644 index b4f8cd507a..0000000000 --- a/repos/effect/.changeset/pre/funny-forks-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Constrain `HttpServerRequest.source` to `object` and key server-side request weak caches by `request.source` so middleware request wrappers share the same cache entries. diff --git a/repos/effect/.changeset/pre/fuzzy-batches-stop.md b/repos/effect/.changeset/pre/fuzzy-batches-stop.md deleted file mode 100644 index 0dbd54ab99..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-batches-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now enforce revision-specific JSON-RPC batch and protocol-version header requirements. diff --git a/repos/effect/.changeset/pre/fuzzy-caches-expire.md b/repos/effect/.changeset/pre/fuzzy-caches-expire.md deleted file mode 100644 index 5e511fe65a..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-caches-expire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor numeric zero time-to-live values in `Cache.make` and `ScopedCache.make`. diff --git a/repos/effect/.changeset/pre/fuzzy-camels-hunt.md b/repos/effect/.changeset/pre/fuzzy-camels-hunt.md deleted file mode 100644 index 969059757c..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-camels-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Prompt.text` and related text prompts to initialize from `default` values so users can edit the default input directly. diff --git a/repos/effect/.changeset/pre/fuzzy-cats-kill.md b/repos/effect/.changeset/pre/fuzzy-cats-kill.md deleted file mode 100644 index 35bae2e9f8..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-cats-kill.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Fix child process termination to escalate to `SIGKILL` when the initial signal does not stop the process within `forceKillAfter`. diff --git a/repos/effect/.changeset/pre/fuzzy-cats-listen.md b/repos/effect/.changeset/pre/fuzzy-cats-listen.md deleted file mode 100644 index eb5b34707f..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-cats-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpRouter.toWebHandler` middleware inference to exclude request services supplied by the HTTP adapter. diff --git a/repos/effect/.changeset/pre/fuzzy-classes-compare.md b/repos/effect/.changeset/pre/fuzzy-classes-compare.md deleted file mode 100644 index eb54cb7f06..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-classes-compare.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch ---- - -Fix equivalence derivation for schema class APIs by adopting the equivalence of -their declared fields. Class declarations previously fell back to -`Equal.equals`, which also compared runtime properties outside the schema and -could make field-equivalent class instances compare as unequal. diff --git a/repos/effect/.changeset/pre/fuzzy-crews-fold.md b/repos/effect/.changeset/pre/fuzzy-crews-fold.md deleted file mode 100644 index 1bf7243e51..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-crews-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add custom error callbacks to Effect.fromOption. diff --git a/repos/effect/.changeset/pre/fuzzy-databases-abort.md b/repos/effect/.changeset/pre/fuzzy-databases-abort.md deleted file mode 100644 index 54923cc70d..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-databases-abort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Abort IndexedDB versionchange transactions when schema migrations fail. diff --git a/repos/effect/.changeset/pre/fuzzy-dodos-help.md b/repos/effect/.changeset/pre/fuzzy-dodos-help.md deleted file mode 100644 index 9e2d5bddcc..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-dodos-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a CI check job that runs `pnpm ai-docgen` and fails if it produces uncommitted changes. diff --git a/repos/effect/.changeset/pre/fuzzy-files-slice.md b/repos/effect/.changeset/pre/fuzzy-files-slice.md deleted file mode 100644 index 8fde69fcc8..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-files-slice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Honor offset and byte-count options in Bun Web File responses. diff --git a/repos/effect/.changeset/pre/fuzzy-graphs-transform.md b/repos/effect/.changeset/pre/fuzzy-graphs-transform.md deleted file mode 100644 index c5d8fae757..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-graphs-transform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add bulk node and edge removal operations, and disallow graph mutations from callbacks that traverse or transform the same graph. diff --git a/repos/effect/.changeset/pre/fuzzy-hornets-wish.md b/repos/effect/.changeset/pre/fuzzy-hornets-wish.md deleted file mode 100644 index d9d06218b2..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-hornets-wish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Handle accepted undefined fields during variant extraction. diff --git a/repos/effect/.changeset/pre/fuzzy-lions-perform.md b/repos/effect/.changeset/pre/fuzzy-lions-perform.md deleted file mode 100644 index 90ec370890..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-lions-perform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": minor ---- - -Add a new public `OpenAiSchema` module with minimal local schemas for responses, streaming SSE events (including unknown-event fallback), and embeddings. diff --git a/repos/effect/.changeset/pre/fuzzy-lions-study.md b/repos/effect/.changeset/pre/fuzzy-lions-study.md deleted file mode 100644 index eb647e0ab2..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-lions-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SqlResolver.findById` failing to complete duplicate requests when id encoding fails, which surfaced as a `RequestResolver did not complete request` defect instead of the underlying `SchemaError`. diff --git a/repos/effect/.changeset/pre/fuzzy-pandas-smile.md b/repos/effect/.changeset/pre/fuzzy-pandas-smile.md deleted file mode 100644 index 835fefafdf..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-pandas-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Preserve autocomplete for known OpenAI-compatible model config properties while allowing provider-specific properties. diff --git a/repos/effect/.changeset/pre/fuzzy-planets-sneeze.md b/repos/effect/.changeset/pre/fuzzy-planets-sneeze.md deleted file mode 100644 index 7e10603c94..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-planets-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix AtomRef notifications when a listener re-subscribes itself during notification. diff --git a/repos/effect/.changeset/pre/fuzzy-rabbits-cancel.md b/repos/effect/.changeset/pre/fuzzy-rabbits-cancel.md deleted file mode 100644 index 4060d9c86d..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-rabbits-cancel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore MCP cancellation notifications for unknown request identifiers. diff --git a/repos/effect/.changeset/pre/fuzzy-ravens-reason.md b/repos/effect/.changeset/pre/fuzzy-ravens-reason.md deleted file mode 100644 index 1858474d81..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-ravens-reason.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Fix the casing of OpenRouter reasoning-end metadata. diff --git a/repos/effect/.changeset/pre/fuzzy-routers-smile.md b/repos/effect/.changeset/pre/fuzzy-routers-smile.md deleted file mode 100644 index a286099ccc..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-routers-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix several edge cases in the vendored FindMyWay router. diff --git a/repos/effect/.changeset/pre/fuzzy-stamps-care.md b/repos/effect/.changeset/pre/fuzzy-stamps-care.md deleted file mode 100644 index a6272595b3..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-stamps-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -update dependencies diff --git a/repos/effect/.changeset/pre/fuzzy-timers-smile.md b/repos/effect/.changeset/pre/fuzzy-timers-smile.md deleted file mode 100644 index 874eef0065..0000000000 --- a/repos/effect/.changeset/pre/fuzzy-timers-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep TestClock nanosecond access total after infinite adjustments. diff --git a/repos/effect/.changeset/pre/general-json-schema-converters.md b/repos/effect/.changeset/pre/general-json-schema-converters.md deleted file mode 100644 index 366962f93a..0000000000 --- a/repos/effect/.changeset/pre/general-json-schema-converters.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make JSON Schema dialect conversions preserve custom keywords, translate conditionals, contains, dependencies, identifiers, and tuples where representable, relocate local references after structural changes, and throw instead of silently changing unsupported constraints. diff --git a/repos/effect/.changeset/pre/giant-jeans-float.md b/repos/effect/.changeset/pre/giant-jeans-float.md deleted file mode 100644 index 3f0eb7ef10..0000000000 --- a/repos/effect/.changeset/pre/giant-jeans-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure one-shot iterables work with Fiber apis diff --git a/repos/effect/.changeset/pre/gold-meteors-move.md b/repos/effect/.changeset/pre/gold-meteors-move.md deleted file mode 100644 index 62f8e5045b..0000000000 --- a/repos/effect/.changeset/pre/gold-meteors-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow using Duration.Input with accessors diff --git a/repos/effect/.changeset/pre/gold-readers-hug.md b/repos/effect/.changeset/pre/gold-readers-hug.md deleted file mode 100644 index c1e74afbbf..0000000000 --- a/repos/effect/.changeset/pre/gold-readers-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -add .reactive to indexeddb .first queries diff --git a/repos/effect/.changeset/pre/gold-rings-start.md b/repos/effect/.changeset/pre/gold-rings-start.md deleted file mode 100644 index a47549eefa..0000000000 --- a/repos/effect/.changeset/pre/gold-rings-start.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix Chat constructor types diff --git a/repos/effect/.changeset/pre/good-cups-reply.md b/repos/effect/.changeset/pre/good-cups-reply.md deleted file mode 100644 index 344af728ac..0000000000 --- a/repos/effect/.changeset/pre/good-cups-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Return MSSQL procedure values through the output property. diff --git a/repos/effect/.changeset/pre/good-tools-work.md b/repos/effect/.changeset/pre/good-tools-work.md deleted file mode 100644 index 7da12bb9e6..0000000000 --- a/repos/effect/.changeset/pre/good-tools-work.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -HttpServerResponse: fix `fromWeb` to preserve Content-Type header when response has a body - -Previously, when converting a web `Response` to an `HttpServerResponse` via `fromWeb`, the `Content-Type` header was not passed to `Body.stream()`, causing it to default to `application/octet-stream`. This affected any code using `HttpApp.fromWebHandler` to wrap web handlers, as JSON responses would incorrectly have their Content-Type set to `application/octet-stream` instead of `application/json`. diff --git a/repos/effect/.changeset/pre/good-trees-pull.md b/repos/effect/.changeset/pre/good-trees-pull.md deleted file mode 100644 index eefb42c6ec..0000000000 --- a/repos/effect/.changeset/pre/good-trees-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -align ProcessInput.Input runtime field name with type definition on Prompt.custom diff --git a/repos/effect/.changeset/pre/graph-acyclic-parallel-undirected-edges.md b/repos/effect/.changeset/pre/graph-acyclic-parallel-undirected-edges.md deleted file mode 100644 index 5bb98abbb5..0000000000 --- a/repos/effect/.changeset/pre/graph-acyclic-parallel-undirected-edges.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.isAcyclic` to detect cycles formed by parallel undirected edges. diff --git a/repos/effect/.changeset/pre/graph-algorithm-fixes.md b/repos/effect/.changeset/pre/graph-algorithm-fixes.md deleted file mode 100644 index 21c18e2eaa..0000000000 --- a/repos/effect/.changeset/pre/graph-algorithm-fixes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -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. diff --git a/repos/effect/.changeset/pre/graph-bellman-ford-infinite-cycle-barriers.md b/repos/effect/.changeset/pre/graph-bellman-ford-infinite-cycle-barriers.md deleted file mode 100644 index e016e39031..0000000000 --- a/repos/effect/.changeset/pre/graph-bellman-ford-infinite-cycle-barriers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.bellmanFord` reporting a negative cycle as affecting a target across an impassable, positive-infinite-weight edge. Targets separated from the cycle by such edges now retain their finite shortest path or remain unreachable, while targets reachable from the cycle through finite-weight edges still report an error. diff --git a/repos/effect/.changeset/pre/graph-finalized-mutation-handle.md b/repos/effect/.changeset/pre/graph-finalized-mutation-handle.md deleted file mode 100644 index 4f628c3269..0000000000 --- a/repos/effect/.changeset/pre/graph-finalized-mutation-handle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject `Graph` mutation operations on mutable handles after `Graph.endMutation` finalizes them. diff --git a/repos/effect/.changeset/pre/graph-guard-predicates.md b/repos/effect/.changeset/pre/graph-guard-predicates.md deleted file mode 100644 index da96b1a562..0000000000 --- a/repos/effect/.changeset/pre/graph-guard-predicates.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Graph.isGraph narrowing for mutable and undirected graphs. diff --git a/repos/effect/.changeset/pre/graph-sync-mutation-callbacks.md b/repos/effect/.changeset/pre/graph-sync-mutation-callbacks.md deleted file mode 100644 index c006de9f87..0000000000 --- a/repos/effect/.changeset/pre/graph-sync-mutation-callbacks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject asynchronous `Graph` mutation callbacks and finalize scoped mutable handles when callbacks fail. diff --git a/repos/effect/.changeset/pre/graph-undirected-edge-equality.md b/repos/effect/.changeset/pre/graph-undirected-edge-equality.md deleted file mode 100644 index 25212ae68b..0000000000 --- a/repos/effect/.changeset/pre/graph-undirected-edge-equality.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix undirected `Graph` equality and hashing to ignore stored edge endpoint orientation. diff --git a/repos/effect/.changeset/pre/graph-walker-iterator-receiver.md b/repos/effect/.changeset/pre/graph-walker-iterator-receiver.md deleted file mode 100644 index a44ad46554..0000000000 --- a/repos/effect/.changeset/pre/graph-walker-iterator-receiver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Graph.Walker` iteration for receiver-sensitive iterables. diff --git a/repos/effect/.changeset/pre/great-trains-mate.md b/repos/effect/.changeset/pre/great-trains-mate.md deleted file mode 100644 index 043f0ee30d..0000000000 --- a/repos/effect/.changeset/pre/great-trains-mate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix Duration.toMillis regression diff --git a/repos/effect/.changeset/pre/great-trams-report.md b/repos/effect/.changeset/pre/great-trams-report.md deleted file mode 100644 index ac69450918..0000000000 --- a/repos/effect/.changeset/pre/great-trams-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -cleanup IndexedDb prototypes diff --git a/repos/effect/.changeset/pre/green-ads-camp.md b/repos/effect/.changeset/pre/green-ads-camp.md deleted file mode 100644 index aa04104820..0000000000 --- a/repos/effect/.changeset/pre/green-ads-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": minor ---- - -Expose object-shaped Toolkit success schemas as MCP tool output schemas. diff --git a/repos/effect/.changeset/pre/green-beds-unref.md b/repos/effect/.changeset/pre/green-beds-unref.md deleted file mode 100644 index db4ec3b3d2..0000000000 --- a/repos/effect/.changeset/pre/green-beds-unref.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch -"@effect/platform-node": patch ---- - -Add `ChildProcessHandle.unref`, returning an `Effect` that restores the child process reference when run. diff --git a/repos/effect/.changeset/pre/green-birds-close.md b/repos/effect/.changeset/pre/green-birds-close.md deleted file mode 100644 index e21ba8438f..0000000000 --- a/repos/effect/.changeset/pre/green-birds-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-wasm": patch ---- - -Close OPFS access handles when the SQLite worker shuts down. diff --git a/repos/effect/.changeset/pre/green-chips-wash.md b/repos/effect/.changeset/pre/green-chips-wash.md deleted file mode 100644 index 3b396434ec..0000000000 --- a/repos/effect/.changeset/pre/green-chips-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Finalize the OpenAPI generator public migration by replacing the `typeOnly` option and `--type-only` CLI flag with the `format` option and `--format` flag, and by adding `httpapi` as a supported output alongside `httpclient` and `httpclient-type-only`. diff --git a/repos/effect/.changeset/pre/green-moons-smile.md b/repos/effect/.changeset/pre/green-moons-smile.md deleted file mode 100644 index 6f3a83232c..0000000000 --- a/repos/effect/.changeset/pre/green-moons-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve `Atom.withReactivity(...)` refresh behavior when registry initial values seed the wrapped atom. diff --git a/repos/effect/.changeset/pre/green-pugs-play.md b/repos/effect/.changeset/pre/green-pugs-play.md deleted file mode 100644 index 4b135069a4..0000000000 --- a/repos/effect/.changeset/pre/green-pugs-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -add defaults to indexeddb reactivity keys diff --git a/repos/effect/.changeset/pre/green-rings-prove.md b/repos/effect/.changeset/pre/green-rings-prove.md deleted file mode 100644 index c7a2ea6063..0000000000 --- a/repos/effect/.changeset/pre/green-rings-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -update Model uuid helpers diff --git a/repos/effect/.changeset/pre/happy-mirrors-dream.md b/repos/effect/.changeset/pre/happy-mirrors-dream.md deleted file mode 100644 index b944244b53..0000000000 --- a/repos/effect/.changeset/pre/happy-mirrors-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Socket.make diff --git a/repos/effect/.changeset/pre/harden-httpapi-documentation-html.md b/repos/effect/.changeset/pre/harden-httpapi-documentation-html.md deleted file mode 100644 index 774dbe3291..0000000000 --- a/repos/effect/.changeset/pre/harden-httpapi-documentation-html.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Harden HttpApi documentation HTML rendering. - -Scalar descriptions and CDN versions were interpolated without attribute-safe escaping. Embedded OpenAPI JSON in Scalar and Swagger also handled only the exact `` sequence, not other valid [script end-tag forms](https://html.spec.whatwg.org/multipage/parsing.html#script-data-end-tag-name-state). - -Attribute values and CDN versions are now encoded for their contexts, and embedded JSON escapes `<` so it cannot close its script element. diff --git a/repos/effect/.changeset/pre/hash-sql-message-dedupe-keys.md b/repos/effect/.changeset/pre/hash-sql-message-dedupe-keys.md deleted file mode 100644 index 9e386e0193..0000000000 --- a/repos/effect/.changeset/pre/hash-sql-message-dedupe-keys.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch ---- - -unstable/cluster: hash over-length SQL message deduplication keys to prevent `message_id` overflow, closes #6317. - -The composed request deduplication key (`entityType/entityId/tag/primaryKey`) can legally exceed the 255-character `message_id` column — the address columns alone allow 458 characters before the RPC primary key is appended. `SqlMessageStorage` now stores a SHA-256 digest (64 hex characters) of the composed key in the unique `message_id` column when the key exceeds 255 characters, so keys of any length work on PostgreSQL, MySQL, MSSQL, and SQLite. Keys that fit are stored as plaintext, byte-compatible with rows written by previous versions, so existing deployments keep deduplicating with no migration or schema change. - -`SqlMessageStorage.layer`/`layerWith` (and consequently `SingleRunner.layer`) now require `Crypto.Crypto`. The Node and Bun cluster convenience layers provide the platform Crypto implementation internally, so their requirements are unchanged. diff --git a/repos/effect/.changeset/pre/hashmap-collision-entries.md b/repos/effect/.changeset/pre/hashmap-collision-entries.md deleted file mode 100644 index fa077d00bd..0000000000 --- a/repos/effect/.changeset/pre/hashmap-collision-entries.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent `HashMap` iterators from exposing mutable internal collision entries. diff --git a/repos/effect/.changeset/pre/hashring-exclusion-endpoint.md b/repos/effect/.changeset/pre/hashring-exclusion-endpoint.md deleted file mode 100644 index 3e54251e1e..0000000000 --- a/repos/effect/.changeset/pre/hashring-exclusion-endpoint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HashRing.getShards` skipping an eligible node at the first ring position when other nodes have reached their allocation quota. diff --git a/repos/effect/.changeset/pre/headers-redacted-name-case.md b/repos/effect/.changeset/pre/headers-redacted-name-case.md deleted file mode 100644 index 70d6358da0..0000000000 --- a/repos/effect/.changeset/pre/headers-redacted-name-case.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Compare header names case-insensitively in `Headers.isRedactedName`. diff --git a/repos/effect/.changeset/pre/headers-stateful-patterns.md b/repos/effect/.changeset/pre/headers-stateful-patterns.md deleted file mode 100644 index db5ebef9ee..0000000000 --- a/repos/effect/.changeset/pre/headers-stateful-patterns.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Headers.redact` and `Headers.isRedactedName` skipping matches when a redaction pattern is a global or sticky regular expression. diff --git a/repos/effect/.changeset/pre/heavy-loops-cut.md b/repos/effect/.changeset/pre/heavy-loops-cut.md deleted file mode 100644 index 0e9730f7fc..0000000000 --- a/repos/effect/.changeset/pre/heavy-loops-cut.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Unwrap `_Success` schema to enable field access. diff --git a/repos/effect/.changeset/pre/heavy-trams-fix.md b/repos/effect/.changeset/pre/heavy-trams-fix.md deleted file mode 100644 index 5077696772..0000000000 --- a/repos/effect/.changeset/pre/heavy-trams-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -make HttpClientResponse pipeable diff --git a/repos/effect/.changeset/pre/hip-friends-kiss.md b/repos/effect/.changeset/pre/hip-friends-kiss.md deleted file mode 100644 index a5c66356c1..0000000000 --- a/repos/effect/.changeset/pre/hip-friends-kiss.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -added graph set operations for combining and comparing graphs - -- `Graph.make` - creates a graph constructor for a dynamically selected graph kind -- `Graph.compose` - composition of two graphs, merging nodes by identity -- `Graph.intersection` - intersection of two graphs, keeping only common nodes and edges -- `Graph.difference` - difference of two graphs, removing edges present in the second graph -- `Graph.symmetricDifference` - symmetric difference of two graphs, keeping edges present in exactly one graph diff --git a/repos/effect/.changeset/pre/hip-socks-travel.md b/repos/effect/.changeset/pre/hip-socks-travel.md deleted file mode 100644 index 3f67b064b9..0000000000 --- a/repos/effect/.changeset/pre/hip-socks-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -short circuit when Fiber.joinAll is called with an empty iterable diff --git a/repos/effect/.changeset/pre/honest-pens-thank.md b/repos/effect/.changeset/pre/honest-pens-thank.md deleted file mode 100644 index d5f0ee6e04..0000000000 --- a/repos/effect/.changeset/pre/honest-pens-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpClient.withRateLimiter` for integrating the `RateLimiter` service with HTTP clients, including optional response-header driven limit updates and automatic 429 retry behavior. diff --git a/repos/effect/.changeset/pre/honest-rivers-notice.md b/repos/effect/.changeset/pre/honest-rivers-notice.md deleted file mode 100644 index f3da306ce2..0000000000 --- a/repos/effect/.changeset/pre/honest-rivers-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -seperate scheduler dispatch from yield decisions diff --git a/repos/effect/.changeset/pre/hot-taxis-fry.md b/repos/effect/.changeset/pre/hot-taxis-fry.md deleted file mode 100644 index 2563a31ed6..0000000000 --- a/repos/effect/.changeset/pre/hot-taxis-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix issue with exported CLI `Completions` types diff --git a/repos/effect/.changeset/pre/hot-teeth-clean.md b/repos/effect/.changeset/pre/hot-teeth-clean.md deleted file mode 100644 index c737634e78..0000000000 --- a/repos/effect/.changeset/pre/hot-teeth-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -change rpc ids to string | number diff --git a/repos/effect/.changeset/pre/http-client-catch-response-result.md b/repos/effect/.changeset/pre/http-client-catch-response-result.md deleted file mode 100644 index 29bd409fb5..0000000000 --- a/repos/effect/.changeset/pre/http-client-catch-response-result.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch ---- - -Constrain the data-first `HttpClient.catch(client, recover)` overload to recover with -`HttpClientResponse` values, matching the data-last overload. Callbacks returning other -success types are now rejected; use `Effect.catch` on the result of `client.execute(request)` -to recover to arbitrary values. diff --git a/repos/effect/.changeset/pre/http-client-redirect-preprocessing-recovery.md b/repos/effect/.changeset/pre/http-client-redirect-preprocessing-recovery.md deleted file mode 100644 index 70c1b6e200..0000000000 --- a/repos/effect/.changeset/pre/http-client-redirect-preprocessing-recovery.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpClient.followRedirects` bypassing response-level recovery when request preprocessing fails. diff --git a/repos/effect/.changeset/pre/http-client-response-schema-json-options.md b/repos/effect/.changeset/pre/http-client-response-schema-json-options.md deleted file mode 100644 index b4a867afa8..0000000000 --- a/repos/effect/.changeset/pre/http-client-response-schema-json-options.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpClientResponse.schemaJson` and `HttpClientResponse.schemaNoBody` to apply parse options when decoding response -schemas. diff --git a/repos/effect/.changeset/pre/http-head-stream-scope.md b/repos/effect/.changeset/pre/http-head-stream-scope.md deleted file mode 100644 index 93f35c1096..0000000000 --- a/repos/effect/.changeset/pre/http-head-stream-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Close request scopes for streaming HEAD responses. diff --git a/repos/effect/.changeset/pre/http-json-reviver.md b/repos/effect/.changeset/pre/http-json-reviver.md deleted file mode 100644 index 51ceb6ddf9..0000000000 --- a/repos/effect/.changeset/pre/http-json-reviver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add custom reviver support to HTTP JSON parsing APIs. diff --git a/repos/effect/.changeset/pre/http-response-content-length.md b/repos/effect/.changeset/pre/http-response-content-length.md deleted file mode 100644 index 55326a9131..0000000000 --- a/repos/effect/.changeset/pre/http-response-content-length.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve `Content-Length` headers in `HttpServerResponse.fromWeb`. diff --git a/repos/effect/.changeset/pre/http-router-normalized-prefix.md b/repos/effect/.changeset/pre/http-router-normalized-prefix.md deleted file mode 100644 index a19f44155c..0000000000 --- a/repos/effect/.changeset/pre/http-router-normalized-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize router prefixes before removing them from handler request URLs. diff --git a/repos/effect/.changeset/pre/http-runner-path-boundary.md b/repos/effect/.changeset/pre/http-runner-path-boundary.md deleted file mode 100644 index 412f194b10..0000000000 --- a/repos/effect/.changeset/pre/http-runner-path-boundary.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpRunner` HTTP and WebSocket client URLs adding an extra leading slash to slash-prefixed paths. Insert the address/path separator only when it is missing, preserving intentional leading and interior slashes. - -This path correction is normally masked by router normalization, but prevents route misses for non-root paths when duplicate-slash normalization is disabled. Applications that compensate for the extra slash may need to remove that compensation. Router defaults and shared trailing-slash handling are unchanged. diff --git a/repos/effect/.changeset/pre/http-server-websocket-options.md b/repos/effect/.changeset/pre/http-server-websocket-options.md deleted file mode 100644 index 177d15b7e9..0000000000 --- a/repos/effect/.changeset/pre/http-server-websocket-options.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"@effect/platform-node": patch -"@effect/platform-bun": patch ---- - -Allow configuring the WebSocket server in `NodeHttpServer` and `BunHttpServer`. - -Both servers now accept a `websocket` option that is forwarded to the underlying implementation, with the wiring/lifecycle options the server manages excluded from the type: - -```ts -// Node: forwarded to the `ws` WebSocketServer -NodeHttpServer.layer(() => createServer(), { - port: 3000, - websocket: { perMessageDeflate: true } -}) - -// Bun: merged into Bun.serve's websocket handler -BunHttpServer.layer({ - port: 3000, - websocket: { perMessageDeflate: true } -}) -``` diff --git a/repos/effect/.changeset/pre/http-status-from-literal.md b/repos/effect/.changeset/pre/http-status-from-literal.md deleted file mode 100644 index e5942772a5..0000000000 --- a/repos/effect/.changeset/pre/http-status-from-literal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a `HttpStatus` module to `effect/unstable/http` that centralizes the mapping from HTTP status literal names to numeric codes and exports `HttpStatus.fromLiteral`. `HttpApiSchema.status` now consumes the new module. diff --git a/repos/effect/.changeset/pre/http-web-handler-cold-start.md b/repos/effect/.changeset/pre/http-web-handler-cold-start.md deleted file mode 100644 index 5fb4aabd0b..0000000000 --- a/repos/effect/.changeset/pre/http-web-handler-cold-start.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch ---- - -Reduce cold start cost of `HttpRouter` and `HttpEffect` web handlers. - -- `HttpServerRespondable` no longer imports `Schema` to detect schema errors, which removes the Schema modules from bundles that do not otherwise use them (about 23% of a minimal `HttpRouter` bundle). -- `HttpRouter.toWebHandler`, `HttpEffect.toWebHandlerLayer` and `HttpEffect.toWebHandlerLayerWith` now build the layer immediately instead of on the first request. A failed build never surfaces as an unhandled rejection; every request rejects with the build error instead. diff --git a/repos/effect/.changeset/pre/httpapi-endpoint-relax-constraints.md b/repos/effect/.changeset/pre/httpapi-endpoint-relax-constraints.md deleted file mode 100644 index b7bc906d7d..0000000000 --- a/repos/effect/.changeset/pre/httpapi-endpoint-relax-constraints.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -HttpApiEndpoint: relax `params`, `query`, and `headers` constraints to accept a full schema in addition to a record of fields. diff --git a/repos/effect/.changeset/pre/httpapi-middleware-error-dedupe.md b/repos/effect/.changeset/pre/httpapi-middleware-error-dedupe.md deleted file mode 100644 index 76d74654b3..0000000000 --- a/repos/effect/.changeset/pre/httpapi-middleware-error-dedupe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiMiddleware`-declared errors being duplicated and mis-encoded. diff --git a/repos/effect/.changeset/pre/httpapi-schema-service-types.md b/repos/effect/.changeset/pre/httpapi-schema-service-types.md deleted file mode 100644 index bc86d0521e..0000000000 --- a/repos/effect/.changeset/pre/httpapi-schema-service-types.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/openapi-generator": patch -"effect": patch ---- - -Add HTTP API streaming response support diff --git a/repos/effect/.changeset/pre/httpapi-sse-decode-options.md b/repos/effect/.changeset/pre/httpapi-sse-decode-options.md deleted file mode 100644 index eb84e9032c..0000000000 --- a/repos/effect/.changeset/pre/httpapi-sse-decode-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow generated `HttpApiClient` methods and `AtomHttpApi` queries and mutations to accept native SSE decode options per call through the request's `sseOptions` field. diff --git a/repos/effect/.changeset/pre/huge-moons-rhyme.md b/repos/effect/.changeset/pre/huge-moons-rhyme.md deleted file mode 100644 index 5488d58220..0000000000 --- a/repos/effect/.changeset/pre/huge-moons-rhyme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -feat: Support Reference classes diff --git a/repos/effect/.changeset/pre/humble-pigs-dig.md b/repos/effect/.changeset/pre/humble-pigs-dig.md deleted file mode 100644 index 212d8895e3..0000000000 --- a/repos/effect/.changeset/pre/humble-pigs-dig.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -align HttpClientRequest constructors with http method names diff --git a/repos/effect/.changeset/pre/hungry-kings-look.md b/repos/effect/.changeset/pre/hungry-kings-look.md deleted file mode 100644 index 602fb34220..0000000000 --- a/repos/effect/.changeset/pre/hungry-kings-look.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"effect": patch ---- - -Add glob to filesystem diff --git a/repos/effect/.changeset/pre/icy-flies-cross.md b/repos/effect/.changeset/pre/icy-flies-cross.md deleted file mode 100644 index a6272595b3..0000000000 --- a/repos/effect/.changeset/pre/icy-flies-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -update dependencies diff --git a/repos/effect/.changeset/pre/improve-json-schema-import.md b/repos/effect/.changeset/pre/improve-json-schema-import.md deleted file mode 100644 index 27024c853a..0000000000 --- a/repos/effect/.changeset/pre/improve-json-schema-import.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -"effect": patch ---- - -Improve `SchemaRepresentation.fromJsonSchemaDocument` and `fromJsonSchemaMultiDocument`: - -- Import `{ not: {} }` as `Schema.Never` (#8137). - -- Import closed records with one `patternProperties` entry, `additionalProperties: false`, and no declared or required properties when `patterns: "apply"` is enabled. These were previously rejected. - - ```json - { - "type": "object", - "patternProperties": { "^a": { "type": "number" } }, - "additionalProperties": false - } - ``` - - ```ts - Schema.Record( - Schema.String.check(Schema.isPattern(/^a/)), - Schema.Finite - ) - ``` - -- Reject open patterned objects with `patterns: "apply"` instead of generating incompatible TypeScript index signatures. - - ```json - { - "type": "object", - "patternProperties": { "^a": { "type": "number" } }, - "additionalProperties": true - } - ``` - - Import now explains that the generated TypeScript index signatures would give incorrect types to unmatched keys, and reports the source path. The same applies when `additionalProperties` is omitted or `{}`. Patterns can still be combined with a closed object in `allOf` when the result has a finite set of keys. Use `patterns: "ignore"` only if you intend to discard the pattern and its value constraints. - -- Reject references inside a subschema with its own `$id` instead of potentially resolving against the wrong definitions. Resolve or flatten these references before importing. A `$id` on the document root remains supported. - - ```json - { - "$id": "https://example.com/root", - "$defs": { "Value": { "type": "string" } }, - "type": "object", - "properties": { - "child": { - "$id": "child", - "$defs": { "Value": { "type": "number" } }, - "$ref": "#/$defs/Value" - } - } - } - ``` - - Here `child` refers to the nested numeric `Value`, not the root string `Value`. Import now reports that references inside a subschema with its own `$id` are unsupported instead of incorrectly using the root definition. - -- Explain import failures using JSON Schema keyword names, the reason for rejection, and the source path. Reference errors distinguish missing definitions, unsupported reference formats, and circular aliases. Pattern errors explain how to opt in for trusted schemas or explicitly discard the constraints. diff --git a/repos/effect/.changeset/pre/indexeddb-out-of-line-primary-keys.md b/repos/effect/.changeset/pre/indexeddb-out-of-line-primary-keys.md deleted file mode 100644 index 30b66a4e5f..0000000000 --- a/repos/effect/.changeset/pre/indexeddb-out-of-line-primary-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Preserve out-of-line primary keys in IndexedDB query results, including secondary-index reads. diff --git a/repos/effect/.changeset/pre/indexeddb-stream-query-limits.md b/repos/effect/.changeset/pre/indexeddb-stream-query-limits.md deleted file mode 100644 index c125931656..0000000000 --- a/repos/effect/.changeset/pre/indexeddb-stream-query-limits.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Keep streamed IndexedDB selects within their query limits. diff --git a/repos/effect/.changeset/pre/internal-json-string-schema.md b/repos/effect/.changeset/pre/internal-json-string-schema.md deleted file mode 100644 index 2a5ab3549f..0000000000 --- a/repos/effect/.changeset/pre/internal-json-string-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Mark `Schema.UnknownFromJsonString` as internal and remove its type-level interface. Use `Schema.fromJsonString(Schema.Unknown)` instead. Add `reviver`, callback or array `replacer`, and `space` options to `Schema.fromJsonString`, and make `SchemaTransformation.fromJsonString` a configurable factory. diff --git a/repos/effect/.changeset/pre/isolate-sql-compiler-cache.md b/repos/effect/.changeset/pre/isolate-sql-compiler-cache.md deleted file mode 100644 index f45018a577..0000000000 --- a/repos/effect/.changeset/pre/isolate-sql-compiler-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Isolate compiled SQL fragment caches by compiler instance. diff --git a/repos/effect/.changeset/pre/isolate-unencodable-hydration.md b/repos/effect/.changeset/pre/isolate-unencodable-hydration.md deleted file mode 100644 index fa72c498e9..0000000000 --- a/repos/effect/.changeset/pre/isolate-unencodable-hydration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent unencodable atom values from aborting dehydration of the rest of an atom registry. diff --git a/repos/effect/.changeset/pre/itchy-radios-poke.md b/repos/effect/.changeset/pre/itchy-radios-poke.md deleted file mode 100644 index 0ab497b087..0000000000 --- a/repos/effect/.changeset/pre/itchy-radios-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Abort HTTP client requests when response streams are consumed only partially. diff --git a/repos/effect/.changeset/pre/itchy-results-bet.md b/repos/effect/.changeset/pre/itchy-results-bet.md deleted file mode 100644 index d3998b75ef..0000000000 --- a/repos/effect/.changeset/pre/itchy-results-bet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure non-middleware http errors are correctly handled diff --git a/repos/effect/.changeset/pre/itchy-shrimps-deny.md b/repos/effect/.changeset/pre/itchy-shrimps-deny.md deleted file mode 100644 index bb120fb383..0000000000 --- a/repos/effect/.changeset/pre/itchy-shrimps-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add ErrorReporter module diff --git a/repos/effect/.changeset/pre/itchy-toes-promise.md b/repos/effect/.changeset/pre/itchy-toes-promise.md deleted file mode 100644 index 2c8c704b10..0000000000 --- a/repos/effect/.changeset/pre/itchy-toes-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Revert `Effect.partition` to Effect v3 behavior by accumulating failures from the effect error channel and never failing. diff --git a/repos/effect/.changeset/pre/k8s-last-transition-null.md b/repos/effect/.changeset/pre/k8s-last-transition-null.md deleted file mode 100644 index d55d7eceaf..0000000000 --- a/repos/effect/.changeset/pre/k8s-last-transition-null.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow Kubernetes pod condition `lastTransitionTime` values to be null in K8sHttpClient schemas. diff --git a/repos/effect/.changeset/pre/keep-httpapi-composition-immutable.md b/repos/effect/.changeset/pre/keep-httpapi-composition-immutable.md deleted file mode 100644 index a4f9793841..0000000000 --- a/repos/effect/.changeset/pre/keep-httpapi-composition-immutable.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Keep HttpApi composition immutable. - -`HttpApi.addHttpApi` applied annotations from the added API by mutating its shared groups. It now creates annotated group copies, keeping the source API and independently annotated variants unchanged while preserving annotation precedence. diff --git a/repos/effect/.changeset/pre/khaki-cats-learn.md b/repos/effect/.changeset/pre/khaki-cats-learn.md deleted file mode 100644 index 5a017b62b2..0000000000 --- a/repos/effect/.changeset/pre/khaki-cats-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpServerRequest.fromClientRequest` for direct client-request-backed server request conversion. diff --git a/repos/effect/.changeset/pre/khaki-melons-appear.md b/repos/effect/.changeset/pre/khaki-melons-appear.md deleted file mode 100644 index 72a00cf25d..0000000000 --- a/repos/effect/.changeset/pre/khaki-melons-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix RpcWorker Protocol service key diff --git a/repos/effect/.changeset/pre/kind-flags-help.md b/repos/effect/.changeset/pre/kind-flags-help.md deleted file mode 100644 index ecf9af6d25..0000000000 --- a/repos/effect/.changeset/pre/kind-flags-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Mark omittable CLI flags and arguments as optional in structured help. diff --git a/repos/effect/.changeset/pre/kind-hounds-float.md b/repos/effect/.changeset/pre/kind-hounds-float.md deleted file mode 100644 index b108a20952..0000000000 --- a/repos/effect/.changeset/pre/kind-hounds-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `Effect.retry` with `times` argument to propagate the original error. diff --git a/repos/effect/.changeset/pre/kind-windows-fall.md b/repos/effect/.changeset/pre/kind-windows-fall.md deleted file mode 100644 index 3786a89ca6..0000000000 --- a/repos/effect/.changeset/pre/kind-windows-fall.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -rename WorkflowEngine.layer diff --git a/repos/effect/.changeset/pre/large-postgres-messages.md b/repos/effect/.changeset/pre/large-postgres-messages.md deleted file mode 100644 index a4fdd57f7a..0000000000 --- a/repos/effect/.changeset/pre/large-postgres-messages.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Add a `maxMessageSize` connection option so PostgreSQL clients can receive backend messages larger than the 16 MiB default. diff --git a/repos/effect/.changeset/pre/late-hotels-rule.md b/repos/effect/.changeset/pre/late-hotels-rule.md deleted file mode 100644 index 4d7fc9e38b..0000000000 --- a/repos/effect/.changeset/pre/late-hotels-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the `Queue.takeN` documentation example to end the queue before showing a partial batch. diff --git a/repos/effect/.changeset/pre/late-lamps-care.md b/repos/effect/.changeset/pre/late-lamps-care.md deleted file mode 100644 index cabcf77eb2..0000000000 --- a/repos/effect/.changeset/pre/late-lamps-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.DurationFromString` and `SchemaTransformation.durationFromString`, support `"Infinity"` and `"-Infinity"` in `Duration.fromInput`, and simplify config duration parsing around the shared schema codec, closes #2092. diff --git a/repos/effect/.changeset/pre/late-rivers-applaud.md b/repos/effect/.changeset/pre/late-rivers-applaud.md deleted file mode 100644 index 37170d6b8f..0000000000 --- a/repos/effect/.changeset/pre/late-rivers-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: relax `asserts` and `is` constraints. diff --git a/repos/effect/.changeset/pre/late-sockets-rest.md b/repos/effect/.changeset/pre/late-sockets-rest.md deleted file mode 100644 index b2fecca5ea..0000000000 --- a/repos/effect/.changeset/pre/late-sockets-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Return `Option.none()` when reading an incoming message's remote address after Node clears its socket. diff --git a/repos/effect/.changeset/pre/layer-error-observer-types.md b/repos/effect/.changeset/pre/layer-error-observer-types.md deleted file mode 100644 index 24bcdb3e2d..0000000000 --- a/repos/effect/.changeset/pre/layer-error-observer-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Layer.tapError` and `Layer.tapCause` to require observers that accept the source layer's complete error type. diff --git a/repos/effect/.changeset/pre/layer-map-dynamic-idle-ttl.md b/repos/effect/.changeset/pre/layer-map-dynamic-idle-ttl.md deleted file mode 100644 index 8a2532a906..0000000000 --- a/repos/effect/.changeset/pre/layer-map-dynamic-idle-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support key-derived `idleTimeToLive` in `LayerMap` options (`make`, `fromRecord`, and `LayerMap.Service`) and add `LayerMap` tests for dynamic TTL behavior. diff --git a/repos/effect/.changeset/pre/layer-span-trace-options.md b/repos/effect/.changeset/pre/layer-span-trace-options.md deleted file mode 100644 index d0cb11a3b4..0000000000 --- a/repos/effect/.changeset/pre/layer-span-trace-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor `captureStackTrace` in both forms of `Layer.withSpan`. Layer construction diagnostics previously reported a location inside `Layer.ts` instead of the `withSpan` call site, and ignored `captureStackTrace: false` or a supplied lazy stack. diff --git a/repos/effect/.changeset/pre/layered-context-storage.md b/repos/effect/.changeset/pre/layered-context-storage.md deleted file mode 100644 index e1bd0703a8..0000000000 --- a/repos/effect/.changeset/pre/layered-context-storage.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/docgen": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-node": patch ---- - -Use layered storage for Context, making `Context.add` O(1) and eliminating per-request service map clones in the HTTP servers. Docgen now omits `@internal` option properties from generated signatures. diff --git a/repos/effect/.changeset/pre/layermap-preloaded-acquisition-errors.md b/repos/effect/.changeset/pre/layermap-preloaded-acquisition-errors.md deleted file mode 100644 index 402864fb98..0000000000 --- a/repos/effect/.changeset/pre/layermap-preloaded-acquisition-errors.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Preserve resource acquisition errors on `LayerMap.Service` when `preload: true` is set. The yielded service instance and its `get`, `contextEffect`, and `contextEffectOption` accessors now retain the resource error type because a resource can fail when reacquired, even if preloading succeeded. - -Consumers that assumed these accessors had a `never` error must handle the resource error. Runtime behavior is unchanged. diff --git a/repos/effect/.changeset/pre/lazy-bun-redis-import.md b/repos/effect/.changeset/pre/lazy-bun-redis-import.md deleted file mode 100644 index 9255d6d97f..0000000000 --- a/repos/effect/.changeset/pre/lazy-bun-redis-import.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Load Bun's `RedisClient` lazily in `BunRedis`. diff --git a/repos/effect/.changeset/pre/lazy-openapi-initialization.md b/repos/effect/.changeset/pre/lazy-openapi-initialization.md deleted file mode 100644 index dc6a553306..0000000000 --- a/repos/effect/.changeset/pre/lazy-openapi-initialization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Defer built-in OpenAPI response generation until the documentation route is first requested, retrying after generation defects. diff --git a/repos/effect/.changeset/pre/lazy-queens-rush.md b/repos/effect/.changeset/pre/lazy-queens-rush.md deleted file mode 100644 index 616b7a5cb2..0000000000 --- a/repos/effect/.changeset/pre/lazy-queens-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Context.mutate diff --git a/repos/effect/.changeset/pre/lazy-recursive-forward-refs.md b/repos/effect/.changeset/pre/lazy-recursive-forward-refs.md deleted file mode 100644 index 29e13a726a..0000000000 --- a/repos/effect/.changeset/pre/lazy-recursive-forward-refs.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Fix generated schema declaration ordering when non-recursive schemas reference recursive schemas, preventing TypeScript use-before-declaration errors in generated clients and HttpApi modules. diff --git a/repos/effect/.changeset/pre/lazy-timers-exist.md b/repos/effect/.changeset/pre/lazy-timers-exist.md deleted file mode 100644 index e66de3a25d..0000000000 --- a/repos/effect/.changeset/pre/lazy-timers-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove fiber-level keep-alive intervals and keep the process alive from `Runtime.makeRunMain` instead. diff --git a/repos/effect/.changeset/pre/lazy-undici-loading.md b/repos/effect/.changeset/pre/lazy-undici-loading.md deleted file mode 100644 index 9c767e4ea9..0000000000 --- a/repos/effect/.changeset/pre/lazy-undici-loading.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Defer loading Undici until an Undici-backed layer is acquired, preventing Node HTTP client imports from replacing Node's global fetch dispatcher. Import Undici APIs from `@effect/platform-node/Undici` instead of the package root. diff --git a/repos/effect/.changeset/pre/legal-pants-drop.md b/repos/effect/.changeset/pre/legal-pants-drop.md deleted file mode 100644 index 9bd6f6c6b7..0000000000 --- a/repos/effect/.changeset/pre/legal-pants-drop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -remove body restriction for HttpClientRequest's diff --git a/repos/effect/.changeset/pre/lemon-taxis-sin.md b/repos/effect/.changeset/pre/lemon-taxis-sin.md deleted file mode 100644 index 750c325411..0000000000 --- a/repos/effect/.changeset/pre/lemon-taxis-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -make HttpApi schema errors defects unless transformed diff --git a/repos/effect/.changeset/pre/libsql-transaction-client-isolation.md b/repos/effect/.changeset/pre/libsql-transaction-client-isolation.md deleted file mode 100644 index 186ff4348e..0000000000 --- a/repos/effect/.changeset/pre/libsql-transaction-client-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-libsql": patch ---- - -Isolate transaction contexts between separately created libSQL clients. diff --git a/repos/effect/.changeset/pre/light-kids-sneeze.md b/repos/effect/.changeset/pre/light-kids-sneeze.md deleted file mode 100644 index 926ba7aaf9..0000000000 --- a/repos/effect/.changeset/pre/light-kids-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -omit scope from HttpApi handlers diff --git a/repos/effect/.changeset/pre/little-dryers-allow.md b/repos/effect/.changeset/pre/little-dryers-allow.md deleted file mode 100644 index 9211c90084..0000000000 --- a/repos/effect/.changeset/pre/little-dryers-allow.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Persist MCP client capability context across HTTP requests by resolving initialized payloads through the standard `Mcp-Session-Id` HTTP header in `McpServer`. - -Adds a regression test that initializes an MCP HTTP client, verifies the MCP server echoes `Mcp-Session-Id`, and then checks a later tool call can still read `McpServer.clientCapabilities`. diff --git a/repos/effect/.changeset/pre/logger-complete-file-writes.md b/repos/effect/.changeset/pre/logger-complete-file-writes.md deleted file mode 100644 index 0c2c6983d7..0000000000 --- a/repos/effect/.changeset/pre/logger-complete-file-writes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Logger.toFile` dropping the remainder of a log batch when a successful file write writes only part of the buffer. File logging now uses the complete-write contract; write errors continue to be ignored. diff --git a/repos/effect/.changeset/pre/long-cameras-think.md b/repos/effect/.changeset/pre/long-cameras-think.md deleted file mode 100644 index aafbd1f5cf..0000000000 --- a/repos/effect/.changeset/pre/long-cameras-think.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Forward `OpenAiLanguageModel` `reasoning` config into chat-completions requests. diff --git a/repos/effect/.changeset/pre/loose-wings-lie.md b/repos/effect/.changeset/pre/loose-wings-lie.md deleted file mode 100644 index fedc375142..0000000000 --- a/repos/effect/.changeset/pre/loose-wings-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent Effect.provideServiceEffect supertype widening diff --git a/repos/effect/.changeset/pre/lovely-cobras-change.md b/repos/effect/.changeset/pre/lovely-cobras-change.md deleted file mode 100644 index e15d453c84..0000000000 --- a/repos/effect/.changeset/pre/lovely-cobras-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/atom-solid": patch ---- - -allow atoms to be computed in solid bindings diff --git a/repos/effect/.changeset/pre/lovely-frogs-rescue.md b/repos/effect/.changeset/pre/lovely-frogs-rescue.md deleted file mode 100644 index eb7ae29c92..0000000000 --- a/repos/effect/.changeset/pre/lovely-frogs-rescue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow mcp errors to be encoded correctly diff --git a/repos/effect/.changeset/pre/lucky-buttons-jump.md b/repos/effect/.changeset/pre/lucky-buttons-jump.md deleted file mode 100644 index 771c41ac39..0000000000 --- a/repos/effect/.changeset/pre/lucky-buttons-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add an optional `message` field to `Effect.ignore` and `Effect.ignoreCause` for custom log output. diff --git a/repos/effect/.changeset/pre/lucky-dingos-smile.md b/repos/effect/.changeset/pre/lucky-dingos-smile.md deleted file mode 100644 index 96d9a4ab3d..0000000000 --- a/repos/effect/.changeset/pre/lucky-dingos-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Map and Set equality allowing a right-side entry to match multiple left-side entries. diff --git a/repos/effect/.changeset/pre/lucky-phones-listen.md b/repos/effect/.changeset/pre/lucky-phones-listen.md deleted file mode 100644 index 0635e0afd7..0000000000 --- a/repos/effect/.changeset/pre/lucky-phones-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpApiClient.urlBuilder` for type-safe endpoint URL construction from group + method/path keys. diff --git a/repos/effect/.changeset/pre/lucky-worms-type.md b/repos/effect/.changeset/pre/lucky-worms-type.md deleted file mode 100644 index d4814ccfbf..0000000000 --- a/repos/effect/.changeset/pre/lucky-worms-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.HashMap` for decoding and encoding `HashMap` values. diff --git a/repos/effect/.changeset/pre/major-chairs-design.md b/repos/effect/.changeset/pre/major-chairs-design.md deleted file mode 100644 index 6e00b3ce65..0000000000 --- a/repos/effect/.changeset/pre/major-chairs-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix RequestResolver interruption diff --git a/repos/effect/.changeset/pre/managed-runtime-async-dispose.md b/repos/effect/.changeset/pre/managed-runtime-async-dispose.md deleted file mode 100644 index 1b4fe0b7c0..0000000000 --- a/repos/effect/.changeset/pre/managed-runtime-async-dispose.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect": patch ---- - -ManagedRuntime: add `Symbol.asyncDispose`, enabling `await using` syntax - -```ts -import { Effect, Layer, ManagedRuntime } from "effect" - -await using runtime = ManagedRuntime.make(Layer.empty) - -await runtime.runPromise(Effect.log("Hello, world!")) -// runtime is disposed automatically at the end of the scope -``` diff --git a/repos/effect/.changeset/pre/many-badgers-obey.md b/repos/effect/.changeset/pre/many-badgers-obey.md deleted file mode 100644 index 6ec45f57f8..0000000000 --- a/repos/effect/.changeset/pre/many-badgers-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow schemas provided to CLI flags / arguments to utilize the environment required by the CLI diff --git a/repos/effect/.changeset/pre/match-generic-value-terminals.md b/repos/effect/.changeset/pre/match-generic-value-terminals.md deleted file mode 100644 index f72279c350..0000000000 --- a/repos/effect/.changeset/pre/match-generic-value-terminals.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Fix `Match.value` terminal combinators failing to typecheck when the input -contains a generic type parameter. - -The fifth type argument of `Matcher` for value matchers is now `ValueFlavor`, -and `ValueMatcher` has a seventh flavor argument; update hand-written -annotations accordingly. diff --git a/repos/effect/.changeset/pre/mcp-http-initialize-version-header.md b/repos/effect/.changeset/pre/mcp-http-initialize-version-header.md deleted file mode 100644 index 5c49c3ad9e..0000000000 --- a/repos/effect/.changeset/pre/mcp-http-initialize-version-header.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect": patch ---- - -Fix MCP initialize rejected over the protocol version header - -`McpServer.layerHttp` validated the `MCP-Protocol-Version` header on every POST, including -the `initialize` request. That header reports the version negotiated by an earlier -`initialize`, so on a fresh connection a client can only send its own default. Whenever -that default was not among the server's registered protocols the `initialize` returned -`400` and never reached version negotiation, even when the body offered a version the -server supports. - -The header check now applies only to requests after initialization, where the -specification requires it. An `initialize` negotiates from the version offered in its -body, through the protocol registry, and reports the selected version in the response. diff --git a/repos/effect/.changeset/pre/mcp-http-resource-template-origins.md b/repos/effect/.changeset/pre/mcp-http-resource-template-origins.md deleted file mode 100644 index b94ed9b0db..0000000000 --- a/repos/effect/.changeset/pre/mcp-http-resource-template-origins.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `McpServer` HTTP resource templates failing to resolve. diff --git a/repos/effect/.changeset/pre/mcp-prompt-decoded-parameter-types.md b/repos/effect/.changeset/pre/mcp-prompt-decoded-parameter-types.md deleted file mode 100644 index 180a6f1ad2..0000000000 --- a/repos/effect/.changeset/pre/mcp-prompt-decoded-parameter-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `McpServer.registerPrompt` callback types to use decoded prompt parameters. diff --git a/repos/effect/.changeset/pre/mcp-protocol-versions.md b/repos/effect/.changeset/pre/mcp-protocol-versions.md deleted file mode 100644 index 6c20ac168c..0000000000 --- a/repos/effect/.changeset/pre/mcp-protocol-versions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now support the 2024-11-05 and 2025-03-26 RPC revisions through version-specific protocol adapters. diff --git a/repos/effect/.changeset/pre/mcp-structured-content-object.md b/repos/effect/.changeset/pre/mcp-structured-content-object.md deleted file mode 100644 index d3d85d737f..0000000000 --- a/repos/effect/.changeset/pre/mcp-structured-content-object.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -McpServer no longer sends `null` or array tool results as `structuredContent`, which MCP requires to be a JSON object. diff --git a/repos/effect/.changeset/pre/mcp-tool-output-schema.md b/repos/effect/.changeset/pre/mcp-tool-output-schema.md deleted file mode 100644 index 3c0399361a..0000000000 --- a/repos/effect/.changeset/pre/mcp-tool-output-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include typed tool output schemas in MCP `tools/list` responses. diff --git a/repos/effect/.changeset/pre/mean-dingos-share.md b/repos/effect/.changeset/pre/mean-dingos-share.md deleted file mode 100644 index 2fd4aef389..0000000000 --- a/repos/effect/.changeset/pre/mean-dingos-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Command.annotate` and `Command.annotateMerge` to unstable CLI commands, and include command annotations in `HelpDoc` so custom help formatters can access command metadata. diff --git a/repos/effect/.changeset/pre/mean-trains-smash.md b/repos/effect/.changeset/pre/mean-trains-smash.md deleted file mode 100644 index 0054e60435..0000000000 --- a/repos/effect/.changeset/pre/mean-trains-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Add BrowserPersistence.layerIndexedDb for composing Persistence.layer with the IndexedDB backing layer, and export BrowserPersistence from the package barrel. diff --git a/repos/effect/.changeset/pre/memoize-idempotent-asts.md b/repos/effect/.changeset/pre/memoize-idempotent-asts.md deleted file mode 100644 index 30c655d7c0..0000000000 --- a/repos/effect/.changeset/pre/memoize-idempotent-asts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Function.memoizeIdempotent` and use it to avoid reprocessing canonical Schema ASTs, including optional and mutable property modifiers. Cache Config schema cursor AST compilation. diff --git a/repos/effect/.changeset/pre/memory-message-storage-clear-controls.md b/repos/effect/.changeset/pre/memory-message-storage-clear-controls.md deleted file mode 100644 index d4f524b693..0000000000 --- a/repos/effect/.changeset/pre/memory-message-storage-clear-controls.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove queued control envelopes when clearing an address from in-memory message storage. diff --git a/repos/effect/.changeset/pre/merge-resource-failures.md b/repos/effect/.changeset/pre/merge-resource-failures.md deleted file mode 100644 index 0f5730493b..0000000000 --- a/repos/effect/.changeset/pre/merge-resource-failures.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Merge effect and finalizer failures during cleanup, preserving other failures alongside `Cause.Done`. diff --git a/repos/effect/.changeset/pre/metal-nails-sneeze.md b/repos/effect/.changeset/pre/metal-nails-sneeze.md deleted file mode 100644 index 148d8c62b6..0000000000 --- a/repos/effect/.changeset/pre/metal-nails-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add LayerRef module diff --git a/repos/effect/.changeset/pre/metal-parts-yell.md b/repos/effect/.changeset/pre/metal-parts-yell.md deleted file mode 100644 index a6d67450a1..0000000000 --- a/repos/effect/.changeset/pre/metal-parts-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -simplify http logger disabling diff --git a/repos/effect/.changeset/pre/metric-attribute-order-identity.md b/repos/effect/.changeset/pre/metric-attribute-order-identity.md deleted file mode 100644 index 608c6d396e..0000000000 --- a/repos/effect/.changeset/pre/metric-attribute-order-identity.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure metrics with equal attributes share a series regardless of attribute insertion order. diff --git a/repos/effect/.changeset/pre/metric-registry-isolation.md b/repos/effect/.changeset/pre/metric-registry-isolation.md deleted file mode 100644 index 68aa520b5d..0000000000 --- a/repos/effect/.changeset/pre/metric-registry-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix metrics reused across different `MetricRegistry` services to read and update the active registry while preserving each registry's values when revisited. diff --git a/repos/effect/.changeset/pre/mighty-games-matter.md b/repos/effect/.changeset/pre/mighty-games-matter.md deleted file mode 100644 index d536ab3504..0000000000 --- a/repos/effect/.changeset/pre/mighty-games-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -remove use of bigint literals diff --git a/repos/effect/.changeset/pre/migrate-persisted-queues.md b/repos/effect/.changeset/pre/migrate-persisted-queues.md deleted file mode 100644 index 6fefa10782..0000000000 --- a/repos/effect/.changeset/pre/migrate-persisted-queues.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Run SQL `PersistedQueue` table creation through versioned migrations so future schema changes can be applied safely. diff --git a/repos/effect/.changeset/pre/migrator-windows-file-url.md b/repos/effect/.changeset/pre/migrator-windows-file-url.md deleted file mode 100644 index 9ad8a9d4d3..0000000000 --- a/repos/effect/.changeset/pre/migrator-windows-file-url.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Import migrations through a file URL in `Migrator.fromFileSystem`, so absolute Windows paths are accepted by the ESM loader. - -Previously the directory and file name were passed to `import` as a plain path. On Windows that produced a specifier such as `D:\migrations\1_init.ts`, which the ESM loader rejects with `Only URLs with a scheme in: file, data, and node are supported`. - -`fromFileSystem` now resolves the specifier through the `Path` service, so its type widens from `Loader` to `Loader`. Callers that already provide an aggregate platform layer such as `NodeServices.layer` are unaffected; callers that provide `FileSystem` on its own now also need a `Path` layer, and on Windows it must be a platform-aware one rather than the POSIX `Path.layer`. diff --git a/repos/effect/.changeset/pre/model-field-option-undefined.md b/repos/effect/.changeset/pre/model-field-option-undefined.md deleted file mode 100644 index 3bca7a6913..0000000000 --- a/repos/effect/.changeset/pre/model-field-option-undefined.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Model.FieldOption` to preserve omitted variants. diff --git a/repos/effect/.changeset/pre/modern-carrots-see.md b/repos/effect/.changeset/pre/modern-carrots-see.md deleted file mode 100644 index 83937c30e4..0000000000 --- a/repos/effect/.changeset/pre/modern-carrots-see.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -RpcGroup.toHandlers is definition first diff --git a/repos/effect/.changeset/pre/modern-uuid-guid-filter.md b/repos/effect/.changeset/pre/modern-uuid-guid-filter.md deleted file mode 100644 index 034948af64..0000000000 --- a/repos/effect/.changeset/pre/modern-uuid-guid-filter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.isGUID` and update `Schema.isUUID` to accept the RFC 9562 max UUID. diff --git a/repos/effect/.changeset/pre/mssql-binary-parameters.md b/repos/effect/.changeset/pre/mssql-binary-parameters.md deleted file mode 100644 index 9ceac54a6a..0000000000 --- a/repos/effect/.changeset/pre/mssql-binary-parameters.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Fix automatic binary parameter binding in `@effect/sql-mssql`. diff --git a/repos/effect/.changeset/pre/mssql-ntlm-domain.md b/repos/effect/.changeset/pre/mssql-ntlm-domain.md deleted file mode 100644 index eaa55815f5..0000000000 --- a/repos/effect/.changeset/pre/mssql-ntlm-domain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Forward the configured domain to the SQL Server driver so NTLM clients can be constructed with a supplied domain. diff --git a/repos/effect/.changeset/pre/multipart-collect-linear.md b/repos/effect/.changeset/pre/multipart-collect-linear.md deleted file mode 100644 index 93f37d8f1e..0000000000 --- a/repos/effect/.changeset/pre/multipart-collect-linear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Channel.mkUint8Array` and reuse it from `Stream` and multipart file collection. This also fixes quadratic buffering in `File.contentEffect`, improving collection of a 16 MiB chunked upload by approximately 90x. diff --git a/repos/effect/.changeset/pre/multipart-onDone-clobbers-error.md b/repos/effect/.changeset/pre/multipart-onDone-clobbers-error.md deleted file mode 100644 index 621db72844..0000000000 --- a/repos/effect/.changeset/pre/multipart-onDone-clobbers-error.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix multipart parser limit violations being silently swallowed diff --git a/repos/effect/.changeset/pre/multipart-parser-limits.md b/repos/effect/.changeset/pre/multipart-parser-limits.md deleted file mode 100644 index d625129223..0000000000 --- a/repos/effect/.changeset/pre/multipart-parser-limits.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Stop multipart parsing after part count, part size, or field size limits are exceeded. diff --git a/repos/effect/.changeset/pre/multipart-streamed-part-guard.md b/repos/effect/.changeset/pre/multipart-streamed-part-guard.md deleted file mode 100644 index f593fc2240..0000000000 --- a/repos/effect/.changeset/pre/multipart-streamed-part-guard.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Multipart.isStreamPart` to recognize only a text `Field` or streamed `File`, while preserving `Multipart.isPart` for all branded multipart parts, including `PersistedFile` values. diff --git a/repos/effect/.changeset/pre/mutable-arrays-preserve.md b/repos/effect/.changeset/pre/mutable-arrays-preserve.md deleted file mode 100644 index 17e37d48a4..0000000000 --- a/repos/effect/.changeset/pre/mutable-arrays-preserve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schema.mutable` to preserve array and tuple metadata and reject node-level encodings. diff --git a/repos/effect/.changeset/pre/mutable-list-bulk-prepend-tail.md b/repos/effect/.changeset/pre/mutable-list-bulk-prepend-tail.md deleted file mode 100644 index 61e8ed55a4..0000000000 --- a/repos/effect/.changeset/pre/mutable-list-bulk-prepend-tail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve values added to an empty `MutableList` by `prependAll` when appending more values. diff --git a/repos/effect/.changeset/pre/mysql2-disable-prepared-statements.md b/repos/effect/.changeset/pre/mysql2-disable-prepared-statements.md deleted file mode 100644 index 02150bcc48..0000000000 --- a/repos/effect/.changeset/pre/mysql2-disable-prepared-statements.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-mysql2": patch ---- - -Add `disablePreparedStatements` to `MysqlClientConfig`, to completely disable prepared statements diff --git a/repos/effect/.changeset/pre/name-random-services.md b/repos/effect/.changeset/pre/name-random-services.md deleted file mode 100644 index 2fef9ba2e0..0000000000 --- a/repos/effect/.changeset/pre/name-random-services.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Export the `Random.Random` service interface and `Metric.MetricRegistry` type so custom service implementations can be annotated without accessing `Context.Reference` phantom types. diff --git a/repos/effect/.changeset/pre/narrow-schedule-while.md b/repos/effect/.changeset/pre/narrow-schedule-while.md deleted file mode 100644 index 85344bc326..0000000000 --- a/repos/effect/.changeset/pre/narrow-schedule-while.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support narrowing schedule input and output types with type guard predicates passed to `Schedule.while`. diff --git a/repos/effect/.changeset/pre/nasty-geese-grow.md b/repos/effect/.changeset/pre/nasty-geese-grow.md deleted file mode 100644 index ed408dc284..0000000000 --- a/repos/effect/.changeset/pre/nasty-geese-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Scheduler.PreventSchedulerYield` and expose it via `References` so fibers can skip scheduler `shouldYield` checks when needed. diff --git a/repos/effect/.changeset/pre/neat-goats-wave.md b/repos/effect/.changeset/pre/neat-goats-wave.md deleted file mode 100644 index bb3f2c4f7b..0000000000 --- a/repos/effect/.changeset/pre/neat-goats-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Stream.service` and `Stream.serviceOption` for accessing services as single-element streams. diff --git a/repos/effect/.changeset/pre/neat-graphs-induced.md b/repos/effect/.changeset/pre/neat-graphs-induced.md deleted file mode 100644 index 74b21df264..0000000000 --- a/repos/effect/.changeset/pre/neat-graphs-induced.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add index-preserving `Graph.inducedSubgraph`. diff --git a/repos/effect/.changeset/pre/neat-kings-chew.md b/repos/effect/.changeset/pre/neat-kings-chew.md deleted file mode 100644 index 57a18a6c69..0000000000 --- a/repos/effect/.changeset/pre/neat-kings-chew.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a new `effect/unstable/http/HttpStaticServer` module for static file serving with MIME resolution, directory index fallback, SPA fallback, and safe path resolution. diff --git a/repos/effect/.changeset/pre/neat-lions-rest.md b/repos/effect/.changeset/pre/neat-lions-rest.md deleted file mode 100644 index e0aa7c3fc6..0000000000 --- a/repos/effect/.changeset/pre/neat-lions-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `WorkflowEngine.layer`, an in-memory layer for the unstable workflow engine. diff --git a/repos/effect/.changeset/pre/neat-pandas-query.md b/repos/effect/.changeset/pre/neat-pandas-query.md deleted file mode 100644 index f78f49222d..0000000000 --- a/repos/effect/.changeset/pre/neat-pandas-query.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-clickhouse": patch ---- - -Propagate ClickHouse result decoding failures as `SqlError` values. diff --git a/repos/effect/.changeset/pre/neat-snails-wash.md b/repos/effect/.changeset/pre/neat-snails-wash.md deleted file mode 100644 index 3ab4515df2..0000000000 --- a/repos/effect/.changeset/pre/neat-snails-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose a `chunkSize` option on `Stream.fromIterable` to control emitted chunk boundaries when constructing streams from iterables. diff --git a/repos/effect/.changeset/pre/neat-taxis-notice.md b/repos/effect/.changeset/pre/neat-taxis-notice.md deleted file mode 100644 index ffdd50ed44..0000000000 --- a/repos/effect/.changeset/pre/neat-taxis-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.forkScoped` data-first typings to include `Scope` in requirements. diff --git a/repos/effect/.changeset/pre/neat-tuples-remember.md b/repos/effect/.changeset/pre/neat-tuples-remember.md deleted file mode 100644 index 526502b748..0000000000 --- a/repos/effect/.changeset/pre/neat-tuples-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve literal element types in `Tuple.make`. diff --git a/repos/effect/.changeset/pre/neat-windows-buy.md b/repos/effect/.changeset/pre/neat-windows-buy.md deleted file mode 100644 index 661cff0cf7..0000000000 --- a/repos/effect/.changeset/pre/neat-windows-buy.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@effect/sql-mssql": patch ---- - -Added some options for the MssqlClient: - -- multiSubnetFailover -- cancelTimeout -- connectionRetryInterval -- maxRetriesOnTransientErrors diff --git a/repos/effect/.changeset/pre/nested-union-sentinels.md b/repos/effect/.changeset/pre/nested-union-sentinels.md deleted file mode 100644 index bba5671a16..0000000000 --- a/repos/effect/.changeset/pre/nested-union-sentinels.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve Union candidate selection: a nested union member is dispatched by the sentinels common to all its members, and candidates whose sentinel the input contradicts are excluded. diff --git a/repos/effect/.changeset/pre/net-address-values.md b/repos/effect/.changeset/pre/net-address-values.md deleted file mode 100644 index b6db60e642..0000000000 --- a/repos/effect/.changeset/pre/net-address-values.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect": patch -"@effect/sql-pg": patch -"@effect/platform-node": patch -"@effect/platform-node-shared": patch -"@effect/platform-deno": patch -"@effect/platform-bun": patch ---- - -Add `NetAddress` under `effect/unstable/net` for MAC, IP, internet socket, and Unix socket addresses, with checked parsing, schemas, equality, canonical string serialization, and URL formatting. Companion modules `IpInterface` and `IpNetwork` represent IP interfaces and CIDR networks. - -HTTP and socket servers now expose `NetAddress.SocketAddress`. Replace TCP `hostname` access with `NetAddress.formatIp(address.address)` and use `UnixPathAddress.path` for Unix sockets. URL helpers bracket IPv6 addresses and reject scoped IPv6. Bun and Deno HTTP server layers can now fail with `ServeError` when listener address conversion fails. - -PostgreSQL `inet` values now use `IpInterface`; `cidr` values use `IpNetwork` and reject addresses with host bits set. diff --git a/repos/effect/.changeset/pre/new-dogs-swim.md b/repos/effect/.changeset/pre/new-dogs-swim.md deleted file mode 100644 index ef306ca7bb..0000000000 --- a/repos/effect/.changeset/pre/new-dogs-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -make defects a retryable network error in websocket mode diff --git a/repos/effect/.changeset/pre/new-toes-stop.md b/repos/effect/.changeset/pre/new-toes-stop.md deleted file mode 100644 index 4270fd00cc..0000000000 --- a/repos/effect/.changeset/pre/new-toes-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -add idb stream and offset diff --git a/repos/effect/.changeset/pre/ninety-books-sit.md b/repos/effect/.changeset/pre/ninety-books-sit.md deleted file mode 100644 index 8f347d0353..0000000000 --- a/repos/effect/.changeset/pre/ninety-books-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Terminate the `Stream.fromEventListener` stream after one item if `once: true`. diff --git a/repos/effect/.changeset/pre/ninety-geese-exist.md b/repos/effect/.changeset/pre/ninety-geese-exist.md deleted file mode 100644 index 20e8ce1b50..0000000000 --- a/repos/effect/.changeset/pre/ninety-geese-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add HttpApiSecurity.http for passing custom schemes diff --git a/repos/effect/.changeset/pre/node-http-status-text.md b/repos/effect/.changeset/pre/node-http-status-text.md deleted file mode 100644 index b83133d661..0000000000 --- a/repos/effect/.changeset/pre/node-http-status-text.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Forward custom and empty status text from NodeHttpServer responses. diff --git a/repos/effect/.changeset/pre/node-redis-client.md b/repos/effect/.changeset/pre/node-redis-client.md deleted file mode 100644 index 1e869ad6df..0000000000 --- a/repos/effect/.changeset/pre/node-redis-client.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Migrate `NodeRedis` from `ioredis` to `redis` (node-redis), replacing the peer dependency with `redis: >=5.0.0 <7.0.0`. - -`layer` and `layerConfig` now accept `RedisClientOptions`: socket settings move under `socket`, `db` becomes `database`, command methods are camelCase, and arbitrary commands use `sendCommand`. Protocol selection follows the installed node-redis version's default. - -Layers connect while being built and can fail with `RedisError`. Initial connections fail fast unless a `socket.reconnectStrategy` is provided; after `ready`, the default reconnect behavior applies. Scope finalization uses `close()`, so in-flight or blocking commands can delay closure. diff --git a/repos/effect/.changeset/pre/node-response-preserve-bytes.md b/repos/effect/.changeset/pre/node-response-preserve-bytes.md deleted file mode 100644 index add6daba82..0000000000 --- a/repos/effect/.changeset/pre/node-response-preserve-bytes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Preserve response bytes when selecting the text reader in Node HTTP clients. diff --git a/repos/effect/.changeset/pre/node-sink-cancel-drain.md b/repos/effect/.changeset/pre/node-sink-cancel-drain.md deleted file mode 100644 index 257aead2e6..0000000000 --- a/repos/effect/.changeset/pre/node-sink-cancel-drain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Prevent Node sinks from submitting buffered writes after interruption while waiting for writable backpressure. diff --git a/repos/effect/.changeset/pre/node-sink-finalization-errors.md b/repos/effect/.changeset/pre/node-sink-finalization-errors.md deleted file mode 100644 index 308442c81a..0000000000 --- a/repos/effect/.changeset/pre/node-sink-finalization-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Fix `NodeSink` hanging when a writable reports an error during finalization. diff --git a/repos/effect/.changeset/pre/node-stream-buffer-size.md b/repos/effect/.changeset/pre/node-stream-buffer-size.md deleted file mode 100644 index 82b55372d8..0000000000 --- a/repos/effect/.changeset/pre/node-stream-buffer-size.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Remove the ignored `bufferSize` option from `NodeStream`. diff --git a/repos/effect/.changeset/pre/node-terminal-idle-ttl.md b/repos/effect/.changeset/pre/node-terminal-idle-ttl.md deleted file mode 100644 index 71ac7237fb..0000000000 --- a/repos/effect/.changeset/pre/node-terminal-idle-ttl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Keep NodeTerminal's readline interface alive briefly between adjacent prompts to avoid a Windows TTY raw-mode hang. diff --git a/repos/effect/.changeset/pre/node-terminal-stdin-eof.md b/repos/effect/.changeset/pre/node-terminal-stdin-eof.md deleted file mode 100644 index 297b295aae..0000000000 --- a/repos/effect/.changeset/pre/node-terminal-stdin-eof.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -NodeTerminal: end key input and fail `readLine` with `QuitError` at stdin EOF instead of hanging diff --git a/repos/effect/.changeset/pre/node-watch-relative.md b/repos/effect/.changeset/pre/node-watch-relative.md deleted file mode 100644 index 792683eba5..0000000000 --- a/repos/effect/.changeset/pre/node-watch-relative.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Fix watch event classification for targets outside the current working directory. diff --git a/repos/effect/.changeset/pre/node-worker-unsafe-send-envelope.md b/repos/effect/.changeset/pre/node-worker-unsafe-send-envelope.md deleted file mode 100644 index 999baa91a6..0000000000 --- a/repos/effect/.changeset/pre/node-worker-unsafe-send-envelope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Preserve reply payloads sent through `NodeWorkerRunner.sendUnsafe`. diff --git a/repos/effect/.changeset/pre/node-writeall-empty.md b/repos/effect/.changeset/pre/node-writeall-empty.md deleted file mode 100644 index f503e0d709..0000000000 --- a/repos/effect/.changeset/pre/node-writeall-empty.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Allow `File.writeAll` to accept empty buffers. diff --git a/repos/effect/.changeset/pre/normalize-collection-counts.md b/repos/effect/.changeset/pre/normalize-collection-counts.md deleted file mode 100644 index 878b667954..0000000000 --- a/repos/effect/.changeset/pre/normalize-collection-counts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize numeric collection and batch counts across `Stream`, `Channel`, `Sink`, `MutableList`, `RequestResolver`, `Queue`, `TxQueue`, `PubSub`, and `HashRing`, preventing fractional, `NaN`, and non-positive counts from producing incorrect output, exceptions, waits for the wrong batch size, or non-terminating pulls. diff --git a/repos/effect/.changeset/pre/normalize-httpapi-payload-media-types.md b/repos/effect/.changeset/pre/normalize-httpapi-payload-media-types.md deleted file mode 100644 index ae6a0a78b3..0000000000 --- a/repos/effect/.changeset/pre/normalize-httpapi-payload-media-types.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Normalize HttpApi payload media types. - -Payload schemas were stored under their exact declared `Content-Type`, but the server lowercased the incoming header and removed its parameters before looking it up. For example, a schema declared as `Application/Vnd.Effect+JSON; profile=declared` was stored under that value, while the server looked for `application/vnd.effect+json`. This could produce a `415` response even when the generated client and server used the same API. - -The same mismatch allowed incompatible encodings for equivalent media types to bypass validation. Generated form-urlencoded requests also ignored custom content types and always used the default one. - -Payload maps now use normalized keys for matching and conflict checks, while each encoding keeps its declared content type. Generated requests and OpenAPI use the declared values, including every parameterized variant, and custom form-urlencoded content types are preserved. diff --git a/repos/effect/.changeset/pre/normalize-unbounded-pubsub-replay.md b/repos/effect/.changeset/pre/normalize-unbounded-pubsub-replay.md deleted file mode 100644 index 8cc87d6653..0000000000 --- a/repos/effect/.changeset/pre/normalize-unbounded-pubsub-replay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize unbounded PubSub replay capacities to positive integers. diff --git a/repos/effect/.changeset/pre/number-remainder-negative-zero-dividend.md b/repos/effect/.changeset/pre/number-remainder-negative-zero-dividend.md deleted file mode 100644 index 0544996962..0000000000 --- a/repos/effect/.changeset/pre/number-remainder-negative-zero-dividend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Number.remainder` to preserve negative-zero dividends with ordinary finite divisors. diff --git a/repos/effect/.changeset/pre/o8drprcu-sqlite-node-node-sqlite.md b/repos/effect/.changeset/pre/o8drprcu-sqlite-node-node-sqlite.md deleted file mode 100644 index b1840466fe..0000000000 --- a/repos/effect/.changeset/pre/o8drprcu-sqlite-node-node-sqlite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-node": patch ---- - -Replace the `better-sqlite3` dependency with Node.js' built-in `node:sqlite` module. diff --git a/repos/effect/.changeset/pre/odd-boats-think.md b/repos/effect/.changeset/pre/odd-boats-think.md deleted file mode 100644 index 7319b914e5..0000000000 --- a/repos/effect/.changeset/pre/odd-boats-think.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `isMutableHashMap` and `isMutableHashSet`, and align nominal guard implementations and tests across collections and transactional data types. diff --git a/repos/effect/.changeset/pre/odd-bulldogs-sleep.md b/repos/effect/.changeset/pre/odd-bulldogs-sleep.md deleted file mode 100644 index baf880811e..0000000000 --- a/repos/effect/.changeset/pre/odd-bulldogs-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add dtslint coverage for `Stream.catchIf` to lock in predicate and refinement inference behavior in both data-first and data-last forms. diff --git a/repos/effect/.changeset/pre/odd-fans-glow.md b/repos/effect/.changeset/pre/odd-fans-glow.md deleted file mode 100644 index ac7a1a804d..0000000000 --- a/repos/effect/.changeset/pre/odd-fans-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.groupedWithin` to stop emitting empty arrays when schedule ticks fire while upstream is idle. diff --git a/repos/effect/.changeset/pre/odd-forks-talk.md b/repos/effect/.changeset/pre/odd-forks-talk.md deleted file mode 100644 index 150e017883..0000000000 --- a/repos/effect/.changeset/pre/odd-forks-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Add dynamic tool support to the Anthropic language model provider when preparing tool definitions for requests. diff --git a/repos/effect/.changeset/pre/odd-laws-draw.md b/repos/effect/.changeset/pre/odd-laws-draw.md deleted file mode 100644 index 695ffcefd6..0000000000 --- a/repos/effect/.changeset/pre/odd-laws-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Schedule.duration`, a one-shot schedule that waits for the provided duration and then completes. diff --git a/repos/effect/.changeset/pre/odd-owls-smoke.md b/repos/effect/.changeset/pre/odd-owls-smoke.md deleted file mode 100644 index 7085a3b592..0000000000 --- a/repos/effect/.changeset/pre/odd-owls-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Schedule.compose` in favor of `Schedule.both`, and update schedule examples to use `Schedule.both`. diff --git a/repos/effect/.changeset/pre/odd-socks-boil.md b/repos/effect/.changeset/pre/odd-socks-boil.md deleted file mode 100644 index af72ca9828..0000000000 --- a/repos/effect/.changeset/pre/odd-socks-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Use a dedicated PostgreSQL client for LISTEN / UNLISTEN subscriptions instead of checking out a pooled connection for the listener lifecycle. diff --git a/repos/effect/.changeset/pre/odd-suns-dance.md b/repos/effect/.changeset/pre/odd-suns-dance.md deleted file mode 100644 index 226e74ffdb..0000000000 --- a/repos/effect/.changeset/pre/odd-suns-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename HttpApiClient request option `withResponse` to `responseMode` and add support for `responseMode: "response-only"` to return the raw `HttpClientResponse` without decoding. diff --git a/repos/effect/.changeset/pre/old-brooms-cry.md b/repos/effect/.changeset/pre/old-brooms-cry.md deleted file mode 100644 index 525a7a8f61..0000000000 --- a/repos/effect/.changeset/pre/old-brooms-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Filter.reason api diff --git a/repos/effect/.changeset/pre/old-facts-stand.md b/repos/effect/.changeset/pre/old-facts-stand.md deleted file mode 100644 index 99a811620a..0000000000 --- a/repos/effect/.changeset/pre/old-facts-stand.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fixed the Error Type on AtomHttpApiClient (Server errors were being incorrectly reported, and we could not determine _tag to handle) diff --git a/repos/effect/.changeset/pre/old-mirrors-float.md b/repos/effect/.changeset/pre/old-mirrors-float.md deleted file mode 100644 index a50aab5e73..0000000000 --- a/repos/effect/.changeset/pre/old-mirrors-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -make Layer.mock work with Stream and Channel diff --git a/repos/effect/.changeset/pre/olive-poems-visit.md b/repos/effect/.changeset/pre/olive-poems-visit.md deleted file mode 100644 index f09144e38a..0000000000 --- a/repos/effect/.changeset/pre/olive-poems-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Workflow.executionId` to use schema `makeUnsafe` instead of the removed `.make` API. diff --git a/repos/effect/.changeset/pre/olive-queues-persist.md b/repos/effect/.changeset/pre/olive-queues-persist.md deleted file mode 100644 index ee9e644cde..0000000000 --- a/repos/effect/.changeset/pre/olive-queues-persist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve `PersistedQueue` reliability across SQL, Redis, and memory stores. Retry policy now lives on `make()`, attempts count on claim, retries follow a `Schedule`, and exhausted or undecodable elements are dead-lettered. Add retention cleanup, durable acknowledgement retries, storage schema fixes, local poll wakeups, and fixes for the memory take race and Redis dedup growth. diff --git a/repos/effect/.changeset/pre/opaque-graph-interface.md b/repos/effect/.changeset/pre/opaque-graph-interface.md deleted file mode 100644 index 47e27a0b92..0000000000 --- a/repos/effect/.changeset/pre/opaque-graph-interface.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make the public Graph interfaces opaque by hiding internal mutable storage fields from their TypeScript surface. diff --git a/repos/effect/.changeset/pre/open-hotels-remain.md b/repos/effect/.changeset/pre/open-hotels-remain.md deleted file mode 100644 index 633584cead..0000000000 --- a/repos/effect/.changeset/pre/open-hotels-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -extend McpSchema to work with extensions diff --git a/repos/effect/.changeset/pre/open-objects-intersect.md b/repos/effect/.changeset/pre/open-objects-intersect.md deleted file mode 100644 index fcc55d4b22..0000000000 --- a/repos/effect/.changeset/pre/open-objects-intersect.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/openapi-generator": patch ---- - -Emit mixed struct and record schema types as intersections, preventing optional -properties in open OpenAPI objects from conflicting with their index signature. diff --git a/repos/effect/.changeset/pre/openai-compat-decode-tool-params.md b/repos/effect/.changeset/pre/openai-compat-decode-tool-params.md deleted file mode 100644 index 5286f106e7..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-decode-tool-params.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Decode streaming and non-streaming tool call parameters with the provider-facing OpenAI schema codec. diff --git a/repos/effect/.changeset/pre/openai-compat-empty-assistant-content.md b/repos/effect/.changeset/pre/openai-compat-empty-assistant-content.md deleted file mode 100644 index 8426aa01fd..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-empty-assistant-content.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Normalize empty assistant message content to an empty string for OpenAI-compatible providers that reject null content values. diff --git a/repos/effect/.changeset/pre/openai-compat-null-tool-calls.md b/repos/effect/.changeset/pre/openai-compat-null-tool-calls.md deleted file mode 100644 index 06b23aed40..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-null-tool-calls.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Preserve streamed text from OpenAI-compatible providers that send `tool_calls: null` on text-only chunks. diff --git a/repos/effect/.changeset/pre/openai-compat-nullable-tool-name.md b/repos/effect/.changeset/pre/openai-compat-nullable-tool-name.md deleted file mode 100644 index 96bc6caaa2..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-nullable-tool-name.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Fix dropped streamed tool-call arguments when a provider sends `function.name: null` on continuation fragments. - -OpenAI-compatible providers such as Fireworks send the tool name only on the first streamed `tool_calls` fragment and `function.name: null` on the continuation fragments that carry the argument deltas. `ChatCompletionToolFunctionDelta.name` was `Schema.optionalKey(Schema.String)` (non-nullable), so chunk validation rejected every continuation and silently discarded its argument delta, leaving the assembled tool call with empty or partial params. `name` is now nullable (`Schema.NullOr(Schema.String)`). diff --git a/repos/effect/.changeset/pre/openai-compat-parallel-tool-calls.md b/repos/effect/.changeset/pre/openai-compat-parallel-tool-calls.md deleted file mode 100644 index d2b2ce4412..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-parallel-tool-calls.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Group consecutive tool calls into one assistant message when using Chat Completions APIs. diff --git a/repos/effect/.changeset/pre/openai-compat-reasoning.md b/repos/effect/.changeset/pre/openai-compat-reasoning.md deleted file mode 100644 index 9a8e6b8aee..0000000000 --- a/repos/effect/.changeset/pre/openai-compat-reasoning.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Surface reasoning tokens from OpenAI-compatible chat completion responses. diff --git a/repos/effect/.changeset/pre/openai-compatible-quota-errors.md b/repos/effect/.changeset/pre/openai-compatible-quota-errors.md deleted file mode 100644 index a12a53a98a..0000000000 --- a/repos/effect/.changeset/pre/openai-compatible-quota-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Support max reasoning effort and quota errors returned by OpenAI-compatible providers. diff --git a/repos/effect/.changeset/pre/openai-image-strings.md b/repos/effect/.changeset/pre/openai-image-strings.md deleted file mode 100644 index 5a8aae24ed..0000000000 --- a/repos/effect/.changeset/pre/openai-image-strings.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -Preserve image data supplied as strings in OpenAI Responses and OpenAI-compatible chat requests. diff --git a/repos/effect/.changeset/pre/openai-optional-sequence-number.md b/repos/effect/.changeset/pre/openai-optional-sequence-number.md deleted file mode 100644 index ffd414cd8f..0000000000 --- a/repos/effect/.changeset/pre/openai-optional-sequence-number.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Allow OpenAI-compatible Responses API stream events to omit `sequence_number`. diff --git a/repos/effect/.changeset/pre/openai-telemetry-response.md b/repos/effect/.changeset/pre/openai-telemetry-response.md deleted file mode 100644 index c2bf786a24..0000000000 --- a/repos/effect/.changeset/pre/openai-telemetry-response.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai-compat": patch ---- - -Fix the OpenAI-compatible telemetry response attribute namespace. diff --git a/repos/effect/.changeset/pre/openai-text-prompt-cache.md b/repos/effect/.changeset/pre/openai-text-prompt-cache.md deleted file mode 100644 index f0f17bc012..0000000000 --- a/repos/effect/.changeset/pre/openai-text-prompt-cache.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"effect": patch ---- - -Add support for explicit cache breakpoints on the OpenAI responses API for GPT-5.6-or-later. diff --git a/repos/effect/.changeset/pre/openai-tolerant-error-stream-event.md b/repos/effect/.changeset/pre/openai-tolerant-error-stream-event.md deleted file mode 100644 index ee2e518a04..0000000000 --- a/repos/effect/.changeset/pre/openai-tolerant-error-stream-event.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Decode nested OpenAI Responses API error events without changing their decoded type. diff --git a/repos/effect/.changeset/pre/openai-web-search-outcomes.md b/repos/effect/.changeset/pre/openai-web-search-outcomes.md deleted file mode 100644 index e6129dcf2a..0000000000 --- a/repos/effect/.changeset/pre/openai-web-search-outcomes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Report failed OpenAI provider-executed tool calls as failure tool results in both `generateText` and `streamText`, preserving the call details. diff --git a/repos/effect/.changeset/pre/openapi-generator-sse-constraint-decoder.md b/repos/effect/.changeset/pre/openapi-generator-sse-constraint-decoder.md deleted file mode 100644 index 358451002a..0000000000 --- a/repos/effect/.changeset/pre/openapi-generator-sse-constraint-decoder.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Fix the generated SSE `sseRequest` helper to reference `Schema.ConstraintDecoder` instead of the no-longer-exported `Schema.Decoder`. - -For specs with `text/event-stream` responses the generator emitted a helper typed as `Schema.Decoder`, but `Schema` exports that decode-only interface as `ConstraintDecoder`, so generated clients failed to compile (`'"effect/Schema"' has no exported member named 'Decoder'`). The emitted helper now uses `Schema.ConstraintDecoder`. diff --git a/repos/effect/.changeset/pre/openapi-omitted-additional-properties.md b/repos/effect/.changeset/pre/openapi-omitted-additional-properties.md deleted file mode 100644 index 4af8dc5ed3..0000000000 --- a/repos/effect/.changeset/pre/openapi-omitted-additional-properties.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Preserve omitted `additionalProperties` when generating schemas from OpenAPI documents. diff --git a/repos/effect/.changeset/pre/openapi-path-parameters.md b/repos/effect/.changeset/pre/openapi-path-parameters.md deleted file mode 100644 index fcf771ed2c..0000000000 --- a/repos/effect/.changeset/pre/openapi-path-parameters.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/openapi-generator": patch ---- - -Allow path-level common parameters in OpenAPI generator input types. diff --git a/repos/effect/.changeset/pre/openapi-pointer-references.md b/repos/effect/.changeset/pre/openapi-pointer-references.md deleted file mode 100644 index e7ef509957..0000000000 --- a/repos/effect/.changeset/pre/openapi-pointer-references.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Decode JSON Pointer escapes when resolving local OpenAPI references. diff --git a/repos/effect/.changeset/pre/openapi-response-variants.md b/repos/effect/.changeset/pre/openapi-response-variants.md deleted file mode 100644 index 60a248f8ce..0000000000 --- a/repos/effect/.changeset/pre/openapi-response-variants.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Fix HttpClient response generation for mixed JSON-compatible representations, -binary success bodies, and bodiless error statuses. diff --git a/repos/effect/.changeset/pre/openrouter-disjoint-usage-details.md b/repos/effect/.changeset/pre/openrouter-disjoint-usage-details.md deleted file mode 100644 index 8b36a80dbf..0000000000 --- a/repos/effect/.changeset/pre/openrouter-disjoint-usage-details.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Fix negative text and uncached token usage when reasoning or cached counts exceed their parent totals. Treat these counts as disjoint, adding them to the total and retaining the parent count as text or uncached usage. - -Disjoint counts at or below their parent totals remain indistinguishable from subsets, so their totals are still undercounted. diff --git a/repos/effect/.changeset/pre/openrouter-encrypted-tool-finish.md b/repos/effect/.changeset/pre/openrouter-encrypted-tool-finish.md deleted file mode 100644 index 4f5006ac1c..0000000000 --- a/repos/effect/.changeset/pre/openrouter-encrypted-tool-finish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Report `tool-calls` instead of `unknown` when an OpenRouter stream ends with `stop` after emitting tool calls and nonempty encrypted reasoning. diff --git a/repos/effect/.changeset/pre/openrouter-generation-usage-schema.md b/repos/effect/.changeset/pre/openrouter-generation-usage-schema.md deleted file mode 100644 index d9b9a67956..0000000000 --- a/repos/effect/.changeset/pre/openrouter-generation-usage-schema.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Regenerate the `Generated` module against OpenRouter's current published specification. This preserves nullable -generation statistics and streamed usage cost metadata while incorporating the broader upstream schema changes. - -Notable generated schema renames include `ChatGenerationParams` to `ChatRequest`, `ChatGenerationTokenUsage` to -`ChatUsage`, `AssistantMessage` to `ChatAssistantMessage`, `ChatStreamingResponseChunk` to `ChatStreamingResponse`, -and `ChatMessageContentItemCacheControl` to `ChatContentCacheControl`. Handwritten public aliases such as -`ChatStreamingResponseChunkData`, `ReasoningDetails`, and `FileAnnotation` retain their existing names. diff --git a/repos/effect/.changeset/pre/openrouter-input-audio.md b/repos/effect/.changeset/pre/openrouter-input-audio.md deleted file mode 100644 index 8dc06d3eb0..0000000000 --- a/repos/effect/.changeset/pre/openrouter-input-audio.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Convert audio file parts in prompts into OpenRouter `input_audio` content blocks. - -Previously, every non-image file part was converted into a generic `file` content block. OpenRouter only accepts audio as base64-encoded `input_audio` content parts, so audio attachments were rejected or mishandled by the upstream provider. - -Audio file parts with a recognized `mediaType` (aac, aiff, flac, m4a, mp3, ogg, pcm16, pcm24, and wav) are now converted into `input_audio` blocks. Unsupported audio media types and `URL` audio data fail with an `AiError` since OpenRouter requires base64-encoded audio data and does not fetch audio URLs. diff --git a/repos/effect/.changeset/pre/openrouter-strict-json-schema.md b/repos/effect/.changeset/pre/openrouter-strict-json-schema.md deleted file mode 100644 index 7366127595..0000000000 --- a/repos/effect/.changeset/pre/openrouter-strict-json-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Exclude the provider-only `strictJsonSchema` option from outgoing OpenRouter request bodies while preserving structured-output and tool strictness settings. diff --git a/repos/effect/.changeset/pre/openrouter-tool-parameter-deltas.md b/repos/effect/.changeset/pre/openrouter-tool-parameter-deltas.md deleted file mode 100644 index 41da9fcad8..0000000000 --- a/repos/effect/.changeset/pre/openrouter-tool-parameter-deltas.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openrouter": patch ---- - -Emit incremental tool parameter fragments from OpenRouter streaming responses. diff --git a/repos/effect/.changeset/pre/opentelemetry-delta-interval-starts.md b/repos/effect/.changeset/pre/opentelemetry-delta-interval-starts.md deleted file mode 100644 index c536f68231..0000000000 --- a/repos/effect/.changeset/pre/opentelemetry-delta-interval-starts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Use collection interval starts for OpenTelemetry delta metrics. diff --git a/repos/effect/.changeset/pre/opentelemetry-render-causes.md b/repos/effect/.changeset/pre/opentelemetry-render-causes.md deleted file mode 100644 index 4ebcd6ef38..0000000000 --- a/repos/effect/.changeset/pre/opentelemetry-render-causes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Add `includeCauseInStack` option to `Cause.prettyErrors` diff --git a/repos/effect/.changeset/pre/optic-projection-replacement.md b/repos/effect/.changeset/pre/optic-projection-replacement.md deleted file mode 100644 index 3523631ae2..0000000000 --- a/repos/effect/.changeset/pre/optic-projection-replacement.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `Optic.pick` and `Optic.omit` delete focused optional fields omitted from a replacement. diff --git a/repos/effect/.changeset/pre/optic-standalone-functions.md b/repos/effect/.changeset/pre/optic-standalone-functions.md deleted file mode 100644 index fc0ebd947c..0000000000 --- a/repos/effect/.changeset/pre/optic-standalone-functions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add dual standalone functions for reading and updating values through optics, closes #7299. diff --git a/repos/effect/.changeset/pre/optic-string-index-delete.md b/repos/effect/.changeset/pre/optic-string-index-delete.md deleted file mode 100644 index 8468eda4a7..0000000000 --- a/repos/effect/.changeset/pre/optic-string-index-delete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Optic.optionalKey` to splice tuple elements selected by string indices. diff --git a/repos/effect/.changeset/pre/optimize-array-equality.md b/repos/effect/.changeset/pre/optimize-array-equality.md deleted file mode 100644 index 1ae1dede64..0000000000 --- a/repos/effect/.changeset/pre/optimize-array-equality.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve the performance of `Array.dedupe`, `Array.union`, `Array.intersection`, `Array.difference`, and Schema unique item validation by using hash-based equality lookup. diff --git a/repos/effect/.changeset/pre/optimize-httpapi-handler-types.md b/repos/effect/.changeset/pre/optimize-httpapi-handler-types.md deleted file mode 100644 index af3799598d..0000000000 --- a/repos/effect/.changeset/pre/optimize-httpapi-handler-types.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -"effect": patch ---- - -Improve unstable `HttpApi` type-level performance. - -The implementation now uses identifier-keyed maps and lighter structural -constraints in several hot type-level paths. Generated group clients consume the -concrete endpoint map directly instead of rebuilding it from the endpoint union. - -## New Features - -- Add `HttpApiBuilder.Handlers.handleAll`, which registers an identifier-keyed batch of endpoint handlers for a group. Each entry can be either a handler function or `{ handler, options }`, and the object can be supplied in multiple partial batches. Endpoint identifiers that were already handled are rejected across batches. -- `HttpApi.groups` now preserves the concrete group type for each group identifier. For example, `Api.groups.users` is typed as the `users` group instead of the full group union. -- `HttpApiGroup.endpoints` now preserves the concrete endpoint type for each endpoint identifier. For example, `Group.endpoints.getUser` is typed as the `getUser` endpoint instead of the full endpoint union. -- `HttpApiEndpoint` values can now be extended as classes, matching the class-like - runtime shape already used by `HttpApi` and `HttpApiGroup`. - -## Measured Type-Level Performance - -Main/current comparisons use identical generated fixtures compiled once per -revision with TypeScript 7.0.2. The recorded revisions are `main` at -`97fdaa9c1f52` and the branch source at `5798fc5fafcd`. The focused pre/post -curves below were captured with the regular `httpapi` regression suite during -development. The retained suite uses representative stress points instead of -rerunning every point in those historical curves. All numbers are -type-instantiation deltas over the corresponding shared baseline. - -Endpoint declaration costs now grow with a lower slope: - -| endpoints | main | current | -| --------: | ------: | ------: | -| 10 | 4,580 | 2,808 | -| 50 | 15,500 | 9,168 | -| 100 | 29,150 | 17,118 | -| 500 | 138,350 | 80,718 | - -Class-like endpoint declarations are slightly cheaper than inline endpoint -values in the same 500-endpoint fixture shape: - -| fixture | inline | class-like | -| ------------- | -----: | ---------: | -| 500 endpoints | 82,207 | 71,850 | - -`HttpApiBuilder` fluent handler registration avoids the previous non-linear -blow-up in the cross-ref comparison: - -| fixture | main | current | -| ---------------- | ---------: | --------: | -| 10 endpoints | 37,856 | 11,582 | -| 50 endpoints | 568,576 | 63,702 | -| 100 endpoints | 2,154,476 | 182,852 | -| 500 endpoints | 51,741,676 | 3,296,052 | -| 500 raw handlers | 51,734,176 | 3,294,550 | - -In the recorded regular-suite measurements, `handleAll` remains the scalable -alternative to the equivalent fluent chain: - -| fixture | fluent | `handleAll` | -| -------------------- | --------: | ----------: | -| 10 endpoints | 11,579 | 9,146 | -| 50 endpoints | 63,699 | 25,106 | -| 100 endpoints | 182,849 | 45,056 | -| 500 endpoints | 3,296,049 | 204,656 | -| 500 eps, two batches | 3,296,049 | 223,613 | - -Generated-client type production also improves for the hot method-building -paths: - -| fixture | main | current | -| --------------------------------------- | ------: | ------: | -| client methods, 500 endpoints | 245,795 | 176,850 | -| top-level client methods, 500 endpoints | 243,651 | 179,809 | -| client endpoint method, 500 endpoints | 56,738 | 46,294 | -| client groups, 100 groups x 5 endpoints | 49,019 | 25,893 | - -The following focused curves were captured immediately before and after each -isolated type-level change. - -The focused `Client.Group` curve shows the improvement from consuming the -identifier-keyed endpoint map directly: - -| endpoints | union remapping | endpoint map | -| --------: | --------------: | -----------: | -| 10 | 12,448 | 12,294 | -| 50 | 19,169 | 18,935 | -| 100 | 27,570 | 27,236 | -| 500 | 94,770 | 93,636 | - -The focused `Client.TopLevelMethods` curve improves by reading endpoint -identifiers directly from the endpoint union: - -| endpoints | pre-change | post-change | -| --------: | ---------: | ----------: | -| 10 | 12,531 | 12,476 | -| 50 | 19,252 | 19,197 | -| 100 | 27,653 | 27,598 | -| 500 | 94,853 | 94,798 | - -The focused `HttpApiClient.endpoint` selection curve improves by reading -endpoint identifiers directly from the selected endpoint union: - -| endpoints | pre-change | post-change | -| --------: | ---------: | ----------: | -| 10 | 7,666 | 7,588 | -| 50 | 8,707 | 8,629 | -| 100 | 10,008 | 9,930 | -| 500 | 20,408 | 20,330 | - -The focused `HttpApiBuilder.endpoint` selection curve improves by reading -endpoint identifiers directly from the selected endpoint union: - -| endpoints | pre-change | post-change | -| --------: | ---------: | ----------: | -| 10 | 12,828 | 12,745 | -| 50 | 13,869 | 13,786 | -| 100 | 15,170 | 15,087 | -| 500 | 25,570 | 25,487 | - -URL builder types now avoid repeatedly expanding the full API/group shape: - -| fixture | main | current | -| ------------------------------------ | ------: | ------: | -| URL builder, 500 endpoints | 211,356 | 91,610 | -| top-level URL builder, 500 endpoints | 210,724 | 93,118 | -| builder endpoint, 500 endpoints | 62,894 | 51,952 | - -## Breaking Changes - -These changes affect unstable `HttpApi` type-level APIs and structural API, -group, and endpoint types. - -### Renamed Constraint Types - -- Broad structural constraint exports have been renamed to align with - `Schema.Constraint` terminology: `HttpApi.Any` to `HttpApi.Constraint`, - `HttpApi.AnyWithProps` to `HttpApi.Top`, `HttpApiGroup.Any` to - `HttpApiGroup.Constraint`, `HttpApiGroup.AnyWithProps` to `HttpApiGroup.Top`, - and `HttpApiEndpoint.Any` to `HttpApiEndpoint.Constraint`. -- `HttpApiEndpoint.AnyWithProps` has been replaced by `HttpApiEndpoint.Top`, whose - schema parameters are constrained to `Schema.Top`, including success and error - schemas. -- Type guards now expose the widened runtime-prop shapes: `HttpApi.isHttpApi` - returns `HttpApi.Top`, `HttpApiGroup.isHttpApiGroup` returns - `HttpApiGroup.Top`, and `HttpApiEndpoint.isHttpApiEndpoint` returns - `HttpApiEndpoint.Top`. -- `HttpApiGroup.ApiGroup` has been renamed to `HttpApiGroup.Service`. - -### API, Group, And Endpoint Shapes - -- `HttpApi.groups` is now typed as an identifier-keyed group map instead of - `ReadonlyRecord`, and `HttpApi` tracks its group union - invariantly. Dynamic string indexing must refine the key first or cast to a - broad runtime record. -- `HttpApiGroup.endpoints` is now typed as an identifier-keyed endpoint map instead of - `ReadonlyRecord`, and `HttpApiGroup` tracks its endpoint - union invariantly. Dynamic string indexing must refine the key first or cast to - a broad runtime record. -- `HttpApiEndpoint` now exposes its stable key as `identifier` instead of `name`, - aligning endpoints with APIs and groups and leaving `name` available for future - class-based endpoint patterns. -- `HttpApiEndpoint` values are now function objects instead of plain objects. - Runtime checks such as `typeof endpoint` now return `"function"`, and - `endpoint.name` is the native function name. Use `endpoint.identifier` for the - stable endpoint key. -- Identifier helper types have been renamed from `Name` / `WithName` to - `Identifier` / `WithIdentifier`; `HttpApiGroup.Service` now exposes - `identifier` instead of `name`. - -### Builder Handler Types - -- `HttpApiBuilder.Handlers` now tracks endpoints through an identifier-keyed endpoint map and a set of handled endpoint identifiers, instead of tracking the remaining endpoint union. Its public type parameters changed from `Handlers` to `Handlers`, and its phantom fields changed from `_Endpoints` to `~EndpointsByIdentifier` / `~HandledIdentifiers`. -- The unused `HttpApiBuilder.Handlers.Any` helper type has been removed. -- The exported `HttpApiBuilder.HandlersTypeId` symbol has been removed; `Handlers` - now uses a private string type id. -- Duplicate `handle` / `handleRaw` registrations for the same endpoint are rejected - at the call site, and `handleAll` rejects endpoint identifiers that were already - handled by an earlier batch. Missing endpoint handlers are still rejected by - the final `HttpApiBuilder.group` return validation. - -### Client Types - -- `HttpApiClient.Client.Group` now derives a client from a concrete group type: `Client.Group`. The previous group-union plus group-identifier form is no longer supported. -- `HttpApiClient.Client.TopLevelMethods` now returns an identifier-keyed method record instead of a union of `[identifier, method]` tuples. -- `HttpApiClient.makeWith` removes the default `HttpClientError.HttpClientError` from custom client error types in the returned `Client`, while preserving any additional custom client errors. - -### Endpoint Helper Types - -- `HttpApiEndpoint.HttpApiEndpoint` now stores lightweight phantom metadata for middleware and request shapes: `~Middleware`, `~MiddlewareServices`, `~Request`, and `~RequestRaw`. Its type identifier field is now `readonly [TypeId]: typeof TypeId`. -- `HttpApiEndpoint.Constraint` is now a lightweight structural endpoint constraint and does not extend `Pipeable`; values typed only as `HttpApiEndpoint.Constraint` do not expose `.pipe`. -- `HttpApiEndpoint.AddError` has been removed; it was not used internally by the `HttpApi` implementation. -- `HttpApiEndpoint.Json` and `HttpApiEndpoint.StringTree` have been removed in - favor of the canonical `Schema.toCodecJson` and `Schema.toCodecStringTree` - types. -- Omitted request-part metadata now remains `never` instead of being wrapped as - `Schema.toCodecStringTree`; codec metadata is applied only when - a params, query, payload, or headers schema is present. -- Success metadata now applies `Schema.toCodecJson` only to buffered - success schemas and preserves stream success schemas unchanged, including - mixed buffered and streaming success arrays. -- Handler request parts are now flattened with `Struct.Simplify`, improving - displayed request types while reducing handler instantiations. -- Endpoint helper types now read metadata fields directly instead of re-inferring all type parameters from the full `HttpApiEndpoint` interface. This affects helpers such as `Identifier`, `Success`, `Error`, `Params`, `Query`, `Payload`, `Headers`, `Middleware`, `MiddlewareServices`, `Errors`, `ErrorServicesEncode`, `ErrorServicesDecode`, `Request`, `RequestRaw`, `ServerServices`, and `ClientServices`. -- `HttpApiClient.Client.Method` and related generated-client helpers now require endpoint types that satisfy `HttpApiEndpoint.ConstraintRequest`. Endpoint-like structural types must include the lightweight request metadata fields to be accepted. diff --git a/repos/effect/.changeset/pre/optimize-node-http-server-response.md b/repos/effect/.changeset/pre/optimize-node-http-server-response.md deleted file mode 100644 index 1cb193cf59..0000000000 --- a/repos/effect/.changeset/pre/optimize-node-http-server-response.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Optimize Node HTTP streaming responses and ensure HEAD completion and stream backpressure are handled once. diff --git a/repos/effect/.changeset/pre/optimize-schema-class-decoding.md b/repos/effect/.changeset/pre/optimize-schema-class-decoding.md deleted file mode 100644 index 61b14ad4cb..0000000000 --- a/repos/effect/.changeset/pre/optimize-schema-class-decoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Avoid validating `Schema.Class` fields twice when decoding. diff --git a/repos/effect/.changeset/pre/optional-ai-embedding-usage.md b/repos/effect/.changeset/pre/optional-ai-embedding-usage.md deleted file mode 100644 index beb5997ae0..0000000000 --- a/repos/effect/.changeset/pre/optional-ai-embedding-usage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow embedding usage input tokens to be omitted during decoding, including after JSON serialization. diff --git a/repos/effect/.changeset/pre/optional-ai-response-fields.md b/repos/effect/.changeset/pre/optional-ai-response-fields.md deleted file mode 100644 index eebdd1ca0e..0000000000 --- a/repos/effect/.changeset/pre/optional-ai-response-fields.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow optional AI response fields to be omitted during decoding, including after JSON serialization. diff --git a/repos/effect/.changeset/pre/order-consumed-criteria.md b/repos/effect/.changeset/pre/order-consumed-criteria.md deleted file mode 100644 index 85dd26e645..0000000000 --- a/repos/effect/.changeset/pre/order-consumed-criteria.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Order.combineAll` consuming one-shot iterables after the first comparison. diff --git a/repos/effect/.changeset/pre/otel-resource-env-precedence.md b/repos/effect/.changeset/pre/otel-resource-env-precedence.md deleted file mode 100644 index c95fd27ddb..0000000000 --- a/repos/effect/.changeset/pre/otel-resource-env-precedence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prefer OTEL resource environment variables over explicit `OtlpResource.fromConfig` options. diff --git a/repos/effect/.changeset/pre/otlp-disabled-batch-flush.md b/repos/effect/.changeset/pre/otlp-disabled-batch-flush.md deleted file mode 100644 index 9ab34b05aa..0000000000 --- a/repos/effect/.changeset/pre/otlp-disabled-batch-flush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix disabled OTLP batching to skip empty exports and avoid resending buffered items. diff --git a/repos/effect/.changeset/pre/otlp-tracer-span-performance.md b/repos/effect/.changeset/pre/otlp-tracer-span-performance.md deleted file mode 100644 index 2319594b70..0000000000 --- a/repos/effect/.changeset/pre/otlp-tracer-span-performance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Speed up `OtlpTracer` span creation and export. Spans now allocate identifiers, attributes, and events lazily, and `Encoding.randomHex` produces flat strings for 16 and 32 character identifiers so serialization no longer flattens ropes. diff --git a/repos/effect/.changeset/pre/parameterize-persistence-lookup-keys.md b/repos/effect/.changeset/pre/parameterize-persistence-lookup-keys.md deleted file mode 100644 index 92960c36a7..0000000000 --- a/repos/effect/.changeset/pre/parameterize-persistence-lookup-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Parameterize persistence lookup keys in both SQL backing stores' `getMany` queries. diff --git a/repos/effect/.changeset/pre/partitioned-semaphore-stale-cleanup.md b/repos/effect/.changeset/pre/partitioned-semaphore-stale-cleanup.md deleted file mode 100644 index 72b3fba030..0000000000 --- a/repos/effect/.changeset/pre/partitioned-semaphore-stale-cleanup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `PartitionedSemaphore` leaving a new waiter suspended when a previously resumed waiter for the same partition is interrupted before its acquisition completes. diff --git a/repos/effect/.changeset/pre/perfect-buckets-tickle.md b/repos/effect/.changeset/pre/perfect-buckets-tickle.md deleted file mode 100644 index a17e31a516..0000000000 --- a/repos/effect/.changeset/pre/perfect-buckets-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add default value support to `Prompt.file`. diff --git a/repos/effect/.changeset/pre/persisted-cache-lookup-throw.md b/repos/effect/.changeset/pre/persisted-cache-lookup-throw.md deleted file mode 100644 index 81b54be07e..0000000000 --- a/repos/effect/.changeset/pre/persisted-cache-lookup-throw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Persist synchronous defects thrown by `PersistedCache` lookups. diff --git a/repos/effect/.changeset/pre/petite-months-allow.md b/repos/effect/.changeset/pre/petite-months-allow.md deleted file mode 100644 index 04ad2d0b60..0000000000 --- a/repos/effect/.changeset/pre/petite-months-allow.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-openai-compat": patch ---- - -add dynamic tooling for openai and openai-compact language models diff --git a/repos/effect/.changeset/pre/pg-client-connect-error-handler.md b/repos/effect/.changeset/pre/pg-client-connect-error-handler.md deleted file mode 100644 index 3eed009726..0000000000 --- a/repos/effect/.changeset/pre/pg-client-connect-error-handler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Prevent unhandled `pg` client error events while `PgClient.makeClient` is connecting. diff --git a/repos/effect/.changeset/pre/pg-explicit-ssl-precedence.md b/repos/effect/.changeset/pre/pg-explicit-ssl-precedence.md deleted file mode 100644 index e52b6246fd..0000000000 --- a/repos/effect/.changeset/pre/pg-explicit-ssl-precedence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Honor explicit `ssl` options when PostgreSQL URLs use `sslmode=prefer` or `sslmode=allow`. diff --git a/repos/effect/.changeset/pre/pglite-json-string-values.md b/repos/effect/.changeset/pre/pglite-json-string-values.md deleted file mode 100644 index f30fa95214..0000000000 --- a/repos/effect/.changeset/pre/pglite-json-string-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pglite": patch ---- - -Preserve string values passed to `sql.json` when using PGlite. diff --git a/repos/effect/.changeset/pre/pipeline-root-stdin.md b/repos/effect/.changeset/pre/pipeline-root-stdin.md deleted file mode 100644 index 1c380f8843..0000000000 --- a/repos/effect/.changeset/pre/pipeline-root-stdin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Route pipeline handle input through the first process. diff --git a/repos/effect/.changeset/pre/plain-tool-results.md b/repos/effect/.changeset/pre/plain-tool-results.md deleted file mode 100644 index b58b402756..0000000000 --- a/repos/effect/.changeset/pre/plain-tool-results.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-openai": patch -"@effect/ai-anthropic": patch -"@effect/ai-openrouter": patch ---- - -Preserve plain-text client tool results when encoding provider requests. diff --git a/repos/effect/.changeset/pre/plain-variant-unions.md b/repos/effect/.changeset/pre/plain-variant-unions.md deleted file mode 100644 index 3671583479..0000000000 --- a/repos/effect/.changeset/pre/plain-variant-unions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include plain variant structs in the default variant union. diff --git a/repos/effect/.changeset/pre/platform-crypto-service.md b/repos/effect/.changeset/pre/platform-crypto-service.md deleted file mode 100644 index afbf216e6c..0000000000 --- a/repos/effect/.changeset/pre/platform-crypto-service.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch -"@effect/platform-node": patch -"@effect/platform-node-shared": patch -"@effect/platform-bun": patch -"@effect/platform-browser": patch ---- - -Add a platform-agnostic `Crypto` service for cryptographic random bytes, secure random generators, UUIDv4 / UUIDv7 generation, and digest operations. UUID generation should now use the `Crypto` service's `randomUUIDv4` or `randomUUIDv7`, which format bytes from the platform `Crypto` service; UUIDv7 also uses the `Clock` service timestamp. `Random.nextUUIDv4` has been removed because the base `Random` service is not cryptographically secure. diff --git a/repos/effect/.changeset/pre/platform-node-shared-barrel.md b/repos/effect/.changeset/pre/platform-node-shared-barrel.md deleted file mode 100644 index f73cae6844..0000000000 --- a/repos/effect/.changeset/pre/platform-node-shared-barrel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Add the package root barrel export. diff --git a/repos/effect/.changeset/pre/plenty-moons-pull.md b/repos/effect/.changeset/pre/plenty-moons-pull.md deleted file mode 100644 index adac8d000f..0000000000 --- a/repos/effect/.changeset/pre/plenty-moons-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -use encoded types for idb queries diff --git a/repos/effect/.changeset/pre/polite-brooms-tickle.md b/repos/effect/.changeset/pre/polite-brooms-tickle.md deleted file mode 100644 index 89a9b755ea..0000000000 --- a/repos/effect/.changeset/pre/polite-brooms-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `LogLevel.isEnabled` for checking a log level against `References.MinimumLogLevel`. diff --git a/repos/effect/.changeset/pre/polite-cameras-rest.md b/repos/effect/.changeset/pre/polite-cameras-rest.md deleted file mode 100644 index 348f740e99..0000000000 --- a/repos/effect/.changeset/pre/polite-cameras-rest.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/platform-browser": patch ---- - -Preserve prototype accessors when code is compiled with loose object spread transforms. diff --git a/repos/effect/.changeset/pre/polite-dingos-unite.md b/repos/effect/.changeset/pre/polite-dingos-unite.md deleted file mode 100644 index 7ae08b5bc1..0000000000 --- a/repos/effect/.changeset/pre/polite-dingos-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve CRLF state across SSE input chunk boundaries. diff --git a/repos/effect/.changeset/pre/polite-pigs-speak.md b/repos/effect/.changeset/pre/polite-pigs-speak.md deleted file mode 100644 index 44d427f9dc..0000000000 --- a/repos/effect/.changeset/pre/polite-pigs-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -use .toJSON for default .toString implementations diff --git a/repos/effect/.changeset/pre/polite-tables-kneel.md b/repos/effect/.changeset/pre/polite-tables-kneel.md deleted file mode 100644 index e80db998f1..0000000000 --- a/repos/effect/.changeset/pre/polite-tables-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-anthropic": patch ---- - -Fix tool calling for the Anthropic Effect AI SDK provider integration diff --git a/repos/effect/.changeset/pre/pool-preserve-reservations.md b/repos/effect/.changeset/pre/pool-preserve-reservations.md deleted file mode 100644 index 0f87426b27..0000000000 --- a/repos/effect/.changeset/pre/pool-preserve-reservations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep `Pool.reserve` items out of shared circulation when other borrowers return or overlapping reservations close. Restore available slots only after the last reservation closes. diff --git a/repos/effect/.changeset/pre/port-effect-reduce.md b/repos/effect/.changeset/pre/port-effect-reduce.md deleted file mode 100644 index 36632f7c28..0000000000 --- a/repos/effect/.changeset/pre/port-effect-reduce.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Port `Effect.reduce` from Effect v3. diff --git a/repos/effect/.changeset/pre/port-http-api-builder-handler.md b/repos/effect/.changeset/pre/port-http-api-builder-handler.md deleted file mode 100644 index a499e2fe28..0000000000 --- a/repos/effect/.changeset/pre/port-http-api-builder-handler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Port `HttpApiBuilder.handler` from v3 to define reusable endpoint callbacks with inferred request, response, error, and service types. diff --git a/repos/effect/.changeset/pre/port-react-hydration.md b/repos/effect/.changeset/pre/port-react-hydration.md deleted file mode 100644 index a7cd38e2f1..0000000000 --- a/repos/effect/.changeset/pre/port-react-hydration.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch -"@effect/atom-react": patch ---- - -Port ReactHydration to effect-smol. - -Add `Hydration` module to `effect/unstable/reactivity` with `dehydrate`, `hydrate`, and `toValues` for SSR state serialization. Add `HydrationBoundary` React component to `@effect/atom-react` with two-phase hydration (new atoms in render, existing atoms after commit). diff --git a/repos/effect/.changeset/pre/postgres-channel-names.md b/repos/effect/.changeset/pre/postgres-channel-names.md deleted file mode 100644 index 7662aa95f9..0000000000 --- a/repos/effect/.changeset/pre/postgres-channel-names.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Reject PostgreSQL LISTEN and NOTIFY channel names longer than 63 UTF-8 bytes. diff --git a/repos/effect/.changeset/pre/precise-clocks-rest.md b/repos/effect/.changeset/pre/precise-clocks-rest.md deleted file mode 100644 index afe1d8a1ea..0000000000 --- a/repos/effect/.changeset/pre/precise-clocks-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve nanosecond precision for large `TestClock` wall-clock timestamps. diff --git a/repos/effect/.changeset/pre/precise-test-clock-nanos.md b/repos/effect/.changeset/pre/precise-test-clock-nanos.md deleted file mode 100644 index aec813c1a1..0000000000 --- a/repos/effect/.changeset/pre/precise-test-clock-nanos.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve nanosecond precision when adjusting `TestClock` with large durations. diff --git a/repos/effect/.changeset/pre/preserve-command-hidden-metadata.md b/repos/effect/.changeset/pre/preserve-command-hidden-metadata.md deleted file mode 100644 index ceb6c68686..0000000000 --- a/repos/effect/.changeset/pre/preserve-command-hidden-metadata.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve hidden command metadata when adding subcommands or shared flags. diff --git a/repos/effect/.changeset/pre/preserve-config-all-input-evidence.md b/repos/effect/.changeset/pre/preserve-config-all-input-evidence.md deleted file mode 100644 index bd8b92ad8b..0000000000 --- a/repos/effect/.changeset/pre/preserve-config-all-input-evidence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve sibling provider input evidence when `Config.all` evaluates a failing child. diff --git a/repos/effect/.changeset/pre/preserve-default-variant-class.md b/repos/effect/.changeset/pre/preserve-default-variant-class.md deleted file mode 100644 index d263ec6aeb..0000000000 --- a/repos/effect/.changeset/pre/preserve-default-variant-class.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve schema classes when extracting their default `VariantSchema` variant. diff --git a/repos/effect/.changeset/pre/preserve-negative-counter-deltas.md b/repos/effect/.changeset/pre/preserve-negative-counter-deltas.md deleted file mode 100644 index d8783c189d..0000000000 --- a/repos/effect/.changeset/pre/preserve-negative-counter-deltas.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/opentelemetry": patch ---- - -Preserve negative counter deltas in OTLP and OpenTelemetry metric exports. diff --git a/repos/effect/.changeset/pre/preserve-openapi-raw-json.md b/repos/effect/.changeset/pre/preserve-openapi-raw-json.md deleted file mode 100644 index 8718904d1c..0000000000 --- a/repos/effect/.changeset/pre/preserve-openapi-raw-json.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve `JSON.rawJSON` values when cloning cached OpenAPI specs. diff --git a/repos/effect/.changeset/pre/preserve-otel-parent-context.md b/repos/effect/.changeset/pre/preserve-otel-parent-context.md deleted file mode 100644 index d436bd9715..0000000000 --- a/repos/effect/.changeset/pre/preserve-otel-parent-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Preserve trace state and locality when adapting active OpenTelemetry parent contexts. diff --git a/repos/effect/.changeset/pre/preserve-unsafe-workflow-interrupts.md b/repos/effect/.changeset/pre/preserve-unsafe-workflow-interrupts.md deleted file mode 100644 index eaff87e4b0..0000000000 --- a/repos/effect/.changeset/pre/preserve-unsafe-workflow-interrupts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve unsafe in-memory workflow interrupts across replay. diff --git a/repos/effect/.changeset/pre/pretty-moments-clap.md b/repos/effect/.changeset/pre/pretty-moments-clap.md deleted file mode 100644 index 0dd186ea21..0000000000 --- a/repos/effect/.changeset/pre/pretty-moments-clap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure handler errors don't cause httpapi security middleware to fallback diff --git a/repos/effect/.changeset/pre/principled-collection-counts.md b/repos/effect/.changeset/pre/principled-collection-counts.md deleted file mode 100644 index 25bf6a2ef7..0000000000 --- a/repos/effect/.changeset/pre/principled-collection-counts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Normalize numeric collection counts consistently across `Array`, `Chunk`, `Iterable`, and `String`, and make `TupleOf` fall back to `Array` for positive fractional lengths. diff --git a/repos/effect/.changeset/pre/prompt-buffer-render.md b/repos/effect/.changeset/pre/prompt-buffer-render.md deleted file mode 100644 index 6cc05a9211..0000000000 --- a/repos/effect/.changeset/pre/prompt-buffer-render.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep the previous prompt frame visible until the next frame or submission is ready to display. diff --git a/repos/effect/.changeset/pre/prompt-lossless-text-serialization.md b/repos/effect/.changeset/pre/prompt-lossless-text-serialization.md deleted file mode 100644 index ead45aa747..0000000000 --- a/repos/effect/.changeset/pre/prompt-lossless-text-serialization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve text parts and provider options when serializing prompts. diff --git a/repos/effect/.changeset/pre/prompt-response-files.md b/repos/effect/.changeset/pre/prompt-response-files.md deleted file mode 100644 index 0f11a7d89c..0000000000 --- a/repos/effect/.changeset/pre/prompt-response-files.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve generated files when converting AI responses to prompts. diff --git a/repos/effect/.changeset/pre/protect-schema-issue-actuals.md b/repos/effect/.changeset/pre/protect-schema-issue-actuals.md deleted file mode 100644 index 0a1c82a3b2..0000000000 --- a/repos/effect/.changeset/pre/protect-schema-issue-actuals.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -"effect": patch ---- - -Remove `actual` fields from every `SchemaIssue` variant, together with -`SchemaIssue.getActual`, `SchemaIssue.redact`, and `Schema.redact`. Built-in -formatters now use static messages that do not interpolate rejected input, -while paths, AST metadata, union successes, and user-provided messages and -annotations are preserved unchanged. - -Runtime performance was measured across the 16 Effect fixtures in the -`schema-benchmarks` suite. These are the scenarios used for the cross-library -comparison with Valibot and Zod. The paired HEAD-versus-`main` run classified 3 -fixtures as improvements, 0 as regressions, and 13 as inconclusive. Negative -changes are faster. Absolute library values are medians from the same -cross-library run; `—` means that the corresponding adapter does not expose -that scenario. - -| Scenario | Effect (ns/op) | Valibot (ns/op) | Zod (ns/op) | HEAD vs main | Classification | -| ------------------------ | -------------: | --------------: | ----------: | -----------: | -------------- | -| `initialization-schema` | 108191.30 | **30549.81** | 212715.66 | -0.92% | inconclusive | -| `initialization-decoder` | **109796.34** | — | — | +1.98% | inconclusive | -| `validation-valid` | 5221.80 | **5070.81** | — | +2.06% | inconclusive | -| `validation-invalid` | 1279.77 | **234.92** | — | +0.59% | inconclusive | -| `parsing-all-valid` | **5144.58** | 5192.19 | 7176.19 | -3.79% | inconclusive | -| `parsing-all-invalid` | **7594.49** | 15236.82 | 37780.35 | -5.94% | improvement | -| `parsing-first-valid` | 5188.33 | **5135.75** | — | -1.49% | inconclusive | -| `parsing-first-invalid` | 1330.82 | **243.64** | — | +1.01% | inconclusive | -| `standard-all-valid` | 5722.01 | 5200.05 | **3801.26** | -1.78% | inconclusive | -| `standard-all-invalid` | **12024.65** | 15528.50 | 30982.17 | -7.78% | improvement | -| `standard-first-valid` | **5655.33** | — | — | +3.84% | inconclusive | -| `standard-first-invalid` | **2001.69** | — | — | -4.56% | inconclusive | -| `codec-typed-encode` | 342.59 | — | **39.29** | -7.62% | inconclusive | -| `codec-typed-decode` | 418.78 | — | **50.14** | -10.89% | improvement | -| `codec-unknown-encode` | **328.38** | — | — | -5.55% | inconclusive | -| `codec-unknown-decode` | **347.35** | — | — | -5.25% | inconclusive | diff --git a/repos/effect/.changeset/pre/proud-ears-say.md b/repos/effect/.changeset/pre/proud-ears-say.md deleted file mode 100644 index 36b35a7bb5..0000000000 --- a/repos/effect/.changeset/pre/proud-ears-say.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Support standalone Effect.forEach data-last usage diff --git a/repos/effect/.changeset/pre/provider-executed-tool-results.md b/repos/effect/.changeset/pre/provider-executed-tool-results.md deleted file mode 100644 index a589416b38..0000000000 --- a/repos/effect/.changeset/pre/provider-executed-tool-results.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Route provider-executed tool results into the assistant message in `Prompt.fromResponseParts` diff --git a/repos/effect/.changeset/pre/public-deer-ring.md b/repos/effect/.changeset/pre/public-deer-ring.md deleted file mode 100644 index 251292bbeb..0000000000 --- a/repos/effect/.changeset/pre/public-deer-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add WorkflowEngine interruptUnsafe diff --git a/repos/effect/.changeset/pre/public-jeans-stop.md b/repos/effect/.changeset/pre/public-jeans-stop.md deleted file mode 100644 index 719c4407cd..0000000000 --- a/repos/effect/.changeset/pre/public-jeans-stop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -reset openai websocket on error diff --git a/repos/effect/.changeset/pre/pubsub-publish-false.md b/repos/effect/.changeset/pre/pubsub-publish-false.md deleted file mode 100644 index 577022ba2f..0000000000 --- a/repos/effect/.changeset/pre/pubsub-publish-false.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -PubSub.publish and PubSub.publishAll now return false on shutdown instead of interrupting, matching Queue.offer semantics. diff --git a/repos/effect/.changeset/pre/pubsub-sliding-single-subscriber.md b/repos/effect/.changeset/pre/pubsub-sliding-single-subscriber.md deleted file mode 100644 index 3584a05098..0000000000 --- a/repos/effect/.changeset/pre/pubsub-sliding-single-subscriber.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix capacity-one PubSub subscriber cursors after sliding past messages, including duplicate delivery and invalid state when unsubscribing from a slid message. diff --git a/repos/effect/.changeset/pre/pull-based-socket.md b/repos/effect/.changeset/pre/pull-based-socket.md deleted file mode 100644 index 98c762f59e..0000000000 --- a/repos/effect/.changeset/pre/pull-based-socket.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"effect": patch -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-browser": patch -"@effect/ai-openai": patch ---- - -Redesign `Socket` around a scoped, pull-based reader with transport backpressure. - -`Socket` now exposes `reader` and `writer`. Client reader acquisition dials and yields a pull of non-empty batches: one buffer for TCP and one entry per WebSocket frame. TCP applies backpressure while paused; pausable WebSockets pause at `highWaterMark` (64 KiB by default) and resume after draining. Browser WebSockets cannot pause, so they can fail with `SocketReadError` at a configured `highWaterMark`. Writes await native drain signals and batch with `cork` / `uncork` where available. - -### Breaking changes - -- `Socket.run`, `Socket.runString`, and `Socket.runRaw` are removed. Acquire `socket.reader` (or `Socket.readerBytes` / `Socket.readerString`) in a scope and pull in a loop. Code before the first pull replaces `onOpen`. -- `Socket.make` now takes `{ reader, writer }`. The writer acquisition is infallible and yields a `Writer` with `write` and `writeAll`; both operations can still fail with `SocketError`. -- Every close fails the pull with `SocketError` wrapping `SocketCloseError`. The close-code predicates are removed; use `Effect.retry` around the scoped read loop to reconnect. -- `Socket.toChannel` and `Socket.toChannelString` now read from the pull and fail on close. `Socket.toStream` is added for read-only consumption. -- `fromWebSocket` drops the `onInitialRun` option; `SendQueueCapacity` is removed. -- Accepted server sockets pause immediately. Their reader attaches to the existing connection and cannot reconnect after close. diff --git a/repos/effect/.changeset/pre/puny-pens-clap.md b/repos/effect/.changeset/pre/puny-pens-clap.md deleted file mode 100644 index c3087e0b6a..0000000000 --- a/repos/effect/.changeset/pre/puny-pens-clap.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/ai-openai": patch -"effect": patch ---- - -retry incremental prompt on invalid request diff --git a/repos/effect/.changeset/pre/purple-bars-prove.md b/repos/effect/.changeset/pre/purple-bars-prove.md deleted file mode 100644 index ca16c96699..0000000000 --- a/repos/effect/.changeset/pre/purple-bars-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -bump undici versions diff --git a/repos/effect/.changeset/pre/purple-schools-float.md b/repos/effect/.changeset/pre/purple-schools-float.md deleted file mode 100644 index caac67d563..0000000000 --- a/repos/effect/.changeset/pre/purple-schools-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve `Prompt.file` to support incremental filtering while typing, including backspace and ctrl-u handling. diff --git a/repos/effect/.changeset/pre/purple-turtles-draw.md b/repos/effect/.changeset/pre/purple-turtles-draw.md deleted file mode 100644 index 95ad9fd7c8..0000000000 --- a/repos/effect/.changeset/pre/purple-turtles-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -add .reverse() to idb select diff --git a/repos/effect/.changeset/pre/queue-manual-flush.md b/repos/effect/.changeset/pre/queue-manual-flush.md deleted file mode 100644 index c7f3fb4233..0000000000 --- a/repos/effect/.changeset/pre/queue-manual-flush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Queue.flush` and `Queue.flushUnsafe` for manually releasing pending takers, including after synchronous offers. diff --git a/repos/effect/.changeset/pre/queue-reentrant-producers.md b/repos/effect/.changeset/pre/queue-reentrant-producers.md deleted file mode 100644 index 3048f717e2..0000000000 --- a/repos/effect/.changeset/pre/queue-reentrant-producers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Queue` message duplication, capacity overruns, and consumer defects when a resumed producer synchronously uses the same queue. Zero-capacity queues now reserve each handed-off message for its consumer before resuming the producer. diff --git a/repos/effect/.changeset/pre/quick-dragons-fix.md b/repos/effect/.changeset/pre/quick-dragons-fix.md deleted file mode 100644 index ded3d77791..0000000000 --- a/repos/effect/.changeset/pre/quick-dragons-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose the optional `orElse` fallback parameter in `Effect.catchTags`. diff --git a/repos/effect/.changeset/pre/quick-falcons-travel.md b/repos/effect/.changeset/pre/quick-falcons-travel.md deleted file mode 100644 index 62cc670ae5..0000000000 --- a/repos/effect/.changeset/pre/quick-falcons-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Argument.variadic(argument)` so it supports direct calls without options. diff --git a/repos/effect/.changeset/pre/quick-geese-relax.md b/repos/effect/.changeset/pre/quick-geese-relax.md deleted file mode 100644 index d0d00ec174..0000000000 --- a/repos/effect/.changeset/pre/quick-geese-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix a regression in `PubSub.shutdown` so shutting down a pubsub interrupts suspended subscribers (including `takeAll`) by ensuring subscriptions are scoped under the pubsub shutdown scope. diff --git a/repos/effect/.changeset/pre/quick-graphs-paths.md b/repos/effect/.changeset/pre/quick-graphs-paths.md deleted file mode 100644 index 2465d0a83c..0000000000 --- a/repos/effect/.changeset/pre/quick-graphs-paths.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add bounded lazy enumeration of simple paths and all tied shortest paths. diff --git a/repos/effect/.changeset/pre/quick-kiwis-remember.md b/repos/effect/.changeset/pre/quick-kiwis-remember.md deleted file mode 100644 index 2c1b55d09a..0000000000 --- a/repos/effect/.changeset/pre/quick-kiwis-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Persist permanent entries in KVS `setMany` operations. diff --git a/repos/effect/.changeset/pre/quick-lamps-dig.md b/repos/effect/.changeset/pre/quick-lamps-dig.md deleted file mode 100644 index c2398444f1..0000000000 --- a/repos/effect/.changeset/pre/quick-lamps-dig.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add HttpApiMiddleware.layerSchemaErrorTransform diff --git a/repos/effect/.changeset/pre/quick-lizards-fall.md b/repos/effect/.changeset/pre/quick-lizards-fall.md deleted file mode 100644 index 10d387f473..0000000000 --- a/repos/effect/.changeset/pre/quick-lizards-fall.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Add rebuild api to idb databases diff --git a/repos/effect/.changeset/pre/quick-schedulers-promise.md b/repos/effect/.changeset/pre/quick-schedulers-promise.md deleted file mode 100644 index 0cddcb3f34..0000000000 --- a/repos/effect/.changeset/pre/quick-schedulers-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use Promise microtasks for synchronous Scheduler dispatch. diff --git a/repos/effect/.changeset/pre/quick-trees-join.md b/repos/effect/.changeset/pre/quick-trees-join.md deleted file mode 100644 index 623775195b..0000000000 --- a/repos/effect/.changeset/pre/quick-trees-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -remove all non-regional service usage diff --git a/repos/effect/.changeset/pre/quiet-buffers-decode.md b/repos/effect/.changeset/pre/quiet-buffers-decode.md deleted file mode 100644 index dbf009e185..0000000000 --- a/repos/effect/.changeset/pre/quiet-buffers-decode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow `SqlEventJournal` to decode entry identifiers and payloads from SQL drivers that return BLOB values as `ArrayBuffer`. diff --git a/repos/effect/.changeset/pre/quiet-buns-route.md b/repos/effect/.changeset/pre/quiet-buns-route.md deleted file mode 100644 index e635008723..0000000000 --- a/repos/effect/.changeset/pre/quiet-buns-route.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -Preserve configured Bun routes when Effect HTTP handlers are installed or restored. diff --git a/repos/effect/.changeset/pre/quiet-carpets-grin.md b/repos/effect/.changeset/pre/quiet-carpets-grin.md deleted file mode 100644 index e3a4e43709..0000000000 --- a/repos/effect/.changeset/pre/quiet-carpets-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add \`Ctrl-A\` and \`Ctrl-E\` key handling for editable CLI text prompts to move the cursor to the beginning or end of the current input line. diff --git a/repos/effect/.changeset/pre/quiet-clis-parse.md b/repos/effect/.changeset/pre/quiet-clis-parse.md deleted file mode 100644 index 2fc507ec82..0000000000 --- a/repos/effect/.changeset/pre/quiet-clis-parse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix unstable CLI subcommands dropping operands after the `--` end-of-options terminator. diff --git a/repos/effect/.changeset/pre/quiet-crons-report.md b/repos/effect/.changeset/pre/quiet-crons-report.md deleted file mode 100644 index d29a3bd233..0000000000 --- a/repos/effect/.changeset/pre/quiet-crons-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Correct the diagnostic for cron step values above a field's maximum. diff --git a/repos/effect/.changeset/pre/quiet-fibers-settle.md b/repos/effect/.changeset/pre/quiet-fibers-settle.md deleted file mode 100644 index 81ca36e683..0000000000 --- a/repos/effect/.changeset/pre/quiet-fibers-settle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix fiber self-interuption from inside a running operation diff --git a/repos/effect/.changeset/pre/quiet-files-hunt.md b/repos/effect/.changeset/pre/quiet-files-hunt.md deleted file mode 100644 index d534ce656e..0000000000 --- a/repos/effect/.changeset/pre/quiet-files-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -wrap httpapi request context with HttpRouter.Request diff --git a/repos/effect/.changeset/pre/quiet-files-write.md b/repos/effect/.changeset/pre/quiet-files-write.md deleted file mode 100644 index fb549eb54d..0000000000 --- a/repos/effect/.changeset/pre/quiet-files-write.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-deno": patch ---- - -Honor `FileSystem.writeFile` open flags in the Deno implementation. diff --git a/repos/effect/.changeset/pre/quiet-graphs-guard.md b/repos/effect/.changeset/pre/quiet-graphs-guard.md deleted file mode 100644 index ababf77f08..0000000000 --- a/repos/effect/.changeset/pre/quiet-graphs-guard.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep graph caches consistent during bulk removals and validate graph kinds at runtime. diff --git a/repos/effect/.changeset/pre/quiet-graphs-search.md b/repos/effect/.changeset/pre/quiet-graphs-search.md deleted file mode 100644 index 7c4a6c3ac7..0000000000 --- a/repos/effect/.changeset/pre/quiet-graphs-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent graph edge reads from exposing internal edge records and reject non-finite A* heuristic values. diff --git a/repos/effect/.changeset/pre/quiet-graphs-simplify.md b/repos/effect/.changeset/pre/quiet-graphs-simplify.md deleted file mode 100644 index cc52f4d7da..0000000000 --- a/repos/effect/.changeset/pre/quiet-graphs-simplify.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the redundant `Graph.Proto` interface. Use `Graph.Graph` when accepting any immutable graph. diff --git a/repos/effect/.changeset/pre/quiet-lamps-jam.md b/repos/effect/.changeset/pre/quiet-lamps-jam.md deleted file mode 100644 index 139ce84ce7..0000000000 --- a/repos/effect/.changeset/pre/quiet-lamps-jam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add OTEL environment variable configuration for unstable OTLP observability. diff --git a/repos/effect/.changeset/pre/quiet-mice-negotiate.md b/repos/effect/.changeset/pre/quiet-mice-negotiate.md deleted file mode 100644 index c54d96dde9..0000000000 --- a/repos/effect/.changeset/pre/quiet-mice-negotiate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add adapter-valued MCP server protocol declarations, route requests through the selected protocol before schema decoding, and add built-in support for MCP `2025-06-18`. diff --git a/repos/effect/.changeset/pre/quiet-observers-report.md b/repos/effect/.changeset/pre/quiet-observers-report.md deleted file mode 100644 index d547f9aac5..0000000000 --- a/repos/effect/.changeset/pre/quiet-observers-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent execution-plan event observer defects from changing attempt outcomes or leaving attempt events unpaired. diff --git a/repos/effect/.changeset/pre/quiet-otters-retry.md b/repos/effect/.changeset/pre/quiet-otters-retry.md deleted file mode 100644 index 7673c47025..0000000000 --- a/repos/effect/.changeset/pre/quiet-otters-retry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor HTTP-date `Retry-After` values when retrying OTLP exports. diff --git a/repos/effect/.changeset/pre/quiet-owls-validate.md b/repos/effect/.changeset/pre/quiet-owls-validate.md deleted file mode 100644 index 6a85ac0b3c..0000000000 --- a/repos/effect/.changeset/pre/quiet-owls-validate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP Streamable HTTP servers now validate content negotiation, session lifecycle, negotiated protocol versions, and browser Origins before dispatching requests. diff --git a/repos/effect/.changeset/pre/quiet-pandas-listen.md b/repos/effect/.changeset/pre/quiet-pandas-listen.md deleted file mode 100644 index 58aaa22f25..0000000000 --- a/repos/effect/.changeset/pre/quiet-pandas-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pglite": patch ---- - -Align PGlite `listen` with the PostgreSQL client. It now returns a scoped dequeue after the listener is installed, providing an explicit readiness boundary and preserving notifications received before the first take. diff --git a/repos/effect/.changeset/pre/quiet-pandas-rebuild.md b/repos/effect/.changeset/pre/quiet-pandas-rebuild.md deleted file mode 100644 index 879a8c4425..0000000000 --- a/repos/effect/.changeset/pre/quiet-pandas-rebuild.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix failed `ResourceRef` rebuilds permanently blocking waiters. diff --git a/repos/effect/.changeset/pre/quiet-pandas-respond.md b/repos/effect/.changeset/pre/quiet-pandas-respond.md deleted file mode 100644 index b52a3f1c36..0000000000 --- a/repos/effect/.changeset/pre/quiet-pandas-respond.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make multipart errors respond with an HTTP status based on their reason and ignore them in the error reporter. diff --git a/repos/effect/.changeset/pre/quiet-radios-wave.md b/repos/effect/.changeset/pre/quiet-radios-wave.md deleted file mode 100644 index fdbce5a888..0000000000 --- a/repos/effect/.changeset/pre/quiet-radios-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Guard transaction connection acquisition in `PgClient.fromPool` so acquire failures stay in the `SqlError` channel. diff --git a/repos/effect/.changeset/pre/quiet-redis-scripts.md b/repos/effect/.changeset/pre/quiet-redis-scripts.md deleted file mode 100644 index c9d068c413..0000000000 --- a/repos/effect/.changeset/pre/quiet-redis-scripts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Retry Redis scripts after `NOSCRIPT` and declare the token bucket refill key diff --git a/repos/effect/.changeset/pre/quiet-rpcs-buffer.md b/repos/effect/.changeset/pre/quiet-rpcs-buffer.md deleted file mode 100644 index 852a1f2025..0000000000 --- a/repos/effect/.changeset/pre/quiet-rpcs-buffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Bound framed RPC server HTTP response streams to 16 items by default, with a configurable buffer size or an unbounded opt-out. diff --git a/repos/effect/.changeset/pre/quiet-savepoints-wait.md b/repos/effect/.changeset/pre/quiet-savepoints-wait.md deleted file mode 100644 index e27e942f13..0000000000 --- a/repos/effect/.changeset/pre/quiet-savepoints-wait.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Serialize concurrent nested SQL transactions to prevent savepoint collisions. Cross-dependent sibling nested -transactions now deadlock instead of interleaving and risking silent data corruption. diff --git a/repos/effect/.changeset/pre/quiet-sockets-close.md b/repos/effect/.changeset/pre/quiet-sockets-close.md deleted file mode 100644 index 6d2d9ab73b..0000000000 --- a/repos/effect/.changeset/pre/quiet-sockets-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Close pending TCP and WebSocket connections when a scoped socket server shuts down diff --git a/repos/effect/.changeset/pre/quiet-spans-rest.md b/repos/effect/.changeset/pre/quiet-spans-rest.md deleted file mode 100644 index 9e574309a3..0000000000 --- a/repos/effect/.changeset/pre/quiet-spans-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Skip HTTP server span attribute collection when the span is not sampled. diff --git a/repos/effect/.changeset/pre/quiet-streams-take.md b/repos/effect/.changeset/pre/quiet-streams-take.md deleted file mode 100644 index c05bac54de..0000000000 --- a/repos/effect/.changeset/pre/quiet-streams-take.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Treat `NaN` as a non-positive count in `Stream.take`. diff --git a/repos/effect/.changeset/pre/quiet-tigers-yell.md b/repos/effect/.changeset/pre/quiet-tigers-yell.md deleted file mode 100644 index 3c567ce806..0000000000 --- a/repos/effect/.changeset/pre/quiet-tigers-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Update `NodeHttpServer.layerConfig`'s type to report the same provided Node services as `NodeHttpServer.layer`. diff --git a/repos/effect/.changeset/pre/quiet-tools-smile.md b/repos/effect/.changeset/pre/quiet-tools-smile.md deleted file mode 100644 index d14443e6e0..0000000000 --- a/repos/effect/.changeset/pre/quiet-tools-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent MCP tool failures from exposing Cause rendering, stack traces, and internal paths while preserving actionable validation messages. diff --git a/repos/effect/.changeset/pre/quiet-turtles-smile.md b/repos/effect/.changeset/pre/quiet-turtles-smile.md deleted file mode 100644 index ef13d5bf91..0000000000 --- a/repos/effect/.changeset/pre/quiet-turtles-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Move `UrlParams.makeUrl` to `Url.make` and return `Url.UrlError` for URL construction failures. diff --git a/repos/effect/.changeset/pre/random-choice.md b/repos/effect/.changeset/pre/random-choice.md deleted file mode 100644 index b0bdd94dcd..0000000000 --- a/repos/effect/.changeset/pre/random-choice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add Random.choice for selecting a random element from an iterable. diff --git a/repos/effect/.changeset/pre/random-exclusive-upper-endpoint.md b/repos/effect/.changeset/pre/random-exclusive-upper-endpoint.md deleted file mode 100644 index ee55cc507e..0000000000 --- a/repos/effect/.changeset/pre/random-exclusive-upper-endpoint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Random.nextBetween` and `Crypto.randomBetween` returning their exclusive upper bound when floating-point arithmetic rounds up. diff --git a/repos/effect/.changeset/pre/random-hex-encoding.md b/repos/effect/.changeset/pre/random-hex-encoding.md deleted file mode 100644 index 9d894f62d5..0000000000 --- a/repos/effect/.changeset/pre/random-hex-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Encoding.randomHex`, a lightweight non-cryptographic generator that coerces lengths to unsigned 32-bit multiples of 8. diff --git a/repos/effect/.changeset/pre/rate-limiter-reset-lifetime.md b/repos/effect/.changeset/pre/rate-limiter-reset-lifetime.md deleted file mode 100644 index 03547308d0..0000000000 --- a/repos/effect/.changeset/pre/rate-limiter-reset-lifetime.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Report the exact remaining store lifetime in `RateLimiter` fixed-window `resetAfter` metadata when `onExceeded` is `"delay"`, instead of rounding up to a whole window. Admission, returned delays, and remaining-token counts are unchanged. diff --git a/repos/effect/.changeset/pre/rcmap-invalidation-release.md b/repos/effect/.changeset/pre/rcmap-invalidation-release.md deleted file mode 100644 index c89b315aea..0000000000 --- a/repos/effect/.changeset/pre/rcmap-invalidation-release.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `RcMap` and `LayerMap` cleanup after invalidating an actively borrowed entry and reacquiring the same key. -The invalidated resource is released when its last borrower closes, even with infinite idle TTL, without removing -the replacement entry. Old idle timers also leave replacement entries untouched. diff --git a/repos/effect/.changeset/pre/rcmap-throwing-lookup.md b/repos/effect/.changeset/pre/rcmap-throwing-lookup.md deleted file mode 100644 index 2e8027ee59..0000000000 --- a/repos/effect/.changeset/pre/rcmap-throwing-lookup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RcMap` entries getting stuck when the lookup function throws synchronously. Later borrowers now receive the defect, and unused entries are released according to their idle TTL instead of permanently consuming capacity. diff --git a/repos/effect/.changeset/pre/rcref-acquisition-shutdown.md b/repos/effect/.changeset/pre/rcref-acquisition-shutdown.md deleted file mode 100644 index 1ee8dedc28..0000000000 --- a/repos/effect/.changeset/pre/rcref-acquisition-shutdown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep `RcRef` closed when an in-flight acquisition finishes after its owning scope has closed. Release the late-acquired resource and interrupt waiting borrowers instead of making the resource available again. diff --git a/repos/effect/.changeset/pre/rcref-release-ownership.md b/repos/effect/.changeset/pre/rcref-release-ownership.md deleted file mode 100644 index 53bd9947b2..0000000000 --- a/repos/effect/.changeset/pre/rcref-release-ownership.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent `RcRef` borrower cleanup from discarding replacement resources after invalidation or reopening a reference after its owner scope has closed. diff --git a/repos/effect/.changeset/pre/read-only-bun-sqlite.md b/repos/effect/.changeset/pre/read-only-bun-sqlite.md deleted file mode 100644 index 9037b67ad0..0000000000 --- a/repos/effect/.changeset/pre/read-only-bun-sqlite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-bun": patch ---- - -Enforce read-only mode when opening Bun SQLite databases. diff --git a/repos/effect/.changeset/pre/ready-olives-divide.md b/repos/effect/.changeset/pre/ready-olives-divide.md deleted file mode 100644 index 2502b4d1a3..0000000000 --- a/repos/effect/.changeset/pre/ready-olives-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -handle openai ws error events diff --git a/repos/effect/.changeset/pre/real-trains-ring.md b/repos/effect/.changeset/pre/real-trains-ring.md deleted file mode 100644 index 5cd40a0a3a..0000000000 --- a/repos/effect/.changeset/pre/real-trains-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow `HttpApiTest.groups` to accept an optional `baseUrl` override while preserving the existing default of `"http://localhost:3000"`. diff --git a/repos/effect/.changeset/pre/reason-annotate-context-only.md b/repos/effect/.changeset/pre/reason-annotate-context-only.md deleted file mode 100644 index 12b1b2c391..0000000000 --- a/repos/effect/.changeset/pre/reason-annotate-context-only.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Type `Cause.Reason#annotate` as accepting a `Context` only. diff --git a/repos/effect/.changeset/pre/record-from-iterable-by-dual.md b/repos/effect/.changeset/pre/record-from-iterable-by-dual.md deleted file mode 100644 index 13fc30740d..0000000000 --- a/repos/effect/.changeset/pre/record-from-iterable-by-dual.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect": patch ---- - -Record: make `fromIterableBy` dual, allowing data-last usage in `pipe` - -```ts -import { pipe, Record } from "effect" - -const users = [ - { id: "2", name: "name2" }, - { id: "1", name: "name1" } -] - -pipe(users, Record.fromIterableBy((user) => user.id)) -``` diff --git a/repos/effect/.changeset/pre/red-pigs-repair.md b/repos/effect/.changeset/pre/red-pigs-repair.md deleted file mode 100644 index 005ae5f888..0000000000 --- a/repos/effect/.changeset/pre/red-pigs-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.annotateLogsScoped` to apply log annotations for the current scope and automatically restore previous annotations when the scope closes. diff --git a/repos/effect/.changeset/pre/redact-cli-wizard-values.md b/repos/effect/.changeset/pre/redact-cli-wizard-values.md deleted file mode 100644 index b5be1435f3..0000000000 --- a/repos/effect/.changeset/pre/redact-cli-wizard-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Redact password prompt values from CLI wizard command output. diff --git a/repos/effect/.changeset/pre/redacted-representation-options.md b/repos/effect/.changeset/pre/redacted-representation-options.md deleted file mode 100644 index da25298806..0000000000 --- a/repos/effect/.changeset/pre/redacted-representation-options.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Preserve `Schema.Redacted` options when roundtripping through schema representations. -This keeps `label` validation and `disallowJsonEncode` behavior intact when -schemas are revived from a representation or emitted through code generation. diff --git a/repos/effect/.changeset/pre/redis-subscribe-api.md b/repos/effect/.changeset/pre/redis-subscribe-api.md deleted file mode 100644 index b6203f7e4a..0000000000 --- a/repos/effect/.changeset/pre/redis-subscribe-api.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"effect": patch -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-node": patch ---- - -Add scoped Redis pub/sub subscriptions that expose received messages through an Effect queue. diff --git a/repos/effect/.changeset/pre/refactor-cli-global-flags.md b/repos/effect/.changeset/pre/refactor-cli-global-flags.md deleted file mode 100644 index ccfecb9289..0000000000 --- a/repos/effect/.changeset/pre/refactor-cli-global-flags.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -"effect": patch ---- - -Refactor CLI built-in options to use Effect services with `GlobalFlag` - -Built-in CLI flags (`--help`, `--version`, `--completions`, `--log-level`) are now implemented as Effect services using `Context.Reference`. This provides: - -- **Visibility**: Built-in flags now appear in help output's "GLOBAL FLAGS" section -- **Extensibility**: Users can register custom global flags via `GlobalFlag.add` -- **Override capability**: Built-in flag behavior can be replaced or disabled -- **Composability**: Flags compose via Effect's service system - -New `GlobalFlag` module exports: - -- `Action` and `Setting` types for different flag behaviors -- `Help`, `Version`, `Completions`, `LogLevel` references for built-in flags -- `add`, `remove`, `clear` functions for managing global flags - -Example: - -```typescript -const app = Command.make("myapp") -Command.run(app, { version: "1.0.0" }).pipe( - GlobalFlag.add(CustomFlag, customFlagValue) -) -``` diff --git a/repos/effect/.changeset/pre/refactor-config-provider.md b/repos/effect/.changeset/pre/refactor-config-provider.md deleted file mode 100644 index 5e9a549c4b..0000000000 --- a/repos/effect/.changeset/pre/refactor-config-provider.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"effect": patch ---- - -Fix config path composition and directory-backed lookup behavior. - -`ConfigProvider.orElse` now keeps each side's own `nested` and `mapInput` -behavior. Applying `nested` or `mapInput` to a combined provider now applies the -same transformation to both sides. - -`ConfigProvider` path transformations now compose as a single path function. -This makes `nested` and `mapInput` behave consistently with normal function -composition. - -`Config.nested` now tracks the logical config path in `Config` itself instead of -wrapping the provider. This keeps lookup paths and schema error paths aligned. -The low-level `Config.make` constructor is no longer exported; use config -constructors and combinators, or implement custom lookup behavior with -`ConfigProvider.make`. - -`ConfigProvider.fromDir` now returns `undefined` when neither a file nor a -directory exists at the requested path, so `orElse` can fall back instead of -failing with `SourceError`. diff --git a/repos/effect/.changeset/pre/refactor-representation-references.md b/repos/effect/.changeset/pre/refactor-representation-references.md deleted file mode 100644 index e72607fe0d..0000000000 --- a/repos/effect/.changeset/pre/refactor-representation-references.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -SchemaRepresentation: only create references for recursive/mutually recursive schemas and schemas with an `identifier` annotation, closes #1560. diff --git a/repos/effect/.changeset/pre/refine-config-absence.md b/repos/effect/.changeset/pre/refine-config-absence.md deleted file mode 100644 index bd2a1c58c7..0000000000 --- a/repos/effect/.changeset/pre/refine-config-absence.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Refine `Config` loading and absence semantics. `Config.schema` now derives a provider loading policy from the encoded `StringTree` schema, materializes mixed-shape union members independently, and leaves separated scalar parsing to `Config.Array` and `Config.Record`. Schemas whose canonical `StringTree` encoding remains opaque, such as `Schema.Any`, `Schema.Unknown`, or `Schema.Json`, are rejected when the config is constructed; use a concrete shape or `Schema.fromJsonString(Schema.Json)` for scalar JSON. Missing or unavailable representations are decoded as `undefined` before `Config.withDefault` and `Config.option` decide absence. Partially supplied `Config.all` groups are rejected, successful values such as `undefined` and explicitly present empty structures are preserved, and the internal path prefix is removed from the public `Config.parse` signature. diff --git a/repos/effect/.changeset/pre/refresh-mcp-roots.md b/repos/effect/.changeset/pre/refresh-mcp-roots.md deleted file mode 100644 index 501d714fd8..0000000000 --- a/repos/effect/.changeset/pre/refresh-mcp-roots.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now refresh roots after capable clients report that their root list changed. diff --git a/repos/effect/.changeset/pre/register-param-alternate-flags.md b/repos/effect/.changeset/pre/register-param-alternate-flags.md deleted file mode 100644 index 511689a84b..0000000000 --- a/repos/effect/.changeset/pre/register-param-alternate-flags.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Register alternate flags used by `Param.orElse` and `Param.orElseResult`. diff --git a/repos/effect/.changeset/pre/release-rpc-worker-pool-entries.md b/repos/effect/.changeset/pre/release-rpc-worker-pool-entries.md deleted file mode 100644 index fe2aa1ba1b..0000000000 --- a/repos/effect/.changeset/pre/release-rpc-worker-pool-entries.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Release worker pool entries when an RPC worker's receive loop fails. diff --git a/repos/effect/.changeset/pre/remove-context-mutate.md b/repos/effect/.changeset/pre/remove-context-mutate.md deleted file mode 100644 index 55d8b1a8cd..0000000000 --- a/repos/effect/.changeset/pre/remove-context-mutate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Context.mutate` and `Context.getReferenceUnsafe`. Context updates now use overlays, and `Context.get` resolves reference defaults. diff --git a/repos/effect/.changeset/pre/remove-effect-transactionwith.md b/repos/effect/.changeset/pre/remove-effect-transactionwith.md deleted file mode 100644 index 5f5c63e2db..0000000000 --- a/repos/effect/.changeset/pre/remove-effect-transactionwith.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `Effect.transaction` to `Effect.tx` and `Effect.retryTransaction` to `Effect.txRetry`, remove `Effect.transactionWith` / `Effect.withTxState`, make nested `Effect.tx` calls compose into the active transaction, and make the public `Tx*` APIs establish atomic transactions without requiring `Transaction` in common usage. diff --git a/repos/effect/.changeset/pre/remove-http-span-counter.md b/repos/effect/.changeset/pre/remove-http-span-counter.md deleted file mode 100644 index e0458e7421..0000000000 --- a/repos/effect/.changeset/pre/remove-http-span-counter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the auto-incrementing suffix from HTTP server logger log span names. diff --git a/repos/effect/.changeset/pre/remove-kubernetes-types.md b/repos/effect/.changeset/pre/remove-kubernetes-types.md deleted file mode 100644 index 395085e4f1..0000000000 --- a/repos/effect/.changeset/pre/remove-kubernetes-types.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the `kubernetes-types` dependency by vendoring the Kubernetes Pod declarations used by the cluster helpers and exporting them from `effect/unstable/cluster/K8sTypes`. diff --git a/repos/effect/.changeset/pre/remove-msgpack.md b/repos/effect/.changeset/pre/remove-msgpack.md deleted file mode 100644 index d7f457806f..0000000000 --- a/repos/effect/.changeset/pre/remove-msgpack.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-node": patch -"effect": patch ---- - -Remove the MessagePack encoding and RPC serialization APIs together with the `msgpackr` dependency. Event-log persistence and remote messages now use SchemaBinary, and cluster transports use SchemaBinary unless NDJSON is selected explicitly. diff --git a/repos/effect/.changeset/pre/remove-nullor.md b/repos/effect/.changeset/pre/remove-nullor.md deleted file mode 100644 index 28617d3a3e..0000000000 --- a/repos/effect/.changeset/pre/remove-nullor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove unused `effect/NullOr` module. diff --git a/repos/effect/.changeset/pre/remove-openapi-fromapi-options.md b/repos/effect/.changeset/pre/remove-openapi-fromapi-options.md deleted file mode 100644 index 97be4cd9a6..0000000000 --- a/repos/effect/.changeset/pre/remove-openapi-fromapi-options.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Remove the `options` parameter from `OpenApi.fromApi`. - -The parameter only carried `additionalProperties`, but the function caches results in a `WeakMap` keyed solely on the `api` instance. Passing different options across calls for the same api was silently ignored, making the parameter order-dependent and effectively single-shot. No call sites were using it, so the signature is now simply `fromApi(api)`. diff --git a/repos/effect/.changeset/pre/remove-schedule-apis.md b/repos/effect/.changeset/pre/remove-schedule-apis.md deleted file mode 100644 index 79d641e92f..0000000000 --- a/repos/effect/.changeset/pre/remove-schedule-apis.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove some Schedule APIs: `collectInputs`, `collectOutputs`, `collectWhile`, `delays`, `reduce`, `satisfiesErrorType`, `satisfiesInputType`, `satisfiesOutputType`, `satisfiesServicesType`, and `unfold`. diff --git a/repos/effect/.changeset/pre/remove-schedule-either.md b/repos/effect/.changeset/pre/remove-schedule-either.md deleted file mode 100644 index ee0be86341..0000000000 --- a/repos/effect/.changeset/pre/remove-schedule-either.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the Schedule.either APIs and add Schedule.min for fastest-duration schedule composition. diff --git a/repos/effect/.changeset/pre/remove-schedule-elapsed.md b/repos/effect/.changeset/pre/remove-schedule-elapsed.md deleted file mode 100644 index 1863f14962..0000000000 --- a/repos/effect/.changeset/pre/remove-schedule-elapsed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Schedule.elapsed`. diff --git a/repos/effect/.changeset/pre/remove-schedule-taps.md b/repos/effect/.changeset/pre/remove-schedule-taps.md deleted file mode 100644 index 18572cb536..0000000000 --- a/repos/effect/.changeset/pre/remove-schedule-taps.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Schedule.tapInput` and `Schedule.tapOutput`. Use `Schedule.tap` instead. diff --git a/repos/effect/.changeset/pre/remove-schema-key-value-combiner.md b/repos/effect/.changeset/pre/remove-schema-key-value-combiner.md deleted file mode 100644 index 05add770fe..0000000000 --- a/repos/effect/.changeset/pre/remove-schema-key-value-combiner.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Remove the `keyValueCombiner` option from `Schema.Record` and the corresponding -`SchemaAST.KeyValueCombiner` and `SchemaAST.IndexSignature.merge` APIs. -For transformed key collisions, sequential parsing keeps the later selected -value, while concurrent parsing keeps the value applied last in completion -order. diff --git a/repos/effect/.changeset/pre/remove-schema-stringtree-keep-declarations.md b/repos/effect/.changeset/pre/remove-schema-stringtree-keep-declarations.md deleted file mode 100644 index f09c22a1bb..0000000000 --- a/repos/effect/.changeset/pre/remove-schema-stringtree-keep-declarations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the `keepDeclarations` option from `Schema.toCodecStringTree`. diff --git a/repos/effect/.changeset/pre/remove-schema-utils.md b/repos/effect/.changeset/pre/remove-schema-utils.md deleted file mode 100644 index cb605c7008..0000000000 --- a/repos/effect/.changeset/pre/remove-schema-utils.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the experimental `SchemaUtils` module and its `getNativeClassSchema` helper. The helper duplicated a composition already available through the primary Schema APIs and did not justify a separate public module. diff --git a/repos/effect/.changeset/pre/remove-types-mergerecord.md b/repos/effect/.changeset/pre/remove-types-mergerecord.md deleted file mode 100644 index 6533e1bfeb..0000000000 --- a/repos/effect/.changeset/pre/remove-types-mergerecord.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove the `Types.MergeRecord` alias. Use `Types.MergeLeft` instead. diff --git a/repos/effect/.changeset/pre/remove-unused-utils-apis.md b/repos/effect/.changeset/pre/remove-unused-utils-apis.md deleted file mode 100644 index 986dd0e0cc..0000000000 --- a/repos/effect/.changeset/pre/remove-unused-utils-apis.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove unused APIs from the `Utils` module. diff --git a/repos/effect/.changeset/pre/remove-with-concurrency.md b/repos/effect/.changeset/pre/remove-with-concurrency.md deleted file mode 100644 index d1d820b426..0000000000 --- a/repos/effect/.changeset/pre/remove-with-concurrency.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove `Effect.withConcurrency`, the `References.CurrentConcurrency` reference backing it, and the `"inherit"` option from `Types.Concurrency`. Use an explicit `number` or `"unbounded"` concurrency value instead. diff --git a/repos/effect/.changeset/pre/rename-rebuild-out.md b/repos/effect/.changeset/pre/rename-rebuild-out.md deleted file mode 100644 index 106bcc1e8e..0000000000 --- a/repos/effect/.changeset/pre/rename-rebuild-out.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: rename `"~rebuild.out"` to `"Rebuild"` diff --git a/repos/effect/.changeset/pre/rename-schema-arbitrary-filter-constraint.md b/repos/effect/.changeset/pre/rename-schema-arbitrary-filter-constraint.md deleted file mode 100644 index be77fbf9d7..0000000000 --- a/repos/effect/.changeset/pre/rename-schema-arbitrary-filter-constraint.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Rename `Schema.Annotations.ToArbitrary.Constraint` to `Schema.Annotations.ToArbitrary.FilterConstraint`. - -Code that refers to the previous type name should update its type annotations to use `FilterConstraint`. diff --git a/repos/effect/.changeset/pre/rename-schema-error-constructors.md b/repos/effect/.changeset/pre/rename-schema-error-constructors.md deleted file mode 100644 index f4ebfb8fe3..0000000000 --- a/repos/effect/.changeset/pre/rename-schema-error-constructors.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch ---- - -Rename the Schema error constructors to align with their `Data` counterparts. - -- `Schema.ErrorClass` is now `Schema.Error`. -- `Schema.TaggedErrorClass` is now `Schema.TaggedError`. -- The JavaScript `Error` instance schema is now `Schema.ErrorInstance`. -- `Schema.ErrorReviver` is now `Schema.ErrorInstanceReviver`. diff --git a/repos/effect/.changeset/pre/rename-schema-transform-effect.md b/repos/effect/.changeset/pre/rename-schema-transform-effect.md deleted file mode 100644 index 273dfb7e4c..0000000000 --- a/repos/effect/.changeset/pre/rename-schema-transform-effect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `SchemaGetter.transformOrFail` to `SchemaGetter.transformEffect` and `SchemaTransformation.transformOrFail` to `SchemaTransformation.transformEffect`. Replace calls to the old names with their `transformEffect` equivalents. diff --git a/repos/effect/.changeset/pre/render-cli-user-errors.md b/repos/effect/.changeset/pre/render-cli-user-errors.md deleted file mode 100644 index cb8229f8a8..0000000000 --- a/repos/effect/.changeset/pre/render-cli-user-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add an optional user-facing message to CLI `UserError` values with safe cause-based fallbacks. `Command.run` and `Command.runWith` now render handler `UserError` failures through the installed output formatter; hosts that already print these errors should remove their duplicate output. Set `renderErrors: false` when the host should own error rendering. diff --git a/repos/effect/.changeset/pre/repeat-or-else-metadata-argument.md b/repos/effect/.changeset/pre/repeat-or-else-metadata-argument.md deleted file mode 100644 index 1e0828ffab..0000000000 --- a/repos/effect/.changeset/pre/repeat-or-else-metadata-argument.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Correct the `Effect.repeatOrElse` fallback type to expose the previous step's `Schedule.Metadata`, matching the existing runtime value. diff --git a/repos/effect/.changeset/pre/report-mcp-tool-failures.md b/repos/effect/.changeset/pre/report-mcp-tool-failures.md deleted file mode 100644 index 531baac797..0000000000 --- a/repos/effect/.changeset/pre/report-mcp-tool-failures.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Report recovered MCP toolkit failures and defects to configured `ErrorReporter`s, including declared tool failures returned with `isError: true`. diff --git a/repos/effect/.changeset/pre/report-schema-input.md b/repos/effect/.changeset/pre/report-schema-input.md deleted file mode 100644 index 038f5d9363..0000000000 --- a/repos/effect/.changeset/pre/report-schema-input.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Add the opt-in `reportInput` parse option for retaining rejected inputs in enumerable fields on value-bearing schema issues and including them in default formatted messages. Value-bearing issue constructors accept the rejected input and parse options directly, and `Schema.Annotations.Issue` now supports `expected` for default messages. - -Schema issues no longer format implicitly through `Issue#toString`. Use `SchemaIssue.makeFormatterDefault()` when a human-readable message is needed. The throwing and Promise-based adapters in `SchemaParser` now use the generic message `"Schema validation failed"` and expose the structured `SchemaIssue.Issue` as the error `cause`; consumers that previously read the formatted error message should inspect and explicitly format that cause instead. - -`Schema.makeEffect` now returns `SchemaIssue.Issue` failures instead of wrapping them in `SchemaError`, and `Schema.withConstructorDefault` accepts an `Effect` that fails with `SchemaIssue.Issue`. Fallible `Optic` operations return structured `SchemaIssue.Issue` failures, while schema failures from `Schema.toIso` and `Schema.toDifferJsonPatch` use the generic error message and preserve the issue in `cause` instead of formatting it internally. diff --git a/repos/effect/.changeset/pre/report-transient-rpc-socket-errors.md b/repos/effect/.changeset/pre/report-transient-rpc-socket-errors.md deleted file mode 100644 index 3c81a40508..0000000000 --- a/repos/effect/.changeset/pre/report-transient-rpc-socket-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Report retried RPC socket open failures through the `onTransientError` protocol hook and fail in-flight requests when the retry policy is exhausted. diff --git a/repos/effect/.changeset/pre/request-cache-cancellation.md b/repos/effect/.changeset/pre/request-cache-cancellation.md deleted file mode 100644 index 9feb720d78..0000000000 --- a/repos/effect/.changeset/pre/request-cache-cancellation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RequestResolver.withCache` retaining abandoned entries when a pending request is cancelled diff --git a/repos/effect/.changeset/pre/request-persisted-resolver-failures.md b/repos/effect/.changeset/pre/request-persisted-resolver-failures.md deleted file mode 100644 index 3268c26466..0000000000 --- a/repos/effect/.changeset/pre/request-persisted-resolver-failures.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve completed results and propagate resolver failures from `RequestResolver.persisted`. diff --git a/repos/effect/.changeset/pre/request-race-cache.md b/repos/effect/.changeset/pre/request-race-cache.md deleted file mode 100644 index 2ae0bcc172..0000000000 --- a/repos/effect/.changeset/pre/request-race-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep completed results in `RequestResolver.withCache` when a losing `RequestResolver.race` resolver is interrupted after the winner completes, avoiding repeated backend requests on subsequent equal lookups. diff --git a/repos/effect/.changeset/pre/request-resolver-failure-causes.md b/repos/effect/.changeset/pre/request-resolver-failure-causes.md deleted file mode 100644 index a77da246ad..0000000000 --- a/repos/effect/.changeset/pre/request-resolver-failure-causes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve typed errors, defects, and interrupts from `RequestResolver.fromEffectTagged` handlers. diff --git a/repos/effect/.changeset/pre/request-resolver-iterable-results.md b/repos/effect/.changeset/pre/request-resolver-iterable-results.md deleted file mode 100644 index 59f70109ea..0000000000 --- a/repos/effect/.changeset/pre/request-resolver-iterable-results.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RequestResolver.fromEffectTagged` to consume handler results as an iterable, allowing arrays, iterators, and generators to resolve requests in order. diff --git a/repos/effect/.changeset/pre/required-boolean-flags.md b/repos/effect/.changeset/pre/required-boolean-flags.md deleted file mode 100644 index c49bea5ed2..0000000000 --- a/repos/effect/.changeset/pre/required-boolean-flags.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make unstable CLI boolean flags required when omitted, allowing optional, default, config, and prompt fallbacks to handle absence consistently. diff --git a/repos/effect/.changeset/pre/required-keys-index-signatures.md b/repos/effect/.changeset/pre/required-keys-index-signatures.md deleted file mode 100644 index 61a9a583db..0000000000 --- a/repos/effect/.changeset/pre/required-keys-index-signatures.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Types.RequiredKeys` dropping named required keys on types with index signatures. Derived type annotations may need to include these keys. diff --git a/repos/effect/.changeset/pre/resolved-client-urls.md b/repos/effect/.changeset/pre/resolved-client-urls.md deleted file mode 100644 index b3554d5300..0000000000 --- a/repos/effect/.changeset/pre/resolved-client-urls.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/platform-browser": patch -"@effect/platform-node": patch -"effect": patch ---- - -Add `HttpClientResponse.url`, including query parameters and excluding the hash. When redirects are followed, it reports -the final URL. diff --git a/repos/effect/.changeset/pre/resource-subscriptions.md b/repos/effect/.changeset/pre/resource-subscriptions.md deleted file mode 100644 index c91274c3b8..0000000000 --- a/repos/effect/.changeset/pre/resource-subscriptions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP servers now support session-scoped resource subscriptions on transports that can deliver server notifications and filter resource updates by each client's subscribed URIs. diff --git a/repos/effect/.changeset/pre/restore-effect-head.md b/repos/effect/.changeset/pre/restore-effect-head.md deleted file mode 100644 index 0c2f7d3e8f..0000000000 --- a/repos/effect/.changeset/pre/restore-effect-head.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.head` for retrieving the first element of an iterable produced by an effect. diff --git a/repos/effect/.changeset/pre/restore-schema-parse-options.md b/repos/effect/.changeset/pre/restore-schema-parse-options.md deleted file mode 100644 index 7a244549ab..0000000000 --- a/repos/effect/.changeset/pre/restore-schema-parse-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Restore support for passing schema parse options when creating decode and encode helpers, closes #2174. diff --git a/repos/effect/.changeset/pre/result-map-error-success-identity.md b/repos/effect/.changeset/pre/result-map-error-success-identity.md deleted file mode 100644 index dbcacc774d..0000000000 --- a/repos/effect/.changeset/pre/result-map-error-success-identity.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Preserve untouched `Result` branches by identity in `Result.map` and -`Result.mapError`. diff --git a/repos/effect/.changeset/pre/retry-redis-script-load.md b/repos/effect/.changeset/pre/retry-redis-script-load.md deleted file mode 100644 index c4017e4130..0000000000 --- a/repos/effect/.changeset/pre/retry-redis-script-load.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Redis script evaluation so transient `SCRIPT LOAD` failures are retried instead of being cached indefinitely. diff --git a/repos/effect/.changeset/pre/reuse-httpapi-response-schemas.md b/repos/effect/.changeset/pre/reuse-httpapi-response-schemas.md deleted file mode 100644 index 1c7d4c0878..0000000000 --- a/repos/effect/.changeset/pre/reuse-httpapi-response-schemas.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Reuse HttpApi response schemas. - -`HttpApiBuilder` looked up cached response schemas by their source AST but stored them by the transformed AST, so the cache normally missed. It now uses the source AST consistently. diff --git a/repos/effect/.changeset/pre/rich-dots-push.md b/repos/effect/.changeset/pre/rich-dots-push.md deleted file mode 100644 index 3424b6148d..0000000000 --- a/repos/effect/.changeset/pre/rich-dots-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix `isNullish()` type predicate diff --git a/repos/effect/.changeset/pre/rich-hoops-nail.md b/repos/effect/.changeset/pre/rich-hoops-nail.md deleted file mode 100644 index 540a3d6aae..0000000000 --- a/repos/effect/.changeset/pre/rich-hoops-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -rename DurationInput to Duration.Input diff --git a/repos/effect/.changeset/pre/rich-sloths-draw.md b/repos/effect/.changeset/pre/rich-sloths-draw.md deleted file mode 100644 index 98a0a99448..0000000000 --- a/repos/effect/.changeset/pre/rich-sloths-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure envelope payloads are correctly encoded for notify path diff --git a/repos/effect/.changeset/pre/ripe-lies-battle.md b/repos/effect/.changeset/pre/ripe-lies-battle.md deleted file mode 100644 index 3f6b04677a..0000000000 --- a/repos/effect/.changeset/pre/ripe-lies-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -EventLog Identity string encodes to base 64 diff --git a/repos/effect/.changeset/pre/rpc-client-http-early-close.md b/repos/effect/.changeset/pre/rpc-client-http-early-close.md deleted file mode 100644 index ce2b353d69..0000000000 --- a/repos/effect/.changeset/pre/rpc-client-http-early-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fail RpcClient HTTP requests with a defect when the response stream closes before the request receives a terminal response. diff --git a/repos/effect/.changeset/pre/rpc-falsy-control-ids.md b/repos/effect/.changeset/pre/rpc-falsy-control-ids.md deleted file mode 100644 index 0c2d815d79..0000000000 --- a/repos/effect/.changeset/pre/rpc-falsy-control-ids.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve zero and empty-string request IDs in JSON-RPC control messages. diff --git a/repos/effect/.changeset/pre/rpc-middleware-provides-fix.md b/repos/effect/.changeset/pre/rpc-middleware-provides-fix.md deleted file mode 100644 index 30a26567df..0000000000 --- a/repos/effect/.changeset/pre/rpc-middleware-provides-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Rpc.ExtractProvides` to use middleware service ID instead of constructor type. diff --git a/repos/effect/.changeset/pre/rpc-server-notifications.md b/repos/effect/.changeset/pre/rpc-server-notifications.md deleted file mode 100644 index d4be791ee5..0000000000 --- a/repos/effect/.changeset/pre/rpc-server-notifications.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Add support for server-originated RPC requests and notifications. Buffered -JSON-RPC HTTP drops notifications until streaming responses are available. diff --git a/repos/effect/.changeset/pre/safe-json-schema-patterns.md b/repos/effect/.changeset/pre/safe-json-schema-patterns.md deleted file mode 100644 index ab10a6aa00..0000000000 --- a/repos/effect/.changeset/pre/safe-json-schema-patterns.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Require explicit handling for regular expression pattern constraints translated from JSON Schema documents, with modes to apply trusted patterns or ignore their constraints. diff --git a/repos/effect/.changeset/pre/scalar-custom-fetch.md b/repos/effect/.changeset/pre/scalar-custom-fetch.md deleted file mode 100644 index 6a03fa15e3..0000000000 --- a/repos/effect/.changeset/pre/scalar-custom-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add support for configuring Scalar API reference pages with a custom fetch implementation. diff --git a/repos/effect/.changeset/pre/scheduler-global-scope-timers.md b/repos/effect/.changeset/pre/scheduler-global-scope-timers.md deleted file mode 100644 index af24f0d771..0000000000 --- a/repos/effect/.changeset/pre/scheduler-global-scope-timers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -The default scheduler falls back to a microtask when setting a timer throws. Cloudflare Workers disallow timers in global scope, so an effect that yielded while running at module load failed with "Disallowed operation called within global scope". diff --git a/repos/effect/.changeset/pre/schema-api-cleanup.md b/repos/effect/.changeset/pre/schema-api-cleanup.md deleted file mode 100644 index b7a4c3d727..0000000000 --- a/repos/effect/.changeset/pre/schema-api-cleanup.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Move the built-in schema revivers from `Schema` to `SchemaRepresentation`. -Rename the reviver constructors to `makeReviverDeclaration`, -`makeReviverFilter`, and `makeReviverFilterGroup`. - -Change `Schema.toEncoderXml` to fail with `SchemaIssue.Issue` directly instead -of wrapping failures in `SchemaError`. Consumers that read `error.issue` should -now use the error value itself. diff --git a/repos/effect/.changeset/pre/schema-arbitrary-factory.md b/repos/effect/.changeset/pre/schema-arbitrary-factory.md deleted file mode 100644 index d0802c815b..0000000000 --- a/repos/effect/.changeset/pre/schema-arbitrary-factory.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Consolidate schema arbitrary derivation into `Schema.toArbitrary`, which now returns a `Schema.Arbitrary` factory that accepts the fast-check module. Remove `Schema.toArbitraryLazy` and arbitrary derivation reports. diff --git a/repos/effect/.changeset/pre/schema-array-leaf-aggregation.md b/repos/effect/.changeset/pre/schema-array-leaf-aggregation.md deleted file mode 100644 index daf0561a86..0000000000 --- a/repos/effect/.changeset/pre/schema-array-leaf-aggregation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve array-valued leaves when `SchemaGetter.makeTreeRecord` aggregates duplicate paths. diff --git a/repos/effect/.changeset/pre/schema-as-class.md b/repos/effect/.changeset/pre/schema-as-class.md deleted file mode 100644 index 79572318c6..0000000000 --- a/repos/effect/.changeset/pre/schema-as-class.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `asClass` API to turn any schema into a class with static method support. - -**Example** - -```ts -import { Schema } from "effect" - -class MyString extends Schema.asClass(Schema.String) { - static readonly decodeUnknownSync = Schema.decodeUnknownSync(this) -} - -MyString.decodeUnknownSync("a") // "a" -``` diff --git a/repos/effect/.changeset/pre/schema-asserts-signature.md b/repos/effect/.changeset/pre/schema-asserts-signature.md deleted file mode 100644 index 173f65875f..0000000000 --- a/repos/effect/.changeset/pre/schema-asserts-signature.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Change `Schema.asserts` and `SchemaParser.asserts` to assert a value directly with `asserts(schema, input)` and remove `Schema.Codec.ToAsserts`. diff --git a/repos/effect/.changeset/pre/schema-aware-rpc-serialization.md b/repos/effect/.changeset/pre/schema-aware-rpc-serialization.md deleted file mode 100644 index bef86f0ae2..0000000000 --- a/repos/effect/.changeset/pre/schema-aware-rpc-serialization.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": minor ---- - -Make RPC serialization schema-aware. - -Add `codecFor` to RPC serialization and client/server protocols so RPC and cluster -network payloads use the transport's schema codec. Framing, cluster storage, and -existing built-in wire formats remain unchanged. diff --git a/repos/effect/.changeset/pre/schema-binary-codec.md b/repos/effect/.changeset/pre/schema-binary-codec.md deleted file mode 100644 index b2628cb3ac..0000000000 --- a/repos/effect/.changeset/pre/schema-binary-codec.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `SchemaBinary`, a compact schema-derived codec with streaming, optional fingerprints and dictionaries, and RPC support. diff --git a/repos/effect/.changeset/pre/schema-binary-preserve-bom.md b/repos/effect/.changeset/pre/schema-binary-preserve-bom.md deleted file mode 100644 index 2248164f13..0000000000 --- a/repos/effect/.changeset/pre/schema-binary-preserve-bom.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve leading U+FEFF characters in SchemaBinary string values when decoding. diff --git a/repos/effect/.changeset/pre/schema-clean-up-additionalProperties.md b/repos/effect/.changeset/pre/schema-clean-up-additionalProperties.md deleted file mode 100644 index 06d3a85fe6..0000000000 --- a/repos/effect/.changeset/pre/schema-clean-up-additionalProperties.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Schema: `toJsonSchemaDocument` now emits JSON Schema `false` for unannotated -`Never` index signatures (including `additionalProperties`) instead of `{ not: {} }`. -Annotated `Never` still emits a schema object so metadata like `description` is preserved. diff --git a/repos/effect/.changeset/pre/schema-codec-narrowing.md b/repos/effect/.changeset/pre/schema-codec-narrowing.md deleted file mode 100644 index c47989842e..0000000000 --- a/repos/effect/.changeset/pre/schema-codec-narrowing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `Schema.Decoder` and `Schema.Encoder`, and accept simpler schema types in APIs that only decode, only encode, or only need the basic schema shape, closes #2536 diff --git a/repos/effect/.changeset/pre/schema-date-valid.md b/repos/effect/.changeset/pre/schema-date-valid.md deleted file mode 100644 index a7fbee4e9f..0000000000 --- a/repos/effect/.changeset/pre/schema-date-valid.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Make `Schema.Date` reject invalid dates and remove the redundant `Schema.DateValid`, `Schema.isDateValid`, and `Schema.isDateValidReviver` APIs. - -`Schema.DateFromString` and `Schema.DateFromMillis` now fail decoding when their input would produce an invalid date. - -Remove `Schema.Annotations.ToArbitrary.GenerationConstraint.valid`; `Schema.Date` arbitraries now generate only valid dates by default. diff --git a/repos/effect/.changeset/pre/schema-datetime-utc-from-string.md b/repos/effect/.changeset/pre/schema-datetime-utc-from-string.md deleted file mode 100644 index bd52ade892..0000000000 --- a/repos/effect/.changeset/pre/schema-datetime-utc-from-string.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: extract shared `dateTimeUtcFromString` transformation for `DateTimeUtc` and `DateTimeUtcFromString`. diff --git a/repos/effect/.changeset/pre/schema-decoding-defaults-services.md b/repos/effect/.changeset/pre/schema-decoding-defaults-services.md deleted file mode 100644 index a3bd9dc377..0000000000 --- a/repos/effect/.changeset/pre/schema-decoding-defaults-services.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Allow Schema decoding defaults to require Effect services. - -The `Effect` passed to `Schema.withDecodingDefault`, `Schema.withDecodingDefaultKey`, `Schema.withDecodingDefaultType`, and `Schema.withDecodingDefaultTypeKey` now accepts a context `R` in its third type parameter. The required services are propagated into the resulting schema's `DecodingServices`. `SchemaGetter.withDefault` is widened in the same way. diff --git a/repos/effect/.changeset/pre/schema-defaults-issue-channel.md b/repos/effect/.changeset/pre/schema-defaults-issue-channel.md deleted file mode 100644 index 07a351b9eb..0000000000 --- a/repos/effect/.changeset/pre/schema-defaults-issue-channel.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Allow Schema constructor and decoding defaults to fail with `SchemaError`. - -The `Effect` passed to `Schema.withConstructorDefault`, `Schema.withDecodingDefault`, `Schema.withDecodingDefaultKey`, `Schema.withDecodingDefaultType`, and `Schema.withDecodingDefaultTypeKey` now accepts `SchemaError` in its error channel. When a default fails, the parser unwraps the underlying `SchemaIssue.Issue` and propagates it as a parse failure with the surrounding path attached. This makes it easy to use another schema's `makeEffect` / `decode*` as the default value. diff --git a/repos/effect/.changeset/pre/schema-direct-class-extension.md b/repos/effect/.changeset/pre/schema-direct-class-extension.md deleted file mode 100644 index 43c457b1a0..0000000000 --- a/repos/effect/.changeset/pre/schema-direct-class-extension.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"effect": patch ---- - -Schema: make schemas directly extendable as classes with static method support -and remove `Schema.asClass`. - -`Bottom` and `BottomLazy` now include the class-compatible `new` signature, -while `BottomWithoutNew` and `BottomLazyWithoutNew` expose the schema protocol -without it for schema types that define a specialized construct signature. - -**Example** - -```ts -import { Schema } from "effect" - -class MyString extends Schema.String { - static readonly decodeUnknownSync = Schema.decodeUnknownSync(this) -} - -MyString.decodeUnknownSync("a") // "a" -``` diff --git a/repos/effect/.changeset/pre/schema-dollar-prefix.md b/repos/effect/.changeset/pre/schema-dollar-prefix.md deleted file mode 100644 index 947fc16d68..0000000000 --- a/repos/effect/.changeset/pre/schema-dollar-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: rename `$` suffix to `$` prefix for type-level identifiers that conflict with built-in names (`Array$` → `$Array`, `Record$` → `$Record`, `ReadonlyMap$` → `$ReadonlyMap`, `ReadonlySet$` → `$ReadonlySet`). diff --git a/repos/effect/.changeset/pre/schema-interpreter-backports.md b/repos/effect/.changeset/pre/schema-interpreter-backports.md deleted file mode 100644 index 87da660f41..0000000000 --- a/repos/effect/.changeset/pre/schema-interpreter-backports.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"effect": patch ---- - -Align Schema construction and parsing semantics, simplify parse options, accept inherited declared fields, and move Union settings into a node-local options object. - -### Breaking changes - -- `Class.make`, `Class.makeOption`, and `Class.makeEffect` now return an existing instance unchanged. This avoids duplicate initialization and makes the construction APIs consistent. Use `new MyClass(input)` when a distinct instance is required. - -- `Literal(0)` and `Literal(-0)` continue to accept either signed zero, but decoding and encoding now preserve the input sign. Add an explicit transformation when a canonical sign is required. - -- `parseOptions` annotations no longer affect parsing. Options passed when creating or calling a decoder, encoder, or constructor adapter now apply to the complete operation. Move operation-wide settings from annotations to the relevant parser API. - -- `propertyOrder` has been removed from `ParseOptions` because preserving input order required a separate, rarely used object reconstruction path. Schema parsing no longer guarantees that decoded object keys follow their input order. Remove the option and apply any required presentation or serialization order after parsing. - -- `concurrency` now applies only to product children: tuple elements, array elements, struct fields, record entries, and structs with rest. It follows `Effect.forEach` semantics, defaults to sequential execution, and applies independently at every nested product. Union candidates remain sequential because speculative candidate evaluation can run transformations that are not selected. Existing product parsing can keep the option. Replace code that relied on concurrent Union candidates with explicitly coordinated parser calls. With concurrent Record key transformations, completion order determines the retained value when transformed keys collide. - -- `onExcessProperty: "preserve"` has been removed because it allowed unvalidated values absent from the schema type to cross the parsing boundary. Model additional properties with `Record` or `StructWithRest`; `"ignore"` and `"error"` remain available. - -- Declared `Struct` fields may now be inherited and are copied to own properties in the output. Dynamic `Record` index signatures remain own-only, while finite literal record keys are declared and may be inherited. The `__proto__` field remains own-only. Check ownership before parsing when every declared field must be own. - -- `SchemaAST.Union.mode` moved to `SchemaAST.Union.options?.mode` so node-local constructor settings live in one options object instead of special top-level fields. An absent value defaults to `"anyOf"`. `SchemaRepresentation.Union` now serializes `{ options: { mode: "oneOf" } }`; update direct AST access and regenerate or migrate persisted representation documents. The public `Schema.Union(members, { mode })` call is unchanged. diff --git a/repos/effect/.changeset/pre/schema-json-schema-options.md b/repos/effect/.changeset/pre/schema-json-schema-options.md deleted file mode 100644 index e358933bc2..0000000000 --- a/repos/effect/.changeset/pre/schema-json-schema-options.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch ---- - -JSON Schema generation now follows the canonical JSON codec more closely and leaves unmodeled object properties open by default, matching Effect decoding. - -### Breaking changes - -`Schema.ToJsonSchemaOptions.additionalProperties` has been replaced by `onExcessProperty`: - -- Replace `{ additionalProperties: true }` with `{ onExcessProperty: "ignore" }`. -- Replace `{ additionalProperties: false }` with `{ onExcessProperty: "error" }`. -- Replace a schema-valued `additionalProperties` option with `Schema.Record` or `Schema.StructWithRest`. - -`Schema.Enum` now rejects non-finite numeric members. `Schema.isMultipleOf` now rejects zero and non-finite divisors, and normalizes negative divisors. - -Generation is more accurate for index signatures, empty structs, template literal alternatives, capitalized strings, and unique symbols. Conjunctive index keys remain open by default; `onExcessProperty: "error"` constrains them with `propertyNames`. diff --git a/repos/effect/.changeset/pre/schema-lazy-bottom.md b/repos/effect/.changeset/pre/schema-lazy-bottom.md deleted file mode 100644 index 81ece19645..0000000000 --- a/repos/effect/.changeset/pre/schema-lazy-bottom.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect": patch ---- - -Improve Schema type-level performance by lazily computing schema views, -specializing common struct projections, and using lighter schema constraints at -API boundaries that do not need the full schema protocol. - -This also adds the Schema type-performance benchmark suite, introduces -`Schema.toCodecArrayFromSingle`, preserves canonical StringTree array codecs, -renames the arbitrary-generation annotation constraint for clarity, and updates -affected codec, parser, channel, SQL, HTTP API, persistence, RPC, AI, OpenAPI, -and workflow typings to match the refined Schema surface. diff --git a/repos/effect/.changeset/pre/schema-missing-self-generic.md b/repos/effect/.changeset/pre/schema-missing-self-generic.md deleted file mode 100644 index 3e877851a5..0000000000 --- a/repos/effect/.changeset/pre/schema-missing-self-generic.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `MissingSelfGeneric` compile-time error for `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass` when the `Self` type parameter is omitted. diff --git a/repos/effect/.changeset/pre/schema-ordered-arbitrary-constraints.md b/repos/effect/.changeset/pre/schema-ordered-arbitrary-constraints.md deleted file mode 100644 index 2c9617db7c..0000000000 --- a/repos/effect/.changeset/pre/schema-ordered-arbitrary-constraints.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch ---- - -Use generic ordered constraints for schema arbitrary derivation. - -Range checks such as `isGreaterThan`, `isLessThan`, and `isBetween` now populate `ctx.constraints.ordered` -instead of type-specific range fields on `number`, `date`, or `bigint` constraints. Custom `toArbitrary` -annotations that read range constraints should migrate to `ctx.constraints.ordered`. - -This also fixes BigDecimal arbitrary generation by adapting decimal bounds to the generated scale, avoiding -invalid fast-check bigint ranges for narrow decimal intervals. diff --git a/repos/effect/.changeset/pre/schema-parser-adapter-errors.md b/repos/effect/.changeset/pre/schema-parser-adapter-errors.md deleted file mode 100644 index 17aeb89892..0000000000 --- a/repos/effect/.changeset/pre/schema-parser-adapter-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Align Schema adapter failures: `Schema` result, promise, and sync adapters now surface `SchemaError`, while `SchemaParser` result, promise, and sync adapters expose `SchemaIssue.Issue`. Mark `SchemaParser` option adapters as internal because their error details are discarded. diff --git a/repos/effect/.changeset/pre/schema-refactor-toCodecJson.md b/repos/effect/.changeset/pre/schema-refactor-toCodecJson.md deleted file mode 100644 index 3bd03e7008..0000000000 --- a/repos/effect/.changeset/pre/schema-refactor-toCodecJson.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: improve `Schema.Unknown` / `Schema.ObjectKeyword` handling in `toCodecJson` and `toCodecStringTree` diff --git a/repos/effect/.changeset/pre/schema-remove-annotate-in.md b/repos/effect/.changeset/pre/schema-remove-annotate-in.md deleted file mode 100644 index 93dd5bb124..0000000000 --- a/repos/effect/.changeset/pre/schema-remove-annotate-in.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: remove `"~annotate.in"` type from `Bottom` interface, inlining it where needed diff --git a/repos/effect/.changeset/pre/schema-rename-makeUnsafe-to-make.md b/repos/effect/.changeset/pre/schema-rename-makeUnsafe-to-make.md deleted file mode 100644 index f2fa96b810..0000000000 --- a/repos/effect/.changeset/pre/schema-rename-makeUnsafe-to-make.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Rename `Schema.makeUnsafe` instance method back to `Schema.make` on all schemas and schema-backed classes. - -Also remove the `static readonly make` override from `ShardId` to avoid conflicting with the inherited schema `make` method. The module-level `ShardId.make(group, id)` function is still available. diff --git a/repos/effect/.changeset/pre/schema-rename-parser-makeUnsafe.md b/repos/effect/.changeset/pre/schema-rename-parser-makeUnsafe.md deleted file mode 100644 index 7573aa817e..0000000000 --- a/repos/effect/.changeset/pre/schema-rename-parser-makeUnsafe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `SchemaParser.makeUnsafe` to `SchemaParser.make`. diff --git a/repos/effect/.changeset/pre/schema-representation-refactoring.md b/repos/effect/.changeset/pre/schema-representation-refactoring.md deleted file mode 100644 index fddaef586a..0000000000 --- a/repos/effect/.changeset/pre/schema-representation-refactoring.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -"effect": patch ---- - -Refactor the `SchemaRepresentation` module to improve clarity and maintainability. - -The representation pipeline is now open and compiler-extensible. The same encoded-side representation is used for JSON persistence, runtime reconstruction, JSON Schema Draft 2020-12 compilation, TypeScript code generation, AI structured output, and HTTP / OpenAPI schemas. - -### New representation model - -- Add `RepresentationAnnotation` and `CheckRepresentationAnnotation`, which identify declarations and checks with a stable `id`, JSON `payload`, and optional schema dependencies. -- Preserve checks on every non-reference representation node instead of storing constraints in the previous closed `meta` unions. -- Add compiler hooks for checks and declarations through `SchemaRepresentation.ToJsonSchema` and `SchemaRepresentation.Generation`. -- Add `SchemaMultiDocument`, `fromSchemaMultiDocument`, and `fromRepresentations` so several live schemas and named definitions can be converted and reconstructed together. Explicit definitions are preserved even when no root references them. -- Preserve shared structural nodes, annotated recursion, union member order, identifiers, reference siblings, and structural checks when projecting encoded schemas. - -### Persistence and revivers - -- Add `toJson`, `fromJson`, `toJsonMultiDocument`, and `fromJsonMultiDocument` as the persistence boundary for representation documents. -- Live representations store literal, enum, and property-name scalars as native values. JSON persistence encodes them as `{ type, value }` tagged unions so their runtime types remain distinct across persistence formats, canonically encodes structural bigint and global symbol values, keeps JSON-valued annotations, and removes runtime-only callbacks and other non-JSON annotation values. -- Replace the generic reviver callback with typed `DeclarationReviver`, `FilterReviver`, and `FilterGroupReviver` contracts. Add `makeDeclarationReviver`, `makeFilterReviver`, and `makeFilterGroupReviver`, which infer their payload type from `payloadSchema`. -- Resolve acyclic references to concrete runtime schemas and reserve `Schema.suspend` wrappers for recursive back-edges. Acyclic alias chains may be normalized while preserving the outer reference identifier. -- Export individual revivers for built-in declarations and checks from `Schema`. Consumers opt in to exactly the revivers accepted when reconstructing persisted documents: - - declaration revivers: `OptionReviver`, `ResultReviver`, `RedactedReviver`, `CauseReasonReviver`, `CauseReviver`, `ErrorReviver`, `ExitReviver`, `ReadonlyMapReviver`, `HashMapReviver`, `ReadonlySetReviver`, `HashSetReviver`, `ChunkReviver`, `RegExpReviver`, `URLReviver`, `DateReviver`, `DurationReviver`, `BigDecimalReviver`, `FileReviver`, `FormDataReviver`, `URLSearchParamsReviver`, `Uint8ArrayReviver`, `DateTimeUtcReviver`, `TimeZoneOffsetReviver`, `TimeZoneNamedReviver`, `TimeZoneReviver`, `DateTimeZonedReviver`, `JsonReviver`, and `MutableJsonReviver` - - check revivers: `isTrimmedReviver`, `isPatternReviver`, `isStringFiniteReviver`, `isStringBigIntReviver`, `isStringSymbolReviver`, `isUUIDReviver`, `isGUIDReviver`, `isULIDReviver`, `isBase64Reviver`, `isBase64UrlReviver`, `isStartsWithReviver`, `isEndsWithReviver`, `isIncludesReviver`, `isUppercasedReviver`, `isLowercasedReviver`, `isCapitalizedReviver`, `isUncapitalizedReviver`, `isFiniteReviver`, `isGreaterThanReviver`, `isGreaterThanOrEqualToReviver`, `isLessThanReviver`, `isLessThanOrEqualToReviver`, `isBetweenReviver`, `isMultipleOfReviver`, `isIntReviver`, `isDateValidReviver`, `isGreaterThanDateReviver`, `isGreaterThanOrEqualToDateReviver`, `isLessThanDateReviver`, `isLessThanOrEqualToDateReviver`, `isBetweenDateReviver`, `isGreaterThanBigIntReviver`, `isGreaterThanOrEqualToBigIntReviver`, `isLessThanBigIntReviver`, `isLessThanOrEqualToBigIntReviver`, `isBetweenBigIntReviver`, `isMinLengthReviver`, `isMaxLengthReviver`, `isLengthBetweenReviver`, `isMinSizeReviver`, `isMaxSizeReviver`, `isSizeBetweenReviver`, `isMinPropertiesReviver`, `isMaxPropertiesReviver`, `isPropertiesLengthBetweenReviver`, `isPropertyNamesReviver`, and `isUniqueReviver` -- Validate reviver payloads with their `payloadSchema`, and report missing or duplicate reviver identifiers. - -### JSON Schema and code generation - -- Compile JSON Schema from the canonical JSON codec and the encoded-side representation. Custom checks can contribute constraints through `Annotations.Filter.toJsonSchema` without modifying a central metadata registry. -- Import JSON Schema directly as live schemas. The importer now supports shared definitions, aliases, recursion, reference siblings, and definitions that are not reachable from a root. -- Add the named `FromJsonSchemaOptions` type for the importer `onEnter` callback. -- Generate code from live `toCode` annotations on declarations and checks. Compiler callbacks receive generated type parameters or schema dependencies and can emit multiple import declarations. -- Add import artifacts to `CodeDocument` and preserve all explicit definitions during multi-document code generation. -- Reject distinct schemas that declare the same identifier instead of silently merging them or generating suffixed references. - -### Canonical codecs and integrations - -- Preserve schema identifiers, property context, key encodings, and applicable checks while deriving canonical JSON codecs. -- Treat `Schema.Json` and `Schema.MutableJson` as already canonical. JSON validation now rejects sparse arrays, and non-finite numbers decode only from the canonical strings `"Infinity"`, `"-Infinity"`, and `"NaN"` rather than raw non-finite numeric inputs. -- Declarations without `toCodecJson` or `toCodec` now use JSON validation as their fallback instead of silently encoding to `null`. `toCodecJson` callbacks may return `undefined` when a declaration is already canonical. -- Add `Annotations.Declaration.toCodecStringTree`; StringTree derivation now requires a declaration to provide a structural StringTree, JSON, or general codec instead of silently encoding an opaque declaration to `undefined`. -- Update AI structured-output, HTTP schema, HttpApi OpenAPI, and OpenAPI generator integrations to consume the same canonical encoded representation and compiler hooks. Provider-specific structured-output transforms may remove unsupported JSON Schema keywords, while the Effect codec remains the validation authority. - -### Breaking changes - -- Rename the low-level representation constructors: - - `SchemaRepresentation.fromAST` -> `SchemaRepresentation.toRepresentation` - - `SchemaRepresentation.fromASTs` -> `SchemaRepresentation.toRepresentations` -- Replace `SchemaRepresentation.toSchema` with `fromRepresentation`, and add `fromRepresentations` for multi-root documents. Both reconstruction functions require `{ revivers: [...] }`; no default reviver is installed implicitly. -- Remove `SchemaRepresentation.toSchemaDefaultReviver`. Pass the required built-in revivers exported by `Schema`, or custom revivers created with the new constructors. -- Replace `DocumentFromJson` and `MultiDocumentFromJson` with the `toJson` / `fromJson` and `toJsonMultiDocument` / `fromJsonMultiDocument` functions. -- The persisted `Document` and `MultiDocument` format is incompatible with the previous format. Nodes now contain `checks`; encoded literal values, enum values, and property signature names use tagged `{ type, value }` objects while decoded documents expose their native scalar values; declarations no longer contain `encodedSchema`; persisted opaque declarations and leaf filters require a `{ id, payload }` representation identity; and checks no longer contain closed `meta` payloads. Regenerate stored documents from their source schemas with the new API, or migrate their shape before passing them to `fromJson`. -- Replace the generic `Reviver` function type with `DeclarationReviver

`, `FilterReviver

`, `FilterGroupReviver

`, `CheckReviver

`, `Reviver

`, and `AnyReviver`. -- Remove the closed metadata types `StringMeta`, `NumberMeta`, `BigIntMeta`, `ArraysMeta`, `ObjectsMeta`, `DateMeta`, `SizeMeta`, `DeclarationMeta`, and `Meta` from `SchemaRepresentation`. -- Remove the exported representation validation schemas and `PrimitiveTree`: `$PrimitiveTree`, `$Annotations`, `$Null`, `$Undefined`, `$Void`, `$Never`, `$Unknown`, `$Any`, `$StringMeta`, `$String`, `$NumberMeta`, `$Number`, `$Boolean`, `$BigInt`, `$Symbol`, `$LiteralValue`, `$Literal`, `$UniqueSymbol`, `$ObjectKeyword`, `$Enum`, `$TemplateLiteral`, `$Element`, `$Arrays`, `$PropertySignature`, `$IndexSignature`, `$ObjectsMeta`, `$Objects`, `$Union`, `$Reference`, `$DateMeta`, `$SizeMeta`, `$DeclarationMeta`, `$Declaration`, `$Suspend`, `$Representation`, `$Document`, and `$MultiDocument`. -- Replace schema annotations as follows: - - remove `Annotations.Bottom.meta` and `Annotations.Filter.meta` - - remove `Annotations.Declaration.typeConstructor`; use `representation` - - remove `Annotations.Declaration.generation`; use the `toCode` callback - - add `Annotations.Filter.representation`, `toJsonSchema`, and `toCode` - - add `Annotations.Augment.contentSchema` as a JSON-valued annotation - - allow `Annotations.Declaration.toCodecJson` and `toCodecStringTree` to return `undefined` -- Remove the top-level `contentMediaType` and `contentSchema` fields from `SchemaRepresentation.String`. Content metadata is now carried in ordinary annotations, and `contentSchema` is a JSON Schema value rather than a nested Effect representation. -- Remove `Schema.Annotations.BuiltInMetaDefinitions`, `BuiltInMeta`, `MetaDefinitions`, and `Meta`. Custom checks should carry a representation identity and compiler callbacks instead of augmenting the metadata registry. -- `fromJsonSchemaDocument` now returns `Schema.Top` instead of a representation `Document`. `fromJsonSchemaMultiDocument` now returns `SchemaMultiDocument` instead of `MultiDocument`; call `fromSchemaMultiDocument` when a representation multi-document is required. -- `toCodeDocument` now accepts only a live `MultiDocument`; remove its `reviver` option. Reconstruct persisted documents first so revivers can restore runtime compiler callbacks. -- Rename the `generation` field of `Artifact` values for symbols and enums to `code`. Declaration generation no longer has an `Encoded` output, and `importDeclaration` is replaced by `importDeclarations` on callback output. -- Remove the exported `sanitizeJavaScriptIdentifier`, `topologicalSort`, and `TopologicalSort` helpers. -- Negative zero no longer receives special representation handling. Do not rely on preserving its sign across JSON persistence or generated code, where it may be normalized to `0`. -- With `{ errors: "all" }`, structural checks run only after their base array, object, or declaration parses successfully; they are no longer added to an already failing child parse. diff --git a/repos/effect/.changeset/pre/schema-result-combinators.md b/repos/effect/.changeset/pre/schema-result-combinators.md deleted file mode 100644 index d313cf0879..0000000000 --- a/repos/effect/.changeset/pre/schema-result-combinators.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: add `decodeUnknownResult` / `decodeResult` and `encodeUnknownResult` / `encodeResult` helpers for synchronous `Result`-based parsing. diff --git a/repos/effect/.changeset/pre/schema-runtime-performance.md b/repos/effect/.changeset/pre/schema-runtime-performance.md deleted file mode 100644 index 5d1b322740..0000000000 --- a/repos/effect/.changeset/pre/schema-runtime-performance.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -"effect": patch ---- - -Improve Schema parsing, schema construction and adapter runtime performance -while preserving current parsing behavior. - -## Runtime performance - -The `effect@beta`, Valibot and Zod timing cases from -[`open-circle/schema-benchmarks`](https://github.com/open-circle/schema-benchmarks) -were reproduced as a dedicated `runtimeperf` suite. The table includes every -case exposed by each upstream adapter; `—` means that the adapter does not -provide that benchmark. - -Effect `main` (`45e781088`) and the branch based on `d775bf4b2` were compared -with five paired processes per case, 150 ms measurement time and 50 ms warmup. -The two initially inconclusive Effect cases were repeated with 15 paired -processes, 500 ms measurement time and 150 ms warmup. Valibot and Zod values -use five processes, 300 ms measurement time and 100 ms warmup. Environment: -Node `v24.12.0`, macOS arm64, Apple M3. - -Zod parsing uses `safeParse` with `{ jitless: true }`; its Standard Schema and -codec cases use the corresponding native adapter APIs. All values are median -microseconds per operation (`µs/op`), lower is better. Cross-library values are -diagnostic because they are independent rather than paired measurements. - -| Scenario | Effect `main` | Effect branch | Valibot | Zod 4 | Delta | 95% CI | Classification | -| ------------------------------------ | ------------: | ------------: | ---------: | ---------: | ------: | ------------------ | -------------- | -| Initialize schema | 137.28 | 118.23 | **40.24** | 318.56 | -12.69% | -21.02% to -5.35% | improvement | -| Initialize schema and decoder | 144.81 | **130.50** | — | — | -10.88% | -14.22% to -3.29% | improvement | -| Validate valid product | 8.478 | **5.415** | 5.63 | — | -35.18% | -41.65% to -32.83% | improvement | -| Validate invalid product | 1.516 | 1.348 | **0.2431** | — | -11.59% | -13.81% to -6.31% | improvement | -| Parse valid product, all errors | 8.360 | 5.366 | **5.22** | 7.16 | -36.28% | -54.41% to -31.67% | improvement | -| Parse invalid product, all errors | 11.302 | **9.100** | 15.70 | 41.58 | -19.42% | -21.32% to -13.12% | improvement | -| Parse valid product, first error | 8.201 | **5.294** | 5.37 | — | -35.44% | -37.75% to -34.59% | improvement | -| Parse invalid product, first error | 1.510 | 1.352 | **0.2572** | — | -10.51% | -12.52% to -9.53% | improvement | -| Standard Schema valid, all errors | 9.284 | 5.935 | 5.35 | **3.83** | -35.96% | -53.29% to -33.49% | improvement | -| Standard Schema invalid, all errors | 16.718 | **15.203** | 16.51 | 32.85 | -11.31% | -13.97% to -7.65% | improvement | -| Standard Schema valid, first error | 8.889 | **5.843** | — | — | -34.17% | -35.13% to -33.94% | improvement | -| Standard Schema invalid, first error | 2.435 | **2.244** | — | — | -8.44% | -12.76% to -4.82% | improvement | -| Typed codec encode | 0.4692 | 0.3420 | — | **0.0405** | -27.60% | -32.35% to -22.50% | improvement | -| Typed codec decode | 0.5191 | 0.3762 | — | **0.0463** | -27.19% | -34.75% to -22.71% | improvement | -| Unknown codec encode | 0.4910 | **0.3472** | — | — | -28.58% | -30.42% to -27.59% | improvement | -| Unknown codec decode | 0.5061 | **0.3637** | — | — | -29.26% | -29.82% to -21.70% | improvement | - -Overall Effect classification: 16 improvements and no regressions. diff --git a/repos/effect/.changeset/pre/schema-struct-simplify.md b/repos/effect/.changeset/pre/schema-struct-simplify.md deleted file mode 100644 index c86120ae47..0000000000 --- a/repos/effect/.changeset/pre/schema-struct-simplify.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Schema: allow using `Struct` type helpers directly, e.g. `Schema.Struct.Type` instead of `Schema.Schema.Type>`. diff --git a/repos/effect/.changeset/pre/schema-union-type-derivation.md b/repos/effect/.changeset/pre/schema-union-type-derivation.md deleted file mode 100644 index a7ac4dac1b..0000000000 --- a/repos/effect/.changeset/pre/schema-union-type-derivation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Precompile union formatters and equivalences, select transformed union members using their decoded type, and allow deriving an equivalence for `Never`. diff --git a/repos/effect/.changeset/pre/scope-persisted-queue-ids.md b/repos/effect/.changeset/pre/scope-persisted-queue-ids.md deleted file mode 100644 index ea6d42d1b6..0000000000 --- a/repos/effect/.changeset/pre/scope-persisted-queue-ids.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Scope custom persisted queue ID deduplication to each named queue. diff --git a/repos/effect/.changeset/pre/scoped-cache-invalidate-all-reentrancy.md b/repos/effect/.changeset/pre/scoped-cache-invalidate-all-reentrancy.md deleted file mode 100644 index 2f7bc048a5..0000000000 --- a/repos/effect/.changeset/pre/scoped-cache-invalidate-all-reentrancy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `ScopedCache.invalidateAll` discarding entries created by reentrant resource finalizers without releasing them. Entries are now removed before their finalizers run, so replacement resources remain cached and are released when the cache closes. diff --git a/repos/effect/.changeset/pre/scoped-cache-refresh-lookup-defects.md b/repos/effect/.changeset/pre/scoped-cache-refresh-lookup-defects.md deleted file mode 100644 index 412dc00d9c..0000000000 --- a/repos/effect/.changeset/pre/scoped-cache-refresh-lookup-defects.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Capture synchronous defects thrown by `ScopedCache.refresh` lookup callbacks. diff --git a/repos/effect/.changeset/pre/scoped-log-nan-restoration.md b/repos/effect/.changeset/pre/scoped-log-nan-restoration.md deleted file mode 100644 index 07413320b8..0000000000 --- a/repos/effect/.changeset/pre/scoped-log-nan-restoration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.annotateLogsScoped` to restore or remove unchanged `NaN` annotations when the scope closes. diff --git a/repos/effect/.changeset/pre/secure-eventlog-identities.md b/repos/effect/.changeset/pre/secure-eventlog-identities.md deleted file mode 100644 index b7d1d9ad39..0000000000 --- a/repos/effect/.changeset/pre/secure-eventlog-identities.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Bind event-log read and write requests to the identities authenticated on their RPC connection. diff --git a/repos/effect/.changeset/pre/select-prompt-optional-message.md b/repos/effect/.changeset/pre/select-prompt-optional-message.md deleted file mode 100644 index 1165109f1e..0000000000 --- a/repos/effect/.changeset/pre/select-prompt-optional-message.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make `message` optional for `Prompt.Select` and `Prompt.MultiSelect`. When omitted, prompts display only the choices and submission shows a tick followed by the selected titles. `Prompt.AutoComplete` still requires a message. diff --git a/repos/effect/.changeset/pre/semantic-matching.md b/repos/effect/.changeset/pre/semantic-matching.md deleted file mode 100644 index edf23cee61..0000000000 --- a/repos/effect/.changeset/pre/semantic-matching.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"effect": patch ---- - -Use semantic matching for TemplateLiteral parsing and index signature keys - -Replace regex-based TemplateLiteral parsing with backtracking segmentation over -template literal parts, applying part checks during matching. - -Use schema membership when selecting Record index signature keys, including -checked string, number, symbol, and TemplateLiteral parameters. Tighten valid -index signature parameters on both type and encoded sides, and preserve key -parameter semantics in codec transformations. diff --git a/repos/effect/.changeset/pre/service-interface-const.md b/repos/effect/.changeset/pre/service-interface-const.md deleted file mode 100644 index 3018bd030e..0000000000 --- a/repos/effect/.changeset/pre/service-interface-const.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use branded interfaces for `Reactivity`, `LanguageModel`, `EmbeddingModel`, and `Chat`. Refer to each service's same-name type instead of `.Service` or `["Service"]`; custom implementations must include `[TypeId]: TypeId`. diff --git a/repos/effect/.changeset/pre/seven-mugs-marry.md b/repos/effect/.changeset/pre/seven-mugs-marry.md deleted file mode 100644 index fd3dd9d737..0000000000 --- a/repos/effect/.changeset/pre/seven-mugs-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix: update Service interface to use 'this: void' in 'of' method signatures diff --git a/repos/effect/.changeset/pre/seven-poems-divide.md b/repos/effect/.changeset/pre/seven-poems-divide.md deleted file mode 100644 index 8d9caf7f40..0000000000 --- a/repos/effect/.changeset/pre/seven-poems-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor populated variables before dotenv expansion defaults in `ConfigProvider`. diff --git a/repos/effect/.changeset/pre/shaggy-birds-stay.md b/repos/effect/.changeset/pre/shaggy-birds-stay.md deleted file mode 100644 index 5834231b92..0000000000 --- a/repos/effect/.changeset/pre/shaggy-birds-stay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow passing void for request constructors diff --git a/repos/effect/.changeset/pre/shaggy-cities-push.md b/repos/effect/.changeset/pre/shaggy-cities-push.md deleted file mode 100644 index dfff5eac29..0000000000 --- a/repos/effect/.changeset/pre/shaggy-cities-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -use NoInfer in Layer constructors to prevent type erasure diff --git a/repos/effect/.changeset/pre/shaggy-numbers-accept.md b/repos/effect/.changeset/pre/shaggy-numbers-accept.md deleted file mode 100644 index df7340ac1d..0000000000 --- a/repos/effect/.changeset/pre/shaggy-numbers-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Correct the type of the schema parameter accepted by the `fileSchema` methods in the CLI to be `Schema.Decoder` diff --git a/repos/effect/.changeset/pre/shaky-beans-throw.md b/repos/effect/.changeset/pre/shaky-beans-throw.md deleted file mode 100644 index e0fa124dd1..0000000000 --- a/repos/effect/.changeset/pre/shaky-beans-throw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -accept UrlParams.Input in some UrlParams apis diff --git a/repos/effect/.changeset/pre/shaky-terms-push.md b/repos/effect/.changeset/pre/shaky-terms-push.md deleted file mode 100644 index 9e004ee1e5..0000000000 --- a/repos/effect/.changeset/pre/shaky-terms-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix HttpRouter nested prefixed application order diff --git a/repos/effect/.changeset/pre/sharding-registration-context-overrides.md b/repos/effect/.changeset/pre/sharding-registration-context-overrides.md deleted file mode 100644 index 67c74a7d12..0000000000 --- a/repos/effect/.changeset/pre/sharding-registration-context-overrides.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor services explicitly supplied when registering cluster entities while retaining construction-context services as fallbacks. diff --git a/repos/effect/.changeset/pre/sharp-emus-applaud.md b/repos/effect/.changeset/pre/sharp-emus-applaud.md deleted file mode 100644 index aa91f34010..0000000000 --- a/repos/effect/.changeset/pre/sharp-emus-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Command.withShortDescription` and use short descriptions for CLI subcommand listings, with fallback to the full command description. diff --git a/repos/effect/.changeset/pre/sharp-goats-wink.md b/repos/effect/.changeset/pre/sharp-goats-wink.md deleted file mode 100644 index 19588ff9bf..0000000000 --- a/repos/effect/.changeset/pre/sharp-goats-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Allow unstable CLI fallback prompts to be created dynamically from an `Effect`. diff --git a/repos/effect/.changeset/pre/sharp-pandas-care.md b/repos/effect/.changeset/pre/sharp-pandas-care.md deleted file mode 100644 index 21c1cb90c2..0000000000 --- a/repos/effect/.changeset/pre/sharp-pandas-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix TestClock adjustment when its layer is provided to programs run without an ambient Scope. diff --git a/repos/effect/.changeset/pre/sharp-peas-march.md b/repos/effect/.changeset/pre/sharp-peas-march.md deleted file mode 100644 index 7bce2812cc..0000000000 --- a/repos/effect/.changeset/pre/sharp-peas-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix AI tool handler error typing so `LanguageModel.generateText` with a toolkit exposes wrapped `AiError` values rather than leaking raw `AiErrorReason` in the error channel. diff --git a/repos/effect/.changeset/pre/sharp-rules-draw.md b/repos/effect/.changeset/pre/sharp-rules-draw.md deleted file mode 100644 index 8246dde8ab..0000000000 --- a/repos/effect/.changeset/pre/sharp-rules-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure each sql client gets a unique transaction service diff --git a/repos/effect/.changeset/pre/sharp-singers-sort.md b/repos/effect/.changeset/pre/sharp-singers-sort.md deleted file mode 100644 index 75c499727c..0000000000 --- a/repos/effect/.changeset/pre/sharp-singers-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix string messages and annotations being double-quoted by simple and logfmt loggers. diff --git a/repos/effect/.changeset/pre/shiny-trains-hug.md b/repos/effect/.changeset/pre/shiny-trains-hug.md deleted file mode 100644 index 451208484d..0000000000 --- a/repos/effect/.changeset/pre/shiny-trains-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add module-level helpers for `Semaphore`, `Latch`, and extracted `PartitionedSemaphore` operations. diff --git a/repos/effect/.changeset/pre/short-cows-relate.md b/repos/effect/.changeset/pre/short-cows-relate.md deleted file mode 100644 index 2eb2ae0ad7..0000000000 --- a/repos/effect/.changeset/pre/short-cows-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Set `Schema.TaggedErrorClass` instance `name` to the tag value, matching `Data.TaggedError` behavior. diff --git a/repos/effect/.changeset/pre/short-foxes-admire.md b/repos/effect/.changeset/pre/short-foxes-admire.md deleted file mode 100644 index 2da4d3a3db..0000000000 --- a/repos/effect/.changeset/pre/short-foxes-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Atom.swr` to `effect/unstable/reactivity` for staleTime-gated stale-while-revalidate reads, optional mount and window-focus revalidation, and forceful manual refresh. diff --git a/repos/effect/.changeset/pre/short-stamps-throw.md b/repos/effect/.changeset/pre/short-stamps-throw.md deleted file mode 100644 index 1948e67888..0000000000 --- a/repos/effect/.changeset/pre/short-stamps-throw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -do not use performance.timeOrigin and calculate origins lazily diff --git a/repos/effect/.changeset/pre/shy-cycles-flow.md b/repos/effect/.changeset/pre/shy-cycles-flow.md deleted file mode 100644 index 60188e0698..0000000000 --- a/repos/effect/.changeset/pre/shy-cycles-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Channel.decodeText corrupting UTF-8 characters split across chunk boundaries. diff --git a/repos/effect/.changeset/pre/shy-geckos-sniff.md b/repos/effect/.changeset/pre/shy-geckos-sniff.md deleted file mode 100644 index bf5f07dcd6..0000000000 --- a/repos/effect/.changeset/pre/shy-geckos-sniff.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"@effect/sql-sqlite-react-native": major -"@effect/openapi-generator": major -"@effect/platform-node-shared": major -"@effect/platform-browser": major -"@effect/sql-sqlite-node": major -"@effect/sql-sqlite-wasm": major -"@effect/sql-clickhouse": major -"@effect/sql-sqlite-bun": major -"@effect/opentelemetry": major -"@effect/platform-node": major -"@effect/sql-sqlite-do": major -"@effect/ai-anthropic": major -"@effect/ai-openai-compat": major -"@effect/ai-openrouter": major -"@effect/platform-bun": major -"@effect/atom-react": major -"@effect/atom-solid": major -"@effect/sql-libsql": major -"@effect/sql-mysql2": major -"@effect/ai-openai": major -"@effect/sql-mssql": major -"@effect/atom-vue": major -"effect": major -"@effect/sql-d1": major -"@effect/sql-pg": major -"@effect/vitest": major ---- - -v4 beta diff --git a/repos/effect/.changeset/pre/shy-seals-smile.md b/repos/effect/.changeset/pre/shy-seals-smile.md deleted file mode 100644 index ac3d45baae..0000000000 --- a/repos/effect/.changeset/pre/shy-seals-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-react-native": patch ---- - -Return selected rows from synchronous and asynchronous OP-SQLite value queries. diff --git a/repos/effect/.changeset/pre/silent-geckos-matter.md b/repos/effect/.changeset/pre/silent-geckos-matter.md deleted file mode 100644 index df5aadf3ba..0000000000 --- a/repos/effect/.changeset/pre/silent-geckos-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -compare transaction connections by reference diff --git a/repos/effect/.changeset/pre/silent-needles-design.md b/repos/effect/.changeset/pre/silent-needles-design.md deleted file mode 100644 index ee84c0eb70..0000000000 --- a/repos/effect/.changeset/pre/silent-needles-design.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/opentelemetry": patch -"effect": patch ---- - -Fix spans never having parent span diff --git a/repos/effect/.changeset/pre/silent-plants-matter.md b/repos/effect/.changeset/pre/silent-plants-matter.md deleted file mode 100644 index e9609eab2c..0000000000 --- a/repos/effect/.changeset/pre/silent-plants-matter.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"@effect/platform-node": patch -"@effect/platform-bun": patch -"effect": patch ---- - -improve http body consumption diff --git a/repos/effect/.changeset/pre/silent-spoons-stare.md b/repos/effect/.changeset/pre/silent-spoons-stare.md deleted file mode 100644 index 6d45025ee3..0000000000 --- a/repos/effect/.changeset/pre/silent-spoons-stare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -catch errors in pullIntoWritable diff --git a/repos/effect/.changeset/pre/silly-dodos-update.md b/repos/effect/.changeset/pre/silly-dodos-update.md deleted file mode 100644 index e90a8a07a3..0000000000 --- a/repos/effect/.changeset/pre/silly-dodos-update.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `SynchronizedRef.getAndUpdateSome` to update its backing ref. diff --git a/repos/effect/.changeset/pre/silly-loops-tickle.md b/repos/effect/.changeset/pre/silly-loops-tickle.md deleted file mode 100644 index a5493c8e05..0000000000 --- a/repos/effect/.changeset/pre/silly-loops-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -DateTime.distance now returns a Duration diff --git a/repos/effect/.changeset/pre/silver-bulk-indexeddb.md b/repos/effect/.changeset/pre/silver-bulk-indexeddb.md deleted file mode 100644 index 7137730b4a..0000000000 --- a/repos/effect/.changeset/pre/silver-bulk-indexeddb.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Fix IndexedDB bulk writes so `insertAll` and `upsertAll` resume when used inside `withTransaction`. diff --git a/repos/effect/.changeset/pre/silver-emus-smoke.md b/repos/effect/.changeset/pre/silver-emus-smoke.md deleted file mode 100644 index 19f10e09d7..0000000000 --- a/repos/effect/.changeset/pre/silver-emus-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove placeholder fallback behavior from CLI prompt inputs now that default values are prefilled. diff --git a/repos/effect/.changeset/pre/silver-kings-poke.md b/repos/effect/.changeset/pre/silver-kings-poke.md deleted file mode 100644 index 11d14a6b50..0000000000 --- a/repos/effect/.changeset/pre/silver-kings-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -simplify SubscriptionRef diff --git a/repos/effect/.changeset/pre/silver-snails-sqlite.md b/repos/effect/.changeset/pre/silver-snails-sqlite.md deleted file mode 100644 index dc4ddd43f7..0000000000 --- a/repos/effect/.changeset/pre/silver-snails-sqlite.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -"effect": patch -"@effect/sql-libsql": patch -"@effect/sql-mssql": patch -"@effect/sql-mysql2": patch -"@effect/sql-pg": patch -"@effect/sql-pglite": patch -"@effect/sql-sqlite-bun": patch -"@effect/sql-sqlite-do": patch -"@effect/sql-sqlite-node": patch -"@effect/sql-sqlite-react-native": patch -"@effect/sql-sqlite-wasm": patch ---- - -Add `UniqueViolation` as a new SQL error reason. Supported unique constraint violations now classify as `UniqueViolation` instead of the broader `ConstraintError` reason. - -This covers PostgreSQL, PGlite, MySQL, MSSQL, and the shared SQLite classification used by the SQLite-family clients. `UniqueViolation.constraint` contains the best available constraint, index, or key identifier and falls back to exactly `"unknown"` when no reliable identifier is available. diff --git a/repos/effect/.changeset/pre/silver-wings-watch.md b/repos/effect/.changeset/pre/silver-wings-watch.md deleted file mode 100644 index 3a1d49eaca..0000000000 --- a/repos/effect/.changeset/pre/silver-wings-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -require a option to make AtomRpc.query atoms serializatable diff --git a/repos/effect/.changeset/pre/simplify-optic-composition.md b/repos/effect/.changeset/pre/simplify-optic-composition.md deleted file mode 100644 index 1ed7423a9a..0000000000 --- a/repos/effect/.changeset/pre/simplify-optic-composition.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Fix three issues in the public `Optic` API: - -- Composed `Iso` and `Prism` setters no longer try to read a source value before writing. -- Calling `notUndefined` on an `Optional` now returns an `Optional`, because writing can still fail. -- The internal `node` property is no longer exposed by public optic types. diff --git a/repos/effect/.changeset/pre/six-cups-taste.md b/repos/effect/.changeset/pre/six-cups-taste.md deleted file mode 100644 index 71d570b69f..0000000000 --- a/repos/effect/.changeset/pre/six-cups-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add args to Stdio service diff --git a/repos/effect/.changeset/pre/six-pumas-take.md b/repos/effect/.changeset/pre/six-pumas-take.md deleted file mode 100644 index a58d9a0492..0000000000 --- a/repos/effect/.changeset/pre/six-pumas-take.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -add advanced graph set operations for deriving related graph structures - -- `Graph.complement` - complement over the existing node set, adding missing edges between distinct nodes -- `Graph.neighborhood` - induced subgraph containing nodes within a radius of a node -- `Graph.sum` - disjoint union of two graphs without merging equal node data diff --git a/repos/effect/.changeset/pre/sixty-mails-shout.md b/repos/effect/.changeset/pre/sixty-mails-shout.md deleted file mode 100644 index b4bff83957..0000000000 --- a/repos/effect/.changeset/pre/sixty-mails-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix VariantSchema.Union diff --git a/repos/effect/.changeset/pre/sixty-socks-yell.md b/repos/effect/.changeset/pre/sixty-socks-yell.md deleted file mode 100644 index 1441984b72..0000000000 --- a/repos/effect/.changeset/pre/sixty-socks-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update `HttpApiClient.urlBuilder` to mirror client shape, and encode params/query via endpoint schemas before building URLs. diff --git a/repos/effect/.changeset/pre/skip-disabled-stack-capture.md b/repos/effect/.changeset/pre/skip-disabled-stack-capture.md deleted file mode 100644 index cbdf29e4f7..0000000000 --- a/repos/effect/.changeset/pre/skip-disabled-stack-capture.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Skip optional stack capture when `Error.stackTraceLimit` is zero. diff --git a/repos/effect/.changeset/pre/slick-signs-wish.md b/repos/effect/.changeset/pre/slick-signs-wish.md deleted file mode 100644 index d7490cbb21..0000000000 --- a/repos/effect/.changeset/pre/slick-signs-wish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `isOpen` to `Latch` to allow querying the latch's open state diff --git a/repos/effect/.changeset/pre/slick-toes-rush.md b/repos/effect/.changeset/pre/slick-toes-rush.md deleted file mode 100644 index ff74b18b5f..0000000000 --- a/repos/effect/.changeset/pre/slick-toes-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -render causes in OtlpTracer exception events diff --git a/repos/effect/.changeset/pre/slimy-melons-admire.md b/repos/effect/.changeset/pre/slimy-melons-admire.md deleted file mode 100644 index 7cc69a0af8..0000000000 --- a/repos/effect/.changeset/pre/slimy-melons-admire.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -"@effect/sql-sqlite-react-native": patch -"@effect/openapi-generator": patch -"@effect/platform-node-shared": patch -"@effect/ai-openai-compat": patch -"@effect/platform-browser": patch -"@effect/sql-sqlite-node": patch -"@effect/sql-sqlite-wasm": patch -"@effect/sql-clickhouse": patch -"@effect/sql-sqlite-bun": patch -"@effect/ai-openrouter": patch -"@effect/opentelemetry": patch -"@effect/platform-deno": patch -"@effect/platform-node": patch -"@effect/sql-sqlite-do": patch -"@effect/ai-anthropic": patch -"@effect/platform-bun": patch -"@effect/docgen": patch -"@effect/atom-react": patch -"@effect/atom-solid": patch -"@effect/sql-libsql": patch -"@effect/sql-mysql2": patch -"@effect/sql-pglite": patch -"@effect/ai-openai": patch -"@effect/sql-mssql": patch -"@effect/atom-vue": patch -"effect": patch -"@effect/sql-d1": patch -"@effect/sql-pg": patch -"@effect/vitest": patch ---- - -Removed explicit ./index entrypoints diff --git a/repos/effect/.changeset/pre/slimy-planets-divide.md b/repos/effect/.changeset/pre/slimy-planets-divide.md deleted file mode 100644 index aed2e85f2e..0000000000 --- a/repos/effect/.changeset/pre/slimy-planets-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow assigning Temporal types to DateTime & Duration input diff --git a/repos/effect/.changeset/pre/slimy-turtles-juggle.md b/repos/effect/.changeset/pre/slimy-turtles-juggle.md deleted file mode 100644 index 2cdb299ae2..0000000000 --- a/repos/effect/.changeset/pre/slimy-turtles-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -refactor SqlSchema apis diff --git a/repos/effect/.changeset/pre/slow-beans-battle.md b/repos/effect/.changeset/pre/slow-beans-battle.md deleted file mode 100644 index e3a80a3691..0000000000 --- a/repos/effect/.changeset/pre/slow-beans-battle.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@effect/sql-sqlite-react-native": patch -"@effect/openapi-generator": patch -"@effect/platform-node-shared": patch -"@effect/ai-openai-compat": patch -"@effect/platform-browser": patch -"@effect/sql-sqlite-node": patch -"@effect/sql-sqlite-wasm": patch -"@effect/sql-clickhouse": patch -"@effect/sql-sqlite-bun": patch -"@effect/ai-openrouter": patch -"@effect/opentelemetry": patch -"@effect/platform-node": patch -"@effect/sql-sqlite-do": patch -"@effect/ai-anthropic": patch -"@effect/platform-bun": patch -"@effect/atom-react": patch -"@effect/sql-libsql": patch -"@effect/sql-mysql2": patch -"@effect/ai-openai": patch -"@effect/sql-mssql": patch -"effect": patch -"@effect/sql-d1": patch -"@effect/sql-pg": patch -"@effect/vitest": patch ---- - -Rename the `ServiceMap` module to `Context` across exports, docs, and tests. diff --git a/repos/effect/.changeset/pre/slow-berries-enjoy.md b/repos/effect/.changeset/pre/slow-berries-enjoy.md deleted file mode 100644 index de89cc726d..0000000000 --- a/repos/effect/.changeset/pre/slow-berries-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Simplify internal and documented request usage by passing request resolvers directly to `Effect.request` instead of wrapping them with `Effect.succeed`. diff --git a/repos/effect/.changeset/pre/slow-entities-register.md b/repos/effect/.changeset/pre/slow-entities-register.md deleted file mode 100644 index 678da84a1e..0000000000 --- a/repos/effect/.changeset/pre/slow-entities-register.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Hold persisted cluster messages while entity layers are still registering, while retaining a bounded failure when -registration never begins. diff --git a/repos/effect/.changeset/pre/slow-spiders-refresh.md b/repos/effect/.changeset/pre/slow-spiders-refresh.md deleted file mode 100644 index 1098787581..0000000000 --- a/repos/effect/.changeset/pre/slow-spiders-refresh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent SQL runner lock refreshes from hanging when reserved connections become unresponsive. diff --git a/repos/effect/.changeset/pre/small-basic-bundle.md b/repos/effect/.changeset/pre/small-basic-bundle.md deleted file mode 100644 index eef21766d8..0000000000 --- a/repos/effect/.changeset/pre/small-basic-bundle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reduce the basic Effect bundle size by keeping cause deduplication local, making encoding lookup tables tree-shakeable, removing redundant cause field declarations, and simplifying primitive hash dispatch without changing hash values. diff --git a/repos/effect/.changeset/pre/small-bugs-hunt.md b/repos/effect/.changeset/pre/small-bugs-hunt.md deleted file mode 100644 index bad2315e98..0000000000 --- a/repos/effect/.changeset/pre/small-bugs-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix JSON-RPC serialization to return an object for non-batched requests while preserving array responses for true batch requests. diff --git a/repos/effect/.changeset/pre/small-crabs-care.md b/repos/effect/.changeset/pre/small-crabs-care.md deleted file mode 100644 index c79ad5cb4f..0000000000 --- a/repos/effect/.changeset/pre/small-crabs-care.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Refine unstable CLI parent/subcommand flag composition. - -- Add `Command.withSharedFlags` conflict validation against existing subcommands, including the `withSubcommands(...).withSharedFlags(...)` composition order. -- Reorder `Command` type parameters to `Command` for clearer parent-context modeling. -- Make `Command.withSubcommands` input typing sound for downstream input-based combinators by reflecting that subcommand paths only carry parent context input. diff --git a/repos/effect/.changeset/pre/small-pandas-cache.md b/repos/effect/.changeset/pre/small-pandas-cache.md deleted file mode 100644 index b1e5b3de51..0000000000 --- a/repos/effect/.changeset/pre/small-pandas-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Preserve OpenAI Responses API cache write token usage in language model responses. diff --git a/repos/effect/.changeset/pre/small-pets-sit.md b/repos/effect/.changeset/pre/small-pets-sit.md deleted file mode 100644 index 5fd8650d52..0000000000 --- a/repos/effect/.changeset/pre/small-pets-sit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Don't transform Tool result schemas, as they aren't sent to the providers as -json schemas diff --git a/repos/effect/.changeset/pre/smart-ducks-jump.md b/repos/effect/.changeset/pre/smart-ducks-jump.md deleted file mode 100644 index 2322e4f7ee..0000000000 --- a/repos/effect/.changeset/pre/smart-ducks-jump.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `Schedule.andThenResult` to initialize the right schedule only after the left schedule completes. -This removes the extra immediate transition tick and correctly completes when the right schedule is finite. diff --git a/repos/effect/.changeset/pre/smart-pillows-buy.md b/repos/effect/.changeset/pre/smart-pillows-buy.md deleted file mode 100644 index f4ac640fa5..0000000000 --- a/repos/effect/.changeset/pre/smart-pillows-buy.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch -"@effect/sql-pg": patch ---- - -Fix `ChildProcess` options type and implement `PgMigrator` diff --git a/repos/effect/.changeset/pre/smart-timers-fly.md b/repos/effect/.changeset/pre/smart-timers-fly.md deleted file mode 100644 index e9a642781b..0000000000 --- a/repos/effect/.changeset/pre/smart-timers-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Cookies.expireCookie` / `expireCookieUnsafe` and `HttpServerResponse.expireCookie` / `expireCookieUnsafe` for emitting expired cookies. diff --git a/repos/effect/.changeset/pre/smart-tips-sort.md b/repos/effect/.changeset/pre/smart-tips-sort.md deleted file mode 100644 index c23b25c0dc..0000000000 --- a/repos/effect/.changeset/pre/smart-tips-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix Latch.release diff --git a/repos/effect/.changeset/pre/social-hoops-knock.md b/repos/effect/.changeset/pre/social-hoops-knock.md deleted file mode 100644 index c91f7ae2ec..0000000000 --- a/repos/effect/.changeset/pre/social-hoops-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Cleanup internals of CLI package diff --git a/repos/effect/.changeset/pre/social-pumas-prove.md b/repos/effect/.changeset/pre/social-pumas-prove.md deleted file mode 100644 index 1e71087bf3..0000000000 --- a/repos/effect/.changeset/pre/social-pumas-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -disable tracer propagation for otlp exporter diff --git a/repos/effect/.changeset/pre/socket-paused-websocket-handoff.md b/repos/effect/.changeset/pre/socket-paused-websocket-handoff.md deleted file mode 100644 index b8ee1aaef1..0000000000 --- a/repos/effect/.changeset/pre/socket-paused-websocket-handoff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Resume paused WebSockets after their readers take ownership. diff --git a/repos/effect/.changeset/pre/socket-websocket-text-byte-watermark.md b/repos/effect/.changeset/pre/socket-websocket-text-byte-watermark.md deleted file mode 100644 index bd35e74b5a..0000000000 --- a/repos/effect/.changeset/pre/socket-websocket-text-byte-watermark.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Count buffered WebSocket text frames by their UTF-8 byte length when enforcing `highWaterMark`. diff --git a/repos/effect/.changeset/pre/soft-comics-wink.md b/repos/effect/.changeset/pre/soft-comics-wink.md deleted file mode 100644 index e93cf52490..0000000000 --- a/repos/effect/.changeset/pre/soft-comics-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Default `Effect.context()` to `Effect.context()` when no type parameter is provided. diff --git a/repos/effect/.changeset/pre/soft-delete-sqlmodel.md b/repos/effect/.changeset/pre/soft-delete-sqlmodel.md deleted file mode 100644 index 08a90e58a5..0000000000 --- a/repos/effect/.changeset/pre/soft-delete-sqlmodel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add optional soft delete column support to SqlModel repositories and resolvers. diff --git a/repos/effect/.changeset/pre/soft-seals-allow.md b/repos/effect/.changeset/pre/soft-seals-allow.md deleted file mode 100644 index 54e3bc761c..0000000000 --- a/repos/effect/.changeset/pre/soft-seals-allow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -cache base idb query builders diff --git a/repos/effect/.changeset/pre/soft-sockets-write.md b/repos/effect/.changeset/pre/soft-sockets-write.md deleted file mode 100644 index c0106d10b4..0000000000 --- a/repos/effect/.changeset/pre/soft-sockets-write.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Map WebSocket send exceptions and transform stream write rejections to typed `SocketError` failures. diff --git a/repos/effect/.changeset/pre/solid-cougars-attack.md b/repos/effect/.changeset/pre/solid-cougars-attack.md deleted file mode 100644 index 199bb36f36..0000000000 --- a/repos/effect/.changeset/pre/solid-cougars-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add -v alias for version flag diff --git a/repos/effect/.changeset/pre/solid-doors-ring.md b/repos/effect/.changeset/pre/solid-doors-ring.md deleted file mode 100644 index 6178bf731f..0000000000 --- a/repos/effect/.changeset/pre/solid-doors-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow creating standalone http handlers from HttpApiEndpoints diff --git a/repos/effect/.changeset/pre/solid-items-tease.md b/repos/effect/.changeset/pre/solid-items-tease.md deleted file mode 100644 index ed43ea9b2e..0000000000 --- a/repos/effect/.changeset/pre/solid-items-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix atom node timeout cleanup diff --git a/repos/effect/.changeset/pre/solid-towns-smoke.md b/repos/effect/.changeset/pre/solid-towns-smoke.md deleted file mode 100644 index 44300aae11..0000000000 --- a/repos/effect/.changeset/pre/solid-towns-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -rename Model.Generated to Model.GeneratedByDb diff --git a/repos/effect/.changeset/pre/sour-bees-sleep.md b/repos/effect/.changeset/pre/sour-bees-sleep.md deleted file mode 100644 index 6c31527a93..0000000000 --- a/repos/effect/.changeset/pre/sour-bees-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Default empty Server-Sent Event types to `message`. diff --git a/repos/effect/.changeset/pre/sour-canyons-rescue.md b/repos/effect/.changeset/pre/sour-canyons-rescue.md deleted file mode 100644 index 7499eba6bc..0000000000 --- a/repos/effect/.changeset/pre/sour-canyons-rescue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add `useCodecs` option to HttpClientEndpoint constructors diff --git a/repos/effect/.changeset/pre/sparkly-bears-act.md b/repos/effect/.changeset/pre/sparkly-bears-act.md deleted file mode 100644 index 655477a384..0000000000 --- a/repos/effect/.changeset/pre/sparkly-bears-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add `Effect.acquireDisposable` diff --git a/repos/effect/.changeset/pre/sparkly-coins-sit.md b/repos/effect/.changeset/pre/sparkly-coins-sit.md deleted file mode 100644 index 2de14b62f5..0000000000 --- a/repos/effect/.changeset/pre/sparkly-coins-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -expose mcp client capabilities diff --git a/repos/effect/.changeset/pre/spicy-doors-unlist.md b/repos/effect/.changeset/pre/spicy-doors-unlist.md deleted file mode 100644 index fc4907aa67..0000000000 --- a/repos/effect/.changeset/pre/spicy-doors-unlist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `Command.withHidden` to `Command.unlisted`, along with the `hidden` command property which is now `unlisted`. diff --git a/repos/effect/.changeset/pre/split-clock-semantics.md b/repos/effect/.changeset/pre/split-clock-semantics.md deleted file mode 100644 index 1d2390cbb8..0000000000 --- a/repos/effect/.changeset/pre/split-clock-semantics.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": minor ---- - -Separate wall-clock timestamps from monotonic elapsed time. - -`Clock.Clock` now requires `monotonicTimeNanosUnsafe()` and `monotonicTimeNanos` for measuring elapsed time. Custom `Clock` implementations must provide both members. The live clock's `currentTimeNanos` now re-anchors its high-resolution Unix wall-clock timestamp when it drifts from `Date.now()`, while `Effect.timed`, duration metric tracking, and `Sink.withDuration` use monotonic time so wall-clock corrections do not distort elapsed durations. diff --git a/repos/effect/.changeset/pre/split-lines-completed-cr.md b/repos/effect/.changeset/pre/split-lines-completed-cr.md deleted file mode 100644 index 5a3608f587..0000000000 --- a/repos/effect/.changeset/pre/split-lines-completed-cr.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Emit CR-terminated lines from `Stream.splitLines` without pulling upstream again. diff --git a/repos/effect/.changeset/pre/spotty-comics-fry.md b/repos/effect/.changeset/pre/spotty-comics-fry.md deleted file mode 100644 index 496c87adf9..0000000000 --- a/repos/effect/.changeset/pre/spotty-comics-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix OpenApi Multipart file upload schema generation diff --git a/repos/effect/.changeset/pre/spotty-masks-own.md b/repos/effect/.changeset/pre/spotty-masks-own.md deleted file mode 100644 index 16f4feff02..0000000000 --- a/repos/effect/.changeset/pre/spotty-masks-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Loosen Stream.addEventListener type parameter diff --git a/repos/effect/.changeset/pre/sql-begin-failure-typed.md b/repos/effect/.changeset/pre/sql-begin-failure-typed.md deleted file mode 100644 index adfdb341ad..0000000000 --- a/repos/effect/.changeset/pre/sql-begin-failure-typed.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"effect": patch ---- - -Propagate a failed `BEGIN` or `SAVEPOINT` from `SqlClient.withTransaction` as a typed `SqlError`. - -`makeWithTransaction` wrapped the `begin` step together with the transaction body, so a -failed `BEGIN` took the rollback branch. No transaction was active at that point, the -`ROLLBACK` failed, and its `Effect.orDie` wrapper replaced the original typed error with a -defect (`cannot rollback - no transaction is active`). Callers could no longer classify the -failure as retryable. The path became reachable when the sqlite client started using -`BEGIN IMMEDIATE`, which acquires a write lock and can fail with `SQLITE_BUSY`. - -Commit and rollback now run only after `begin` or `savepoint` succeeds. A failed `begin` or -`savepoint` fails with its original `SqlError`, leaves the wrapped effect unexecuted, and -still closes the acquired connection scope. diff --git a/repos/effect/.changeset/pre/sql-event-journal-callback-errors.md b/repos/effect/.changeset/pre/sql-event-journal-callback-errors.md deleted file mode 100644 index 2d30161755..0000000000 --- a/repos/effect/.changeset/pre/sql-event-journal-callback-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve callback error identity in `SqlEventJournal.write` and `SqlEventJournal.withRemoteUncommited`. diff --git a/repos/effect/.changeset/pre/sql-message-storage-joined-reply-id.md b/repos/effect/.changeset/pre/sql-message-storage-joined-reply-id.md deleted file mode 100644 index 37a3db749f..0000000000 --- a/repos/effect/.changeset/pre/sql-message-storage-joined-reply-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve reply IDs in SQL-backed `MessageStorage.unprocessedMessagesById` reads. diff --git a/repos/effect/.changeset/pre/sql-migrator-mjs-mts.md b/repos/effect/.changeset/pre/sql-migrator-mjs-mts.md deleted file mode 100644 index 071d942c8a..0000000000 --- a/repos/effect/.changeset/pre/sql-migrator-mjs-mts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Accept `.mjs` and `.mts` migration files in SQL migrator loaders. diff --git a/repos/effect/.changeset/pre/sql-nested-placeholder-cache.md b/repos/effect/.changeset/pre/sql-nested-placeholder-cache.md deleted file mode 100644 index 9788260519..0000000000 --- a/repos/effect/.changeset/pre/sql-nested-placeholder-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix placeholder numbering for cached fragments used in returning helpers. diff --git a/repos/effect/.changeset/pre/sql-optional-span-propagation.md b/repos/effect/.changeset/pre/sql-optional-span-propagation.md deleted file mode 100644 index f925c1ad08..0000000000 --- a/repos/effect/.changeset/pre/sql-optional-span-propagation.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"effect": patch ---- - -Add `Statement.SpanPropagationEnabled` to scope driver span parenting under `sql.execute` for any SQL client. Disabled by default. - -```ts -import { Effect } from "effect" -import { Statement } from "effect/unstable/sql" - -query.pipe(Effect.provideService(Statement.SpanPropagationEnabled, true)) -``` diff --git a/repos/effect/.changeset/pre/sql-returning-identifier.md b/repos/effect/.changeset/pre/sql-returning-identifier.md deleted file mode 100644 index 7922dc865d..0000000000 --- a/repos/effect/.changeset/pre/sql-returning-identifier.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix SQL returning helpers to compile identifiers with dialect-specific escaping. diff --git a/repos/effect/.changeset/pre/sql-runner-requested-shard-results.md b/repos/effect/.changeset/pre/sql-runner-requested-shard-results.md deleted file mode 100644 index bbce8efe4d..0000000000 --- a/repos/effect/.changeset/pre/sql-runner-requested-shard-results.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure PostgreSQL shard acquisition and refresh return only the requested shards. diff --git a/repos/effect/.changeset/pre/sqlite-bun-prepare-error-channel.md b/repos/effect/.changeset/pre/sqlite-bun-prepare-error-channel.md deleted file mode 100644 index ab744c5f45..0000000000 --- a/repos/effect/.changeset/pre/sqlite-bun-prepare-error-channel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-bun": patch ---- - -Fail with a typed `SqlError` when Bun SQLite statement preparation throws (for example a missing table or a syntax error), instead of letting the driver error escape as a defect, closes #2385. diff --git a/repos/effect/.changeset/pre/sqlite-bun-transaction-export.md b/repos/effect/.changeset/pre/sqlite-bun-transaction-export.md deleted file mode 100644 index 52739714a6..0000000000 --- a/repos/effect/.changeset/pre/sqlite-bun-transaction-export.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/sql-sqlite-bun": patch ---- - -Allow database exports inside `withTransaction` to complete instead of waiting indefinitely. The exported snapshot -includes the transaction's uncommitted writes, while exports outside that transaction still wait for it to finish. diff --git a/repos/effect/.changeset/pre/sqlite-client-locking-defaults.md b/repos/effect/.changeset/pre/sqlite-client-locking-defaults.md deleted file mode 100644 index ee01605b9f..0000000000 --- a/repos/effect/.changeset/pre/sqlite-client-locking-defaults.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/sql-sqlite-bun": patch -"@effect/sql-sqlite-node": patch ---- - -Use a configurable five-second busy timeout and immediate transactions by default to avoid SQLite lock failures under concurrent access. Busy waits can block the event loop, while immediate transactions serialize behind other writers. diff --git a/repos/effect/.changeset/pre/sqlite-do-durable-object-transactions.md b/repos/effect/.changeset/pre/sqlite-do-durable-object-transactions.md deleted file mode 100644 index b0016a9fcd..0000000000 --- a/repos/effect/.changeset/pre/sqlite-do-durable-object-transactions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-do": patch ---- - -Support Cloudflare Durable Object SQLite transactions by allowing `SqliteClient` to be configured with `DurableObjectStorage` and routing `withTransaction` through `storage.transaction`. diff --git a/repos/effect/.changeset/pre/sqlite-do-stream-errors.md b/repos/effect/.changeset/pre/sqlite-do-stream-errors.md deleted file mode 100644 index af0b108b00..0000000000 --- a/repos/effect/.changeset/pre/sqlite-do-stream-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-do": patch ---- - -Report synchronous Durable Object SQL streaming failures as recoverable `SqlError` values. diff --git a/repos/effect/.changeset/pre/sqlite-node-unprepared-errors.md b/repos/effect/.changeset/pre/sqlite-node-unprepared-errors.md deleted file mode 100644 index be4ae32fa6..0000000000 --- a/repos/effect/.changeset/pre/sqlite-node-unprepared-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-node": patch ---- - -Capture unprepared statement preparation failures as typed `SqlError`s. diff --git a/repos/effect/.changeset/pre/sqlite-wasm-messageport-startup.md b/repos/effect/.changeset/pre/sqlite-wasm-messageport-startup.md deleted file mode 100644 index bd2530e4c5..0000000000 --- a/repos/effect/.changeset/pre/sqlite-wasm-messageport-startup.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-wasm": patch ---- - -Start incoming MessagePort queues after registering the SQLite WASM client and OPFS worker listeners. Fresh ports, including a SharedWorker's client port, no longer require manual activation to receive ready messages and query replies or process worker requests. diff --git a/repos/effect/.changeset/pre/sqlite-wasm-statement-columns.md b/repos/effect/.changeset/pre/sqlite-wasm-statement-columns.md deleted file mode 100644 index 889b6cc7da..0000000000 --- a/repos/effect/.changeset/pre/sqlite-wasm-statement-columns.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-wasm": patch ---- - -Preserve statement-specific column names in SQLite WASM worker-backed query results. diff --git a/repos/effect/.changeset/pre/sqlite-wasm-worker-error-metadata.md b/repos/effect/.changeset/pre/sqlite-wasm-worker-error-metadata.md deleted file mode 100644 index b4e0486ccc..0000000000 --- a/repos/effect/.changeset/pre/sqlite-wasm-worker-error-metadata.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-wasm": patch ---- - -Preserve native SQLite error codes in OPFS worker replies so the client can classify constraint failures as `ConstraintError` instead of `UnknownError`. For coded worker failures, `SqlError.reason.cause` now contains a `{ message, code }` record instead of a string. diff --git a/repos/effect/.changeset/pre/sqlmodel-insert-decoding-services.md b/repos/effect/.changeset/pre/sqlmodel-insert-decoding-services.md deleted file mode 100644 index 58b1a15a3d..0000000000 --- a/repos/effect/.changeset/pre/sqlmodel-insert-decoding-services.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Include the model's decoding services in the public requirements of `SqlModel.makeResolvers().insert`, alongside its existing input-encoding services. - -This intentionally tightens compile-time checking: previously accepted callers must now provide the services already needed to decode inserted rows at runtime. Provide those services when executing the insert with `SqlResolver.request`. `insertVoid` still requires only input-encoding services, and service-free models need no changes. Runtime behavior is unchanged. diff --git a/repos/effect/.changeset/pre/stable-graph-traversal-config.md b/repos/effect/.changeset/pre/stable-graph-traversal-config.md deleted file mode 100644 index a4663f38af..0000000000 --- a/repos/effect/.changeset/pre/stable-graph-traversal-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Validate Graph traversal radii, isolate traversal start configuration, and prioritize the first supplied DFS root. diff --git a/repos/effect/.changeset/pre/stale-dots-tell.md b/repos/effect/.changeset/pre/stale-dots-tell.md deleted file mode 100644 index c6609de6b0..0000000000 --- a/repos/effect/.changeset/pre/stale-dots-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -improve HttpClient.withRateLimiter initial state tracking diff --git a/repos/effect/.changeset/pre/stale-graph-traversal-skips.md b/repos/effect/.changeset/pre/stale-graph-traversal-skips.md deleted file mode 100644 index 6bf269405b..0000000000 --- a/repos/effect/.changeset/pre/stale-graph-traversal-skips.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix Graph BFS, topological sort, and DFS postorder iterators to skip nodes removed from a MutableGraph without recursive self-calls. diff --git a/repos/effect/.changeset/pre/stale-laws-do.md b/repos/effect/.changeset/pre/stale-laws-do.md deleted file mode 100644 index f114fbec1a..0000000000 --- a/repos/effect/.changeset/pre/stale-laws-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-bun": patch ---- - -optimize bun stream reading diff --git a/repos/effect/.changeset/pre/stale-snakes-know.md b/repos/effect/.changeset/pre/stale-snakes-know.md deleted file mode 100644 index a6272595b3..0000000000 --- a/repos/effect/.changeset/pre/stale-snakes-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -update dependencies diff --git a/repos/effect/.changeset/pre/standard-schema-module.md b/repos/effect/.changeset/pre/standard-schema-module.md deleted file mode 100644 index b3ac3eb109..0000000000 --- a/repos/effect/.changeset/pre/standard-schema-module.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a public `StandardSchema` module containing the vendored Standard Schema V1 specification and remove the direct dependency on `@standard-schema/spec`. diff --git a/repos/effect/.changeset/pre/stream-rechunk-large-source.md b/repos/effect/.changeset/pre/stream-rechunk-large-source.md deleted file mode 100644 index f96dfb7d31..0000000000 --- a/repos/effect/.changeset/pre/stream-rechunk-large-source.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Stream.rechunk` failing on large source chunks. diff --git a/repos/effect/.changeset/pre/strict-areas-end.md b/repos/effect/.changeset/pre/strict-areas-end.md deleted file mode 100644 index c469085e2f..0000000000 --- a/repos/effect/.changeset/pre/strict-areas-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Track ManagedRuntime fibers in a scope diff --git a/repos/effect/.changeset/pre/strict-buckets-hug.md b/repos/effect/.changeset/pre/strict-buckets-hug.md deleted file mode 100644 index 73164ef283..0000000000 --- a/repos/effect/.changeset/pre/strict-buckets-hug.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@effect/ai-openai-compat": patch -"@effect/ai-openrouter": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch ---- - -allow undefined for ai config diff --git a/repos/effect/.changeset/pre/strict-content-length.md b/repos/effect/.changeset/pre/strict-content-length.md deleted file mode 100644 index f4fcb06141..0000000000 --- a/repos/effect/.changeset/pre/strict-content-length.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Parse `Content-Length` metadata strictly across HTTP modules, ignoring malformed or unsafe values instead of coercing them. diff --git a/repos/effect/.changeset/pre/strict-cookie-names.md b/repos/effect/.changeset/pre/strict-cookie-names.md deleted file mode 100644 index 4795f4d71a..0000000000 --- a/repos/effect/.changeset/pre/strict-cookie-names.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ignore `Set-Cookie` headers whose cookie names do not satisfy the RFC 6265 token syntax. diff --git a/repos/effect/.changeset/pre/strip-resolved-approvals.md b/repos/effect/.changeset/pre/strip-resolved-approvals.md deleted file mode 100644 index 4cdf441af0..0000000000 --- a/repos/effect/.changeset/pre/strip-resolved-approvals.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Strip resolved tool approval artifacts from prompt before sending to provider, preventing errors when providers reject pre-resolved approval requests. diff --git a/repos/effect/.changeset/pre/strong-balloons-tickle.md b/repos/effect/.changeset/pre/strong-balloons-tickle.md deleted file mode 100644 index c778264028..0000000000 --- a/repos/effect/.changeset/pre/strong-balloons-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Include toolkit tool handler requirements in AI generation API environment inference. diff --git a/repos/effect/.changeset/pre/strong-bees-queue.md b/repos/effect/.changeset/pre/strong-bees-queue.md deleted file mode 100644 index d7c401842d..0000000000 --- a/repos/effect/.changeset/pre/strong-bees-queue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add the unstable workflow DurableQueue module. diff --git a/repos/effect/.changeset/pre/strong-insects-film.md b/repos/effect/.changeset/pre/strong-insects-film.md deleted file mode 100644 index 075c42f857..0000000000 --- a/repos/effect/.changeset/pre/strong-insects-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure WithTransaction wraps entire rpc handler diff --git a/repos/effect/.changeset/pre/struct-numeric-selection.md b/repos/effect/.changeset/pre/struct-numeric-selection.md deleted file mode 100644 index 7a947d2afd..0000000000 --- a/repos/effect/.changeset/pre/struct-numeric-selection.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor numeric property selectors in Struct selection and mapping utilities. diff --git a/repos/effect/.changeset/pre/struct-record.md b/repos/effect/.changeset/pre/struct-record.md deleted file mode 100644 index 9a6d46a74c..0000000000 --- a/repos/effect/.changeset/pre/struct-record.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Struct: add `Struct.Record` constructor for creating records with the given keys and value. diff --git a/repos/effect/.changeset/pre/structural-schema-nodes.md b/repos/effect/.changeset/pre/structural-schema-nodes.md deleted file mode 100644 index 41d9897d24..0000000000 --- a/repos/effect/.changeset/pre/structural-schema-nodes.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Expose `SchemaAST` nodes, `SchemaIssue` nodes, `SchemaGetter.Getter`, and the `SchemaTransformation` models through structural instance interfaces instead of concrete class declarations. The constructors remain usable with `new` and `instanceof`, but their `prototype` is no longer part of the public TypeScript API. Replace type-level access through a constructor's `prototype` with the corresponding named instance interface, such as `SchemaGetter.Getter`. - -`SchemaAST.Base` is no longer exported. Use `SchemaAST.AST` when accepting any AST node, and use the `SchemaAST.is*` guards to narrow individual variants. diff --git a/repos/effect/.changeset/pre/sunny-ads-hang.md b/repos/effect/.changeset/pre/sunny-ads-hang.md deleted file mode 100644 index d51143ef68..0000000000 --- a/repos/effect/.changeset/pre/sunny-ads-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add ClusterSchema.WithTransaction annotation diff --git a/repos/effect/.changeset/pre/sunny-bikes-sleep.md b/repos/effect/.changeset/pre/sunny-bikes-sleep.md deleted file mode 100644 index 79412b327e..0000000000 --- a/repos/effect/.changeset/pre/sunny-bikes-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -default ws close codes to 1001 in case they are undefined diff --git a/repos/effect/.changeset/pre/sunny-rooms-invent.md b/repos/effect/.changeset/pre/sunny-rooms-invent.md deleted file mode 100644 index 4e6390169f..0000000000 --- a/repos/effect/.changeset/pre/sunny-rooms-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Refine `ExtractServices` to omit tool handler requirements when automatic tool resolution is explicitly disabled through the `disableToolCallResolution` option. diff --git a/repos/effect/.changeset/pre/sweet-donuts-bet.md b/repos/effect/.changeset/pre/sweet-donuts-bet.md deleted file mode 100644 index 3d09822fc6..0000000000 --- a/repos/effect/.changeset/pre/sweet-donuts-bet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add graceful shutdown to http servers diff --git a/repos/effect/.changeset/pre/sweet-hotels-give.md b/repos/effect/.changeset/pre/sweet-hotels-give.md deleted file mode 100644 index a54bddee0f..0000000000 --- a/repos/effect/.changeset/pre/sweet-hotels-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -make EntityResource lazy by default diff --git a/repos/effect/.changeset/pre/sweet-lizards-sing.md b/repos/effect/.changeset/pre/sweet-lizards-sing.md deleted file mode 100644 index 12248afd9f..0000000000 --- a/repos/effect/.changeset/pre/sweet-lizards-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Fix `NodeStream.toString` registering a duplicate `error` event listener. diff --git a/repos/effect/.changeset/pre/sweet-schedules-matter.md b/repos/effect/.changeset/pre/sweet-schedules-matter.md deleted file mode 100644 index dcc8d4a930..0000000000 --- a/repos/effect/.changeset/pre/sweet-schedules-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Update `Schedule.addDelay` and `Schedule.modifyDelay` to receive full schedule metadata instead of separate output and delay arguments. diff --git a/repos/effect/.changeset/pre/sweet-views-learn.md b/repos/effect/.changeset/pre/sweet-views-learn.md deleted file mode 100644 index dda17ba48d..0000000000 --- a/repos/effect/.changeset/pre/sweet-views-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -ensure workflow failures are not squashed by suspension interrupts diff --git a/repos/effect/.changeset/pre/swift-geese-count.md b/repos/effect/.changeset/pre/swift-geese-count.md deleted file mode 100644 index 352cd448eb..0000000000 --- a/repos/effect/.changeset/pre/swift-geese-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the return type of `Channel.runCount` to expose its numeric result. diff --git a/repos/effect/.changeset/pre/swift-pandas-parse.md b/repos/effect/.changeset/pre/swift-pandas-parse.md deleted file mode 100644 index 6bbf259a58..0000000000 --- a/repos/effect/.changeset/pre/swift-pandas-parse.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch -"@effect/platform-browser": patch -"@effect/platform-node": patch ---- - -Vendor the multipart parser as `effect/unstable/http/MultipartParser`, add the Node.js adapter at `@effect/platform-node/NodeMultipartParser`, and remove the external `multipasta` dependency. diff --git a/repos/effect/.changeset/pre/swift-spiders-unpack.md b/repos/effect/.changeset/pre/swift-spiders-unpack.md deleted file mode 100644 index 3f1f957bcd..0000000000 --- a/repos/effect/.changeset/pre/swift-spiders-unpack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Layer.suspend` as a lazy constructor for dynamically choosing a layer while preserving normal layer sharing. diff --git a/repos/effect/.changeset/pre/swift-symbols-stand.md b/repos/effect/.changeset/pre/swift-symbols-stand.md deleted file mode 100644 index 2dbec99111..0000000000 --- a/repos/effect/.changeset/pre/swift-symbols-stand.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -remove rpc client nesting to improve type performance diff --git a/repos/effect/.changeset/pre/synchronized-ref-modify-some-effect-currying.md b/repos/effect/.changeset/pre/synchronized-ref-modify-some-effect-currying.md deleted file mode 100644 index 8691cfd97a..0000000000 --- a/repos/effect/.changeset/pre/synchronized-ref-modify-some-effect-currying.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix the curried `SynchronizedRef.modifySomeEffect` overload to accept only the callback, matching its runtime behavior. diff --git a/repos/effect/.changeset/pre/synchronized-ref-not-ref.md b/repos/effect/.changeset/pre/synchronized-ref-not-ref.md deleted file mode 100644 index 13bee8c918..0000000000 --- a/repos/effect/.changeset/pre/synchronized-ref-not-ref.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Stop declaring `SynchronizedRef` as a subtype of `Ref`, preventing `Ref` combinators from accepting values that do not implement the required runtime representation. diff --git a/repos/effect/.changeset/pre/tagged-error-class-optional-empty-props.md b/repos/effect/.changeset/pre/tagged-error-class-optional-empty-props.md deleted file mode 100644 index 7b63b56e58..0000000000 --- a/repos/effect/.changeset/pre/tagged-error-class-optional-empty-props.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -`Schema.TaggedErrorClass`, `Schema.Class`, and `Schema.ErrorClass` constructors now allow omitting the props argument when all fields have constructor defaults (e.g. `new MyError()` instead of `new MyError({})`). diff --git a/repos/effect/.changeset/pre/tall-hairs-return.md b/repos/effect/.changeset/pre/tall-hairs-return.md deleted file mode 100644 index 551d144e9b..0000000000 --- a/repos/effect/.changeset/pre/tall-hairs-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpServerResponse.toClientResponse` for converting server responses into `HttpClientResponse` values. diff --git a/repos/effect/.changeset/pre/tall-ideas-fix.md b/repos/effect/.changeset/pre/tall-ideas-fix.md deleted file mode 100644 index 0cf7925e29..0000000000 --- a/repos/effect/.changeset/pre/tall-ideas-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Don’t create a table when it’s not needed diff --git a/repos/effect/.changeset/pre/tall-mails-listen.md b/repos/effect/.changeset/pre/tall-mails-listen.md deleted file mode 100644 index 96d7af428f..0000000000 --- a/repos/effect/.changeset/pre/tall-mails-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.catchNoSuchElement`, a renamed port of v3 `Effect.optionFromOptional` that converts `NoSuchElementError` failures into `Option.none`. diff --git a/repos/effect/.changeset/pre/tall-melons-shave.md b/repos/effect/.changeset/pre/tall-melons-shave.md deleted file mode 100644 index cdb764935b..0000000000 --- a/repos/effect/.changeset/pre/tall-melons-shave.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Add low-level PostgreSQL protocol, binary type, and authentication codecs to `@effect/sql-pg`. - -`PgProtocol` encodes protocol 3.0 messages and incrementally parses backend frames. Its stateful parser throws terminal errors. `PgTypes` handles binary scalar and one-dimensional array OIDs; its public codecs return typed `Result` failures, while parser field readers use an internal throwing fast path. `PgAuth` implements MD5 and SCRAM-SHA-256 with typed `Result` failures. - -Encoded frames and decoded byte fields are stable views over internal buffers. Copy data that must outlive its message. `PgClient` remains unchanged and still uses `pg` at runtime. diff --git a/repos/effect/.changeset/pre/tall-queens-cheer.md b/repos/effect/.changeset/pre/tall-queens-cheer.md deleted file mode 100644 index cffebd7952..0000000000 --- a/repos/effect/.changeset/pre/tall-queens-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `UrlParams.Input` usage to accept interface-typed records in HTTP client and server helpers while keeping coercion constraints for url parameter values. diff --git a/repos/effect/.changeset/pre/tall-wombats-wave.md b/repos/effect/.changeset/pre/tall-wombats-wave.md deleted file mode 100644 index 8bf297ccee..0000000000 --- a/repos/effect/.changeset/pre/tall-wombats-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix ai LanguageModel streaming finish parts so finish events are always emitted when a toolkit is provided. diff --git a/repos/effect/.changeset/pre/tame-json-schema-intersections.md b/repos/effect/.changeset/pre/tame-json-schema-intersections.md deleted file mode 100644 index 49d9af01cf..0000000000 --- a/repos/effect/.changeset/pre/tame-json-schema-intersections.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"effect": patch ---- - -Fix JSON Schema imports: - -- Type-specific keywords no longer imply a type. For example, `minLength` validates strings without rejecting - non-string values. -- Constraints next to `const`, `enum`, and `$ref` are now applied instead of being ignored. -- Disjoint and linear union intersections are imported without a Cartesian expansion. Other overlapping union - intersections fail with an explicit error. -- References to definitions without unions no longer make otherwise linear intersections fail. -- Imported `oneOf` schemas remain `oneOf` when exported again. -- `minItems` is preserved when `prefixItems` does not fully enforce it. diff --git a/repos/effect/.changeset/pre/tangy-colts-lose.md b/repos/effect/.changeset/pre/tangy-colts-lose.md deleted file mode 100644 index 9678f5343a..0000000000 --- a/repos/effect/.changeset/pre/tangy-colts-lose.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -simplify NodeChildSpawner stdout streams diff --git a/repos/effect/.changeset/pre/tangy-plants-run.md b/repos/effect/.changeset/pre/tangy-plants-run.md deleted file mode 100644 index 111cd174bb..0000000000 --- a/repos/effect/.changeset/pre/tangy-plants-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -only interrupt cache lookup when all awaiters are gone diff --git a/repos/effect/.changeset/pre/tap-defect-saved-error-inference.md b/repos/effect/.changeset/pre/tap-defect-saved-error-inference.md deleted file mode 100644 index 9c85a3983a..0000000000 --- a/repos/effect/.changeset/pre/tap-defect-saved-error-inference.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve the source error type when a saved `Effect.tapDefect` operator is applied. The source error is now inferred from each application instead of when the operator is created. Runtime behavior is unchanged. diff --git a/repos/effect/.changeset/pre/tasty-comics-send.md b/repos/effect/.changeset/pre/tasty-comics-send.md deleted file mode 100644 index 78bed2e768..0000000000 --- a/repos/effect/.changeset/pre/tasty-comics-send.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -improve runSync error when executing async effects diff --git a/repos/effect/.changeset/pre/tasty-moments-post.md b/repos/effect/.changeset/pre/tasty-moments-post.md deleted file mode 100644 index 8a2d4e14d9..0000000000 --- a/repos/effect/.changeset/pre/tasty-moments-post.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Effect.setContext for fully replacing the fiber context diff --git a/repos/effect/.changeset/pre/tcp-schema-binary-default.md b/repos/effect/.changeset/pre/tcp-schema-binary-default.md deleted file mode 100644 index d0a2e0072f..0000000000 --- a/repos/effect/.changeset/pre/tcp-schema-binary-default.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"@effect/platform-bun": patch -"@effect/platform-deno": patch -"@effect/platform-node": patch -"effect": patch ---- - -Use SchemaBinary as the default RPC serialization for TCP cluster connections, including configurable frame limits. - -Cluster payloads are encoded with the binary codec on the wire. When a persisted reply cannot be encoded for JSON storage, the defect fallback that storage records is now also the reply delivered to waiting callers, so live replies always match what was persisted. - -SchemaBinary codecs are memoized by schema identity and wire mode, so per-message codec requests reuse the derived codec instead of rebuilding it. diff --git a/repos/effect/.changeset/pre/template-literal-parts-without-encoding.md b/repos/effect/.changeset/pre/template-literal-parts-without-encoding.md deleted file mode 100644 index 246f3b36a2..0000000000 --- a/repos/effect/.changeset/pre/template-literal-parts-without-encoding.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"effect": patch ---- - -Separate template literal validation from transformed tuple parsing. `TemplateLiteralParser` now propagates its parts' decoding and encoding service requirements. - -### Breaking changes - -`Schema.TemplateLiteral` and `SchemaAST.TemplateLiteral` now throw during construction when a part contains an encoding, including inside unions and nested templates. This also rejects transformations whose decoded and encoded types are equal. Brands and supported checks without encodings remain valid. - -Use `Schema.Literals([0, 1])` to describe bit spellings or `Schema.Finite` to describe finite numeric spellings. Use `Schema.TemplateLiteralParser` when you need to decode transformed parts into a tuple. Explicit `Schema.toType` or `Schema.toEncoded` projections can remove an encoding, but do not necessarily preserve the strings accepted by the old template. For example, a `Finite` part rejects the empty segment accepted by `FiniteFromString`. - -`Schema.toEncoded(Schema.TemplateLiteralParser(...))` now validates the structure of the template instead of accepting any string. Use `Schema.String` when unrestricted strings are intended. - -When parser parts require services, provide those services to the corresponding decoding or encoding effect. These requirements were previously omitted from the parser's types. diff --git a/repos/effect/.changeset/pre/ten-kings-fry.md b/repos/effect/.changeset/pre/ten-kings-fry.md deleted file mode 100644 index 593dc94600..0000000000 --- a/repos/effect/.changeset/pre/ten-kings-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix empty body decoding in HttpApiBuilder diff --git a/repos/effect/.changeset/pre/tender-deserts-pull.md b/repos/effect/.changeset/pre/tender-deserts-pull.md deleted file mode 100644 index c7fda450b7..0000000000 --- a/repos/effect/.changeset/pre/tender-deserts-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject truncated MessagePack frames at the end of a stream. diff --git a/repos/effect/.changeset/pre/tender-files-complete.md b/repos/effect/.changeset/pre/tender-files-complete.md deleted file mode 100644 index 9b9210b4a1..0000000000 --- a/repos/effect/.changeset/pre/tender-files-complete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve file and directory semantics in CLI completion descriptors. diff --git a/repos/effect/.changeset/pre/tender-points-sleep.md b/repos/effect/.changeset/pre/tender-points-sleep.md deleted file mode 100644 index 3206a87c20..0000000000 --- a/repos/effect/.changeset/pre/tender-points-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Generate unique persisted paths for multipart files with duplicate filenames. diff --git a/repos/effect/.changeset/pre/terminate-openai-failed-streams.md b/repos/effect/.changeset/pre/terminate-openai-failed-streams.md deleted file mode 100644 index 66599bb5f3..0000000000 --- a/repos/effect/.changeset/pre/terminate-openai-failed-streams.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -Terminate OpenAI HTTP and WebSocket response streams when a `response.failed` event arrives. diff --git a/repos/effect/.changeset/pre/testschema-own-field-asts.md b/repos/effect/.changeset/pre/testschema-own-field-asts.md deleted file mode 100644 index 42b34e7520..0000000000 --- a/repos/effect/.changeset/pre/testschema-own-field-asts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `TestSchema.Asserts.ast.fields.equals` to compare ASTs for all own struct fields, including symbol and non-enumerable keys. Equivalent field schemas now compare equally regardless of schema instance identity, while differing ASTs and distinct symbol keys remain unequal. diff --git a/repos/effect/.changeset/pre/theme-cli-prompts.md b/repos/effect/.changeset/pre/theme-cli-prompts.md deleted file mode 100644 index 45ea5758b2..0000000000 --- a/repos/effect/.changeset/pre/theme-cli-prompts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Replace per-prompt prefix options with a context-based theme for CLI prompt symbols and colors. diff --git a/repos/effect/.changeset/pre/thick-pandas-wait.md b/repos/effect/.changeset/pre/thick-pandas-wait.md deleted file mode 100644 index 5ddc891451..0000000000 --- a/repos/effect/.changeset/pre/thick-pandas-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add default value support to CLI integer prompts. diff --git a/repos/effect/.changeset/pre/thin-ducks-wonder.md b/repos/effect/.changeset/pre/thin-ducks-wonder.md deleted file mode 100644 index 2176226f91..0000000000 --- a/repos/effect/.changeset/pre/thin-ducks-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Rename `HttpClient.retryTransient` option `mode` to `retryOn` and rename `"both"` to `"errors-and-responses"`. diff --git a/repos/effect/.changeset/pre/thin-ends-hug.md b/repos/effect/.changeset/pre/thin-ends-hug.md deleted file mode 100644 index e3d613b8a2..0000000000 --- a/repos/effect/.changeset/pre/thin-ends-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -dispatch websocket events directly diff --git a/repos/effect/.changeset/pre/thirty-ducks-go.md b/repos/effect/.changeset/pre/thirty-ducks-go.md deleted file mode 100644 index b3f3aa17e3..0000000000 --- a/repos/effect/.changeset/pre/thirty-ducks-go.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -HttpClient.withRateLimiter adds delay from retry-after headers diff --git a/repos/effect/.changeset/pre/thirty-forks-march.md b/repos/effect/.changeset/pre/thirty-forks-march.md deleted file mode 100644 index 249bb675d1..0000000000 --- a/repos/effect/.changeset/pre/thirty-forks-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Drop unreachable concurrency guard in iteratorEagerImpl diff --git a/repos/effect/.changeset/pre/thirty-pans-love.md b/repos/effect/.changeset/pre/thirty-pans-love.md deleted file mode 100644 index 0d0a4f9c14..0000000000 --- a/repos/effect/.changeset/pre/thirty-pans-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -allow Model.Class for indexeddb schemas diff --git a/repos/effect/.changeset/pre/three-corners-sort.md b/repos/effect/.changeset/pre/three-corners-sort.md deleted file mode 100644 index ceb67c6a70..0000000000 --- a/repos/effect/.changeset/pre/three-corners-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -port Url module from v3 diff --git a/repos/effect/.changeset/pre/three-ravens-jam.md b/repos/effect/.changeset/pre/three-ravens-jam.md deleted file mode 100644 index ba528ccbfe..0000000000 --- a/repos/effect/.changeset/pre/three-ravens-jam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Effect.findFirst` and `Effect.findFirstFilter` for short-circuiting effectful searches over iterables. diff --git a/repos/effect/.changeset/pre/three-tomatoes-wave.md b/repos/effect/.changeset/pre/three-tomatoes-wave.md deleted file mode 100644 index 886f44fd49..0000000000 --- a/repos/effect/.changeset/pre/three-tomatoes-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `unstable/sql/SqlSchema` request input typing so `findAll` and `findNonEmpty` accept `Request["Type"]` instead of `Request["Encoded"]`. diff --git a/repos/effect/.changeset/pre/tidy-apples-rest.md b/repos/effect/.changeset/pre/tidy-apples-rest.md deleted file mode 100644 index 1c92897332..0000000000 --- a/repos/effect/.changeset/pre/tidy-apples-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Round Redis persistence TTLs up to whole milliseconds before passing them to integer-only expiration commands. diff --git a/repos/effect/.changeset/pre/tidy-carpets-smile.md b/repos/effect/.changeset/pre/tidy-carpets-smile.md deleted file mode 100644 index af1b48da15..0000000000 --- a/repos/effect/.changeset/pre/tidy-carpets-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent malformed encoded multipart filenames from throwing during parsing. diff --git a/repos/effect/.changeset/pre/tidy-cats-smile.md b/repos/effect/.changeset/pre/tidy-cats-smile.md deleted file mode 100644 index 951a09ca7b..0000000000 --- a/repos/effect/.changeset/pre/tidy-cats-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix unencrypted event log conflict scanning to inspect the newer history suffix. diff --git a/repos/effect/.changeset/pre/tidy-cats-stream.md b/repos/effect/.changeset/pre/tidy-cats-stream.md deleted file mode 100644 index c14e87c6a1..0000000000 --- a/repos/effect/.changeset/pre/tidy-cats-stream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve lexical ordering in streaming template interpolation. diff --git a/repos/effect/.changeset/pre/tidy-codecs-inherit.md b/repos/effect/.changeset/pre/tidy-codecs-inherit.md deleted file mode 100644 index 137e05fd86..0000000000 --- a/repos/effect/.changeset/pre/tidy-codecs-inherit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -When canonical JSON derivation adds a transformation for a schema without a direct JSON representation, keep source checks and annotations on the source side. This prevents duplicate check execution and ensures generated JSON Schema documents describe only the encoded target, closes #7192. diff --git a/repos/effect/.changeset/pre/tidy-dates-smile.md b/repos/effect/.changeset/pre/tidy-dates-smile.md deleted file mode 100644 index 529a47fdf1..0000000000 --- a/repos/effect/.changeset/pre/tidy-dates-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Correct year, ordinal, and meridiem date-mask formatting. diff --git a/repos/effect/.changeset/pre/tidy-floats-edit.md b/repos/effect/.changeset/pre/tidy-floats-edit.md deleted file mode 100644 index 10924f810e..0000000000 --- a/repos/effect/.changeset/pre/tidy-floats-edit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve fractional leading zeros while editing float prompts. diff --git a/repos/effect/.changeset/pre/tidy-foxes-own.md b/repos/effect/.changeset/pre/tidy-foxes-own.md deleted file mode 100644 index 8a4f4b85ec..0000000000 --- a/repos/effect/.changeset/pre/tidy-foxes-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -clean up otlp config diff --git a/repos/effect/.changeset/pre/tidy-geese-release.md b/repos/effect/.changeset/pre/tidy-geese-release.md deleted file mode 100644 index 9b2cb9701e..0000000000 --- a/repos/effect/.changeset/pre/tidy-geese-release.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `RcRef` leaking resources acquired before a failed acquisition. diff --git a/repos/effect/.changeset/pre/tidy-graphs-cycle.md b/repos/effect/.changeset/pre/tidy-graphs-cycle.md deleted file mode 100644 index 9d4cb2d1a6..0000000000 --- a/repos/effect/.changeset/pre/tidy-graphs-cycle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Throw `GraphError` when a negative cycle affects a Bellman-Ford target, reserving `Option.none()` for unreachable paths. diff --git a/repos/effect/.changeset/pre/tidy-icons-glow.md b/repos/effect/.changeset/pre/tidy-icons-glow.md deleted file mode 100644 index dde7979233..0000000000 --- a/repos/effect/.changeset/pre/tidy-icons-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a `Config.literals` convenience constructor for `Schema.Literals`, closes #2091. diff --git a/repos/effect/.changeset/pre/tidy-int32-annotations.md b/repos/effect/.changeset/pre/tidy-int32-annotations.md deleted file mode 100644 index ca99cdd742..0000000000 --- a/repos/effect/.changeset/pre/tidy-int32-annotations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `isInt32` to apply custom annotations only to its filter group. diff --git a/repos/effect/.changeset/pre/tidy-json-objects.md b/repos/effect/.changeset/pre/tidy-json-objects.md deleted file mode 100644 index 80e7463706..0000000000 --- a/repos/effect/.changeset/pre/tidy-json-objects.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Add `Schema.JsonObject` for readonly string-keyed records containing JSON-compatible values. This provides a canonical, -reusable schema instead of requiring callers to repeatedly compose `Schema.Record(Schema.String, Schema.Json)`. diff --git a/repos/effect/.changeset/pre/tidy-json-schema-object-scopes.md b/repos/effect/.changeset/pre/tidy-json-schema-object-scopes.md deleted file mode 100644 index 2d3f1b7b41..0000000000 --- a/repos/effect/.changeset/pre/tidy-json-schema-object-scopes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve JSON Schema object keyword scopes when importing `allOf` intersections, including closed empty objects and required-only keys. Emit intersecting index signatures without weakening their constraints, and reject object scope intersections that cannot be represented faithfully. diff --git a/repos/effect/.changeset/pre/tidy-lions-smile.md b/repos/effect/.changeset/pre/tidy-lions-smile.md deleted file mode 100644 index 871c3ab65c..0000000000 --- a/repos/effect/.changeset/pre/tidy-lions-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-libsql": patch ---- - -Release transaction serialization when beginning a libSQL transaction fails, allowing later operations to retry. diff --git a/repos/effect/.changeset/pre/tidy-matches-select.md b/repos/effect/.changeset/pre/tidy-matches-select.md deleted file mode 100644 index 985f71da0b..0000000000 --- a/repos/effect/.changeset/pre/tidy-matches-select.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Match.fn` for reusable matchers that select a value from multiple arguments. diff --git a/repos/effect/.changeset/pre/tidy-mice-grin.md b/repos/effect/.changeset/pre/tidy-mice-grin.md deleted file mode 100644 index 1dcc53752c..0000000000 --- a/repos/effect/.changeset/pre/tidy-mice-grin.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@effect/platform-node-shared": patch -"effect": patch ---- - -remove file descriptor type diff --git a/repos/effect/.changeset/pre/tidy-pandas-smile.md b/repos/effect/.changeset/pre/tidy-pandas-smile.md deleted file mode 100644 index 73f4b88032..0000000000 --- a/repos/effect/.changeset/pre/tidy-pandas-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `HttpApiError.UnprocessableEntity` and `HttpApiError.UnprocessableEntityNoContent` for status 422 responses. diff --git a/repos/effect/.changeset/pre/tidy-parts-narrow.md b/repos/effect/.changeset/pre/tidy-parts-narrow.md deleted file mode 100644 index 7e0cb7c7e9..0000000000 --- a/repos/effect/.changeset/pre/tidy-parts-narrow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix response tool part assignability after narrowing generic intersected tool records. diff --git a/repos/effect/.changeset/pre/tidy-plums-remember.md b/repos/effect/.changeset/pre/tidy-plums-remember.md deleted file mode 100644 index e9361ab064..0000000000 --- a/repos/effect/.changeset/pre/tidy-plums-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve Schema representation identity, anonymous-reference eligibility, and JSON Schema alias finalization. diff --git a/repos/effect/.changeset/pre/tidy-ravens-import.md b/repos/effect/.changeset/pre/tidy-ravens-import.md deleted file mode 100644 index 58265b76b7..0000000000 --- a/repos/effect/.changeset/pre/tidy-ravens-import.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Reject unsupported JSON Schema validation keywords and object or array `const` / `enum` values during import instead of -silently weakening validation. diff --git a/repos/effect/.changeset/pre/tidy-schema-errors.md b/repos/effect/.changeset/pre/tidy-schema-errors.md deleted file mode 100644 index 1ad564cebd..0000000000 --- a/repos/effect/.changeset/pre/tidy-schema-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Move `SchemaError` into the `Schema` module and remove the standalone `SchemaError` module. diff --git a/repos/effect/.changeset/pre/tidy-scoped-refs-close.md b/repos/effect/.changeset/pre/tidy-scoped-refs-close.md deleted file mode 100644 index 4bb41bafa5..0000000000 --- a/repos/effect/.changeset/pre/tidy-scoped-refs-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure `ScopedRef.set` releases a replacement when the previous value's finalizer defects. diff --git a/repos/effect/.changeset/pre/tidy-spans-rest.md b/repos/effect/.changeset/pre/tidy-spans-rest.md deleted file mode 100644 index 7dc60bafb5..0000000000 --- a/repos/effect/.changeset/pre/tidy-spans-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/opentelemetry": patch ---- - -Fix wrapped spans treating non-error OpenTelemetry statuses as errors. diff --git a/repos/effect/.changeset/pre/tidy-sse-events.md b/repos/effect/.changeset/pre/tidy-sse-events.md deleted file mode 100644 index 2ccb437baf..0000000000 --- a/repos/effect/.changeset/pre/tidy-sse-events.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/openapi-generator": patch ---- - -Decode Effect SSE event schemas as complete events, including reserved failure events, in generated HTTP clients. diff --git a/repos/effect/.changeset/pre/tidy-stacks-encode.md b/repos/effect/.changeset/pre/tidy-stacks-encode.md deleted file mode 100644 index d9e9a2438d..0000000000 --- a/repos/effect/.changeset/pre/tidy-stacks-encode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Encode HTTP API client path parameters when building request URLs. diff --git a/repos/effect/.changeset/pre/tidy-stars-drive.md b/repos/effect/.changeset/pre/tidy-stars-drive.md deleted file mode 100644 index b31ea0808d..0000000000 --- a/repos/effect/.changeset/pre/tidy-stars-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `TestClock.currentTimeNanosUnsafe()` to floor fractional millisecond instants before converting them to `BigInt`. diff --git a/repos/effect/.changeset/pre/tidy-tools-finish.md b/repos/effect/.changeset/pre/tidy-tools-finish.md deleted file mode 100644 index 3a66ee3538..0000000000 --- a/repos/effect/.changeset/pre/tidy-tools-finish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Make automatic tool resolution interruption-safe for incomplete language model responses. diff --git a/repos/effect/.changeset/pre/tidy-tools-handle.md b/repos/effect/.changeset/pre/tidy-tools-handle.md deleted file mode 100644 index 155071c9f5..0000000000 --- a/repos/effect/.changeset/pre/tidy-tools-handle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Preserve encoded AI tool call parameters when automatic tool call resolution is disabled, and update -`Toolkit.handle` to accept the encoded parameter type it decodes at runtime. diff --git a/repos/effect/.changeset/pre/tidy-tools-juggle.md b/repos/effect/.changeset/pre/tidy-tools-juggle.md deleted file mode 100644 index 439548407f..0000000000 --- a/repos/effect/.changeset/pre/tidy-tools-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Ensure discarded non-persisted cluster messages complete without waiting for the entity reply. diff --git a/repos/effect/.changeset/pre/tidy-tuples-rest.md b/repos/effect/.changeset/pre/tidy-tuples-rest.md deleted file mode 100644 index 2adc9de9c3..0000000000 --- a/repos/effect/.changeset/pre/tidy-tuples-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve `maxItems` semantics when importing JSON Schema `prefixItems`. diff --git a/repos/effect/.changeset/pre/tidy-wasps-wait.md b/repos/effect/.changeset/pre/tidy-wasps-wait.md deleted file mode 100644 index 192e4ed6ff..0000000000 --- a/repos/effect/.changeset/pre/tidy-wasps-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-sqlite-wasm": patch ---- - -Settle pending SQLite WASM requests before replacing failed workers. diff --git a/repos/effect/.changeset/pre/timeout-error-message.md b/repos/effect/.changeset/pre/timeout-error-message.md deleted file mode 100644 index acfb35d2b1..0000000000 --- a/repos/effect/.changeset/pre/timeout-error-message.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Restore the `Effect.timeout` error message so `TimeoutError` includes the elapsed duration. diff --git a/repos/effect/.changeset/pre/timeout-or-else-cleanup-before-fallback.md b/repos/effect/.changeset/pre/timeout-or-else-cleanup-before-fallback.md deleted file mode 100644 index 02eb8f7206..0000000000 --- a/repos/effect/.changeset/pre/timeout-or-else-cleanup-before-fallback.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.timeoutOrElse` to finish interrupting the source before evaluating the fallback, preventing the source from winning after the timeout. - -Fallbacks now run in the caller fiber and inherit its interruptibility and supervision. diff --git a/repos/effect/.changeset/pre/tiny-buckets-wave.md b/repos/effect/.changeset/pre/tiny-buckets-wave.md deleted file mode 100644 index b01e82b815..0000000000 --- a/repos/effect/.changeset/pre/tiny-buckets-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Underline the active label in CLI multi-select prompts and add a scratchpad example for manual verification. diff --git a/repos/effect/.changeset/pre/tiny-dodos-juggle.md b/repos/effect/.changeset/pre/tiny-dodos-juggle.md deleted file mode 100644 index c721b66705..0000000000 --- a/repos/effect/.changeset/pre/tiny-dodos-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Honor custom split and strip regular expressions passed to `String.noCase`. diff --git a/repos/effect/.changeset/pre/tiny-files-flow.md b/repos/effect/.changeset/pre/tiny-files-flow.md deleted file mode 100644 index a8f71a0d8a..0000000000 --- a/repos/effect/.changeset/pre/tiny-files-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix partial file-backed HTTP bodies to report the selected byte range as their content length. diff --git a/repos/effect/.changeset/pre/tiny-lilies-flash.md b/repos/effect/.changeset/pre/tiny-lilies-flash.md deleted file mode 100644 index f636865889..0000000000 --- a/repos/effect/.changeset/pre/tiny-lilies-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -fix idb entries transaction diff --git a/repos/effect/.changeset/pre/tiny-lizards-correct.md b/repos/effect/.changeset/pre/tiny-lizards-correct.md deleted file mode 100644 index 82da87418d..0000000000 --- a/repos/effect/.changeset/pre/tiny-lizards-correct.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -MCP HTTP servers now reject requests sent before initialization with the required lifecycle response. diff --git a/repos/effect/.changeset/pre/tiny-rabbits-smile.md b/repos/effect/.changeset/pre/tiny-rabbits-smile.md deleted file mode 100644 index 9cc27e3780..0000000000 --- a/repos/effect/.changeset/pre/tiny-rabbits-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Relax `Ndjson` byte-stream channel signatures to accept plain `Uint8Array`. diff --git a/repos/effect/.changeset/pre/to-codec-json-schema.md b/repos/effect/.changeset/pre/to-codec-json-schema.md deleted file mode 100644 index 2cee85ea0d..0000000000 --- a/repos/effect/.changeset/pre/to-codec-json-schema.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Expose the original input schema on `Schema.toType`, `Schema.toEncoded`, `Schema.toCodecJson`, and `Schema.toCodecStringTree` results via the `schema` property. This aligns these schema wrappers with other wrappers that retain their source schema for type-level and runtime introspection. diff --git a/repos/effect/.changeset/pre/tocodecjson-return-json-type.md b/repos/effect/.changeset/pre/tocodecjson-return-json-type.md deleted file mode 100644 index 401d58c2c9..0000000000 --- a/repos/effect/.changeset/pre/tocodecjson-return-json-type.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"effect": patch -"@effect/platform-browser": patch -"@effect/platform-bun": patch -"@effect/platform-node": patch ---- - -Schema: `toCodecJson` now returns `Codec` instead of `Codec`. - -Http: the `json` property on `HttpIncomingMessage`, `HttpClientResponse`, `HttpServerRequest`, and `HttpServerResponse` now returns `Effect` instead of `Effect`. diff --git a/repos/effect/.changeset/pre/token-bucket-elapsed-refill.md b/repos/effect/.changeset/pre/token-bucket-elapsed-refill.md deleted file mode 100644 index 6ee54efc45..0000000000 --- a/repos/effect/.changeset/pre/token-bucket-elapsed-refill.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Fix token-bucket `retryAfter`, `delay` and `resetAfter` in the memory and Redis stores. Timing now follows whole-token refill boundaries and accounts for elapsed time, including fractional token costs. Redis preserves signed fractional counts and keeps keys until capacity actually refills. - -`RateLimiterStore.tokenBucket` now returns `[remaining, elapsedMillis]` instead of `remaining`. Custom stores must return both values from the same atomic operation; see the `tokenBucket` docs for the contract. Returning `[remaining, 0]` keeps the old timing bug. diff --git a/repos/effect/.changeset/pre/tokenizer-whole-prompt-truncation.md b/repos/effect/.changeset/pre/tokenizer-whole-prompt-truncation.md deleted file mode 100644 index 6258e5a228..0000000000 --- a/repos/effect/.changeset/pre/tokenizer-whole-prompt-truncation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Tokenizer.truncate` to account for token costs between messages. diff --git a/repos/effect/.changeset/pre/tool-get-json-schema-tests.md b/repos/effect/.changeset/pre/tool-get-json-schema-tests.md deleted file mode 100644 index f94d0cf776..0000000000 --- a/repos/effect/.changeset/pre/tool-get-json-schema-tests.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Tool.make` type and runtime behavior when `parameters` is not provided. diff --git a/repos/effect/.changeset/pre/tool-result-branch-encoding.md b/repos/effect/.changeset/pre/tool-result-branch-encoding.md deleted file mode 100644 index 5c609b07a7..0000000000 --- a/repos/effect/.changeset/pre/tool-result-branch-encoding.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Encode tool results with the schema for their known success or failure branch. diff --git a/repos/effect/.changeset/pre/tough-graphs-cache.md b/repos/effect/.changeset/pre/tough-graphs-cache.md deleted file mode 100644 index 010a1f8311..0000000000 --- a/repos/effect/.changeset/pre/tough-graphs-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix mutable graph cache consistency and guard weighted pathfinding against inconsistent snapshots and numeric overflow. diff --git a/repos/effect/.changeset/pre/tough-rooms-camp.md b/repos/effect/.changeset/pre/tough-rooms-camp.md deleted file mode 100644 index 26ae61b4f8..0000000000 --- a/repos/effect/.changeset/pre/tough-rooms-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject NDJSON values without a JSON representation. diff --git a/repos/effect/.changeset/pre/tough-taxis-own.md b/repos/effect/.changeset/pre/tough-taxis-own.md deleted file mode 100644 index 74191d3109..0000000000 --- a/repos/effect/.changeset/pre/tough-taxis-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Harden JSON-RPC wire message classification against inherited properties. diff --git a/repos/effect/.changeset/pre/tracer-disabled-timing.md b/repos/effect/.changeset/pre/tracer-disabled-timing.md deleted file mode 100644 index 42bf23ef58..0000000000 --- a/repos/effect/.changeset/pre/tracer-disabled-timing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Keep span end times at zero when tracer timing is disabled. diff --git a/repos/effect/.changeset/pre/tracer-perf.md b/repos/effect/.changeset/pre/tracer-perf.md deleted file mode 100644 index 3a85e6d80e..0000000000 --- a/repos/effect/.changeset/pre/tracer-perf.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Improve tracing performance in span creation and HTTP middleware. diff --git a/repos/effect/.changeset/pre/track-mapped-error-domain.md b/repos/effect/.changeset/pre/track-mapped-error-domain.md deleted file mode 100644 index 3c4c9a2c39..0000000000 --- a/repos/effect/.changeset/pre/track-mapped-error-domain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.track(metric, mapper)` to reject source errors the mapper cannot handle. diff --git a/repos/effect/.changeset/pre/trie-remove-valued-prefix.md b/repos/effect/.changeset/pre/trie-remove-valued-prefix.md deleted file mode 100644 index beabebf6c5..0000000000 --- a/repos/effect/.changeset/pre/trie-remove-valued-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve valued prefix nodes when removing a longer key from a `Trie`. diff --git a/repos/effect/.changeset/pre/true-actors-battle.md b/repos/effect/.changeset/pre/true-actors-battle.md deleted file mode 100644 index b93e185826..0000000000 --- a/repos/effect/.changeset/pre/true-actors-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -fix fs.stat when blksize is undefined diff --git a/repos/effect/.changeset/pre/try-direct-error-types.md b/repos/effect/.changeset/pre/try-direct-error-types.md deleted file mode 100644 index 659c45466a..0000000000 --- a/repos/effect/.changeset/pre/try-direct-error-types.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"effect": patch ---- - -Correct the error types of `Effect.try` and `Effect.tryPromise`. Direct function forms retain `Cause.UnknownError`, while `{ try, catch }` options use the error type returned by `catch`. - -Explicit two-generic direct calls, union-valued arguments, and generic aliases that combine the two forms no longer compile. Use `{ try, catch }` with a real error mapper, or narrow a union before calling the constructor. - -Runtime behavior, callback arguments, and error mapping are unchanged. diff --git a/repos/effect/.changeset/pre/try-promise-catch-defect.md b/repos/effect/.changeset/pre/try-promise-catch-defect.md deleted file mode 100644 index 0d6d52edde..0000000000 --- a/repos/effect/.changeset/pre/try-promise-catch-defect.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.try` thunk usage and `Effect.tryPromise` mapper and signal handling defects. - -`Effect.try` now supports passing a thunk directly, matching `Effect.tryPromise`. Thrown values from direct-thunk usage are mapped to `Cause.UnknownError`. - -When a promise handled by `Effect.tryPromise` rejected and the custom `catch` mapper threw while mapping that rejection, the effect could remain pending and produce an unhandled rejection. The mapper is now guarded consistently with the synchronous throw path, so a thrown mapper error becomes an Effect defect. The JSDoc for `Effect.try` and `Effect.tryPromise` was also corrected. - -`Effect.tryPromise` now also only creates an `AbortController` when the wrapped thunk declares an `AbortSignal` parameter. diff --git a/repos/effect/.changeset/pre/tuple-optional-evolve-result-types.md b/repos/effect/.changeset/pre/tuple-optional-evolve-result-types.md deleted file mode 100644 index c770854fd5..0000000000 --- a/repos/effect/.changeset/pre/tuple-optional-evolve-result-types.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Correct `Tuple.evolve` result types when a transform may be `undefined`. The result now includes both the transformed and unchanged element types, matching the existing runtime behavior. Accepted inputs and runtime behavior are unchanged. - -Code relying on the previous, incorrect result type must handle both outcomes. For example, a number-to-string transform that may be absent now produces `number | string`, so callers assuming a number-only result must adjust. diff --git a/repos/effect/.changeset/pre/twelve-dragons-move.md b/repos/effect/.changeset/pre/twelve-dragons-move.md deleted file mode 100644 index bf9c5af1a1..0000000000 --- a/repos/effect/.changeset/pre/twelve-dragons-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -deduplicate SqlResolver.findById requests diff --git a/repos/effect/.changeset/pre/twenty-buttons-cheer.md b/repos/effect/.changeset/pre/twenty-buttons-cheer.md deleted file mode 100644 index d1b709b46e..0000000000 --- a/repos/effect/.changeset/pre/twenty-buttons-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -expose more atom Node properties diff --git a/repos/effect/.changeset/pre/twenty-facts-laugh.md b/repos/effect/.changeset/pre/twenty-facts-laugh.md deleted file mode 100644 index 7430b4f99f..0000000000 --- a/repos/effect/.changeset/pre/twenty-facts-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -use Sets to track atom relationships diff --git a/repos/effect/.changeset/pre/twenty-garlics-marry.md b/repos/effect/.changeset/pre/twenty-garlics-marry.md deleted file mode 100644 index 96087da85b..0000000000 --- a/repos/effect/.changeset/pre/twenty-garlics-marry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpRouter.Middleware.layer` to provide request error services for errors declared in `handles`, and expose global -middleware errors from `HttpRouter.toHttpEffect`. diff --git a/repos/effect/.changeset/pre/two-roses-double.md b/repos/effect/.changeset/pre/two-roses-double.md deleted file mode 100644 index 6efc5f52b0..0000000000 --- a/repos/effect/.changeset/pre/two-roses-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -improve openai websocket error status diff --git a/repos/effect/.changeset/pre/undici-response-form-data.md b/repos/effect/.changeset/pre/undici-response-form-data.md deleted file mode 100644 index 21a307e9ee..0000000000 --- a/repos/effect/.changeset/pre/undici-response-form-data.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Parse URL-encoded and multipart response bodies in the Undici HTTP client. diff --git a/repos/effect/.changeset/pre/unify-error-defect-stack-options.md b/repos/effect/.changeset/pre/unify-error-defect-stack-options.md deleted file mode 100644 index 6c032f6ab4..0000000000 --- a/repos/effect/.changeset/pre/unify-error-defect-stack-options.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"effect": patch ---- - -Replace the `Schema.Error` and `Schema.Defect` schema constants with constructor -functions, `Schema.Error()` and `Schema.Defect()`. - -Unify `Schema.ErrorWithStack` into `Schema.Error({ includeStack: true })` and -`Schema.DefectWithStack` into `Schema.Defect({ includeStack: true })`. - -Error causes are encoded by default using the same JSON defect encoding -semantics used by `Schema.Defect`; pass `{ excludeCause: true }` to omit nested -cause data. - -Equivalent `Schema.Error` and `Schema.Defect` options are canonicalized, so -repeated constructor calls with the same option values reuse the same schema. - -`Schema.Defect()` now models defects as `unknown` values with a JSON encoded -form. Error-shaped JSON objects with a string `message` decode to JavaScript -`Error` values, so non-`Error` objects such as `{ message: "boom" }` do not -round-trip unchanged. Other non-`Error` values are normalized through JSON -serialization, with non-JSON values falling back to Effect's formatted string -representation. diff --git a/repos/effect/.changeset/pre/unique-directed-neighbors.md b/repos/effect/.changeset/pre/unique-directed-neighbors.md deleted file mode 100644 index fd62b1615e..0000000000 --- a/repos/effect/.changeset/pre/unique-directed-neighbors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Deduplicate directed neighbor-node queries while preserving first edge occurrence order. diff --git a/repos/effect/.changeset/pre/unstable-http-schemas.md b/repos/effect/.changeset/pre/unstable-http-schemas.md deleted file mode 100644 index 809b62c34a..0000000000 --- a/repos/effect/.changeset/pre/unstable-http-schemas.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Move the Cookie, Cookies, Headers, and UrlParams schemas from `effect/unstable/http` to `effect/Schema`, including their record and JSON-field helper schemas. diff --git a/repos/effect/.changeset/pre/update-schema-arbitrary-report.md b/repos/effect/.changeset/pre/update-schema-arbitrary-report.md deleted file mode 100644 index ed3b7942c2..0000000000 --- a/repos/effect/.changeset/pre/update-schema-arbitrary-report.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"effect": patch ---- - -Update Schema arbitrary derivation to use the new filter metadata, candidate generation, optional derivation reports, recursion-aware generation, and the renamed `OrderedConstraint` model. - -Migration from the previous v4 API: - -- Replace filter annotations from `toArbitraryConstraint: constraint` to `arbitrary: { constraint }`. When a filter cannot be described as a constraint, use `arbitrary: { candidate }` to add a weighted source that is still checked by the filter. -- Replace bucketed constraints with the flat `Schema.Annotations.ToArbitrary.Constraint` shape: - - `string.minLength`, `array.minLength`, object property counts, collection sizes -> `minLength` - - `string.maxLength`, `array.maxLength`, object property counts, collection sizes -> `maxLength` - - `string.patterns` -> `patterns` - - `number.isInteger` -> `integer` - - `number.noNaN` -> `noNaN` - - `number.noDefaultInfinity` -> `noInfinity` - - `date.noInvalidDate` -> `valid` - - `array.comparator` for uniqueness -> `unique` using Effect equality - - `ordered.min` / `minExcluded` / `max` / `maxExcluded` -> `ordered.minimum` / `exclusiveMinimum` / `maximum` / `exclusiveMaximum` -- In arbitrary hooks, read `context.constraint` instead of `context.constraints`. Replace `context.isSuspend` with `context.recursion`; when combining finite and recursive branches, pass `context.recursion` to `fc.oneof` with the finite branch first. -- Generic declaration hooks now receive type parameters as `{ arbitrary, terminal }`. Atomic declarations may still return a bare `FastCheck.Arbitrary`, but generic declarations should return `{ arbitrary, terminal }` when they can preserve a finite terminal branch. -- `Schema.toArbitrary(schema, { report: true })` now returns `{ value, report }`; without `{ report: true }`, it keeps returning the arbitrary directly. `Schema.toArbitraryLazy` always returns a lazy arbitrary. diff --git a/repos/effect/.changeset/pre/upgrade-socket-error-listener.md b/repos/effect/.changeset/pre/upgrade-socket-error-listener.md deleted file mode 100644 index f09a832151..0000000000 --- a/repos/effect/.changeset/pre/upgrade-socket-error-listener.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Stop a reset upgrade connection from crashing the process in `NodeHttpServer` diff --git a/repos/effect/.changeset/pre/upgraded-request-skips-http-response-write.md b/repos/effect/.changeset/pre/upgraded-request-skips-http-response-write.md deleted file mode 100644 index 1748e362a7..0000000000 --- a/repos/effect/.changeset/pre/upgraded-request-skips-http-response-write.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node": patch ---- - -Prevent NodeHttpServer from writing the route's HTTP response onto a connection that was upgraded to a WebSocket connection because stricter clients will interpret those bytes as WebSocket frames, logging "Invalid frame header" and failing the connection with an untyped 1006 error instead of the actual close code that the server sent. diff --git a/repos/effect/.changeset/pre/upset-colts-stick.md b/repos/effect/.changeset/pre/upset-colts-stick.md deleted file mode 100644 index 5e87fdd32e..0000000000 --- a/repos/effect/.changeset/pre/upset-colts-stick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add missing Equivalence.Date diff --git a/repos/effect/.changeset/pre/urlparams-null-input.md b/repos/effect/.changeset/pre/urlparams-null-input.md deleted file mode 100644 index 274b0d09ea..0000000000 --- a/repos/effect/.changeset/pre/urlparams-null-input.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `UrlParams.fromInput` to stringify `null` values. diff --git a/repos/effect/.changeset/pre/urlparams-setall-immutable.md b/repos/effect/.changeset/pre/urlparams-setall-immutable.md deleted file mode 100644 index 25422eb6f0..0000000000 --- a/repos/effect/.changeset/pre/urlparams-setall-immutable.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Prevent `UrlParams.setAll` from mutating reusable overrides. diff --git a/repos/effect/.changeset/pre/use-canonical-array-indices.md b/repos/effect/.changeset/pre/use-canonical-array-indices.md deleted file mode 100644 index a3bda5d217..0000000000 --- a/repos/effect/.changeset/pre/use-canonical-array-indices.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Treat only unpadded decimal integers from `0` through `4294967294` as array indices in environment-backed configuration and bracket-path decoding. This preserves numeric-looking object keys and prevents out-of-range environment keys from producing impossible array lengths. Bracket paths that intend to address arrays must use `[1]` instead of `[01]`. diff --git a/repos/effect/.changeset/pre/use-url-can-parse.md b/repos/effect/.changeset/pre/use-url-can-parse.md deleted file mode 100644 index 959e2e641f..0000000000 --- a/repos/effect/.changeset/pre/use-url-can-parse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use `URL.canParse` to validate URL string schema decoding before constructing a `URL`. This avoids relying on thrown exceptions for routine validation while preserving the same invalid URL issue and successful decode output. diff --git a/repos/effect/.changeset/pre/valid-owls-rest.md b/repos/effect/.changeset/pre/valid-owls-rest.md deleted file mode 100644 index 35f256b235..0000000000 --- a/repos/effect/.changeset/pre/valid-owls-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Validate object-based DateTime instants before construction. diff --git a/repos/effect/.changeset/pre/validate-httpapi-handler-registration.md b/repos/effect/.changeset/pre/validate-httpapi-handler-registration.md deleted file mode 100644 index a2896b0f27..0000000000 --- a/repos/effect/.changeset/pre/validate-httpapi-handler-registration.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject unknown and duplicate HttpApi handler registrations with descriptive errors. diff --git a/repos/effect/.changeset/pre/validate-openapi-global-conflicts.md b/repos/effect/.changeset/pre/validate-openapi-global-conflicts.md deleted file mode 100644 index 4859757c3c..0000000000 --- a/repos/effect/.changeset/pre/validate-openapi-global-conflicts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Reject duplicate OpenAPI operations and operation identifiers, and reject incompatible security schemes that reuse a name. diff --git a/repos/effect/.changeset/pre/vast-bananas-send.md b/repos/effect/.changeset/pre/vast-bananas-send.md deleted file mode 100644 index 815d683fbc..0000000000 --- a/repos/effect/.changeset/pre/vast-bananas-send.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -clean up ShardId diff --git a/repos/effect/.changeset/pre/vast-deserts-travel.md b/repos/effect/.changeset/pre/vast-deserts-travel.md deleted file mode 100644 index 262cd5463c..0000000000 --- a/repos/effect/.changeset/pre/vast-deserts-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Add `endOnDone` option to Stdio stdout / stderr diff --git a/repos/effect/.changeset/pre/violet-peaches-feel.md b/repos/effect/.changeset/pre/violet-peaches-feel.md deleted file mode 100644 index b4e2ca243e..0000000000 --- a/repos/effect/.changeset/pre/violet-peaches-feel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -add Effectable module diff --git a/repos/effect/.changeset/pre/violet-pugs-tickle.md b/repos/effect/.changeset/pre/violet-pugs-tickle.md deleted file mode 100644 index 7da0c9ae14..0000000000 --- a/repos/effect/.changeset/pre/violet-pugs-tickle.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Cluster shard-lock recovery no longer stalls behind a wedged reserved SQL connection. - -While lock storage is unhealthy, the empty liveness probe (`refresh(address, [])`) now runs on the shared pool instead of the reserved lock connection, so a hung reserved connection cannot block recovery. Failed probes are also logged as warnings instead of being silently swallowed. diff --git a/repos/effect/.changeset/pre/violet-tips-open.md b/repos/effect/.changeset/pre/violet-tips-open.md deleted file mode 100644 index e26432ffd9..0000000000 --- a/repos/effect/.changeset/pre/violet-tips-open.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/sql-pg": patch ---- - -Fix `PgClient.makeClient` to connect the underlying `pg.Client` during resource acquisition. diff --git a/repos/effect/.changeset/pre/vitest-await-timeout-finalizers.md b/repos/effect/.changeset/pre/vitest-await-timeout-finalizers.md deleted file mode 100644 index 1fbd8b6b0f..0000000000 --- a/repos/effect/.changeset/pre/vitest-await-timeout-finalizers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Await asynchronous test and layer finalizers after an Effect test times out before starting the next test. diff --git a/repos/effect/.changeset/pre/vitest-five-migration.md b/repos/effect/.changeset/pre/vitest-five-migration.md deleted file mode 100644 index 349baff340..0000000000 --- a/repos/effect/.changeset/pre/vitest-five-migration.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"@effect/vitest": patch -"@effect/doctest": patch ---- - -Require Vitest `>=5.0.0 <6.0.0` and Node.js `^22.12.0 || ^24.0.0 || >=26.0.0`. - -### Breaking changes - -- Replace `.sequential` and `{ sequential: true }` with `{ concurrent: false }`. -- Use `bench` from the test context and await `bench(name, fn).run()`. The top-level benchmark API is removed. -- Use `Assertion` or `Assertion, T>`. Define custom matchers through `vitest.Matchers`, not `@vitest/expect`. -- Import reporter types from `vitest/node` and environment/snapshot APIs from `vitest/runtime`. Set `outputFile` when consuming JSON reports. -- Await asynchronous assertions. Mock history now clears before each test. - -See the [Vitest migration guide](https://vitest.dev/guide/migration/) for removed types and other upstream changes. diff --git a/repos/effect/.changeset/pre/vitest-layer-concurrency.md b/repos/effect/.changeset/pre/vitest-layer-concurrency.md deleted file mode 100644 index 2b1922a264..0000000000 --- a/repos/effect/.changeset/pre/vitest-layer-concurrency.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Add a `concurrent` option to named `layer` and `it.layer` suites. Omitted options and anonymous layers preserve inherited concurrency. diff --git a/repos/effect/.changeset/pre/vitest-layer-top-level-options.md b/repos/effect/.changeset/pre/vitest-layer-top-level-options.md deleted file mode 100644 index 2ef279d0fc..0000000000 --- a/repos/effect/.changeset/pre/vitest-layer-top-level-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/vitest": patch ---- - -Allow top-level `it.layer` to accept `memoMap`, `timeout`, and `excludeTestServices` options, matching the standalone `layer` export. Nested `it.layer` calls remain restricted to `timeout` only. diff --git a/repos/effect/.changeset/pre/wacky-grapes-poke.md b/repos/effect/.changeset/pre/wacky-grapes-poke.md deleted file mode 100644 index 6e9284d8e9..0000000000 --- a/repos/effect/.changeset/pre/wacky-grapes-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Remove superfluous error from SqlSchema.findAll signature diff --git a/repos/effect/.changeset/pre/wacky-rice-add.md b/repos/effect/.changeset/pre/wacky-rice-add.md deleted file mode 100644 index 9de72c4df0..0000000000 --- a/repos/effect/.changeset/pre/wacky-rice-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -tighten Schema on \_meta fields in McpSchema; closes #1463 diff --git a/repos/effect/.changeset/pre/warm-clocks-format.md b/repos/effect/.changeset/pre/warm-clocks-format.md deleted file mode 100644 index 04173b8c37..0000000000 --- a/repos/effect/.changeset/pre/warm-clocks-format.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Cron.format` for converting a `Cron` instance to a cron expression, with an option to include the seconds field. diff --git a/repos/effect/.changeset/pre/warm-dolls-brake.md b/repos/effect/.changeset/pre/warm-dolls-brake.md deleted file mode 100644 index dc177bf212..0000000000 --- a/repos/effect/.changeset/pre/warm-dolls-brake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add meta-var that shows log level and bash options in command line. diff --git a/repos/effect/.changeset/pre/warm-friends-tie.md b/repos/effect/.changeset/pre/warm-friends-tie.md deleted file mode 100644 index b600cb2266..0000000000 --- a/repos/effect/.changeset/pre/warm-friends-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -fix cli subcommand context diff --git a/repos/effect/.changeset/pre/warm-graphs-degree.md b/repos/effect/.changeset/pre/warm-graphs-degree.md deleted file mode 100644 index f140e4d846..0000000000 --- a/repos/effect/.changeset/pre/warm-graphs-degree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add incident-edge, edges-between, and directed and undirected degree queries to `Graph`. diff --git a/repos/effect/.changeset/pre/warm-rivers-cache.md b/repos/effect/.changeset/pre/warm-rivers-cache.md deleted file mode 100644 index 23a22720b2..0000000000 --- a/repos/effect/.changeset/pre/warm-rivers-cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Preserve original HTTP response bytes when reading response text first. diff --git a/repos/effect/.changeset/pre/warm-snails-shop.md b/repos/effect/.changeset/pre/warm-snails-shop.md deleted file mode 100644 index 907de8b5a9..0000000000 --- a/repos/effect/.changeset/pre/warm-snails-shop.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/ai-openrouter": patch -"@effect/ai-anthropic": patch -"@effect/ai-openai": patch ---- - -Fix the generated schemas for ai providers diff --git a/repos/effect/.changeset/pre/wet-news-invent.md b/repos/effect/.changeset/pre/wet-news-invent.md deleted file mode 100644 index f13ab2efb1..0000000000 --- a/repos/effect/.changeset/pre/wet-news-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add a `requireServicesAt` option to `PersistedCache.make` so lookup-service requirements can be configured like `Cache`. diff --git a/repos/effect/.changeset/pre/whole-pets-build.md b/repos/effect/.changeset/pre/whole-pets-build.md deleted file mode 100644 index 91629728a9..0000000000 --- a/repos/effect/.changeset/pre/whole-pets-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -clean up more references on fiber exit diff --git a/repos/effect/.changeset/pre/wild-donuts-brake.md b/repos/effect/.changeset/pre/wild-donuts-brake.md deleted file mode 100644 index 4bfeecea7f..0000000000 --- a/repos/effect/.changeset/pre/wild-donuts-brake.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -"effect": patch ---- - -Bound cluster runner entity residency and storage reads. - -`ShardingConfig` gains two knobs: - -- `maxResidentEntities` (default `10_000`): the maximum number of entities - that can be resident on a runner at the same time. At the cap, the storage - read loop stops admitting messages for new entity addresses (they stay in - storage until a slot frees up) and volatile sends to new addresses fail with - `MailboxFull`. Persisted sends still succeed. `"unbounded"` restores the - previous behaviour and can only be set programmatically. -- `unprocessedMessageBatchSize` (default `1024`): the maximum number of - unprocessed messages read from storage in a single poll. - -`MessageStorage.unprocessedMessages` accepts an optional -`{ limit, addresses }` argument, and only claims the messages it actually -returns. The memory implementation now applies the same ten-minute claim -window as SQL, so bounded reads advance past in-flight requests; resetting an -address or shard makes its claimed messages immediately eligible again. - -The encoded driver contract replaces `Encoded.resetAddress` with the batched -`Encoded.resetAddresses` operation. `SqlMessageStorage.makeEncoded` constructs -the SQL encoded driver directly for custom storage composition. - -`ClusterWorkflowEngine` entities (workflows and the durable clock) now use a -fixed ten-second idle time, so completed and suspended executions release their -entity slots quickly. Their state is durable, so an evicted execution is -rebuilt from storage when its next message arrives. diff --git a/repos/effect/.changeset/pre/wild-readers-clean.md b/repos/effect/.changeset/pre/wild-readers-clean.md deleted file mode 100644 index 40b517e505..0000000000 --- a/repos/effect/.changeset/pre/wild-readers-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -mcp http request with no session header is 404 response diff --git a/repos/effect/.changeset/pre/wild-suns-bearer-space.md b/repos/effect/.changeset/pre/wild-suns-bearer-space.md deleted file mode 100644 index 71a6805b40..0000000000 --- a/repos/effect/.changeset/pre/wild-suns-bearer-space.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApiSecurity` bearer/http credential decoding diff --git a/repos/effect/.changeset/pre/windows-hide-child-process-console.md b/repos/effect/.changeset/pre/windows-hide-child-process-console.md deleted file mode 100644 index 77d53516a6..0000000000 --- a/repos/effect/.changeset/pre/windows-hide-child-process-console.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Pass Node's `windowsHide` flag for spawned Windows children by default (except detached processes), with an independent -`windowsHide` option for callers that need visible GUI windows. Process-group cleanup now invokes `taskkill` without a -`cmd.exe` wrapper and hides its window. diff --git a/repos/effect/.changeset/pre/wise-ants-wave.md b/repos/effect/.changeset/pre/wise-ants-wave.md deleted file mode 100644 index e4966722f0..0000000000 --- a/repos/effect/.changeset/pre/wise-ants-wave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `AtomHttpApi.query` to forward v4 `params` / `query` request fields to `HttpApiClient` at runtime. -Also align `AtomHttpApi` endpoint type inference with v4 `HttpApiEndpoint` params/query naming and add a regression test. diff --git a/repos/effect/.changeset/pre/wise-bats-encrypt.md b/repos/effect/.changeset/pre/wise-bats-encrypt.md deleted file mode 100644 index 3a251349ac..0000000000 --- a/repos/effect/.changeset/pre/wise-bats-encrypt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Use a distinct AES-GCM initialization vector for each encrypted event log entry. `EventLogEncryption.encrypt` now returns each IV with its ciphertext, and encrypted event log clients and servers must be upgraded together because the `WriteEntries` wire shape changed. diff --git a/repos/effect/.changeset/pre/wise-files-watch.md b/repos/effect/.changeset/pre/wise-files-watch.md deleted file mode 100644 index 41cd84d9ae..0000000000 --- a/repos/effect/.changeset/pre/wise-files-watch.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@effect/platform-deno": patch -"@effect/platform-node-shared": patch -"effect": patch ---- - -Restore the `recursive` option for `FileSystem.watch`, with non-recursive watching as the default. diff --git a/repos/effect/.changeset/pre/wise-flags-shift.md b/repos/effect/.changeset/pre/wise-flags-shift.md deleted file mode 100644 index c470c58a67..0000000000 --- a/repos/effect/.changeset/pre/wise-flags-shift.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"effect": patch ---- - -Refactor unstable CLI global flags to command-scoped declarations. - -### Breaking changes - -- Remove `GlobalFlag.add`, `GlobalFlag.remove`, and `GlobalFlag.clear` -- Add `Command.withGlobalFlags(...)` as the declaration API for command/subcommand scope -- Change `GlobalFlag.setting` constructor to curried form which carries type-level identifier: - - before: `GlobalFlag.setting({ flag, ... })` - - after: `GlobalFlag.setting("id")({ flag })` -- Change setting context identity to a stable type-level string: - - `effect/unstable/cli/GlobalFlag/${id}` - -### Behavior changes - -- Global flags are now scoped by command path (root-to-leaf declarations) -- Out-of-scope global flags are rejected for the selected subcommand path -- Help now renders only global flags active for the requested command path -- Setting defaults are sourced from `Flag` combinators (`optional`, `withDefault`) rather than setting constructor defaults diff --git a/repos/effect/.changeset/pre/wise-graphs-connect.md b/repos/effect/.changeset/pre/wise-graphs-connect.md deleted file mode 100644 index 8ac3f4b7a7..0000000000 --- a/repos/effect/.changeset/pre/wise-graphs-connect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add unweighted reachability, explicit weak and strong connectivity predicates, weak components, and tree detection to `Graph`. diff --git a/repos/effect/.changeset/pre/wise-oranges-stay.md b/repos/effect/.changeset/pre/wise-oranges-stay.md deleted file mode 100644 index 263143b8c5..0000000000 --- a/repos/effect/.changeset/pre/wise-oranges-stay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -allow Context.Key to be covariant diff --git a/repos/effect/.changeset/pre/wise-pandas-lock.md b/repos/effect/.changeset/pre/wise-pandas-lock.md deleted file mode 100644 index 8f15bcab38..0000000000 --- a/repos/effect/.changeset/pre/wise-pandas-lock.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"effect": patch ---- - -Namespace PostgreSQL advisory shard locks by the `SqlRunnerStorage` table prefix. - -This changes the advisory-lock protocol. PostgreSQL clusters using advisory locks require a full cluster stop before upgrading; a rolling deploy is unsafe because old and new runners use different lock keys and can both acquire the same shard. diff --git a/repos/effect/.changeset/pre/with-error-reporting-effect-result.md b/repos/effect/.changeset/pre/with-error-reporting-effect-result.md deleted file mode 100644 index f359ab0767..0000000000 --- a/repos/effect/.changeset/pre/with-error-reporting-effect-result.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect": patch ---- - -Fix `Effect.withErrorReporting` to return an `Effect` instead of preserving input -subtypes such as `Exit`, whose subtype-specific fields are not present on the wrapper. diff --git a/repos/effect/.changeset/pre/witty-lobsters-share.md b/repos/effect/.changeset/pre/witty-lobsters-share.md deleted file mode 100644 index c786da6867..0000000000 --- a/repos/effect/.changeset/pre/witty-lobsters-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Replace the default HttpApi schema-validation error with `HttpApiError.BadRequestNoContent`. diff --git a/repos/effect/.changeset/pre/worker-run-early-exit.md b/repos/effect/.changeset/pre/worker-run-early-exit.md deleted file mode 100644 index a5d68a6069..0000000000 --- a/repos/effect/.changeset/pre/worker-run-early-exit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `Worker.run` hanging uninterruptibly when a worker dies before the ready handshake. diff --git a/repos/effect/.changeset/pre/worker-runner-multi-disconnect.md b/repos/effect/.changeset/pre/worker-runner-multi-disconnect.md deleted file mode 100644 index 2d49ff0cdb..0000000000 --- a/repos/effect/.changeset/pre/worker-runner-multi-disconnect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Forward every worker-runner client disconnect to the RPC server, not just the first one. diff --git a/repos/effect/.changeset/pre/workflow-proxy-discard-execution-id.md b/repos/effect/.changeset/pre/workflow-proxy-discard-execution-id.md deleted file mode 100644 index 24b90519bc..0000000000 --- a/repos/effect/.changeset/pre/workflow-proxy-discard-execution-id.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Return workflow execution IDs from generated RPC and HTTP discard endpoints. diff --git a/repos/effect/.changeset/pre/xhr-arraybuffer-readers.md b/repos/effect/.changeset/pre/xhr-arraybuffer-readers.md deleted file mode 100644 index d3ce6d29e7..0000000000 --- a/repos/effect/.changeset/pre/xhr-arraybuffer-readers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-browser": patch ---- - -Support text, JSON, and stream readers when the XHR response type is `"arraybuffer"`. diff --git a/repos/effect/.changeset/pre/yellow-adults-study.md b/repos/effect/.changeset/pre/yellow-adults-study.md deleted file mode 100644 index 015ba9db52..0000000000 --- a/repos/effect/.changeset/pre/yellow-adults-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/ai-openai": patch ---- - -openai ws tweaks diff --git a/repos/effect/.changeset/pre/yellow-clocks-dance.md b/repos/effect/.changeset/pre/yellow-clocks-dance.md deleted file mode 100644 index 707dfbc527..0000000000 --- a/repos/effect/.changeset/pre/yellow-clocks-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Command.withAlias` for unstable CLI commands, including subcommand parsing by alias and help output that renders aliases as `name, alias` in subcommand listings. diff --git a/repos/effect/.changeset/pre/yellow-dingos-jump.md b/repos/effect/.changeset/pre/yellow-dingos-jump.md deleted file mode 100644 index 1a6af4ea8d..0000000000 --- a/repos/effect/.changeset/pre/yellow-dingos-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Fix `HttpApi.prefix` so it updates endpoint path types the same way `HttpApiGroup.prefix` does. diff --git a/repos/effect/.changeset/pre/young-doors-change.md b/repos/effect/.changeset/pre/young-doors-change.md deleted file mode 100644 index c5ec50dbeb..0000000000 --- a/repos/effect/.changeset/pre/young-doors-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"effect": patch ---- - -Add `Random.shuffle` to shuffle iterables with seeded randomness support. diff --git a/repos/effect/.changeset/pre/zero-node-socket-timeout.md b/repos/effect/.changeset/pre/zero-node-socket-timeout.md deleted file mode 100644 index 8123778659..0000000000 --- a/repos/effect/.changeset/pre/zero-node-socket-timeout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@effect/platform-node-shared": patch ---- - -Respect zero-duration open timeouts when opening Node sockets. diff --git a/repos/effect/.github/workflows/check.yml b/repos/effect/.github/workflows/check.yml index dac001679a..5bbcfe8496 100644 --- a/repos/effect/.github/workflows/check.yml +++ b/repos/effect/.github/workflows/check.yml @@ -50,6 +50,9 @@ jobs: with: deno-version: v2.9.4 - run: pnpm check + env: + # Leave memory headroom for the runner by collecting the Go heap earlier. + GOMEMLIMIT: 4GiB - run: deno check . - run: pnpm test-types --target '>=5.9' diff --git a/repos/effect/.github/workflows/snapshot.yml b/repos/effect/.github/workflows/snapshot.yml index 7ce71e4a8f..69e9e25288 100644 --- a/repos/effect/.github/workflows/snapshot.yml +++ b/repos/effect/.github/workflows/snapshot.yml @@ -39,6 +39,10 @@ jobs: - name: Run codemods run: pnpm codemod - name: Build package + env: + # Leave memory for the runner and package builds on the 8 GB runner. + # The native TypeScript compiler uses Go's soft memory limit. + GOMEMLIMIT: 4GiB run: pnpm build - name: Create snapshot id: snapshot diff --git a/repos/effect/.vscode/settings.json b/repos/effect/.vscode/settings.json index 721746cf3a..672eea7858 100644 --- a/repos/effect/.vscode/settings.json +++ b/repos/effect/.vscode/settings.json @@ -44,8 +44,8 @@ "deno.enablePaths": [ "./packages/platform/deno" ], - "js/ts.tsdk.path": "./node_modules/typescript/lib", - "js/ts.tsdk.additionalLocations": ["./node_modules/typescript/lib"], + "js/ts.tsdk.path": "./node_modules/typescript/bin", + "js/ts.tsdk.additionalLocations": ["./node_modules/typescript/bin"], "js/ts.tsdk.promptToUseWorkspaceVersion": true, "js/ts.experimental.useTsgo": true } diff --git a/repos/effect/LLMS.md b/repos/effect/LLMS.md index 7e2ee6e8a5..963a7a5bd5 100644 --- a/repos/effect/LLMS.md +++ b/repos/effect/LLMS.md @@ -302,7 +302,7 @@ Use it when your Effect programs need testable current time, safe parsing, stabl Effect has built-in support for structured logging, distributed tracing, and metrics. For exporting telemetry, use the lightweight Otlp modules from -`effect/unstable/observability` in new projects, or use +`effect/observability` in new projects, or use `@effect/opentelemetry` NodeSdk when integrating with an existing OpenTelemetry setup. @@ -344,7 +344,7 @@ if (Predicate.isObject(thing)) { ## Working with SQL databases -Use the `effect/unstable/sql` modules together with a driver package such as +Use the `effect/sql` modules together with a driver package such as `@effect/sql-sqlite-node` to access SQL databases. Define domain models with `Model.Class` to derive schemas for the database and JSON boundaries, run migrations, and write type-safe queries. @@ -372,13 +372,13 @@ Build http clients with the `HttpClient` module. ## Working with child processes -Use the `effect/unstable/process` modules to define child processes and run them with `ChildProcessSpawner`. +Use the `effect/process` modules to define child processes and run them with `ChildProcessSpawner`. - **[Working with child processes](./ai-docs/src/60_child-process/10_working-with-child-processes.ts)**: This example shows how to collect process output, compose pipelines, and stream long-running command output. ## Building CLI applications -Use the "effect/unstable/cli" modules to build CLI applications. These modules +Use the "effect/cli" modules to build CLI applications. These modules provide utilities for parsing command-line arguments, handling user input, and managing the flow of a CLI application. diff --git a/repos/effect/MIGRATION.md b/repos/effect/MIGRATION.md index 902888a685..65ec537561 100644 --- a/repos/effect/MIGRATION.md +++ b/repos/effect/MIGRATION.md @@ -1,8 +1,8 @@ # Migrating from Effect v3 to Effect v4 -> **Note:** Effect v4 is currently in beta. APIs may change between beta -> releases. This guide will evolve as the beta progresses and community -> feedback is incorporated. +> **Note:** If you run into a migration issue this guide doesn't cover, let us +> know on [Discord](https://discord.gg/effect-ts) or open an +> [issue](https://github.com/Effect-TS/effect/issues). ## Background @@ -16,8 +16,8 @@ and imported has changed significantly. All Effect ecosystem packages now share a **single version number** and are released together. In v3, packages were versioned independently (e.g. `effect@3.x`, `@effect/platform@0.x`, `@effect/sql@0.x`), making compatibility -between packages difficult to track. In v4, if you use `effect@4.0.0-beta.0`, -the matching SQL package is `@effect/sql-pg@4.0.0-beta.0`. +between packages difficult to track. In v4, if you use `effect@4.0.0`, +the matching SQL package is `@effect/sql-pg@4.0.0`. ### Package Consolidation @@ -35,19 +35,35 @@ technology-specific: - `@effect/atom-*` — framework-specific atom bindings - `@effect/vitest` — Vitest testing utilities -These packages must be bumped to matching v4 beta versions alongside `effect`. +These packages must be bumped to matching v4 versions alongside `effect`. ### Unstable Module System -v4 introduces **unstable modules** under `effect/unstable/*` import paths. -These modules may receive breaking changes in minor releases, while modules -outside `unstable/` follow strict semver. +v4 includes **unstable modules** under `effect/*` import paths. Their paths no +longer contain an `unstable` segment, but their API documentation is marked +with `@stability unstable`. + +`@stability unstable` means an API may receive breaking changes in minor +releases. `@stability experimental` means it may receive breaking changes +across patch versions. APIs without a stability tag follow strict semver. + +APIs that expose a third-party dependency are also marked `@stability +unstable`, because that dependency's own releases can change them. This covers +accessors to underlying clients (for example `NodeRedis`'s `client` and `use`), +options typed as the dependency's options, and re-exports such as +`@effect/platform-node/Undici`. It includes the generated provider schemas in +the `@effect/ai-*` packages and the `@effect/opentelemetry` integration. + +Imports using `effect/unstable/` must drop the `unstable` segment. +For example, replace `effect/unstable/http` with `effect/http` and +`effect/unstable/ai/LanguageModel` with `effect/ai/LanguageModel`. There +are no compatibility exports for the old paths. Unstable modules include: `ai`, `cli`, `cluster`, `devtools`, `eventlog`, -`http`, `httpapi`, `jsonschema`, `observability`, `persistence`, `process`, +`http`, `http-api`, `jsonschema`, `observability`, `persistence`, `process`, `reactivity`, `rpc`, `schema`, `socket`, `sql`, `workflow`, `workers`. -As these modules stabilize, they graduate to the top-level `effect/*` namespace. +Moving these modules does not stabilize their APIs. ### Performance and Bundle Size diff --git a/repos/effect/README.md b/repos/effect/README.md index 93973efa24..fff448c8d3 100644 --- a/repos/effect/README.md +++ b/repos/effect/README.md @@ -6,12 +6,12 @@ Effect is a library for building robust, maintainable, type-safe, and production grade applications in TypeScript. It helps you handle the hard problems at scale: typed errors, dependency injection, structured concurrency, scheduling, tracing, and unified schema validation. -> **Effect V4 is currently a release candidate.** The `main` branch contains v4 development. +> **Effect 4.x is a long-term support (LTS) release.** If you are upgrading from Effect 3.x, follow the [migration guide](MIGRATION.md). -## Install V4 RC +## Installation ```sh -npm install effect@rc +npm install effect ``` ## Requirements @@ -20,13 +20,42 @@ npm install effect@rc - **Node.js 18 or newer** is the general minimum for running Effect on Node.js. Some integration packages require newer runtimes; for example, `@effect/sql-sqlite-node` requires Node.js 22.16 or newer. - **Strict type-checking:** the `strict` flag must be enabled in your `tsconfig.json`. +## Links + +- [Website](https://effect.website): documentation, guides, and news +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests +- [Jobs](https://effect.website/effect-jobs): companies hiring Effect developers +- Follow us on [X](https://x.com/EffectTS_), [Bluesky](https://bsky.app/profile/effect-ts.bsky.social), and [LinkedIn](https://www.linkedin.com/company/effect-ts) + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. + +Feedback from teams using and evaluating Effect directly shapes what we stabilize and build next. + +## Long-term support + +Teams depend on Effect for systems they expect to run for years. Effect 4.x is a long-term support (LTS) release with the following guarantees: + +- At least three years of support, including bug and security fixes. +- Bug fixes for one year after the next major version is released. +- Security fixes for two years after the next major version is released. + +Stable APIs reserve breaking changes for major releases. APIs marked unstable may change in minor releases, and experimental APIs may change in patch releases. + ## Effect v3 -The Effect v3 source code is available on the [`v3`](https://github.com/Effect-TS/effect/tree/v3) branch, which is also where issues and pull requests meant for Effect v3 should be targeted. +The Effect v3 source code is available on the [`v3`](https://github.com/Effect-TS/effect/tree/v3) branch, which is also where issues and pull requests meant for Effect v3 should be targeted. To upgrade, see the [migration guide](MIGRATION.md). ## Packages -This monorepo contains the core `effect` package alongside integration packages that extend it. All v4 packages are published under the `rc` tag on npm. +This monorepo contains the core `effect` package alongside integration packages that extend it. All packages listed below are released together with synchronized versions. | Package | Description | API Reference | | --------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------ | @@ -50,6 +79,7 @@ This monorepo contains the core `effect` package alongside integration packages | [`@effect/sql-sqlite-wasm`](packages/sql/sqlite-wasm) | SQL client for SQLite compiled to WebAssembly | [docs](https://effect.website/docs/v4/api/sql-sqlite-wasm) | | [`@effect/ai-anthropic`](packages/ai/anthropic) | Anthropic provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-anthropic) | | [`@effect/ai-openai`](packages/ai/openai) | OpenAI provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai) | +| [`@effect/ai-typesafe`](packages/ai/typesafe) | TypeSafe decision provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-typesafe) | | [`@effect/ai-openai-compat`](packages/ai/openai-compat) | OpenAI-compatible API provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai-compat) | | [`@effect/ai-openrouter`](packages/ai/openrouter) | OpenRouter provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openrouter) | | [`@effect/atom-react`](packages/atom/react) | React bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-react) | @@ -61,13 +91,6 @@ This monorepo contains the core `effect` package alongside integration packages | [`@effect/doctest`](packages/tools/doctest) | Runs JSDoc examples as Vitest tests | [docs](https://effect.website/docs/v4/api/doctest) | | [`@effect/openapi-generator`](packages/tools/openapi-generator) | Generate Effect code from OpenAPI specifications | [docs](https://effect.website/docs/v4/api/openapi-generator) | -## Resources - -- Documentation (https://effect.website) -- Discord (https://discord.gg/effect-ts) -- Effect v3 source (https://github.com/Effect-TS/effect/tree/v3) -- Effect v4 source (https://github.com/Effect-TS/effect/tree/main) - ## License MIT diff --git a/repos/effect/ai-docs/package.json b/repos/effect/ai-docs/package.json index be4bf47a57..60c4a4afcf 100644 --- a/repos/effect/ai-docs/package.json +++ b/repos/effect/ai-docs/package.json @@ -27,10 +27,10 @@ "@effect/sql-sqlite-wasm": "workspace:*", "@effect/vitest": "workspace:*", "effect": "workspace:*", - "hono": "^4.13.7", - "nodemailer": "^10.0.0" + "hono": "^4.13.9", + "nodemailer": "^10.0.10" }, "devDependencies": { - "@types/nodemailer": "^8.0.1" + "@types/nodemailer": "^8.0.2" } } diff --git a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts b/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts index fe73d0e2df..755e1de077 100644 --- a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts +++ b/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts @@ -7,7 +7,7 @@ import { PgClient } from "@effect/sql-pg" import { Array, Config, Context, Effect, Layer, type Option, Schema } from "effect" -import { SqlClient, SqlError } from "effect/unstable/sql" +import { SqlClient, SqlError } from "effect/sql" // Define a layer for the SqlClient service export const SqlClientLayer: Layer.Layer< diff --git a/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts b/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts index e92e1242dd..ba06a71028 100644 --- a/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts +++ b/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts @@ -5,7 +5,7 @@ */ import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer } from "effect" -import { HttpRouter, HttpServerResponse } from "effect/unstable/http" +import { HttpRouter, HttpServerResponse } from "effect/http" import { createServer } from "node:http" // Build a tiny HTTP app with a health-check endpoint. diff --git a/repos/effect/ai-docs/src/03_stream/30_encoding.ts b/repos/effect/ai-docs/src/03_stream/30_encoding.ts index 456b7cc899..f0f57a8456 100644 --- a/repos/effect/ai-docs/src/03_stream/30_encoding.ts +++ b/repos/effect/ai-docs/src/03_stream/30_encoding.ts @@ -5,7 +5,7 @@ * decode and encode streams of structured data. */ import { DateTime, Schema, Stream } from "effect" -import { Ndjson, SchemaBinary } from "effect/unstable/encoding" +import { Ndjson, SchemaBinary } from "effect/encoding" // SchemaBinary derives a framed binary decoder directly from a schema. export const schemaBinaryDecoder = SchemaBinary.decode(Schema.Struct({ diff --git a/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts b/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts index 121e795dbe..daea8bb97f 100644 --- a/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts +++ b/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts @@ -5,8 +5,8 @@ */ import { NodeRuntime } from "@effect/platform-node" import { Context, Effect, Layer } from "effect" -import { FetchHttpClient } from "effect/unstable/http" -import { OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/unstable/observability" +import { FetchHttpClient } from "effect/http" +import { OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/observability" // Configure OTLP span export. export const OtlpTracingLayer = OtlpTracer.layer({ diff --git a/repos/effect/ai-docs/src/08_observability/index.md b/repos/effect/ai-docs/src/08_observability/index.md index df0399ad45..e0efc8b13a 100644 --- a/repos/effect/ai-docs/src/08_observability/index.md +++ b/repos/effect/ai-docs/src/08_observability/index.md @@ -2,6 +2,6 @@ Effect has built-in support for structured logging, distributed tracing, and metrics. For exporting telemetry, use the lightweight Otlp modules from -`effect/unstable/observability` in new projects, or use +`effect/observability` in new projects, or use `@effect/opentelemetry` NodeSdk when integrating with an existing OpenTelemetry setup. diff --git a/repos/effect/ai-docs/src/40_sql/10_basics.ts b/repos/effect/ai-docs/src/40_sql/10_basics.ts index 59a590ae2c..96874e1afb 100644 --- a/repos/effect/ai-docs/src/40_sql/10_basics.ts +++ b/repos/effect/ai-docs/src/40_sql/10_basics.ts @@ -7,8 +7,8 @@ import { NodeRuntime } from "@effect/platform-node" import { SqliteClient, SqliteMigrator } from "@effect/sql-sqlite-node" import { Context, Effect, Layer, Schema } from "effect" -import { Model } from "effect/unstable/schema" -import { SqlClient, SqlModel, SqlSchema } from "effect/unstable/sql" +import { Model } from "effect/schema" +import { SqlClient, SqlModel, SqlSchema } from "effect/sql" // Use a branded string for the group id, so it cannot be mixed up with other // string ids in the application. diff --git a/repos/effect/ai-docs/src/40_sql/index.md b/repos/effect/ai-docs/src/40_sql/index.md index cf026b2c87..92958bfde3 100644 --- a/repos/effect/ai-docs/src/40_sql/index.md +++ b/repos/effect/ai-docs/src/40_sql/index.md @@ -1,6 +1,6 @@ ## Working with SQL databases -Use the `effect/unstable/sql` modules together with a driver package such as +Use the `effect/sql` modules together with a driver package such as `@effect/sql-sqlite-node` to access SQL databases. Define domain models with `Model.Class` to derive schemas for the database and JSON boundaries, run migrations, and write type-safe queries. diff --git a/repos/effect/ai-docs/src/50_http-client/10_basics.ts b/repos/effect/ai-docs/src/50_http-client/10_basics.ts index 195d5fe555..25202ccb1d 100644 --- a/repos/effect/ai-docs/src/50_http-client/10_basics.ts +++ b/repos/effect/ai-docs/src/50_http-client/10_basics.ts @@ -4,7 +4,7 @@ * Define a service that uses the HttpClient module to fetch data from an external API */ import { Context, Effect, flow, Layer, Schedule, Schema } from "effect" -import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http" class Todo extends Schema.Class("Todo")({ userId: Schema.Int, diff --git a/repos/effect/ai-docs/src/51_http-server/10_basics.ts b/repos/effect/ai-docs/src/51_http-server/10_basics.ts index 0edea44653..ae7ac415e3 100644 --- a/repos/effect/ai-docs/src/51_http-server/10_basics.ts +++ b/repos/effect/ai-docs/src/51_http-server/10_basics.ts @@ -6,8 +6,8 @@ */ import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Context, Effect, flow, Layer, Schedule } from "effect" -import { FetchHttpClient, HttpClient, HttpClientRequest, HttpRouter, HttpServer } from "effect/unstable/http" -import { HttpApiBuilder, HttpApiClient, HttpApiMiddleware, HttpApiScalar } from "effect/unstable/httpapi" +import { FetchHttpClient, HttpClient, HttpClientRequest, HttpRouter, HttpServer } from "effect/http" +import { HttpApiBuilder, HttpApiClient, HttpApiMiddleware, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" // Api definitions should **always** be seperate from the server implementation, // so that they can be shared between the server and client without leaking diff --git a/repos/effect/ai-docs/src/51_http-server/20_testing.ts b/repos/effect/ai-docs/src/51_http-server/20_testing.ts index e34d13d22b..b63c2f4d1b 100644 --- a/repos/effect/ai-docs/src/51_http-server/20_testing.ts +++ b/repos/effect/ai-docs/src/51_http-server/20_testing.ts @@ -6,8 +6,8 @@ */ import { assert, layer } from "@effect/vitest" import { Effect, Layer } from "effect" -import { HttpClientRequest, HttpServer } from "effect/unstable/http" -import { HttpApiMiddleware, HttpApiTest } from "effect/unstable/httpapi" +import { HttpClientRequest, HttpServer } from "effect/http" +import { HttpApiMiddleware, HttpApiTest } from "effect/http-api" import { Api } from "./fixtures/api/Api.ts" import { Authorization } from "./fixtures/api/Authorization.ts" import { UserId } from "./fixtures/domain/User.ts" diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts index 20140ae7cb..6bc1b80cb2 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts @@ -1,4 +1,4 @@ -import { HttpApi, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, OpenApi } from "effect/http-api" import { SystemApi } from "./System.ts" import { UsersApiGroup } from "./Users.ts" diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts index 30898c729a..523f76807c 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts @@ -1,5 +1,5 @@ import { Context, Schema } from "effect" -import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi" +import { HttpApiMiddleware, HttpApiSecurity } from "effect/http-api" import type { User } from "../domain/User.ts" export class CurrentUser extends Context.Service()("acme/HttpApi/Authorization/CurrentUser") {} diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts index c66565b065..f6ee895544 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts @@ -1,4 +1,4 @@ -import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" // Top level groups are added to the root of the derived HttpApiClient. // diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts index ad370e1631..72a483f101 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts @@ -1,5 +1,5 @@ import { Schema } from "effect" -import { HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" +import { HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/http-api" import { User, UserId } from "../domain/User.ts" import { SearchQueryTooShort, UserNotFound } from "../domain/UserErrors.ts" import { Authorization } from "./Authorization.ts" diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts index 43ac4f5cff..67b8fdc90a 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts @@ -1,5 +1,5 @@ import { Schema } from "effect" -import { Model } from "effect/unstable/schema" +import { Model } from "effect/schema" export const UserId = Schema.String.pipe(Schema.brand("UserId")) export type UserId = typeof UserId.Type diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts index 485d1e3482..4262a23793 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts @@ -1,6 +1,6 @@ import { SqliteClient, SqliteMigrator } from "@effect/sql-sqlite-node" import { Context, Effect, Layer, Schema } from "effect" -import { SqlClient, SqlModel, SqlSchema } from "effect/unstable/sql" +import { SqlClient, SqlModel, SqlSchema } from "effect/sql" import { User } from "../domain/User.ts" import type { UserId } from "../domain/User.ts" import { SearchQueryTooShort, UserNotFound, UsersError } from "../domain/UserErrors.ts" diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts b/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts index d60563d83d..d50e9e883c 100644 --- a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +++ b/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts @@ -1,5 +1,5 @@ import { Effect, Layer } from "effect" -import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi" +import { HttpApiBuilder, HttpApiError } from "effect/http-api" import { Api } from "../../api/Api.ts" import { CurrentUser } from "../../api/Authorization.ts" import { AuthorizationLayer } from "../Authorization.ts" diff --git a/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts b/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts index c7b1bbbcdf..f94c47487c 100644 --- a/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts +++ b/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts @@ -5,7 +5,7 @@ */ import { NodeServices } from "@effect/platform-node" import { Console, Context, Effect, Layer, Schema, Stream, String } from "effect" -import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" +import { ChildProcess, ChildProcessSpawner } from "effect/process" export class DevToolsError extends Schema.TaggedError()("DevToolsError", { cause: Schema.Defect() diff --git a/repos/effect/ai-docs/src/60_child-process/index.md b/repos/effect/ai-docs/src/60_child-process/index.md index ae0016bc3b..19a9b0e6a1 100644 --- a/repos/effect/ai-docs/src/60_child-process/index.md +++ b/repos/effect/ai-docs/src/60_child-process/index.md @@ -1,3 +1,3 @@ ## Working with child processes -Use the `effect/unstable/process` modules to define child processes and run them with `ChildProcessSpawner`. +Use the `effect/process` modules to define child processes and run them with `ChildProcessSpawner`. diff --git a/repos/effect/ai-docs/src/70_cli/10_basics.ts b/repos/effect/ai-docs/src/70_cli/10_basics.ts index 3f4a40c46b..ccd74b6f26 100644 --- a/repos/effect/ai-docs/src/70_cli/10_basics.ts +++ b/repos/effect/ai-docs/src/70_cli/10_basics.ts @@ -6,7 +6,7 @@ */ import { NodeRuntime, NodeServices } from "@effect/platform-node" import { Console, Effect, Option, Schema } from "effect" -import { Argument, Command, Flag } from "effect/unstable/cli" +import { Argument, Command, Flag } from "effect/cli" // You can define flags outside of commands and reuse them across multiple // commands. diff --git a/repos/effect/ai-docs/src/70_cli/index.md b/repos/effect/ai-docs/src/70_cli/index.md index 96a3b756f6..c4b6545acb 100644 --- a/repos/effect/ai-docs/src/70_cli/index.md +++ b/repos/effect/ai-docs/src/70_cli/index.md @@ -1,5 +1,5 @@ ## Building CLI applications -Use the "effect/unstable/cli" modules to build CLI applications. These modules +Use the "effect/cli" modules to build CLI applications. These modules provide utilities for parsing command-line arguments, handling user input, and managing the flow of a CLI application. diff --git a/repos/effect/ai-docs/src/71_ai/10_language-model.ts b/repos/effect/ai-docs/src/71_ai/10_language-model.ts index 6bbede9268..c998f1b266 100644 --- a/repos/effect/ai-docs/src/71_ai/10_language-model.ts +++ b/repos/effect/ai-docs/src/71_ai/10_language-model.ts @@ -7,8 +7,8 @@ import { AnthropicClient, AnthropicLanguageModel } from "@effect/ai-anthropic" import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" import { Config, Context, Effect, ExecutionPlan, Layer, Schema, Stream } from "effect" -import { AiError, LanguageModel, Model, type Response } from "effect/unstable/ai" -import { FetchHttpClient } from "effect/unstable/http" +import { AiError, LanguageModel, Model, type Response } from "effect/ai" +import { FetchHttpClient } from "effect/http" import { LaunchPlan } from "./fixtures/domain/LaunchPlan.ts" // You can use Config to create ai clients diff --git a/repos/effect/ai-docs/src/71_ai/20_tools.ts b/repos/effect/ai-docs/src/71_ai/20_tools.ts index 2b0b169297..c5f1c957b9 100644 --- a/repos/effect/ai-docs/src/71_ai/20_tools.ts +++ b/repos/effect/ai-docs/src/71_ai/20_tools.ts @@ -6,8 +6,8 @@ */ import { OpenAiClient, OpenAiLanguageModel, OpenAiTool } from "@effect/ai-openai" import { Config, Context, Effect, Layer, Schema } from "effect" -import { AiError, LanguageModel, Tool, Toolkit } from "effect/unstable/ai" -import { FetchHttpClient } from "effect/unstable/http" +import { AiError, LanguageModel, Tool, Toolkit } from "effect/ai" +import { FetchHttpClient } from "effect/http" // --------------------------------------------------------------------------- // 1. Defining tools diff --git a/repos/effect/ai-docs/src/71_ai/30_chat.ts b/repos/effect/ai-docs/src/71_ai/30_chat.ts index 745d4d0334..c40c3e9b99 100644 --- a/repos/effect/ai-docs/src/71_ai/30_chat.ts +++ b/repos/effect/ai-docs/src/71_ai/30_chat.ts @@ -6,8 +6,8 @@ */ import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" import { Config, Context, DateTime, Effect, Layer, Ref, Schema } from "effect" -import { AiError, Chat, Prompt, Tool, Toolkit } from "effect/unstable/ai" -import { FetchHttpClient } from "effect/unstable/http" +import { AiError, Chat, Prompt, Tool, Toolkit } from "effect/ai" +import { FetchHttpClient } from "effect/http" // --------------------------------------------------------------------------- // Provider setup diff --git a/repos/effect/ai-docs/src/80_cluster/10_entities.ts b/repos/effect/ai-docs/src/80_cluster/10_entities.ts index 466197f9bd..b827bf09e2 100644 --- a/repos/effect/ai-docs/src/80_cluster/10_entities.ts +++ b/repos/effect/ai-docs/src/80_cluster/10_entities.ts @@ -5,9 +5,9 @@ */ import { NodeClusterSocket, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Ref, Schema } from "effect" -import { ClusterSchema, Entity, TestRunner } from "effect/unstable/cluster" -import { Rpc } from "effect/unstable/rpc" -import type { SqlClient } from "effect/unstable/sql" +import { ClusterSchema, Entity, TestRunner } from "effect/cluster" +import { Rpc } from "effect/rpc" +import type { SqlClient } from "effect/sql" export const Increment = Rpc.make("Increment", { payload: { amount: Schema.Int }, diff --git a/repos/effect/migration/annotations/effect__Arbitrary.yaml b/repos/effect/migration/annotations/effect__Arbitrary.yaml index 68be8ec479..b8ea997d8e 100644 --- a/repos/effect/migration/annotations/effect__Arbitrary.yaml +++ b/repos/effect/migration/annotations/effect__Arbitrary.yaml @@ -1,6 +1,6 @@ "effect/Arbitrary": - replacement: "effect/unstable/arbitrary/Arbitrary" - note: "Schema-derived generation moved to the native Arbitrary module. Effect no longer bridges to fast-check." + replacement: "effect/Arbitrary" + note: "Schema-derived generation now uses the native Arbitrary module. Effect no longer bridges to fast-check." "effect/Arbitrary#ArbitraryAnnotation": replacement: "Schema.Annotations.ToArbitrary.Declaration" note: "Arbitrary derivation annotations now live in Schema.Annotations. Attach a toCodecArbitrary declaration callback that returns a Schema Link." @@ -8,13 +8,13 @@ replacement: "Schema.Annotations.ToArbitrary.DeclarationInput" note: "Native arbitrary callbacks receive DeclarationInput with decoded type-parameter schemas and normalized constraints." "effect/Arbitrary#LazyArbitrary": - replacement: "effect/unstable/arbitrary/Arbitrary#Arbitrary" - note: "The generated-value description is now the native Arbitrary interface from effect/unstable/arbitrary." + replacement: "effect/Arbitrary#Arbitrary" + note: "The generated-value description is now the native Arbitrary interface from effect/Arbitrary." "effect/Arbitrary#make": - replacement: "effect/unstable/arbitrary/Arbitrary#schema" + replacement: "effect/Arbitrary#schema" note: "Derive a native Arbitrary from a Schema. Effect no longer bridges to fast-check." example: "Arbitrary.schema(schema)" "effect/Arbitrary#makeLazy": - replacement: "effect/unstable/arbitrary/Arbitrary#schema" + replacement: "effect/Arbitrary#schema" note: "Lazy and eager Schema derivation are the same native Arbitrary.schema constructor." example: "Arbitrary.schema(schema)" diff --git a/repos/effect/migration/annotations/effect__Array.yaml b/repos/effect/migration/annotations/effect__Array.yaml index 930975b29c..91f85d1aa3 100644 --- a/repos/effect/migration/annotations/effect__Array.yaml +++ b/repos/effect/migration/annotations/effect__Array.yaml @@ -49,9 +49,13 @@ "effect/Array#modifyOption": replacement: "Array.modify" note: "The Option suffix was dropped; an out-of-bounds index still returns Option.none." +"effect/Array#partition": + include: true + replacement: "Array.partition" + note: "Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]." "effect/Array#partitionMap": replacement: "Array.partition" - note: "Pass a Result-returning mapper instead of Either; the output remains [failures, successes], corresponding to v3 [lefts, rights]." + note: "Pass a Result-returning mapper instead of Either. The tuple is [passes, fails], the reverse of v3 [lefts, rights]." "effect/Array#ReadonlyArray": replacement: "Array.ReadonlyArray" note: "The namespace and its Infer, With, OrNonEmpty, AndNonEmpty, and Flatten utility types remain." @@ -61,6 +65,10 @@ "effect/Array#replaceOption": replacement: "Array.replace" note: "The Option suffix was dropped; an out-of-bounds index still returns Option.none." +"effect/Array#separate": + include: true + replacement: "Array.separate" + note: "The tuple is now [successes, failures], the reverse of v3 [lefts, rights]." "effect/Array#setNonEmptyHead": replacement: "Array.setHeadNonEmpty" note: "Direct word-order rename with the same non-empty-preserving result." diff --git a/repos/effect/migration/annotations/effect__Chunk.yaml b/repos/effect/migration/annotations/effect__Chunk.yaml index 2771afb04c..f4de37322d 100644 --- a/repos/effect/migration/annotations/effect__Chunk.yaml +++ b/repos/effect/migration/annotations/effect__Chunk.yaml @@ -7,15 +7,23 @@ "effect/Chunk#modifyOption": replacement: "Chunk.modify" note: "The Option suffix was dropped; an out-of-bounds index still returns Option.none." +"effect/Chunk#partition": + include: true + replacement: "Chunk.partition" + note: "Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]." "effect/Chunk#partitionMap": replacement: "Chunk.partition" - note: "Pass a Result-returning mapper instead of Either; the output remains [failures, successes]." + note: "Pass a Result-returning mapper instead of Either. The tuple is [passes, fails], the reverse of v3 [failures, successes]." "effect/Chunk#removeOption": replacement: "Chunk.remove" note: "The closest API now returns the unchanged Chunk out of bounds; use Chunk.get before Chunk.remove to preserve the old Option result." "effect/Chunk#replaceOption": replacement: "Chunk.replace" note: "The Option suffix was dropped; an out-of-bounds index still returns Option.none." +"effect/Chunk#separate": + include: true + replacement: "Chunk.separate" + note: "The tuple is now [successes, failures], the reverse of v3 [failures, successes]." "effect/Chunk#TypeId": replacement: "none" note: "The v4 Chunk brand key is private; no public Chunk.TypeId type or value is exported." diff --git a/repos/effect/migration/annotations/effect__Config.yaml b/repos/effect/migration/annotations/effect__Config.yaml index 04c784718e..bf59ed37bb 100644 --- a/repos/effect/migration/annotations/effect__Config.yaml +++ b/repos/effect/migration/annotations/effect__Config.yaml @@ -1,6 +1,6 @@ "effect/Config#all": replacement: "Config.all" - note: "Combine an iterable or record of Config values. A wholly absent product can use Config.withDefault or Config.option, while a partially supplied product fails." + note: "Combine an iterable or record of Config values. If any child is absent and none fail, Config.withDefault replaces the whole group and Config.option returns None. Apply defaults to individual children to preserve other supplied values." "effect/Config#array": replacement: "Config.Array(valueSchema, path)" note: "Array parsing is schema-based in v4; rebuild the element Config as a Schema and pass it with the optional path to Config.Array." @@ -67,6 +67,14 @@ "effect/Config#number": replacement: "Config.Number" note: "Direct constructor rename; use Config.Finite when NaN and infinities must be rejected." +"effect/Config#option": + include: true + replacement: "Config.option" + note: "Returns None for an absent config, including an all group with a missing child and no failed children. Successfully decoded undefined is wrapped in Some; validation and source errors still propagate." +"effect/Config#orElse": + include: true + replacement: "Config.orElse" + note: "The fallback receives Config.ConfigError and its result replaces the original failure. Unlike v3, failed attempts are not combined: an absent fallback can be handled by a later withDefault or option even if the original input was invalid." "effect/Config#orElseIf": replacement: "Config.orElse" note: "The fallback now receives Config.ConfigError; test it in the callback and re-fail with Config.fail(error.cause) when the predicate is false." @@ -103,6 +111,10 @@ "effect/Config#validate": replacement: "Config.schema(schema.check(check), path)" note: "Validation moved to Schema checks; attach the predicate and message to the Schema used by Config.schema." +"effect/Config#withDefault": + include: true + replacement: "Config.withDefault" + note: "Defaults absent configs, including all groups with missing children and no failed children. A group default replaces the whole group; use defaults on children to preserve other supplied values. Validation and source errors still propagate." "effect/Config#withDescription": replacement: "Config.schema(schema.annotate({ description }), path)" note: "Config descriptions moved to Schema annotations in v4." diff --git a/repos/effect/migration/annotations/effect__ConfigError.yaml b/repos/effect/migration/annotations/effect__ConfigError.yaml index 51146f3ab9..7522890272 100644 --- a/repos/effect/migration/annotations/effect__ConfigError.yaml +++ b/repos/effect/migration/annotations/effect__ConfigError.yaml @@ -30,13 +30,13 @@ note: "Parsing and validation failures are SchemaError causes; inspect the contained SchemaIssue for finer classification." "effect/ConfigError#isMissingData": replacement: "none" - note: "Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they distinguish absent provider input from successful undefined, invalid input, and partial products." + note: "Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they recover missing data, including Config.all groups with missing children, while preserving successful undefined and propagating validation and source errors." "effect/ConfigError#isMissingDataOnly": replacement: "Config.withDefault / Config.option" - note: "The public classifier was removed. These combinators use provider lookup evidence rather than recursively classifying SchemaIssue values." + note: "The public classifier was removed. These combinators handle absence without recursively classifying SchemaIssue values; invalid input and source errors still propagate." "effect/ConfigError#isOr": replacement: "error.cause.issue._tag === \"AnyOf\"" - note: "After narrowing cause with Schema.isSchemaError, inspect the SchemaIssue tag; the old Or node no longer exists." + note: "After narrowing cause with Schema.isSchemaError, AnyOf identifies schema union failures. Config.orElse does not combine failed attempts; it propagates only the fallback's error." "effect/ConfigError#isSourceUnavailable": replacement: "error.cause instanceof ConfigProvider.SourceError" note: "Provider source failures now use the ConfigProvider.SourceError class." @@ -51,7 +51,7 @@ note: "The shared constructor options type was removed; ConfigProvider.SourceError accepts message and optional cause, while Schema issues have issue-specific constructors." "effect/ConfigError#Or": replacement: "SchemaIssue.AnyOf" - note: "The ConfigError boolean ADT was removed; alternative schema failures are represented inside Config.ConfigError.cause as SchemaIssue.AnyOf." + note: "The ConfigError boolean ADT was removed. Schema union failures can use SchemaIssue.AnyOf inside Config.ConfigError.cause; Config.orElse adopts the fallback's result instead of combining failed attempts." "effect/ConfigError#prefixed": replacement: "SchemaIssue.Pointer" note: "Represent path context by wrapping the underlying SchemaIssue in a Pointer before constructing SchemaError." diff --git a/repos/effect/migration/annotations/effect__Effect.yaml b/repos/effect/migration/annotations/effect__Effect.yaml index a70dcdb40c..019969d243 100644 --- a/repos/effect/migration/annotations/effect__Effect.yaml +++ b/repos/effect/migration/annotations/effect__Effect.yaml @@ -20,6 +20,7 @@ effect/Effect#allWith: replacement: "Effect.all" note: "Wrap `Effect.all(values, options)` in a lambda when a data-last combinator is needed. Adapt arguments and imports to the v4 API." effect/Effect#annotateLogs: + include: true replacement: "Effect.annotateLogs" note: "Still exported in v4; update call sites for the revised signature, options, and channel inference." effect/Effect#ap: @@ -346,6 +347,10 @@ effect/Effect#parallelErrors: effect/Effect#parallelFinalizers: replacement: "none" note: "Parallel finalizer strategy mutation was removed; fork independent cleanup explicitly when ordering is irrelevant. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive." +effect/Effect#partition: + include: true + replacement: "Effect.partition" + note: "The tuple is now [passes, fails], the reverse of v3 [failures, successes]. Every effect still runs, and the outer effect does not fail." effect/Effect#patchFiberRefs: replacement: "none" note: "Bulk FiberRefs patching was removed; update individual context references with `Effect.updateService`. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive." @@ -605,6 +610,7 @@ effect/Effect#withFiberRuntime: replacement: "none" note: "Direct FiberRuntime access was removed; use public Effect, Fiber, and Context operations. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive." effect/Effect#withLogSpan: + include: true replacement: "Effect.withLogSpan" note: "Still exported in v4; update call sites for the revised signature, options, and channel inference." effect/Effect#withMaxOpsBeforeYield: diff --git a/repos/effect/migration/annotations/effect__Encoding.yaml b/repos/effect/migration/annotations/effect__Encoding.yaml index 9b1508e3e7..6cbbab65dc 100644 --- a/repos/effect/migration/annotations/effect__Encoding.yaml +++ b/repos/effect/migration/annotations/effect__Encoding.yaml @@ -1,24 +1,57 @@ +"effect/Encoding": + replacement: "none" + note: "Import each format or the shared error API from its direct subpath." "effect/Encoding#DecodeException": - replacement: "Encoding.EncodingError" + replacement: "effect/encoding/EncodingError#EncodingError" note: "Use the unified error class with kind Decode." "effect/Encoding#DecodeExceptionTypeId": - replacement: "Encoding.EncodingErrorTypeId" + replacement: "effect/encoding/EncodingError#EncodingErrorTypeId" note: "Decode and encode failures now share one marker." "effect/Encoding#decodeUriComponent": replacement: "Result.try" note: "Wrap decodeURIComponent in Result.try and map failure to EncodingError, or decode Schema.StringFromUriComponent." "effect/Encoding#EncodeException": - replacement: "Encoding.EncodingError" + replacement: "effect/encoding/EncodingError#EncodingError" note: "Use the unified error class with kind Encode." "effect/Encoding#EncodeExceptionTypeId": - replacement: "Encoding.EncodingErrorTypeId" + replacement: "effect/encoding/EncodingError#EncodingErrorTypeId" note: "Decode and encode failures now share one marker." "effect/Encoding#encodeUriComponent": replacement: "Result.try" note: "Wrap encodeURIComponent in Result.try and map failure to EncodingError, or encode Schema.StringFromUriComponent." "effect/Encoding#isDecodeException": - replacement: "Encoding.isEncodingError" + replacement: "effect/encoding/EncodingError#isEncodingError" note: "Use the unified guard and test kind === Decode when decode-only narrowing is required." "effect/Encoding#isEncodeException": - replacement: "Encoding.isEncodingError" + replacement: "effect/encoding/EncodingError#isEncodingError" note: "Use the unified guard and test kind === Encode when encode-only narrowing is required." +"effect/Encoding#encodeBase64": + replacement: "effect/encoding/Base64#encode" + note: "Import the Base64 helpers from their direct subpath." +"effect/Encoding#decodeBase64": + replacement: "effect/encoding/Base64#decode" + note: "Import the Base64 helpers from their direct subpath." +"effect/Encoding#decodeBase64String": + replacement: "effect/encoding/Base64#decodeString" + note: "Import the Base64 helpers from their direct subpath." +"effect/Encoding#encodeBase64Url": + replacement: "effect/encoding/Base64Url#encode" + note: "Import the Base64Url helpers from their direct subpath." +"effect/Encoding#decodeBase64Url": + replacement: "effect/encoding/Base64Url#decode" + note: "Import the Base64Url helpers from their direct subpath." +"effect/Encoding#decodeBase64UrlString": + replacement: "effect/encoding/Base64Url#decodeString" + note: "Import the Base64Url helpers from their direct subpath." +"effect/Encoding#encodeHex": + replacement: "effect/encoding/Hex#encode" + note: "Import the Hex helpers from their direct subpath." +"effect/Encoding#decodeHex": + replacement: "effect/encoding/Hex#decode" + note: "Import the Hex helpers from their direct subpath." +"effect/Encoding#decodeHexString": + replacement: "effect/encoding/Hex#decodeString" + note: "Import the Hex helpers from their direct subpath." +"effect/Encoding#randomHex": + replacement: "effect/encoding/Hex#random" + note: "randomHex was renamed to random in the Hex module." \ No newline at end of file diff --git a/repos/effect/migration/annotations/effect__FastCheck.yaml b/repos/effect/migration/annotations/effect__FastCheck.yaml index 8a0c421dd8..37f1938b23 100644 --- a/repos/effect/migration/annotations/effect__FastCheck.yaml +++ b/repos/effect/migration/annotations/effect__FastCheck.yaml @@ -1,6 +1,6 @@ "effect/FastCheck": replacement: "fast-check" - note: "Effect no longer re-exports fast-check. Depend on the fast-check package and import it directly. For Schema-derived generation, use Arbitrary.schema from effect/unstable/arbitrary." + note: "Effect no longer re-exports fast-check. Depend on the fast-check package and import it directly. For Schema-derived generation, use Arbitrary.schema from effect/Arbitrary." "effect/FastCheck#ascii": replacement: "FastCheck.string" note: "Depend on fast-check and import it directly. fast-check v4 replaced character arbitraries with string units." diff --git a/repos/effect/migration/annotations/effect__List.yaml b/repos/effect/migration/annotations/effect__List.yaml index ce1ec1d1e2..624461a100 100644 --- a/repos/effect/migration/annotations/effect__List.yaml +++ b/repos/effect/migration/annotations/effect__List.yaml @@ -69,10 +69,10 @@ note: "List was removed; use Array.of. It preserves ordering but returns arrays rather than persistent linked lists." "effect/List#partition": replacement: "Array.partition" - note: "Use a Result-returning callback: failure values form the first array and success values the second." + note: "Use a Result-returning callback: success values form the first array and failure values the second." "effect/List#partitionMap": replacement: "Array.partition" - note: "Migrate the Either-returning mapper to Result; failures form the first array and successes the second." + note: "Migrate the Either-returning mapper to Result; successes form the first array and failures the second." "effect/List#prependAll": replacement: "Array.prependAll" note: "List was removed; use Array.prependAll. It preserves ordering but returns arrays rather than persistent linked lists." diff --git a/repos/effect/migration/annotations/effect__Option.yaml b/repos/effect/migration/annotations/effect__Option.yaml index 75f930ac9b..a4595a7bb1 100644 --- a/repos/effect/migration/annotations/effect__Option.yaml +++ b/repos/effect/migration/annotations/effect__Option.yaml @@ -37,6 +37,10 @@ "effect/Option#orElseEither": replacement: "Option.orElseResult" note: "Either was replaced by Result; source tracking now uses Failure and Success." +"effect/Option#partitionMap": + include: true + replacement: "Option.partitionMap" + note: "The tuple is now [passes, fails], the reverse of v3 [left, right]." "effect/Option#Some": replacement: "Option.Some" note: "The variant remains, but Option is no longer an Effect or STM subtype." diff --git a/repos/effect/migration/annotations/effect__ParseResult.yaml b/repos/effect/migration/annotations/effect__ParseResult.yaml index 72c6479214..da9f26d0b4 100644 --- a/repos/effect/migration/annotations/effect__ParseResult.yaml +++ b/repos/effect/migration/annotations/effect__ParseResult.yaml @@ -13,7 +13,7 @@ note: "Composite parse failures moved to SchemaIssue. The v4 constructor takes the failing AST and an array of nested issues; input is retained only when reportInput is enabled." "effect/ParseResult#DeclarationDecodeUnknown": replacement: "SchemaGetter.Getter" - note: "Custom declaration decoding now uses SchemaGetter values and Schema.declare annotations." + note: "Custom declaration decoding now uses SchemaGetter values and Schema.declare annotations. Getter is a tagged union, not a constructor; use transformOptionalEffect for an Option-to-Effect decoder. Use standalone SchemaGetter.run, map, and compose instead of instance methods." "effect/ParseResult#decodeEither": replacement: "Schema.decodeExit" note: "Either parsing was replaced by Exit parsing." diff --git a/repos/effect/migration/annotations/effect__RateLimiter.yaml b/repos/effect/migration/annotations/effect__RateLimiter.yaml index 77dd05c4f0..5fd96b5f8e 100644 --- a/repos/effect/migration/annotations/effect__RateLimiter.yaml +++ b/repos/effect/migration/annotations/effect__RateLimiter.yaml @@ -1,3 +1,3 @@ effect/RateLimiter: replacement: none - note: The old limit, interval, and algorithm options belonged to the removed in-process limiter; choose and configure an application limiter explicitly. The scoped in-process callable limiter was not ported to v4; effect/unstable/persistence/RateLimiter is a keyed persistence service with different semantics, not a drop-in replacement. The FiberRef-based per-effect cost annotation was removed with the core RateLimiter; pass token cost explicitly to the replacement limiter. + note: The old limit, interval, and algorithm options belonged to the removed in-process limiter; choose and configure an application limiter explicitly. The scoped in-process callable limiter was not ported to v4; effect/persistence/RateLimiter is a keyed persistence service with different semantics, not a drop-in replacement. The FiberRef-based per-effect cost annotation was removed with the core RateLimiter; pass token cost explicitly to the replacement limiter. diff --git a/repos/effect/migration/annotations/effect__Record.yaml b/repos/effect/migration/annotations/effect__Record.yaml index 4e86dff117..ae50cc689e 100644 --- a/repos/effect/migration/annotations/effect__Record.yaml +++ b/repos/effect/migration/annotations/effect__Record.yaml @@ -10,9 +10,13 @@ "effect/Record#modifyOption": replacement: "Record.modify" note: "The Option suffix was dropped; missing keys still return Option.none." +"effect/Record#partition": + include: true + replacement: "Record.partition" + note: "Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]." "effect/Record#partitionMap": replacement: "Record.partition" - note: "Pass a mapper returning Result; failures and successes form the two output records." + note: "Pass a mapper returning Result. The tuple is [passes, fails], the reverse of v3 [failures, successes]." "effect/Record#ReadonlyRecord": replacement: "Record.ReadonlyRecord" note: "The public type and parameter order are unchanged." @@ -22,3 +26,7 @@ "effect/Record#replaceOption": replacement: "Record.replace" note: "The Option suffix was dropped; missing keys still return Option.none." +"effect/Record#separate": + include: true + replacement: "Record.separate" + note: "The tuple is now [successes, failures], the reverse of v3 [failures, successes]." diff --git a/repos/effect/migration/annotations/effect__STM.yaml b/repos/effect/migration/annotations/effect__STM.yaml index d11b88e98b..78bdade90f 100644 --- a/repos/effect/migration/annotations/effect__STM.yaml +++ b/repos/effect/migration/annotations/effect__STM.yaml @@ -246,7 +246,7 @@ effect/STM#orTry: note: "V4 exposes no recoverable retry signal or public transactional savepoint; restructure branch selection before Effect.txRetry." effect/STM#partition: replacement: "Effect.partition" - note: "The combinator keeps its name, but STM values are now ordinary Effects. Run the complete transaction with Effect.tx. V4 Tx operations return ordinary Effects; compose multiple operations under one outer Effect.tx to keep them atomic." + note: "STM values are now ordinary Effects. Run the complete transaction with Effect.tx. The tuple is [passes, fails], the reverse of v3 [failures, successes]." effect/STM#provideServiceSTM: replacement: "Effect.provideServiceEffect" note: "The effectful service provider was renamed on Effect." diff --git a/repos/effect/migration/annotations/effect__Schedule.yaml b/repos/effect/migration/annotations/effect__Schedule.yaml index 00770ca6ca..5898a000e1 100644 --- a/repos/effect/migration/annotations/effect__Schedule.yaml +++ b/repos/effect/migration/annotations/effect__Schedule.yaml @@ -136,9 +136,6 @@ "effect/Schedule#modifyDelayEffect": replacement: "Schedule.modifyDelay" note: "The v4 delay modifier is effectful by default and receives full Schedule.Metadata." -"effect/Schedule#once": - replacement: "Schedule.duration(Duration.zero)" - note: "A zero-duration schedule recurs once and then completes; map its Duration output to void if needed." "effect/Schedule#onDecision": replacement: "Schedule.tap" note: "Use Schedule.tap for effects on recurrence metadata. To also observe final completion, wrap Schedule.toStep with Pull.matchEffect in Schedule.fromStep." diff --git a/repos/effect/migration/annotations/effect__Schema.yaml b/repos/effect/migration/annotations/effect__Schema.yaml index 6be9789034..202849859f 100644 --- a/repos/effect/migration/annotations/effect__Schema.yaml +++ b/repos/effect/migration/annotations/effect__Schema.yaml @@ -120,10 +120,10 @@ effect/Schema#BooleanFromUnknown: note: Use the boolean schema and perform any coercion explicitly before decoding. effect/Schema#brand: replacement: Schema.brand - note: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. + note: Pass one concrete string literal as the brand identifier. Widened strings, unions, open template literals, and symbol identifiers are rejected; apply brand repeatedly to compose brands. V4 branding is type-only and does not add runtime AST metadata. effect/Schema#BrandSchema: replacement: Schema.brand - note: Use the schema returned by the v4 `brand` combinator and infer its concrete type. + note: Use the schema returned by the v4 `brand` combinator and infer its concrete type. The brand exists only in the TypeScript type and cannot be inspected through the runtime AST. effect/Schema#BrandSchemaId: replacement: none note: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -305,8 +305,8 @@ effect/Schema#encodeUnknownPromise: replacement: Schema.encodeUnknownPromise note: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. effect/Schema#endsWith: - replacement: Schema.isEndsWith - note: Rename the string predicate to `isEndsWith` and apply it with `Schema.check` or a schema's `check` method. + replacement: Schema.isEndingWith + note: Rename the string predicate to `isEndingWith` and apply it with `Schema.check` or a schema's `check` method. effect/Schema#EndsWithSchemaId: replacement: none note: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -372,7 +372,7 @@ effect/Schema#format: note: Build a representation with `SchemaRepresentation.toRepresentation`, `toMultiDocument`, then `toCodeDocument`. effect/Schema#fromBrand: replacement: Schema.fromBrand - note: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. + note: Pass the constructor's single concrete string brand key as the first argument, followed by the constructor and then the schema. Apply fromBrand repeatedly for multiple brands; use Schema.Union for alternatives. Constructor checks remain at runtime, while the brand is type-only and does not add AST metadata. effect/Schema#fromKey: replacement: Schema.encodeKeys note: Use `encodeKeys` to map decoded property names to encoded keys. @@ -464,8 +464,8 @@ effect/Schema#headOrElse: replacement: none note: Compared the v3 declaration with v4 Schema and the schema migration guide; no direct public replacement remains. Rebuild the behavior from public v4 codecs/getters where still required. effect/Schema#includes: - replacement: Schema.isIncludes - note: Rename the string predicate to `isIncludes` and apply it with `Schema.check` or a schema's `check` method. + replacement: Schema.isIncluding + note: Rename the string predicate to `isIncluding` and apply it with `Schema.check` or a schema's `check` method. effect/Schema#IncludesSchemaId: replacement: none note: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -509,7 +509,7 @@ effect/Schema#isSchema: replacement: Schema.isSchema note: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. effect/Schema#itemsCount: - replacement: Schema.isLengthBetween + replacement: Schema.isBetweenLength note: Use the v4 collection-size check and apply it with `Schema.check` or a schema's `check` method. effect/Schema#ItemsCountSchemaId: replacement: none @@ -527,8 +527,8 @@ effect/Schema#LeftEncoded: replacement: none note: Compared the v3 declaration with v4 Schema and the schema migration guide; no direct public replacement remains. Rebuild the behavior from public v4 codecs/getters where still required. effect/Schema#length: - replacement: Schema.isLengthBetween - note: Use `isLengthBetween` with equal minimum and maximum values for an exact string length. + replacement: Schema.isBetweenLength + note: Use `isBetweenLength` with equal minimum and maximum values for an exact string length. effect/Schema#LengthSchemaId: replacement: none note: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -1070,8 +1070,8 @@ effect/Schema#standardSchemaV1: replacement: Schema.toStandardSchemaV1 note: Rename the Standard Schema adapter. effect/Schema#startsWith: - replacement: Schema.isStartsWith - note: Rename the string predicate to `isStartsWith` and apply it with `Schema.check` or a schema's `check` method. + replacement: Schema.isStartingWith + note: Rename the string predicate to `isStartingWith` and apply it with `Schema.check` or a schema's `check` method. effect/Schema#StartsWithSchemaId: replacement: none note: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -1139,7 +1139,7 @@ effect/Schema#TaggedErrorClass: replacement: Schema.TaggedError note: The exported helper interface was removed; use the class returned by Schema.TaggedError and infer its types. effect/Schema#TaggedRequest: - replacement: effect/unstable/rpc/Rpc.make + replacement: effect/rpc/Rpc.make note: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. effect/Schema#TaggedRequest.All: replacement: none @@ -1148,7 +1148,7 @@ effect/Schema#TaggedRequest.Any: replacement: none note: The v3 helper/protocol type was removed by the v4 Schema model rewrite. Use the public v4 constructor and infer its result types instead. effect/Schema#TaggedRequestClass: - replacement: effect/unstable/rpc/Rpc.make + replacement: effect/rpc/Rpc.make note: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. effect/Schema#TaggedStruct: replacement: Schema.TaggedStruct diff --git a/repos/effect/migration/annotations/effect__SchemaAST.yaml b/repos/effect/migration/annotations/effect__SchemaAST.yaml index 043934cf5b..51b523da92 100644 --- a/repos/effect/migration/annotations/effect__SchemaAST.yaml +++ b/repos/effect/migration/annotations/effect__SchemaAST.yaml @@ -35,11 +35,11 @@ replacement: "SchemaAST.Boolean" note: "The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role." "effect/SchemaAST#BrandAnnotation": - replacement: "Schema.Annotations.Bottom[\"brands\"]" - note: "Brands are stored under the string-keyed brands annotation and normally added with Schema.brand." + replacement: "none" + note: "Runtime brand annotations were removed. Schema.brand adds a TypeScript-only nominal distinction and does not store brand metadata in the AST." "effect/SchemaAST#BrandAnnotationId": replacement: "Schema.brand" - note: "Symbol annotation IDs were removed; add brands through Schema.brand." + note: "Use Schema.brand for a TypeScript-only nominal distinction. V4 does not store brand metadata in the AST." "effect/SchemaAST#Compiler": replacement: "none" note: "The generic AST compiler abstraction was removed; traverse the discriminated SchemaAST.AST union directly or use a higher-level Schema derivation API." @@ -119,8 +119,8 @@ replacement: "none" note: "Batching annotations were removed. ParseOptions.concurrency controls product child parsing only; configure request batching separately." "effect/SchemaAST#getBrandAnnotation": - replacement: "SchemaAST.resolveAt(\"brands\")" - note: "Resolve the string-keyed brands annotation." + replacement: "none" + note: "Runtime brand inspection was removed because Schema.brand is type-only and does not store brand metadata in the AST." "effect/SchemaAST#getCompiler": replacement: "none" note: "The Match-based compiler was removed; traverse SchemaAST.AST directly or use the relevant Schema derivation API." diff --git a/repos/effect/migration/annotations/effect__Stream.yaml b/repos/effect/migration/annotations/effect__Stream.yaml index c2d861cd59..e4396a5919 100644 --- a/repos/effect/migration/annotations/effect__Stream.yaml +++ b/repos/effect/migration/annotations/effect__Stream.yaml @@ -1,3 +1,39 @@ +effect/Stream#bind: + include: true + replacement: "Stream.bind" + note: "The input must be a record. Start from Stream.Do or use Stream.bindTo(name) to wrap a non-record element. The field name may now re-bind an existing key; the resulting record type is Simplify & Record>." +effect/Stream#bindEffect: + include: true + replacement: "Stream.bindEffect" + note: "The input must be a record. Start from Stream.Do or use Stream.bindTo(name) to wrap a non-record element. Existing keys may be re-bound, with the new value replacing the old field type. The callback still returns an Effect." +effect/Stream#catchTags: + include: true + replacement: "Stream.catchTags" + note: "Only keys present in the tagged error union are accepted; remove handlers for tags the stream cannot fail with. Untagged errors remain in the error channel." +effect/Stream#mapBoth: + include: true + replacement: "Stream.mapBoth" + note: "Rename the options onSuccess to onElement and onFailure to onError. The callbacks still transform emitted elements and typed errors respectively." +effect/Stream#partition: + include: true + replacement: "Stream.partition" + note: "Pass a Result-returning Filter instead of a boolean predicate or refinement; Filter.fromPredicate(predicate) adapts existing predicates. The tuple order is now [passes, fails], the reverse of v3 [excluded, satisfying]. Rename bufferSize to capacity (default 16; also accepts \"unbounded\"). Acquisition is scoped and does not fail with E; the returned streams carry E." +effect/Stream#scan: + include: true + replacement: "Stream.scan" + note: "Wrap the initial state in a thunk: Stream.scan(() => initial, step). The thunk is evaluated for each stream run; allocate mutable initial state inside it to avoid sharing state between runs." +effect/Stream#scanEffect: + include: true + replacement: "Stream.scanEffect" + note: "Wrap the initial state in a thunk: Stream.scanEffect(() => initial, step). It is evaluated for each stream run; the step still returns an Effect." +effect/Stream#runIntoPubSub: + include: true + replacement: "Stream.runIntoPubSub" + note: "Pass a PubSub of plain elements rather than Take wrappers. Both overloads return Effect, so handle stream failures on the returned Effect instead of expecting them as PubSub messages. The optional shutdownOnEnd flag controls PubSub shutdown." +effect/Stream#let: + include: true + replacement: "Stream.let" + note: "Retained in v4 and aligned with Effect.let: the field name may re-bind an existing key (the computed value replaces it) and the record type is Simplify & Record>." effect/Stream#Stream: replacement: "Stream" note: "The Stream interface is unchanged in shape and keeps the effect/Stream import path; the type-id key is now the string literal \"~effect/Stream\" instead of a unique symbol." @@ -29,8 +65,9 @@ effect/Stream#aggregateWithinEither: replacement: "Stream.aggregateWithin" note: "Either-emitting variant removed; v4 aggregateWithin(sink, schedule) emits only the sink outputs B (schedule outputs are no longer surfaced as Either.right)." effect/Stream#as: - replacement: "Stream.map(() => value)" - note: "Stream.as was removed; replace each element with a constant via Stream.map." + include: true + replacement: "Stream.as" + note: "Retained in v4 with the same signature; replaces every element with the provided constant value." effect/Stream#async: replacement: "Stream.callback" note: "Stream.callback((queue) => Effect | void, { bufferSize?, strategy? }) replaces the Emit-based async; push with Queue.offer/offerAll, end with Queue.end, fail with Queue.fail." @@ -392,6 +429,7 @@ effect/Stream#toReadableStreamRuntime: replacement: "Stream.toReadableStreamWith" note: "Renamed; takes a `Context.Context` instead of a Runtime (v4 removed Runtime); options `{ strategy?: QueuingStrategy }` unchanged." effect/Stream#transduce: + include: true replacement: "Stream.transduce" note: "Unchanged name and Sink-based shape; chunks are plain arrays in v4." effect/Stream#unfoldChunk: diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml b/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml index a2cb45a97c..dcf76eb723 100644 --- a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml +++ b/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml @@ -1,6 +1,6 @@ "@effect/ai-anthropic/AnthropicTokenizer#layer": replacement: "Tokenizer.make" - note: "The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required." + note: "The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required." "@effect/ai-anthropic/AnthropicTokenizer#make": replacement: "Tokenizer.make" - note: "The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required." + note: "The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required." diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml index 9c459de0cb..41aa005201 100644 --- a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml +++ b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml @@ -1,6 +1,6 @@ "@effect/ai-openai/OpenAiTokenizer#layer": replacement: "Tokenizer.make" - note: "The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required." + note: "The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required." "@effect/ai-openai/OpenAiTokenizer#make": replacement: "Tokenizer.make" - note: "The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required." + note: "The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required." diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTool.yaml b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTool.yaml new file mode 100644 index 0000000000..7cf5207b16 --- /dev/null +++ b/repos/effect/migration/annotations/effect__ai-openai__OpenAiTool.yaml @@ -0,0 +1,14 @@ +"@effect/ai-openai/OpenAiTool#CodeInterpreter": + replacement: "OpenAiTool.CodeInterpreter" + note: "Results now wrap outputs in an object with status and outputs, rather than returning the output array or null directly. Only completed is successful; other statuses produce failure results that preserve outputs." +"@effect/ai-openai/OpenAiTool#FileSearch": + replacement: "OpenAiTool.FileSearch" + note: "Results contain status, queries, and a required results field holding matches or null. Only completed is successful; other statuses produce failure results that preserve partial matches." +"@effect/ai-openai/OpenAiTool#WebSearch": + include: true + replacement: "OpenAiTool.WebSearch" + note: "Results now include action alongside status. Only completed is successful; other statuses produce failure results. Inspect isFailure before treating a result as a successful search. Inspect each search source by its type: URL sources have url, while API sources have name and no url." +"@effect/ai-openai/OpenAiTool#WebSearchPreview": + include: true + replacement: "OpenAiTool.WebSearchPreview" + note: "Results now include action alongside status. Only completed is successful; other statuses produce failure results. Inspect isFailure before treating a result as a successful search. Inspect each search source by its type: URL sources have url, while API sources have name and no url." diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml b/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml index 81023018b4..a410652734 100644 --- a/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml +++ b/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml @@ -12,4 +12,4 @@ note: "The standalone streaming chunk schema was replaced by the decoded data type from Generated.ChatStreamingResponse." "@effect/ai-openrouter/OpenRouterClient#Service": replacement: "OpenRouterClient.Service" - note: "Still exported in v4; adapt to the regenerated client, revised request and response schemas, and the new streaming result tuple." + note: "Still exported in v4; adapt to the regenerated client, revised request and response schemas, and the new streaming result tuple. Custom service implementations and mocks must also provide createDecisions, returning an Effect of [decoded DecisionsResponse, HttpClientResponse] with AiError failures." diff --git a/repos/effect/migration/annotations/effect__ai.yaml b/repos/effect/migration/annotations/effect__ai.yaml index 2a4a652993..0d82aa78bd 100644 --- a/repos/effect/migration/annotations/effect__ai.yaml +++ b/repos/effect/migration/annotations/effect__ai.yaml @@ -1,3 +1,3 @@ "@effect/ai": - replacement: "effect/unstable/ai" - note: "The @effect/ai package was merged into the effect package; import the effect/unstable/ai barrel or import specific modules directly (e.g. effect/unstable/ai/)." + replacement: "effect/ai" + note: "The @effect/ai package was merged into the effect package; import the effect/ai barrel or import specific modules directly (e.g. effect/ai/)." diff --git a/repos/effect/migration/annotations/effect__ai__AiError.yaml b/repos/effect/migration/annotations/effect__ai__AiError.yaml index 34d041d504..b5c46028f5 100644 --- a/repos/effect/migration/annotations/effect__ai__AiError.yaml +++ b/repos/effect/migration/annotations/effect__ai__AiError.yaml @@ -1,6 +1,6 @@ "@effect/ai/AiError#AiError": replacement: "AiError.AiError" - note: "Moved to effect/unstable/ai/AiError and redesigned from a union of separately tagged errors into one AiError wrapper with a semantic reason. Construct it with AiError.make({ module, method, reason }) and match error.reason rather than the old top-level tags." + note: "Moved to effect/ai/AiError and redesigned from a union of separately tagged errors into one AiError wrapper with a semantic reason. Construct it with AiError.make({ module, method, reason }) and match error.reason rather than the old top-level tags." "@effect/ai/AiError#HttpRequestError": replacement: "AiError.make + AiError.NetworkError" note: "Replace the old top-level request error with an AiError whose reason is NetworkError. NetworkError.fromRequestError converts a v4 HttpClientError.RequestError." @@ -21,4 +21,4 @@ note: "UnknownError is now a semantic reason rather than a top-level error. Put module and method on AiError.make and inspect reason._tag when handling the outer AiError." "@effect/ai/AiError#HttpRequestDetails": replacement: "AiError.HttpRequestDetails" - note: "Retained in effect/unstable/ai/AiError and also re-exported as Response.HttpRequestDetails. Hash is an optional string instead of Option, headers may contain Redacted strings, and method includes TRACE." + note: "Retained in effect/ai/AiError and also re-exported as Response.HttpRequestDetails. Hash is an optional string instead of Option, headers may contain Redacted strings, and method includes TRACE." diff --git a/repos/effect/migration/annotations/effect__ai__IdGenerator.yaml b/repos/effect/migration/annotations/effect__ai__IdGenerator.yaml index 8079a23755..790ba418ba 100644 --- a/repos/effect/migration/annotations/effect__ai__IdGenerator.yaml +++ b/repos/effect/migration/annotations/effect__ai__IdGenerator.yaml @@ -1,3 +1,3 @@ "@effect/ai/IdGenerator#make": replacement: "IdGenerator.make" - note: "Moved to effect/unstable/ai/IdGenerator with the same configurable alphabet, prefix, separator, and size behavior. Invalid configuration now fails with Cause.IllegalArgumentError." + note: "Moved to effect/ai/IdGenerator with the same configurable alphabet, prefix, separator, and size behavior. Invalid configuration now fails with Cause.IllegalArgumentError." diff --git a/repos/effect/migration/annotations/effect__ai__LanguageModel.yaml b/repos/effect/migration/annotations/effect__ai__LanguageModel.yaml index 19b26391dc..f7de84e3fc 100644 --- a/repos/effect/migration/annotations/effect__ai__LanguageModel.yaml +++ b/repos/effect/migration/annotations/effect__ai__LanguageModel.yaml @@ -3,7 +3,7 @@ note: "V4 inlines this provider-adapter shape in LanguageModel.make. Pass generateText and streamText directly to make, with optional codecTransformer, instead of naming a constructor-parameter type." "@effect/ai/LanguageModel#ExtractContext": replacement: "LanguageModel.ExtractServices" - note: "Renamed in effect/unstable/ai/LanguageModel. ExtractServices infers toolkit handler, result-decoding, and effectful-toolkit service requirements." + note: "Renamed in effect/ai/LanguageModel. ExtractServices infers toolkit handler, result-decoding, and effectful-toolkit service requirements." "@effect/ai/LanguageModel#LanguageModel": replacement: "LanguageModel.LanguageModel" note: "Use the LanguageModel interface for implementations and the Context.Service value as the service key; it is no longer a class. Prefer LanguageModel.make to construct the branded implementation." diff --git a/repos/effect/migration/annotations/effect__ai__McpSchema.yaml b/repos/effect/migration/annotations/effect__ai__McpSchema.yaml index 2c7a565d17..8aed30bf95 100644 --- a/repos/effect/migration/annotations/effect__ai__McpSchema.yaml +++ b/repos/effect/migration/annotations/effect__ai__McpSchema.yaml @@ -1,27 +1,30 @@ "@effect/ai/McpSchema#ContentBlock": replacement: "McpSchema.ContentBlock" - note: "Moved to effect/unstable/ai/McpSchema. It remains the MCP content-block union, but v4 exports it as a const schema rather than a Schema.Union subclass. Binary image, audio, and blob data still use Uint8Array values with base64 wire encoding." + note: "Moved to effect/ai/McpSchema. It remains the MCP content-block union, but v4 exports it as a const schema rather than a Schema.Union subclass. Binary image, audio, and blob data still use Uint8Array values with base64 wire encoding." "@effect/ai/McpSchema#ElicitResult": replacement: "McpSchema.ElicitResult" - note: "Moved to effect/unstable/ai/McpSchema. It remains discriminated by action, but accepted content is now optional and, when present, is a record of strings, finite numbers, booleans, or string arrays; declined and canceled responses still omit content." + note: "Moved to effect/ai/McpSchema. It remains discriminated by action, but accepted content is now optional and, when present, is a record of strings, finite numbers, booleans, or string arrays; declined and canceled responses still omit content." "@effect/ai/McpSchema#FailureEncoded": replacement: "McpSchema.FailureEncoded" - note: "Moved to effect/unstable/ai/McpSchema and still derives an encoded JSON-RPC failure union from an RpcGroup." + note: "Moved to effect/ai/McpSchema and still derives an encoded JSON-RPC failure union from an RpcGroup." "@effect/ai/McpSchema#FromClientEncoded": replacement: "McpSchema.FromClientEncoded" - note: "Moved to effect/unstable/ai/McpSchema and remains the union of client requests and client notifications." + note: "Moved to effect/ai/McpSchema and remains the union of client requests and client notifications." "@effect/ai/McpSchema#FromServerEncoded": replacement: "McpSchema.FromServerEncoded" - note: "Moved to effect/unstable/ai/McpSchema and remains the union of server results and server notifications." + note: "Moved to effect/ai/McpSchema and remains the union of server results and server notifications." "@effect/ai/McpSchema#McpError": replacement: "McpSchema.McpError" note: "Moved, but changed from a constructable base class to a union schema of standard tagged protocol errors plus McpErrorBase. Use McpErrorBase to construct a generic MCP error." +"@effect/ai/McpSchema#McpServerClient": + replacement: "McpSchema.McpRequestContext / McpSchema.McpServerClient" + note: "Use McpRequestContext for client identity, capabilities, and request metadata in protocol-neutral handlers. McpServerClient remains available for initialized stateful protocols and reverse client requests; it is not provided by the stateless 2026-07-28 adapter. Registration helpers exclude McpRequestContext from handler requirements, not McpServerClient." "@effect/ai/McpSchema#param": replacement: "McpSchema.param" - note: "Moved to effect/unstable/ai/McpSchema. V4 wraps the schema and exposes Param.name and Param.schema instead of attaching a public symbol annotation." + note: "Moved to effect/ai/McpSchema. V4 wraps the schema and exposes Param.name and Param.schema instead of attaching a public symbol annotation." "@effect/ai/McpSchema#ParamAnnotation": replacement: "McpSchema.isParam / Param.name" note: "The public symbol annotation was removed. Detect parameter wrappers with McpSchema.isParam and read the narrowed Param.name instead of inspecting AST annotations." "@effect/ai/McpSchema#SuccessEncoded": replacement: "McpSchema.SuccessEncoded" - note: "Moved to effect/unstable/ai/McpSchema and still derives an encoded JSON-RPC success union from an RpcGroup." + note: "Moved to effect/ai/McpSchema and still derives an encoded JSON-RPC success union from an RpcGroup." diff --git a/repos/effect/migration/annotations/effect__ai__McpServer.yaml b/repos/effect/migration/annotations/effect__ai__McpServer.yaml index 7ded9767a5..51598405d8 100644 --- a/repos/effect/migration/annotations/effect__ai__McpServer.yaml +++ b/repos/effect/migration/annotations/effect__ai__McpServer.yaml @@ -1,15 +1,36 @@ "@effect/ai/McpServer#layer": replacement: "McpServer.layer" - note: "Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/unstable/ai; it still runs over a caller-provided RpcServer.Protocol." + note: "Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/ai; it still runs over a caller-provided RpcServer.Protocol." "@effect/ai/McpServer#layerHttp": replacement: "McpServer.layerHttp" - note: "Moved to effect/unstable/ai/McpServer and the unified HttpRouter. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/unstable/ai." + note: "Moved to effect/ai/McpServer and the unified HttpRouter. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/ai." "@effect/ai/McpServer#layerHttpRouter": replacement: "McpServer.layerHttp" - note: "Renamed and consolidated. V4 layerHttp registers the Streamable HTTP endpoint in the unified HttpRouter; pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/unstable/ai." + note: "Renamed and consolidated. V4 layerHttp registers the Streamable HTTP endpoint in the unified HttpRouter; pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/ai." "@effect/ai/McpServer#layerStdio": replacement: "McpServer.layerStdio" - note: "Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/unstable/ai." + note: "Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/ai." "@effect/ai/McpServer#run": replacement: "McpServer.run" - note: "Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/unstable/ai; it remains the Effect-level runner over RpcServer.Protocol." + note: "Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025_06_18], imported with McpProtocol from effect/ai; it remains the Effect-level runner over RpcServer.Protocol." +"@effect/ai/McpServer#McpServer": + replacement: "McpServer.McpServer" + note: "The service is now a Context.Service. initializedClients and notificationsMailbox are no longer exposed. Registration callbacks use McpSchema.McpRequestContext; McpServerClient is only supplied for initialized stateful requests. Prefer registerToolkit, registerResource, and registerPrompt over implementing the registry shape directly." +"@effect/ai/McpServer#registerToolkit": + replacement: "McpServer.registerToolkit" + note: "Moved to effect/ai/McpServer. Handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams." +"@effect/ai/McpServer#toolkit": + replacement: "McpServer.toolkit" + note: "Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema." +"@effect/ai/McpServer#registerResource": + replacement: "McpServer.registerResource" + note: "Moved to effect/ai/McpServer. Resource and completion handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata." +"@effect/ai/McpServer#resource": + replacement: "McpServer.resource" + note: "Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from resource and completion handler requirements instead of McpServerClient." +"@effect/ai/McpServer#registerPrompt": + replacement: "McpServer.registerPrompt" + note: "Moved to effect/ai/McpServer. Prompt decoding and handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata." +"@effect/ai/McpServer#prompt": + replacement: "McpServer.prompt" + note: "Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding and handler requirements instead of McpServerClient." diff --git a/repos/effect/migration/annotations/effect__ai__Prompt.yaml b/repos/effect/migration/annotations/effect__ai__Prompt.yaml index d08583bc6e..ab6e174be3 100644 --- a/repos/effect/migration/annotations/effect__ai__Prompt.yaml +++ b/repos/effect/migration/annotations/effect__ai__Prompt.yaml @@ -1,36 +1,36 @@ "@effect/ai/Prompt#FilePart": replacement: "Prompt.FilePart" - note: "Moved to effect/unstable/ai/Prompt with the same file-part model and schema; update the module import." + note: "Moved to effect/ai/Prompt with the same file-part model and schema; update the module import." "@effect/ai/Prompt#FilePartEncoded": replacement: "Prompt.FilePartEncoded" - note: "Moved to effect/unstable/ai/Prompt with the same encoded file-part shape; update the module import." + note: "Moved to effect/ai/Prompt with the same encoded file-part shape; update the module import." "@effect/ai/Prompt#FromJson": replacement: "Schema.fromJsonString(Prompt.Prompt)" note: "The module-specific JSON schema was removed. Compose the general v4 JSON-string codec with the public Prompt codec." "@effect/ai/Prompt#isPart": replacement: "Prompt.isPart" - note: "Moved to effect/unstable/ai/Prompt and remains the public runtime guard for prompt parts." + note: "Moved to effect/ai/Prompt and remains the public runtime guard for prompt parts." "@effect/ai/Prompt#isPrompt": replacement: "Prompt.isPrompt" - note: "Moved to effect/unstable/ai/Prompt and remains the public runtime guard for Prompt values." + note: "Moved to effect/ai/Prompt and remains the public runtime guard for Prompt values." "@effect/ai/Prompt#makePart": replacement: "Prompt.makePart" - note: "Moved to effect/unstable/ai/Prompt. The generic constructor also supports the new tool-approval request and response part variants." + note: "Moved to effect/ai/Prompt. The generic constructor also supports the new tool-approval request and response part variants." "@effect/ai/Prompt#merge": replacement: "Prompt.concat" note: "Renamed in v4. concat preserves the old dual API and concatenates the messages from a Prompt with additional raw input." "@effect/ai/Prompt#MessageContentFromString": replacement: "Prompt.ContentFromString" - note: "Renamed in effect/unstable/ai/Prompt. It still decodes a string to a non-empty array containing one TextPart and encodes the first part's text." + note: "Renamed in effect/ai/Prompt. It still decodes a string to a non-empty array containing one TextPart and encodes the first part's text." "@effect/ai/Prompt#MessageTypeId": replacement: "Prompt.isMessage" note: "The message type id is private in v4. Use the public isMessage guard for runtime refinement instead of importing or inspecting the marker." "@effect/ai/Prompt#Part": replacement: "Prompt.Part" - note: "Moved to effect/unstable/ai/Prompt. The union now also includes tool-approval request and response parts." + note: "Moved to effect/ai/Prompt. The union now also includes tool-approval request and response parts." "@effect/ai/Prompt#PartEncoded": replacement: "Prompt.PartEncoded" - note: "Moved to effect/unstable/ai/Prompt. The encoded union now also includes tool-approval request and response parts." + note: "Moved to effect/ai/Prompt. The encoded union now also includes tool-approval request and response parts." "@effect/ai/Prompt#PartTypeId": replacement: "Prompt.isPart" note: "The part type id is private in v4. Use the public isPart guard for runtime refinement instead of importing or inspecting the marker." @@ -39,31 +39,31 @@ note: "The standalone declared from-self schema was removed. Use the public Prompt codec for prompt validation and encoding, or Prompt.isPrompt when only runtime refinement is needed." "@effect/ai/Prompt#ReasoningPart": replacement: "Prompt.ReasoningPart" - note: "Moved to effect/unstable/ai/Prompt with the same reasoning-part model and schema; update the module import." + note: "Moved to effect/ai/Prompt with the same reasoning-part model and schema; update the module import." "@effect/ai/Prompt#ReasoningPartEncoded": replacement: "Prompt.ReasoningPartEncoded" - note: "Moved to effect/unstable/ai/Prompt with the same encoded reasoning payload; update the module import." + note: "Moved to effect/ai/Prompt with the same encoded reasoning payload; update the module import." "@effect/ai/Prompt#TextPart": replacement: "Prompt.TextPart" - note: "Moved to effect/unstable/ai/Prompt with the same text-part model and schema; update the module import." + note: "Moved to effect/ai/Prompt with the same text-part model and schema; update the module import." "@effect/ai/Prompt#TextPartEncoded": replacement: "Prompt.TextPartEncoded" - note: "Moved to effect/unstable/ai/Prompt with the same encoded text payload; update the module import." + note: "Moved to effect/ai/Prompt with the same encoded text payload; update the module import." "@effect/ai/Prompt#toolCallPart": replacement: "Prompt.toolCallPart" - note: "Moved to effect/unstable/ai/Prompt and remains the typed convenience constructor over makePart(\"tool-call\", params)." + note: "Moved to effect/ai/Prompt and remains the typed convenience constructor over makePart(\"tool-call\", params)." "@effect/ai/Prompt#ToolCallPart": replacement: "Prompt.ToolCallPart" - note: "Moved to effect/unstable/ai/Prompt with the same tool-call model and schema; update the module import." + note: "Moved to effect/ai/Prompt with the same tool-call model and schema; update the module import." "@effect/ai/Prompt#ToolCallPartEncoded": replacement: "Prompt.ToolCallPartEncoded" - note: "Moved to effect/unstable/ai/Prompt with the same encoded tool-call shape; update the module import." + note: "Moved to effect/ai/Prompt with the same encoded tool-call shape; update the module import." "@effect/ai/Prompt#toolResultPart": replacement: "Prompt.toolResultPart" - note: "Moved to effect/unstable/ai/Prompt. V4 removes providerExecuted from prompt tool-result parts; provider-executed response results are handled when converting Response parts." + note: "Moved to effect/ai/Prompt. V4 removes providerExecuted from prompt tool-result parts; provider-executed response results are handled when converting Response parts." "@effect/ai/Prompt#ToolResultPartEncoded": replacement: "Prompt.ToolResultPartEncoded" - note: "Moved to effect/unstable/ai/Prompt, but providerExecuted was removed from the encoded prompt tool-result shape." + note: "Moved to effect/ai/Prompt, but providerExecuted was removed from the encoded prompt tool-result shape." "@effect/ai/Prompt#TypeId": replacement: "Prompt.isPrompt" note: "The Prompt type id is private in v4 and its internal literal changed. Use the public isPrompt guard instead of importing or inspecting the marker." diff --git a/repos/effect/migration/annotations/effect__ai__Response.yaml b/repos/effect/migration/annotations/effect__ai__Response.yaml index 886befa160..2b56d44a22 100644 --- a/repos/effect/migration/annotations/effect__ai__Response.yaml +++ b/repos/effect/migration/annotations/effect__ai__Response.yaml @@ -6,13 +6,13 @@ note: "The lowercase convenience constructor was removed; construct the retained error part through Response.makePart." "@effect/ai/Response#FilePartEncoded": replacement: "Response.FilePartEncoded" - note: "Moved to effect/unstable/ai/Response; file data remains a base64 string in the encoded representation." + note: "Moved to effect/ai/Response; file data remains a base64 string in the encoded representation." "@effect/ai/Response#finishPart": replacement: "Response.makePart(\"finish\", params)" note: "The lowercase convenience constructor was removed. V4 Usage has nested inputTokens and outputTokens objects, and FinishPart adds optional HTTP response details." "@effect/ai/Response#Part": replacement: "Response.Part" - note: "Moved to effect/unstable/ai/Response. The non-streaming union now also includes ToolApprovalRequestPart." + note: "Moved to effect/ai/Response. The non-streaming union now also includes ToolApprovalRequestPart." "@effect/ai/Response#PartTypeId": replacement: "Response.isPart" note: "The public PartTypeId was removed and the marker is internal in v4. Use Response.isPart for runtime refinement." @@ -24,7 +24,7 @@ note: "The lowercase convenience constructor was removed; construct the retained ReasoningEndPart through Response.makePart." "@effect/ai/Response#ReasoningPartEncoded": replacement: "Response.ReasoningPartEncoded" - note: "Moved to effect/unstable/ai/Response; the encoded reasoning payload remains text: string." + note: "Moved to effect/ai/Response; the encoded reasoning payload remains text: string." "@effect/ai/Response#reasoningStartPart": replacement: "Response.makePart(\"reasoning-start\", params)" note: "The lowercase convenience constructor was removed; construct the retained ReasoningStartPart through Response.makePart." @@ -39,16 +39,16 @@ note: "The lowercase convenience constructor was removed; construct the retained TextEndPart through Response.makePart." "@effect/ai/Response#TextPartEncoded": replacement: "Response.TextPartEncoded" - note: "Moved to effect/unstable/ai/Response; the encoded text payload remains text: string." + note: "Moved to effect/ai/Response; the encoded text payload remains text: string." "@effect/ai/Response#textStartPart": replacement: "Response.makePart(\"text-start\", params)" note: "The lowercase convenience constructor was removed; construct the retained TextStartPart through Response.makePart." "@effect/ai/Response#toolCallPart": replacement: "Response.toolCallPart" - note: "Moved to effect/unstable/ai/Response. The constructor remains, but providerName was removed from tool-call parts." + note: "Moved to effect/ai/Response. The constructor remains, but providerName was removed from tool-call parts." "@effect/ai/Response#ToolCallPartEncoded": replacement: "Response.ToolCallPartEncoded" - note: "Moved to effect/unstable/ai/Response; providerName was removed while providerExecuted remains optional when encoded." + note: "Moved to effect/ai/Response; providerName was removed while providerExecuted remains optional when encoded." "@effect/ai/Response#toolParamsDeltaPart": replacement: "Response.makePart(\"tool-params-delta\", params)" note: "The lowercase convenience constructor was removed; construct the retained ToolParamsDeltaPart through Response.makePart." @@ -60,10 +60,10 @@ note: "The lowercase convenience constructor was removed; providerName was also removed from ToolParamsStartPart in v4." "@effect/ai/Response#toolResultPart": replacement: "Response.toolResultPart" - note: "Moved to effect/unstable/ai/Response; providerName was removed and decoded tool results now require preliminary, normally false." + note: "Moved to effect/ai/Response; providerName was removed and decoded tool results now require preliminary, normally false." "@effect/ai/Response#ToolResultPartEncoded": replacement: "Response.ToolResultPartEncoded" - note: "Moved to effect/unstable/ai/Response; providerName was removed and optional preliminary was added to the encoded shape." + note: "Moved to effect/ai/Response; providerName was removed and optional preliminary was added to the encoded shape." "@effect/ai/Response#urlSourcePart": replacement: "Response.makePart(\"source\", { ...params, sourceType: \"url\" })" note: "The lowercase convenience constructor was removed. The UrlSourcePart model remains, and the generic constructor now requires the URL source discriminator." diff --git a/repos/effect/migration/annotations/effect__ai__Tool.yaml b/repos/effect/migration/annotations/effect__ai__Tool.yaml index e0fefb0440..a1d7dc4f23 100644 --- a/repos/effect/migration/annotations/effect__ai__Tool.yaml +++ b/repos/effect/migration/annotations/effect__ai__Tool.yaml @@ -6,10 +6,10 @@ note: "The TaggedRequest-specific Tool adapter contract was removed. Model the operation directly with Tool.make and ordinary v4 Schema.Constraint values." "@effect/ai/Tool#Destructive": replacement: "Tool.Destructive" - note: "Moved to effect/unstable/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains true." + note: "Moved to effect/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains true." "@effect/ai/Tool#Failure": replacement: "Tool.Failure" - note: "Moved to effect/unstable/ai/Tool and remains the utility type that extracts a tool's decoded failure type." + note: "Moved to effect/ai/Tool and remains the utility type that extracts a tool's decoded failure type." "@effect/ai/Tool#fromTaggedRequest": replacement: "Tool.make" note: "The adapter was removed. Rebuild the tool explicitly with Tool.make(name, { parameters, success, failure }); Toolkit.make no longer converts schema values automatically." @@ -24,25 +24,25 @@ note: "Renamed to accept a Schema.Constraint instead of a raw AST and now emits the v4 JSON Schema model. Wrap a raw AST with Schema.make first." "@effect/ai/Tool#Idempotent": replacement: "Tool.Idempotent" - note: "Moved to effect/unstable/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains false." + note: "Moved to effect/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains false." "@effect/ai/Tool#OpenWorld": replacement: "Tool.OpenWorld" - note: "Moved to effect/unstable/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains true." + note: "Moved to effect/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains true." "@effect/ai/Tool#ProviderDefinedTypeId": replacement: "Tool.ProviderDefinedTypeId" - note: "Moved to effect/unstable/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string." + note: "Moved to effect/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string." "@effect/ai/Tool#Readonly": replacement: "Tool.Readonly" - note: "Moved to effect/unstable/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains false." + note: "Moved to effect/ai/Tool. It is now a Context.Reference value rather than a Reference subclass; its default remains false." "@effect/ai/Tool#Requirements": replacement: "Tool.HandlerServices" note: "Renamed and refined. HandlerServices combines parameter-decoding, result-encoding, and request-level dependencies required by a tool handler." "@effect/ai/Tool#Success": replacement: "Tool.Success" - note: "Moved to effect/unstable/ai/Tool and remains the utility type that extracts a tool's decoded success type." + note: "Moved to effect/ai/Tool and remains the utility type that extracts a tool's decoded success type." "@effect/ai/Tool#Title": replacement: "Tool.Title" - note: "Moved to effect/unstable/ai/Tool. It is now a Context.Service annotation key; continue attaching the string title with tool.annotate(Tool.Title, value)." + note: "Moved to effect/ai/Tool. It is now a Context.Service annotation key; continue attaching the string title with tool.annotate(Tool.Title, value)." "@effect/ai/Tool#Tool.ProviderDefinedProto": replacement: "Tool.ProviderDefined" note: "This implementation-brand interface is no longer public. Use Tool.ProviderDefined for the model type and Tool.isProviderDefined for runtime narrowing." @@ -54,7 +54,7 @@ note: "This implementation variance payload is no longer public; the requirements marker is inline in Tool.Tool and should not be named independently." "@effect/ai/Tool#TypeId": replacement: "Tool.TypeId" - note: "Moved to effect/unstable/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string." + note: "Moved to effect/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string." "@effect/ai/Tool#Result": replacement: "Tool.Result" note: "The result includes success, declared failure, and execution-denied or execution-interrupted values in both failure modes. Return mode also includes AiError; handle these variants when narrowing results." diff --git a/repos/effect/migration/annotations/effect__ai__Toolkit.yaml b/repos/effect/migration/annotations/effect__ai__Toolkit.yaml index 7a07a03696..1cbb064825 100644 --- a/repos/effect/migration/annotations/effect__ai__Toolkit.yaml +++ b/repos/effect/migration/annotations/effect__ai__Toolkit.yaml @@ -1,9 +1,9 @@ "@effect/ai/Toolkit#HandlersFrom": replacement: "Toolkit.HandlersFrom" - note: "Moved to effect/unstable/ai/Toolkit. V4 handlers receive a HandlerContext argument and may fail with the declared failure, AiError, or AiErrorReason while requiring Tool.HandlerServices." + note: "Moved to effect/ai/Toolkit. V4 handlers receive a HandlerContext argument and may fail with the declared failure, AiError, or AiErrorReason while requiring Tool.HandlerServices." "@effect/ai/Toolkit#make": replacement: "Toolkit.make" - note: "Moved to effect/unstable/ai/Toolkit. It now accepts Tool.Any values only and no longer converts TaggedRequest schemas; create each tool explicitly with Tool.make first." + note: "Moved to effect/ai/Toolkit. It now accepts Tool.Any values only and no longer converts TaggedRequest schemas; create each tool explicitly with Tool.make first." "@effect/ai/Toolkit#TypeId": replacement: "Toolkit.Toolkit / Toolkit.Any" note: "The toolkit nominal id is private in v4. Use Toolkit.Toolkit or Toolkit.Any for typing instead of importing or inspecting the marker." diff --git a/repos/effect/migration/annotations/effect__ai__index.yaml b/repos/effect/migration/annotations/effect__ai__index.yaml index c8787f31a4..a830557d1d 100644 --- a/repos/effect/migration/annotations/effect__ai__index.yaml +++ b/repos/effect/migration/annotations/effect__ai__index.yaml @@ -1,3 +1,3 @@ "@effect/ai/index": - replacement: "effect/unstable/ai" - note: "The package barrel was removed; import the same namespaces from the effect/unstable/ai barrel or import specific modules directly." + replacement: "effect/ai" + note: "The package barrel was removed; import the same namespaces from the effect/ai barrel or import specific modules directly." diff --git a/repos/effect/migration/annotations/effect__cli.yaml b/repos/effect/migration/annotations/effect__cli.yaml index a52d95b5a9..10339419d9 100644 --- a/repos/effect/migration/annotations/effect__cli.yaml +++ b/repos/effect/migration/annotations/effect__cli.yaml @@ -1,3 +1,3 @@ "@effect/cli": - replacement: "effect/unstable/cli" - note: "The @effect/cli package was merged into the effect package; import the effect/unstable/cli barrel or import specific modules directly (e.g. effect/unstable/cli/)." + replacement: "effect/cli" + note: "The @effect/cli package was merged into the effect package; import the effect/cli barrel or import specific modules directly (e.g. effect/cli/)." diff --git a/repos/effect/migration/annotations/effect__cli__Args.yaml b/repos/effect/migration/annotations/effect__cli__Args.yaml index c07e2b7fe0..876713515d 100644 --- a/repos/effect/migration/annotations/effect__cli__Args.yaml +++ b/repos/effect/migration/annotations/effect__cli__Args.yaml @@ -9,7 +9,7 @@ note: "Infer the output of a command config record; standalone argument collections were removed." "@effect/cli/Args#Args": replacement: "Argument.Argument" - note: "Args was renamed to Argument in effect/unstable/cli." + note: "Args was renamed to Argument in effect/cli." "@effect/cli/Args#Args.BaseArgsConfig": replacement: "name: string" note: "Argument constructors now take the name as a required first parameter." diff --git a/repos/effect/migration/annotations/effect__cli__Options.yaml b/repos/effect/migration/annotations/effect__cli__Options.yaml index 966f792e3f..8f4c5ad585 100644 --- a/repos/effect/migration/annotations/effect__cli__Options.yaml +++ b/repos/effect/migration/annotations/effect__cli__Options.yaml @@ -90,7 +90,7 @@ note: "Use the moved combinator; it still returns Option." "@effect/cli/Options#Options": replacement: "Flag.Flag" - note: "Options was renamed to Flag in effect/unstable/cli." + note: "Options was renamed to Flag in effect/cli." "@effect/cli/Options#Options.BooleanOptionsConfig": replacement: "Flag.Boolean + Flag.withAlias + Flag.map" note: "The config object was removed; aliases and value inversion are combinators, while custom negation names need application logic." diff --git a/repos/effect/migration/annotations/effect__cli__Prompt.yaml b/repos/effect/migration/annotations/effect__cli__Prompt.yaml index 04071fb7be..caf0fa4fd8 100644 --- a/repos/effect/migration/annotations/effect__cli__Prompt.yaml +++ b/repos/effect/migration/annotations/effect__cli__Prompt.yaml @@ -42,7 +42,7 @@ note: "Use the renamed constructor." "@effect/cli/Prompt#Prompt": replacement: "Prompt.Prompt" - note: "The model moved to effect/unstable/cli; quitting now fails with Terminal.QuitError." + note: "The model moved to effect/cli; quitting now fails with Terminal.QuitError." "@effect/cli/Prompt#Prompt.Variance": replacement: "Prompt.Prompt" note: "The named variance artifact was removed; use Prompt." diff --git a/repos/effect/migration/annotations/effect__cli__index.yaml b/repos/effect/migration/annotations/effect__cli__index.yaml index 8a416c6191..06e2f9a750 100644 --- a/repos/effect/migration/annotations/effect__cli__index.yaml +++ b/repos/effect/migration/annotations/effect__cli__index.yaml @@ -1,3 +1,3 @@ "@effect/cli/index": - replacement: "effect/unstable/cli" - note: "The package barrel was removed; import the same namespaces from the effect/unstable/cli barrel or import specific modules directly." + replacement: "effect/cli" + note: "The package barrel was removed; import the same namespaces from the effect/cli barrel or import specific modules directly." diff --git a/repos/effect/migration/annotations/effect__cluster.yaml b/repos/effect/migration/annotations/effect__cluster.yaml index ecbb121172..50233ef9c1 100644 --- a/repos/effect/migration/annotations/effect__cluster.yaml +++ b/repos/effect/migration/annotations/effect__cluster.yaml @@ -1,3 +1,3 @@ "@effect/cluster": - replacement: "effect/unstable/cluster" - note: "The @effect/cluster package was merged into the effect package; import the effect/unstable/cluster barrel or import specific modules directly (e.g. effect/unstable/cluster/)." + replacement: "effect/cluster" + note: "The @effect/cluster package was merged into the effect package; import the effect/cluster barrel or import specific modules directly (e.g. effect/cluster/)." diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml b/repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml index e32af55c54..6df806ab73 100644 --- a/repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml @@ -1,3 +1,3 @@ "@effect/cluster/ClusterCron#make": - replacement: "effect/unstable/cluster/ClusterCron#make" + replacement: "effect/cluster/ClusterCron#make" note: "Moved into core Effect. The constructor remains; Duration.DurationInput is now Duration.Input." diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml b/repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml index dc3c5c316f..190979e661 100644 --- a/repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml @@ -1,12 +1,12 @@ "@effect/cluster/ClusterSchema#ClientTracingEnabled": - replacement: "effect/unstable/cluster/ClusterSchema#ClientTracingEnabled" + replacement: "effect/cluster/ClusterSchema#ClientTracingEnabled" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value." "@effect/cluster/ClusterSchema#Persisted": - replacement: "effect/unstable/cluster/ClusterSchema#Persisted" + replacement: "effect/cluster/ClusterSchema#Persisted" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default." "@effect/cluster/ClusterSchema#ShardGroup": - replacement: "effect/unstable/cluster/ClusterSchema#ShardGroup" + replacement: "effect/cluster/ClusterSchema#ShardGroup" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value." "@effect/cluster/ClusterSchema#Uninterruptible": - replacement: "effect/unstable/cluster/ClusterSchema#Uninterruptible" + replacement: "effect/cluster/ClusterSchema#Uninterruptible" note: "Now a Context.Reference value. Replace its static methods with ClusterSchema.isUninterruptibleForServer and isUninterruptibleForClient." diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml b/repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml index d7bf79fc13..66c3868647 100644 --- a/repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml @@ -1,6 +1,6 @@ "@effect/cluster/ClusterWorkflowEngine#layer": - replacement: "effect/unstable/cluster/ClusterWorkflowEngine#layer" + replacement: "effect/cluster/ClusterWorkflowEngine#layer" note: "Moved into core Effect with the same cluster-backed WorkflowEngine layer composition." "@effect/cluster/ClusterWorkflowEngine#make": - replacement: "effect/unstable/cluster/ClusterWorkflowEngine#make" + replacement: "effect/cluster/ClusterWorkflowEngine#make" note: "Moved into core Effect; the constructor still uses Sharding and MessageStorage." diff --git a/repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml b/repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml index bba0f7b70d..6d96ff7d6e 100644 --- a/repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml +++ b/repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml @@ -1,3 +1,3 @@ "@effect/cluster/DeliverAt#symbol": - replacement: "effect/unstable/cluster/DeliverAt#symbol" + replacement: "effect/cluster/DeliverAt#symbol" note: "Moved into core Effect; the protocol key is now the string literal ~effect/cluster/DeliverAt rather than a global symbol." diff --git a/repos/effect/migration/annotations/effect__cluster__Entity.yaml b/repos/effect/migration/annotations/effect__cluster__Entity.yaml index 661a7e0f40..0e243e532d 100644 --- a/repos/effect/migration/annotations/effect__cluster__Entity.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Entity.yaml @@ -1,15 +1,15 @@ "@effect/cluster/Entity#Any": - replacement: "effect/unstable/cluster/Entity#Any" + replacement: "effect/cluster/Entity#Any" note: "Moved into core Effect with the same erased entity type." "@effect/cluster/Entity#makeTestClient": - replacement: "effect/unstable/cluster/Entity#makeTestClient" + replacement: "effect/cluster/Entity#makeTestClient" note: "Moved into core Effect; adapt its inputs and requirements to the v4 RPC, Layer, Scope, and Context APIs." "@effect/cluster/Entity#TypeId": replacement: "none" note: "The entity marker is private in v4. Use Entity.isEntity for runtime refinement." "@effect/cluster/Entity#HandlersFrom": - replacement: "effect/unstable/cluster/Entity#HandlersFrom" + replacement: "effect/cluster/Entity#HandlersFrom" note: "Moved into core Effect; handler results now use Rpc.WrapperOr, which accepts either the raw RPC result or its wrapper." "@effect/cluster/Entity#Replier.Success": - replacement: "effect/unstable/cluster/Entity#Replier.Success" + replacement: "effect/cluster/Entity#Replier.Success" note: "Moved into core Effect; streaming replies may use Queue.Dequeue with Cause.Done instead of the removed Mailbox type." diff --git a/repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml b/repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml index 0bc1367ae2..3c2d14796f 100644 --- a/repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml +++ b/repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml @@ -1,8 +1,8 @@ "@effect/cluster/EntityAddress#EntityAddressFromSelf": - replacement: "effect/unstable/cluster/EntityAddress#EntityAddress" + replacement: "effect/cluster/EntityAddress#EntityAddress" note: "The separate self schema was removed; the v4 Schema.Class is itself the EntityAddress schema." "@effect/cluster/EntityAddress#make": - replacement: "effect/unstable/cluster/EntityAddress#make" + replacement: "effect/cluster/EntityAddress#make" note: "Moved into core Effect with the same options-object constructor." "@effect/cluster/EntityAddress#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__cluster__EntityId.yaml b/repos/effect/migration/annotations/effect__cluster__EntityId.yaml index dbc9ad7b05..aab4633e5c 100644 --- a/repos/effect/migration/annotations/effect__cluster__EntityId.yaml +++ b/repos/effect/migration/annotations/effect__cluster__EntityId.yaml @@ -1,3 +1,3 @@ "@effect/cluster/EntityId#make": - replacement: "effect/unstable/cluster/EntityId#make" + replacement: "effect/cluster/EntityId#make" note: "Moved into core Effect; the branding helper remains and performs no validation or normalization." diff --git a/repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml b/repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml index f6e0df345d..c465fd5921 100644 --- a/repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml +++ b/repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml @@ -1,6 +1,6 @@ "@effect/cluster/EntityProxy#ConvertHttpApi": - replacement: "effect/unstable/cluster/EntityProxy#ConvertHttpApi" + replacement: "effect/cluster/EntityProxy#ConvertHttpApi" note: "Moved into core Effect and updated to the v4 HttpApiEndpoint and Schema types." "@effect/cluster/EntityProxy#ConvertRpcs": - replacement: "effect/unstable/cluster/EntityProxy#ConvertRpcs" + replacement: "effect/cluster/EntityProxy#ConvertRpcs" note: "Moved into core Effect and updated to the v4 Rpc and Schema type parameters." diff --git a/repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml b/repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml index 57ef342ba0..9e5a9c9332 100644 --- a/repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml +++ b/repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml @@ -1,9 +1,9 @@ "@effect/cluster/EntityProxyServer#layerHttpApi": - replacement: "effect/unstable/cluster/EntityProxyServer#layerHttpApi" + replacement: "effect/cluster/EntityProxyServer#layerHttpApi" note: "Moved into core Effect. Use v4 HttpApi identifiers and provide Sharding plus both Rpc.ServicesServer and Rpc.ServicesClient codec requirements." "@effect/cluster/EntityProxyServer#layerRpcHandlers": - replacement: "effect/unstable/cluster/EntityProxyServer#layerRpcHandlers" + replacement: "effect/cluster/EntityProxyServer#layerRpcHandlers" note: "Moved into core Effect; replace Rpc.Context with both Rpc.ServicesServer and Rpc.ServicesClient codec requirements, alongside Sharding." "@effect/cluster/EntityProxyServer#RpcHandlers": - replacement: "effect/unstable/cluster/EntityProxyServer#RpcHandlers" + replacement: "effect/cluster/EntityProxyServer#RpcHandlers" note: "Moved into core Effect and updated for the additional v4 Rpc requirements type parameter." diff --git a/repos/effect/migration/annotations/effect__cluster__EntityResource.yaml b/repos/effect/migration/annotations/effect__cluster__EntityResource.yaml index 19472a5e20..2f68ff6d67 100644 --- a/repos/effect/migration/annotations/effect__cluster__EntityResource.yaml +++ b/repos/effect/migration/annotations/effect__cluster__EntityResource.yaml @@ -1,6 +1,6 @@ "@effect/cluster/EntityResource#make": - replacement: "effect/unstable/cluster/EntityResource#make" + replacement: "effect/cluster/EntityResource#make" note: "Moved into core Effect. Acquisition is lazy by default in v4; set acquireEagerly: true to preserve v3 behavior." "@effect/cluster/EntityResource#TypeId": - replacement: "effect/unstable/cluster/EntityResource#TypeId" + replacement: "effect/cluster/EntityResource#TypeId" note: "Moved into core Effect; its literal changed to ~effect/cluster/EntityResource." diff --git a/repos/effect/migration/annotations/effect__cluster__Envelope.yaml b/repos/effect/migration/annotations/effect__cluster__Envelope.yaml index 2d749b6f7c..145925286e 100644 --- a/repos/effect/migration/annotations/effect__cluster__Envelope.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Envelope.yaml @@ -1,44 +1,44 @@ "@effect/cluster/Envelope#Envelope.Any": - replacement: "effect/unstable/cluster/Envelope#Envelope.Any" + replacement: "effect/cluster/Envelope#Envelope.Any" note: "Moved into core Effect with the same erased envelope type." "@effect/cluster/Envelope#Envelope.Encoded": - replacement: "effect/unstable/cluster/Envelope#Encoded" + replacement: "effect/cluster/Envelope#Encoded" note: "The encoded envelope union moved to the module-level Encoded type." "@effect/cluster/Envelope#Envelope.PartialEncoded": - replacement: "effect/unstable/cluster/Envelope#Partial" + replacement: "effect/cluster/Envelope#Partial" note: "The partially decoded runtime union was renamed to Partial; use PartialJson for its JSON codec." "@effect/cluster/Envelope#EnvelopeFromSelf": - replacement: "effect/unstable/cluster/Envelope#Envelope" + replacement: "effect/cluster/Envelope#Envelope" note: "The self schema was renamed to Envelope and declaration-merges with the envelope type and namespace." "@effect/cluster/Envelope#PartialEncoded": - replacement: "effect/unstable/cluster/Envelope#PartialJson" + replacement: "effect/cluster/Envelope#PartialJson" note: "The partially decoded envelope JSON codec was renamed to PartialJson." "@effect/cluster/Envelope#PartialEncodedArray": - replacement: "effect/unstable/cluster/Envelope#PartialArray" + replacement: "effect/cluster/Envelope#PartialArray" note: "The mutable array codec was renamed to PartialArray." "@effect/cluster/Envelope#PartialEncodedFromSelf": - replacement: "effect/unstable/cluster/Envelope#Partial" + replacement: "effect/cluster/Envelope#Partial" note: "The separate self schema was folded into Partial; derive JSON encoding with PartialJson." "@effect/cluster/Envelope#PartialEncodedRequest": replacement: "Schema.toCodecJson(Envelope.PartialRequest)" note: "V4 exports the self schema as PartialRequest and derives its JSON codec with Schema.toCodecJson." "@effect/cluster/Envelope#PartialEncodedRequestFromSelf": - replacement: "effect/unstable/cluster/Envelope#PartialRequest" + replacement: "effect/cluster/Envelope#PartialRequest" note: "The partially decoded request self schema was renamed to PartialRequest." "@effect/cluster/Envelope#Request": - replacement: "effect/unstable/cluster/Envelope#Request" + replacement: "effect/cluster/Envelope#Request" note: "The request interface remains and declaration-merges with the exported Request schema." "@effect/cluster/Envelope#Request.Any": - replacement: "effect/unstable/cluster/Envelope#Request.Any" + replacement: "effect/cluster/Envelope#Request.Any" note: "Moved into core Effect with the same erased request type." "@effect/cluster/Envelope#Request.Encoded": - replacement: "effect/unstable/cluster/Envelope#PartialRequestEncoded" + replacement: "effect/cluster/Envelope#PartialRequestEncoded" note: "The JSON request shape moved to the module-level PartialRequestEncoded interface." "@effect/cluster/Envelope#Request.PartialEncoded": - replacement: "effect/unstable/cluster/Envelope#PartialRequest" + replacement: "effect/cluster/Envelope#PartialRequest" note: "The partially decoded request shape moved to the module-level PartialRequest class and type." "@effect/cluster/Envelope#RequestFromSelf": - replacement: "effect/unstable/cluster/Envelope#Request" + replacement: "effect/cluster/Envelope#Request" note: "The request self schema was renamed to Request and declaration-merges with the runtime interface." "@effect/cluster/Envelope#TypeId": replacement: "typeof Envelope.TypeId" diff --git a/repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml b/repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml index 77a7346752..8ce0cd2636 100644 --- a/repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml +++ b/repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml @@ -1,12 +1,12 @@ "@effect/cluster/HttpRunner#layerClient": - replacement: "effect/unstable/cluster/HttpRunner#layerClient" + replacement: "effect/cluster/HttpRunner#layerClient" note: "Moved into core Effect with the same client-side Sharding and Runners layer composition." "@effect/cluster/HttpRunner#layerHttp": - replacement: "effect/unstable/cluster/HttpRunner#layerHttp" + replacement: "effect/cluster/HttpRunner#layerHttp" note: "Moved into core Effect with the same HTTP runner composition." "@effect/cluster/HttpRunner#toHttpEffect": - replacement: "effect/unstable/cluster/HttpRunner#toHttpEffect" + replacement: "effect/cluster/HttpRunner#toHttpEffect" note: "Moved into core Effect with the same nested HTTP server effect and service requirements." "@effect/cluster/HttpRunner#toHttpEffectWebsocket": - replacement: "effect/unstable/cluster/HttpRunner#toHttpEffectWebsocket" + replacement: "effect/cluster/HttpRunner#toHttpEffectWebsocket" note: "Moved into core Effect with the same WebSocket HTTP effect shape and requirements." diff --git a/repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml b/repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml index ea31aef070..a7b1063a89 100644 --- a/repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml +++ b/repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml @@ -1,3 +1,3 @@ "@effect/cluster/K8sHttpClient#layer": - replacement: "effect/unstable/cluster/K8sHttpClient#layer" + replacement: "effect/cluster/K8sHttpClient#layer" note: "Moved into core Effect with the same in-cluster Kubernetes client behavior." diff --git a/repos/effect/migration/annotations/effect__cluster__MachineId.yaml b/repos/effect/migration/annotations/effect__cluster__MachineId.yaml index 95933caa43..a5c772cc56 100644 --- a/repos/effect/migration/annotations/effect__cluster__MachineId.yaml +++ b/repos/effect/migration/annotations/effect__cluster__MachineId.yaml @@ -1,3 +1,3 @@ "@effect/cluster/MachineId#make": - replacement: "effect/unstable/cluster/MachineId#make" + replacement: "effect/cluster/MachineId#make" note: "Moved into core Effect. The v4 helper is an unchecked cast; validate external input with the MachineId schema when needed." diff --git a/repos/effect/migration/annotations/effect__cluster__Message.yaml b/repos/effect/migration/annotations/effect__cluster__Message.yaml index e41b871a43..1e17c2f144 100644 --- a/repos/effect/migration/annotations/effect__cluster__Message.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Message.yaml @@ -1,3 +1,3 @@ "@effect/cluster/Message#serialize": - replacement: "effect/unstable/cluster/Message#serialize" + replacement: "effect/cluster/Message#serialize" note: "Moved into core Effect. Pass the transport's codecFor as the second argument; use serializeEnvelope for the JSON Envelope.Encoded form." diff --git a/repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml b/repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml index 11f3b99c31..12b7c6b720 100644 --- a/repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml +++ b/repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml @@ -1,15 +1,15 @@ "@effect/cluster/MessageStorage#layerMemory": - replacement: "effect/unstable/cluster/MessageStorage#layerMemory" + replacement: "effect/cluster/MessageStorage#layerMemory" note: "Moved into core Effect; it still provides MessageStorage and MemoryDriver and requires ShardingConfig." "@effect/cluster/MessageStorage#layerNoop": - replacement: "effect/unstable/cluster/MessageStorage#layerNoop" + replacement: "effect/cluster/MessageStorage#layerNoop" note: "Moved into core Effect with the same dependency-free no-op implementation." "@effect/cluster/MessageStorage#make": - replacement: "effect/unstable/cluster/MessageStorage#make" + replacement: "effect/cluster/MessageStorage#make" note: "Moved into core Effect. Context service projections now use the Service property instead of Type. Custom service implementations must also provide resetAddresses for batched mailbox resets." "@effect/cluster/MessageStorage#makeEncoded": - replacement: "effect/unstable/cluster/MessageStorage#makeEncoded" + replacement: "effect/cluster/MessageStorage#makeEncoded" note: "Moved into core Effect. Custom encoded drivers must replace resetAddress with resetAddresses and may use the new limit and addresses options passed to unprocessedMessages." "@effect/cluster/MessageStorage#Encoded": - replacement: "effect/unstable/cluster/MessageStorage#Encoded" + replacement: "effect/cluster/MessageStorage#Encoded" note: "Moved into core Effect; use the v4 Envelope.Encoded and Reply.Encoded aliases. Custom drivers now implement batched resetAddresses, and unprocessedMessages receives optional limit and address filters." diff --git a/repos/effect/migration/annotations/effect__cluster__Reply.yaml b/repos/effect/migration/annotations/effect__cluster__Reply.yaml index 842dd729a6..09b256f951 100644 --- a/repos/effect/migration/annotations/effect__cluster__Reply.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Reply.yaml @@ -1,8 +1,8 @@ "@effect/cluster/Reply#ReplyEncoded": - replacement: "effect/unstable/cluster/Reply#Encoded" + replacement: "effect/cluster/Reply#Encoded" note: "Renamed to Encoded and no longer parameterized by an Rpc; payload fields are unknown and validated by Reply.Reply(rpc, codecFor) with the transport's codec." "@effect/cluster/Reply#serialize": - replacement: "effect/unstable/cluster/Reply#serialize" + replacement: "effect/cluster/Reply#serialize" note: "Moved into core Effect and now returns the non-generic Reply.Encoded wire union. Pass the transport's codecFor as the second argument." "@effect/cluster/Reply#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml b/repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml index 33246c9a4a..ddb754e7c3 100644 --- a/repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml +++ b/repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml @@ -1,5 +1,5 @@ "@effect/cluster/RunnerAddress#make": - replacement: "effect/unstable/cluster/RunnerAddress#make" + replacement: "effect/cluster/RunnerAddress#make" note: "Moved into core Effect with the same host and port constructor; the host schema is now Schema.String." "@effect/cluster/RunnerAddress#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml b/repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml index ed573b9b0d..0bbdbe9135 100644 --- a/repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml +++ b/repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml @@ -1,3 +1,3 @@ "@effect/cluster/RunnerHealth#layerNoop": - replacement: "effect/unstable/cluster/RunnerHealth#layerNoop" + replacement: "effect/cluster/RunnerHealth#layerNoop" note: "Moved into core Effect with the same dependency-free health implementation." diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml b/repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml index b74c5f1cc8..067e8935b9 100644 --- a/repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml +++ b/repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml @@ -1,6 +1,6 @@ "@effect/cluster/RunnerServer#layer": - replacement: "effect/unstable/cluster/RunnerServer#layer" + replacement: "effect/cluster/RunnerServer#layer" note: "Moved into core Effect; it still serves runner RPCs over a separately provided RpcServer.Protocol." "@effect/cluster/RunnerServer#layerClientOnly": - replacement: "effect/unstable/cluster/RunnerServer#layerClientOnly" + replacement: "effect/cluster/RunnerServer#layerClientOnly" note: "Moved into core Effect with the same client-only Sharding and Runners composition." diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml b/repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml index 59560ad170..9f63365b7d 100644 --- a/repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml +++ b/repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml @@ -1,6 +1,6 @@ "@effect/cluster/RunnerStorage#layerMemory": - replacement: "effect/unstable/cluster/RunnerStorage#layerMemory" + replacement: "effect/cluster/RunnerStorage#layerMemory" note: "Moved into core Effect with the same in-memory registration and shard-lock implementation for tests and local use." "@effect/cluster/RunnerStorage#makeMemory": - replacement: "effect/unstable/cluster/RunnerStorage#makeMemory" + replacement: "effect/cluster/RunnerStorage#makeMemory" note: "Moved into core Effect; it still constructs the in-memory RunnerStorage service implementation." diff --git a/repos/effect/migration/annotations/effect__cluster__Runners.yaml b/repos/effect/migration/annotations/effect__cluster__Runners.yaml index e380f964df..a199899cf9 100644 --- a/repos/effect/migration/annotations/effect__cluster__Runners.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Runners.yaml @@ -1,9 +1,9 @@ "@effect/cluster/Runners#layerNoop": - replacement: "effect/unstable/cluster/Runners#layerNoop" + replacement: "effect/cluster/Runners#layerNoop" note: "Moved into core Effect with the same no-op runner communication layer." "@effect/cluster/Runners#make": - replacement: "effect/unstable/cluster/Runners#make" + replacement: "effect/cluster/Runners#make" note: "Moved into core Effect. Its options now require codecFor; pass the codec used by the remote runner transport, such as RpcSerialization.json.codecFor for JSON. Context service projections now use Service instead of Type." "@effect/cluster/Runners#makeNoop": - replacement: "effect/unstable/cluster/Runners#makeNoop" + replacement: "effect/cluster/Runners#makeNoop" note: "Moved into core Effect; it returns the Context.Service implementation through the Service projection instead of Type." diff --git a/repos/effect/migration/annotations/effect__cluster__ShardId.yaml b/repos/effect/migration/annotations/effect__cluster__ShardId.yaml index 3f416f62a2..8972ea0dec 100644 --- a/repos/effect/migration/annotations/effect__cluster__ShardId.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ShardId.yaml @@ -1,8 +1,8 @@ "@effect/cluster/ShardId#make": - replacement: "effect/unstable/cluster/ShardId#make" + replacement: "effect/cluster/ShardId#make" note: "Moved into core Effect with the same cached group and id constructor." "@effect/cluster/ShardId#ShardId": - replacement: "effect/unstable/cluster/ShardId#ShardId" + replacement: "effect/cluster/ShardId#ShardId" note: "The class became a merged interface and schema value. Use ShardId.make; former static parsers and printers are module functions." "@effect/cluster/ShardId#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__cluster__Sharding.yaml b/repos/effect/migration/annotations/effect__cluster__Sharding.yaml index 774b80e243..ff8fa86467 100644 --- a/repos/effect/migration/annotations/effect__cluster__Sharding.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Sharding.yaml @@ -1,3 +1,3 @@ "@effect/cluster/Sharding#layer": - replacement: "effect/unstable/cluster/Sharding#layer" + replacement: "effect/cluster/Sharding#layer" note: "Moved into core Effect with the same main sharding runtime composition and public service requirements." diff --git a/repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml b/repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml index f429966f0e..5b8a502ce8 100644 --- a/repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml @@ -1,9 +1,9 @@ "@effect/cluster/ShardingConfig#config": - replacement: "effect/unstable/cluster/ShardingConfig#config" + replacement: "effect/cluster/ShardingConfig#config" note: "Moved into core Effect; its Context service value type now uses the Service property instead of Type." "@effect/cluster/ShardingConfig#defaults": - replacement: "effect/unstable/cluster/ShardingConfig#defaults" + replacement: "effect/cluster/ShardingConfig#defaults" note: "Moved into core Effect; service type projections now use Service instead of Type. V4 also defaults maxResidentEntities to 10,000 and unprocessedMessageBatchSize to 1,024." "@effect/cluster/ShardingConfig#layer": - replacement: "effect/unstable/cluster/ShardingConfig#layer" + replacement: "effect/cluster/ShardingConfig#layer" note: "Moved into core Effect with the same shallow default merge; service type projections now use Service instead of Type." diff --git a/repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml b/repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml index 8aeb99b276..cb2b77caf5 100644 --- a/repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml +++ b/repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml @@ -1,3 +1,3 @@ "@effect/cluster/ShardingRegistrationEvent#match": - replacement: "effect/unstable/cluster/ShardingRegistrationEvent#match" + replacement: "effect/cluster/ShardingRegistrationEvent#match" note: "Moved into core Effect with the same tagged-enum matcher." diff --git a/repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml b/repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml index d8dd53d8d5..ca7810410a 100644 --- a/repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml +++ b/repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml @@ -1,3 +1,3 @@ "@effect/cluster/SingleRunner#layer": - replacement: "effect/unstable/cluster/SingleRunner#layer" + replacement: "effect/cluster/SingleRunner#layer" note: "Moved into core Effect. V4 additionally requires Crypto.Crypto because SQL message storage hashes long deduplication keys." diff --git a/repos/effect/migration/annotations/effect__cluster__Singleton.yaml b/repos/effect/migration/annotations/effect__cluster__Singleton.yaml index dd97459118..ab8fcdbf47 100644 --- a/repos/effect/migration/annotations/effect__cluster__Singleton.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Singleton.yaml @@ -1,3 +1,3 @@ "@effect/cluster/Singleton#make": - replacement: "effect/unstable/cluster/Singleton#make" + replacement: "effect/cluster/Singleton#make" note: "Moved into core Effect with the same singleton Layer constructor." diff --git a/repos/effect/migration/annotations/effect__cluster__Snowflake.yaml b/repos/effect/migration/annotations/effect__cluster__Snowflake.yaml index 67e87e5d46..eaa6031943 100644 --- a/repos/effect/migration/annotations/effect__cluster__Snowflake.yaml +++ b/repos/effect/migration/annotations/effect__cluster__Snowflake.yaml @@ -1,9 +1,10 @@ "@effect/cluster/Snowflake#Generator": - replacement: "effect/unstable/cluster/Snowflake#Generator" + include: true + replacement: "effect/cluster/Snowflake#Generator" note: "Moved into core Effect and changed to Context.Service; its unsafeNext method was renamed to nextUnsafe." "@effect/cluster/Snowflake#make": - replacement: "effect/unstable/cluster/Snowflake#make" + replacement: "effect/cluster/Snowflake#make" note: "Moved into core Effect with the same timestamp, machine-id, and sequence packing constructor." "@effect/cluster/Snowflake#TypeId": - replacement: "effect/unstable/cluster/Snowflake#TypeId" + replacement: "effect/cluster/Snowflake#TypeId" note: "Moved into core Effect; the public marker is now the string literal ~effect/cluster/Snowflake." diff --git a/repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml b/repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml index 76e9b2481d..5201be6fd8 100644 --- a/repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml +++ b/repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml @@ -1,6 +1,6 @@ "@effect/cluster/SocketRunner#layer": - replacement: "effect/unstable/cluster/SocketRunner#layer" + replacement: "effect/cluster/SocketRunner#layer" note: "Moved into core Effect with the same full socket runner composition." "@effect/cluster/SocketRunner#layerClientOnly": - replacement: "effect/unstable/cluster/SocketRunner#layerClientOnly" + replacement: "effect/cluster/SocketRunner#layerClientOnly" note: "Moved into core Effect; it remains the client-only runner layer and does not start a socket server." diff --git a/repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml b/repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml index 833229baea..0c443aacc4 100644 --- a/repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml +++ b/repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml @@ -1,9 +1,9 @@ "@effect/cluster/SqlMessageStorage#layer": - replacement: "effect/unstable/cluster/SqlMessageStorage#layer" + replacement: "effect/cluster/SqlMessageStorage#layer" note: "Moved into core Effect. V4 adds a Crypto.Crypto requirement for hashing long deduplication keys." "@effect/cluster/SqlMessageStorage#layerWith": - replacement: "effect/unstable/cluster/SqlMessageStorage#layerWith" + replacement: "effect/cluster/SqlMessageStorage#layerWith" note: "Moved into core Effect with the same optional table prefix; v4 additionally requires Crypto.Crypto." "@effect/cluster/SqlMessageStorage#make": - replacement: "effect/unstable/cluster/SqlMessageStorage#make" + replacement: "effect/cluster/SqlMessageStorage#make" note: "Moved into core Effect with the same prefix option; v4 additionally requires Crypto.Crypto." diff --git a/repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml b/repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml index 39c6b0c37f..c22eafd88f 100644 --- a/repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml +++ b/repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml @@ -1,9 +1,9 @@ "@effect/cluster/SqlRunnerStorage#layer": - replacement: "effect/unstable/cluster/SqlRunnerStorage#layer" + replacement: "effect/cluster/SqlRunnerStorage#layer" note: "Moved into core Effect with the same default-prefix SQL runner storage layer." "@effect/cluster/SqlRunnerStorage#layerWith": - replacement: "effect/unstable/cluster/SqlRunnerStorage#layerWith" + replacement: "effect/cluster/SqlRunnerStorage#layerWith" note: "Moved into core Effect with the same optional table prefix." "@effect/cluster/SqlRunnerStorage#make": - replacement: "effect/unstable/cluster/SqlRunnerStorage#make" + replacement: "effect/cluster/SqlRunnerStorage#make" note: "Moved into core Effect with the same prefix option and service requirements." diff --git a/repos/effect/migration/annotations/effect__cluster__TestRunner.yaml b/repos/effect/migration/annotations/effect__cluster__TestRunner.yaml index 521f937d8c..4029ef1f7f 100644 --- a/repos/effect/migration/annotations/effect__cluster__TestRunner.yaml +++ b/repos/effect/migration/annotations/effect__cluster__TestRunner.yaml @@ -1,3 +1,3 @@ "@effect/cluster/TestRunner#layer": - replacement: "effect/unstable/cluster/TestRunner#layer" + replacement: "effect/cluster/TestRunner#layer" note: "Moved into core Effect with the same dependency-free in-memory test cluster composition." diff --git a/repos/effect/migration/annotations/effect__cluster__index.yaml b/repos/effect/migration/annotations/effect__cluster__index.yaml index 00d9e4089c..af615fab50 100644 --- a/repos/effect/migration/annotations/effect__cluster__index.yaml +++ b/repos/effect/migration/annotations/effect__cluster__index.yaml @@ -1,3 +1,3 @@ "@effect/cluster/index": - replacement: "effect/unstable/cluster" - note: "The package barrel was removed; import the same namespaces from the effect/unstable/cluster barrel or import specific modules directly." + replacement: "effect/cluster" + note: "The package barrel was removed; import the same namespaces from the effect/cluster barrel or import specific modules directly." diff --git a/repos/effect/migration/annotations/effect__experimental.yaml b/repos/effect/migration/annotations/effect__experimental.yaml index 8f4c3ffa2a..d916713e5d 100644 --- a/repos/effect/migration/annotations/effect__experimental.yaml +++ b/repos/effect/migration/annotations/effect__experimental.yaml @@ -1,3 +1,3 @@ "@effect/experimental": replacement: "none" - note: "The @effect/experimental package was folded into the effect package, split across effect/unstable/* (devtools, eventlog, persistence, reactivity, ...); follow the Import Map for each module." + note: "The @effect/experimental package was folded into the effect package, split across effect/* (devtools, eventlog, persistence, reactivity, ...); follow the Import Map for each module." diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools.yaml b/repos/effect/migration/annotations/effect__experimental__DevTools.yaml index 44ce415b77..555a90bf44 100644 --- a/repos/effect/migration/annotations/effect__experimental__DevTools.yaml +++ b/repos/effect/migration/annotations/effect__experimental__DevTools.yaml @@ -1,6 +1,6 @@ "@effect/experimental/DevTools#layer": - replacement: effect/unstable/devtools/DevTools#layer + replacement: effect/devtools/DevTools#layer note: Import layer from the v4 unstable DevTools module. "@effect/experimental/DevTools#layerWebSocket": - replacement: effect/unstable/devtools/DevTools#layerWebSocket + replacement: effect/devtools/DevTools#layerWebSocket note: Import layerWebSocket from the v4 unstable DevTools module. diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml b/repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml index 6009364c92..38275914d2 100644 --- a/repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml +++ b/repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml @@ -1,15 +1,15 @@ "@effect/experimental/DevTools/Client#Client": - replacement: effect/unstable/devtools/DevToolsClient#DevToolsClient + replacement: effect/devtools/DevToolsClient#DevToolsClient note: Client was renamed to the DevToolsClient Context.Service class. "@effect/experimental/DevTools/Client#ClientImpl": - replacement: effect/unstable/devtools/DevToolsClient#DevToolsClient["Service"] + replacement: effect/devtools/DevToolsClient#DevToolsClient["Service"] note: Use the service shape from DevToolsClient; unsafeAddSpan was replaced by sendUnsafe. "@effect/experimental/DevTools/Client#layer": - replacement: effect/unstable/devtools/DevToolsClient#layer + replacement: effect/devtools/DevToolsClient#layer note: Import layer from the v4 unstable DevToolsClient module. "@effect/experimental/DevTools/Client#layerTracer": - replacement: effect/unstable/devtools/DevToolsClient#layerTracer + replacement: effect/devtools/DevToolsClient#layerTracer note: Import layerTracer from the v4 unstable DevToolsClient module. "@effect/experimental/DevTools/Client#make": - replacement: effect/unstable/devtools/DevToolsClient#make + replacement: effect/devtools/DevToolsClient#make note: Import make from the v4 unstable DevToolsClient module. diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml b/repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml index 71f36dcb22..44e97eb56b 100644 --- a/repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml +++ b/repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml @@ -1,18 +1,18 @@ "@effect/experimental/DevTools/Domain#ExternalSpanFrom": - replacement: effect/Schema#Codec.Encoded + replacement: effect/Schema#Codec.Encoded note: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ExternalSpan. "@effect/experimental/DevTools/Domain#metric": replacement: none note: The metric schema helper is private in v4; use the exported Counter, Frequency, Gauge, Histogram, Summary, or Metric schemas, or build a Schema.Struct. "@effect/experimental/DevTools/Domain#MetricFrom": - replacement: effect/Schema#Codec.Encoded + replacement: effect/Schema#Codec.Encoded note: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Metric. "@effect/experimental/DevTools/Domain#MetricsSnapshotFrom": - replacement: effect/Schema#Codec.Encoded + replacement: effect/Schema#Codec.Encoded note: The named encoded alias was removed; derive it with Schema.Codec.Encoded from MetricsSnapshot. "@effect/experimental/DevTools/Domain#ParentSpanFrom": - replacement: effect/Schema#Codec.Encoded + replacement: effect/Schema#Codec.Encoded note: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ParentSpan. "@effect/experimental/DevTools/Domain#SpanFrom": - replacement: effect/Schema#Codec.Encoded + replacement: effect/Schema#Codec.Encoded note: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Span. diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml b/repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml index 15396b28ae..0c0c9586cf 100644 --- a/repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml +++ b/repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml @@ -1,3 +1,3 @@ "@effect/experimental/DevTools/Server#run": - replacement: effect/unstable/devtools/DevToolsServer#run + replacement: effect/devtools/DevToolsServer#run note: Import run from the v4 unstable DevToolsServer module. diff --git a/repos/effect/migration/annotations/effect__experimental__Event.yaml b/repos/effect/migration/annotations/effect__experimental__Event.yaml index 89e43d33b5..e5052a2f25 100644 --- a/repos/effect/migration/annotations/effect__experimental__Event.yaml +++ b/repos/effect/migration/annotations/effect__experimental__Event.yaml @@ -1,42 +1,42 @@ "@effect/experimental/Event#Event.AddError": - replacement: effect/unstable/eventlog/Event#AddError + replacement: effect/eventlog/Event#AddError note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.Any": - replacement: effect/unstable/eventlog/Event#Any + replacement: effect/eventlog/Event#Any note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.Context": - replacement: effect/unstable/eventlog/Event#Services + replacement: effect/eventlog/Event#Services note: Event schema context is now represented by decoding and encoding Services. "@effect/experimental/Event#Event.ContextWithTag": - replacement: effect/unstable/eventlog/Event#Services> + replacement: effect/eventlog/Event#Services> note: Filter with WithTag and derive its decoding and encoding Services. "@effect/experimental/Event#Event.Error": - replacement: effect/unstable/eventlog/Event#Error + replacement: effect/eventlog/Event#Error note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.ErrorSchema": - replacement: effect/unstable/eventlog/Event#ErrorSchema + replacement: effect/eventlog/Event#ErrorSchema note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.Payload": - replacement: effect/unstable/eventlog/Event#Payload + replacement: effect/eventlog/Event#Payload note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.PayloadSchema": - replacement: effect/unstable/eventlog/Event#PayloadSchema + replacement: effect/eventlog/Event#PayloadSchema note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.Success": - replacement: effect/unstable/eventlog/Event#Success + replacement: effect/eventlog/Event#Success note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.SuccessSchema": - replacement: effect/unstable/eventlog/Event#SuccessSchema + replacement: effect/eventlog/Event#SuccessSchema note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.Tag": - replacement: effect/unstable/eventlog/Event#Tag + replacement: effect/eventlog/Event#Tag note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#Event.ToService": - replacement: effect/unstable/eventlog/Event#ToService + replacement: effect/eventlog/Event#ToService note: This type moved from the Event namespace to a top-level export. "@effect/experimental/Event#make": - replacement: effect/unstable/eventlog/Event#make + replacement: effect/eventlog/Event#make note: Import make from the v4 unstable Event module. "@effect/experimental/Event#TypeId": - replacement: effect/unstable/eventlog/Event#TypeId + replacement: effect/eventlog/Event#TypeId note: Import TypeId from the v4 unstable Event module; its runtime representation is now a string brand. diff --git a/repos/effect/migration/annotations/effect__experimental__EventGroup.yaml b/repos/effect/migration/annotations/effect__experimental__EventGroup.yaml index 75f54efe3a..9d97577c53 100644 --- a/repos/effect/migration/annotations/effect__experimental__EventGroup.yaml +++ b/repos/effect/migration/annotations/effect__experimental__EventGroup.yaml @@ -1,15 +1,15 @@ "@effect/experimental/EventGroup#EventGroup.Any": - replacement: effect/unstable/eventlog/EventGroup#Any + replacement: effect/eventlog/EventGroup#Any note: This type moved from the EventGroup namespace to a top-level export. "@effect/experimental/EventGroup#EventGroup.AnyWithProps": - replacement: effect/unstable/eventlog/EventGroup#AnyWithProps + replacement: effect/eventlog/EventGroup#AnyWithProps note: This type moved from the EventGroup namespace to a top-level export. "@effect/experimental/EventGroup#EventGroup.Context": - replacement: effect/unstable/eventlog/EventGroup#ServicesClient | effect/unstable/eventlog/EventGroup#ServicesServer + replacement: effect/eventlog/EventGroup#ServicesClient | effect/eventlog/EventGroup#ServicesServer note: Choose the client or server schema services for the required direction. "@effect/experimental/EventGroup#EventGroup.ToService": - replacement: effect/unstable/eventlog/EventGroup#ToService + replacement: effect/eventlog/EventGroup#ToService note: This type moved from the EventGroup namespace to a top-level export. "@effect/experimental/EventGroup#TypeId": - replacement: effect/unstable/eventlog/EventGroup#TypeId + replacement: effect/eventlog/EventGroup#TypeId note: Import TypeId from the v4 unstable EventGroup module; its runtime representation is now a string brand. diff --git a/repos/effect/migration/annotations/effect__experimental__EventJournal.yaml b/repos/effect/migration/annotations/effect__experimental__EventJournal.yaml index d7ad705bce..a63c391dde 100644 --- a/repos/effect/migration/annotations/effect__experimental__EventJournal.yaml +++ b/repos/effect/migration/annotations/effect__experimental__EventJournal.yaml @@ -1,18 +1,18 @@ "@effect/experimental/EventJournal#EntryIdTypeId": - replacement: effect/unstable/eventlog/EventJournal#EntryIdTypeId + replacement: effect/eventlog/EventJournal#EntryIdTypeId note: Import EntryIdTypeId from the v4 EventJournal module; it is now a string brand. "@effect/experimental/EventJournal#ErrorTypeId": replacement: none note: The v4 error marker is private; narrow with EventJournalError instead. "@effect/experimental/EventJournal#makeEntryId": - replacement: effect/unstable/eventlog/EventJournal#makeEntryIdUnsafe + replacement: effect/eventlog/EventJournal#makeEntryIdUnsafe note: The unchecked EntryId constructor was renamed to makeEntryIdUnsafe. "@effect/experimental/EventJournal#makeRemoteId": - replacement: effect/unstable/eventlog/EventJournal#makeRemoteIdUnsafe + replacement: effect/eventlog/EventJournal#makeRemoteIdUnsafe note: The unchecked RemoteId constructor was renamed to makeRemoteIdUnsafe. "@effect/experimental/EventJournal#RemoteIdTypeId": - replacement: effect/unstable/eventlog/EventJournal#RemoteIdTypeId + replacement: effect/eventlog/EventJournal#RemoteIdTypeId note: Import RemoteIdTypeId from the v4 EventJournal module; it is now a string brand. "@effect/experimental/EventJournal#makeMemory": - replacement: effect/unstable/eventlog/EventJournal#makeMemory + replacement: effect/eventlog/EventJournal#makeMemory note: The in-memory constructor moved into core Effect and now returns the Context.Service implementation through its Service projection. diff --git a/repos/effect/migration/annotations/effect__experimental__EventLog.yaml b/repos/effect/migration/annotations/effect__experimental__EventLog.yaml index 8697535225..9d8229a1e3 100644 --- a/repos/effect/migration/annotations/effect__experimental__EventLog.yaml +++ b/repos/effect/migration/annotations/effect__experimental__EventLog.yaml @@ -1,27 +1,27 @@ "@effect/experimental/EventLog#group": - replacement: effect/unstable/eventlog/EventLog#group + replacement: effect/eventlog/EventLog#group note: Import group from the v4 EventLog module; it now requires the shared Registry service. "@effect/experimental/EventLog#Handlers": - replacement: effect/unstable/eventlog/EventLog#Handlers + replacement: effect/eventlog/EventLog#Handlers note: Import Handlers from the v4 EventLog module; handlers now also receive storeId. "@effect/experimental/EventLog#Handlers.Error": - replacement: effect/unstable/eventlog/EventLog#Handlers.Error + replacement: effect/eventlog/EventLog#Handlers.Error note: Import the retained Handlers.Error type from the v4 EventLog module. "@effect/experimental/EventLog#Handlers.ValidateReturn": - replacement: effect/unstable/eventlog/EventLog#Handlers.ValidateReturn + replacement: effect/eventlog/EventLog#Handlers.ValidateReturn note: Import the retained Handlers.ValidateReturn type from the v4 EventLog module. "@effect/experimental/EventLog#HandlersTypeId": - replacement: effect/unstable/eventlog/EventLog#HandlersTypeId + replacement: effect/eventlog/EventLog#HandlersTypeId note: Import HandlersTypeId from the v4 EventLog module. "@effect/experimental/EventLog#layer": - replacement: effect/unstable/eventlog/EventLog#layer + replacement: effect/eventlog/EventLog#layer note: The v4 layer takes both the schema and handler layer; use layerEventLog for runtime only. "@effect/experimental/EventLog#layerIdentityKvs": replacement: none note: Compose KeyValueStore.toSchemaStore, EventLog.IdentitySchema, EventLog.makeIdentity, and Layer.effect manually. "@effect/experimental/EventLog#schema": - replacement: effect/unstable/eventlog/EventLog#schema + replacement: effect/eventlog/EventLog#schema note: Import schema from the v4 EventLog module. "@effect/experimental/EventLog#SchemaTypeId": - replacement: effect/unstable/eventlog/EventLog#SchemaTypeId + replacement: effect/eventlog/EventLog#SchemaTypeId note: Import SchemaTypeId from the v4 EventLog module. diff --git a/repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml b/repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml index c0016a6dcd..8e632aa219 100644 --- a/repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml +++ b/repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml @@ -2,55 +2,55 @@ replacement: none note: A write acknowledgement is now the void success of EventLogMessage.WriteSingleRpc or WriteChunkedRpc. "@effect/experimental/EventLogRemote#Changes": - replacement: effect/unstable/eventlog/EventLogMessage#ChangesRpc + replacement: effect/eventlog/EventLogMessage#ChangesRpc note: ChangesRpc replaces the separate request and response models with one streaming RPC. "@effect/experimental/EventLogRemote#decodeRequest": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request decoder. "@effect/experimental/EventLogRemote#decodeResponse": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response decoder. "@effect/experimental/EventLogRemote#encodeRequest": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request encoder. "@effect/experimental/EventLogRemote#encodeResponse": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response encoder. "@effect/experimental/EventLogRemote#EventLogRemote": - replacement: effect/unstable/eventlog/EventLogRemote#EventLogRemote + replacement: effect/eventlog/EventLogRemote#EventLogRemote note: Use the v4 Context.Service; methods now take storeId-aware options. "@effect/experimental/EventLogRemote#fromSocket": - replacement: effect/unstable/eventlog/EventLogRemote#makeEncrypted + effect/unstable/rpc/RpcClient#makeProtocolSocket + replacement: effect/eventlog/EventLogRemote#makeEncrypted + effect/rpc/RpcClient#makeProtocolSocket note: Construct the encrypted remote separately from its generic RPC socket protocol. "@effect/experimental/EventLogRemote#Hello": - replacement: effect/unstable/eventlog/EventLogMessage#HelloResponse + replacement: effect/eventlog/EventLogMessage#HelloResponse note: HelloResponse replaces Hello and includes the v4 authentication challenge; HelloRpc defines the endpoint. "@effect/experimental/EventLogRemote#layerWebSocket": - replacement: effect/unstable/eventlog/EventLogRemote#layerEncrypted + effect/unstable/rpc/RpcClient#layerProtocolSocket + replacement: effect/eventlog/EventLogRemote#layerEncrypted + effect/rpc/RpcClient#layerProtocolSocket note: Compose the encrypted remote with the generic socket protocol, SchemaBinary serialization, and a Socket provider. "@effect/experimental/EventLogRemote#layerWebSocketBrowser": - replacement: effect/unstable/eventlog/EventLogRemote#layerEncrypted + effect/unstable/rpc/RpcClient#layerProtocolSocket + @effect/platform-browser/BrowserSocket#layerWebSocket + replacement: effect/eventlog/EventLogRemote#layerEncrypted + effect/rpc/RpcClient#layerProtocolSocket + @effect/platform-browser/BrowserSocket#layerWebSocket note: Compose the encrypted remote and generic RPC socket protocol with the browser WebSocket layer. "@effect/experimental/EventLogRemote#Pong": replacement: none note: The event-log Pong model was removed; heartbeats belong to the generic RPC socket protocol. "@effect/experimental/EventLogRemote#ProtocolRequest": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: EventLogRemoteRpcs and generic RPC serialization replace the old protocol request union. "@effect/experimental/EventLogRemote#ProtocolRequestMsgPack": - replacement: effect/unstable/rpc/RpcSerialization#layerSchemaBinary + replacement: effect/rpc/RpcSerialization#layerSchemaBinary note: Use the generic SchemaBinary RPC serialization layer instead of a request-specific schema. "@effect/experimental/EventLogRemote#ProtocolResponse": - replacement: effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs + replacement: effect/eventlog/EventLogMessage#EventLogRemoteRpcs note: EventLogRemoteRpcs and generic RPC serialization replace the old protocol response union. "@effect/experimental/EventLogRemote#ProtocolResponseMsgPack": - replacement: effect/unstable/rpc/RpcSerialization#layerSchemaBinary + replacement: effect/rpc/RpcSerialization#layerSchemaBinary note: Use the generic SchemaBinary RPC serialization layer instead of a response-specific schema. "@effect/experimental/EventLogRemote#RemoteAdditions": replacement: none note: This unused protocol model has no v4 counterpart. "@effect/experimental/EventLogRemote#RequestChanges": - replacement: effect/unstable/eventlog/EventLogMessage#ChangesRpc + replacement: effect/eventlog/EventLogMessage#ChangesRpc note: ChangesRpc replaces the separate request model with one streaming RPC. "@effect/experimental/EventLogRemote#StopChanges": replacement: none diff --git a/repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml b/repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml index 8a044978f6..4968aa18bb 100644 --- a/repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml +++ b/repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml @@ -1,15 +1,17 @@ "@effect/experimental/EventLogServer#layerStorageMemory": - replacement: effect/unstable/eventlog/EventLogServerEncrypted#layerStorageMemory + include: true + replacement: effect/eventlog/EventLogServerEncrypted#layerStorageMemory note: Use the encrypted server memory storage layer; storage is now storeId- and session-aware. "@effect/experimental/EventLogServer#makeHandler": - replacement: effect/unstable/eventlog/EventLogServerEncrypted#layer + effect/unstable/rpc/RpcServer#layerProtocolSocketServer + replacement: effect/eventlog/EventLogServerEncrypted#layer + effect/rpc/RpcServer#layerProtocolSocketServer note: Compose the encrypted server layer with the generic RPC socket server; there is no per-socket handler factory. "@effect/experimental/EventLogServer#makeHandlerHttp": - replacement: effect/unstable/eventlog/EventLogServerEncrypted#layer + effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket + replacement: effect/eventlog/EventLogServerEncrypted#layer + effect/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket note: Use the returned httpEffect for upgrades and provide its protocol to the encrypted server layer. "@effect/experimental/EventLogServer#makeStorageMemory": - replacement: effect/unstable/eventlog/EventLogServerEncrypted#makeStorageMemory + replacement: effect/eventlog/EventLogServerEncrypted#makeStorageMemory note: Use the encrypted server memory storage constructor. "@effect/experimental/EventLogServer#Storage": - replacement: effect/unstable/eventlog/EventLogServerEncrypted#Storage + include: true + replacement: effect/eventlog/EventLogServerEncrypted#Storage note: Use the encrypted server Storage service, which is storeId- and session-aware. diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml b/repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml index d766b9c6be..cc3cb6796b 100644 --- a/repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml +++ b/repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml @@ -1,3 +1,3 @@ "@effect/experimental/PersistedCache#make": - replacement: effect/unstable/persistence/PersistedCache#make + replacement: effect/persistence/PersistedCache#make note: Pass lookup as the first argument and options second; timeToLive now receives exit before request and the service is Persistence.Persistence. diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml b/repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml index 68aada2327..a6b8c6e6b3 100644 --- a/repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml +++ b/repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml @@ -1,15 +1,15 @@ "@effect/experimental/PersistedQueue#layer": - replacement: effect/unstable/persistence/PersistedQueue#layer + replacement: effect/persistence/PersistedQueue#layer note: Import layer from the v4 unstable PersistedQueue module. "@effect/experimental/PersistedQueue#layerStoreMemory": - replacement: effect/unstable/persistence/PersistedQueue#layerStoreMemory + replacement: effect/persistence/PersistedQueue#layerStoreMemory note: Import layerStoreMemory from the v4 unstable PersistedQueue module. "@effect/experimental/PersistedQueue#make": - replacement: effect/unstable/persistence/PersistedQueue#make + replacement: effect/persistence/PersistedQueue#make note: Import make from the v4 unstable PersistedQueue module. "@effect/experimental/PersistedQueue#ErrorTypeId": - replacement: effect/unstable/persistence/PersistedQueue#ErrorTypeId + replacement: effect/persistence/PersistedQueue#ErrorTypeId note: Retained as a string brand; the runtime marker now uses the persistence module path. "@effect/experimental/PersistedQueue#TypeId": - replacement: effect/unstable/persistence/PersistedQueue#TypeId + replacement: effect/persistence/PersistedQueue#TypeId note: Import TypeId from the v4 unstable PersistedQueue module; it is now a string brand. diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml b/repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml index cc9dd54beb..0dbc1c4b55 100644 --- a/repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml +++ b/repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml @@ -1,9 +1,9 @@ "@effect/experimental/PersistedQueue/Redis#layerStore": - replacement: effect/unstable/persistence/PersistedQueue#layerStoreRedis + replacement: effect/persistence/PersistedQueue#layerStoreRedis note: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. "@effect/experimental/PersistedQueue/Redis#layerStoreConfig": replacement: none note: Configure a Redis provider such as NodeRedis.layerConfig separately, then compose it with PersistedQueue.layerStoreRedis. "@effect/experimental/PersistedQueue/Redis#make": - replacement: effect/unstable/persistence/PersistedQueue#makeStoreRedis + replacement: effect/persistence/PersistedQueue#makeStoreRedis note: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. diff --git a/repos/effect/migration/annotations/effect__experimental__Persistence.yaml b/repos/effect/migration/annotations/effect__experimental__Persistence.yaml index 44ee172478..6fb8d2458d 100644 --- a/repos/effect/migration/annotations/effect__experimental__Persistence.yaml +++ b/repos/effect/migration/annotations/effect__experimental__Persistence.yaml @@ -1,5 +1,5 @@ "@effect/experimental/Persistence#BackingPersistence": - replacement: effect/unstable/persistence/Persistence#BackingPersistence + replacement: effect/persistence/Persistence#BackingPersistence note: Use the v4 BackingPersistence Context.Service class. "@effect/experimental/Persistence#BackingPersistenceTypeId": replacement: none @@ -8,43 +8,43 @@ replacement: none note: The v4 persistence error identifier is private; narrow with the exported error classes. "@effect/experimental/Persistence#layerKeyValueStore": - replacement: effect/unstable/persistence/Persistence#layerBackingKvs + replacement: effect/persistence/Persistence#layerBackingKvs note: The KeyValueStore backing layer was renamed to layerBackingKvs. "@effect/experimental/Persistence#layerMemory": - replacement: effect/unstable/persistence/Persistence#layerBackingMemory + replacement: effect/persistence/Persistence#layerBackingMemory note: Use layerBackingMemory for the old backing service; v4 layerMemory creates the higher-level Persistence service. "@effect/experimental/Persistence#layerResult": - replacement: effect/unstable/persistence/Persistence#layer + replacement: effect/persistence/Persistence#layer note: The ResultPersistence service layer was renamed to layer. "@effect/experimental/Persistence#layerResultKeyValueStore": - replacement: effect/unstable/persistence/Persistence#layerKvs + replacement: effect/persistence/Persistence#layerKvs note: The combined KeyValueStore-backed result layer was renamed to layerKvs. "@effect/experimental/Persistence#layerResultMemory": - replacement: effect/unstable/persistence/Persistence#layerMemory + replacement: effect/persistence/Persistence#layerMemory note: The combined memory-backed result layer was renamed to layerMemory. "@effect/experimental/Persistence#PersistenceBackingError": - replacement: effect/unstable/persistence/Persistence#PersistenceError + replacement: effect/persistence/Persistence#PersistenceError note: PersistenceError now represents failures from the backing persistence implementation. "@effect/experimental/Persistence#PersistenceError": - replacement: effect/unstable/persistence/Persistence#PersistenceError | effect/Schema#SchemaError + replacement: effect/persistence/Persistence#PersistenceError | effect/Schema#SchemaError note: The old combined alias was split into backing PersistenceError and schema SchemaError. "@effect/experimental/Persistence#PersistenceParseError": replacement: effect/Schema#SchemaError note: Persistence parsing failures now use the core SchemaError type. "@effect/experimental/Persistence#ResultPersistence": - replacement: effect/unstable/persistence/Persistence#Persistence + replacement: effect/persistence/Persistence#Persistence note: ResultPersistence was renamed to Persistence and is now a Context.Service class. "@effect/experimental/Persistence#ResultPersistence.Key": - replacement: effect/unstable/persistence/Persistable#Persistable + replacement: effect/persistence/Persistable#Persistable note: Persistable is the v4 schema-backed persistence key contract. "@effect/experimental/Persistence#ResultPersistence.KeyAny": - replacement: effect/unstable/persistence/Persistable#Any + replacement: effect/persistence/Persistable#Any note: Use Persistable.Any for an arbitrary v4 persistence key contract. "@effect/experimental/Persistence#ResultPersistence.TimeToLiveArgs": - replacement: Parameters> + replacement: Parameters> note: Derive the tuple from TimeToLiveFn; its order is now exit then request. "@effect/experimental/Persistence#ResultPersistenceStore": - replacement: effect/unstable/persistence/Persistence#PersistenceStore + replacement: effect/persistence/Persistence#PersistenceStore note: ResultPersistenceStore was renamed to PersistenceStore. "@effect/experimental/Persistence#ResultPersistenceTypeId": replacement: none diff --git a/repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml b/repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml index dfc6b6e410..50becc8c7b 100644 --- a/repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml +++ b/repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml @@ -1,11 +1,11 @@ "@effect/experimental/Persistence/Redis#layer": - replacement: effect/unstable/persistence/Persistence#layerBackingRedis + replacement: effect/persistence/Persistence#layerBackingRedis note: The Redis backing adapter was merged into Persistence and now requires the generic Redis.Redis service. "@effect/experimental/Persistence/Redis#layerConfig": replacement: none note: Compose Persistence.layerBackingRedis with a config-driven provider such as NodeRedis.layerConfig. "@effect/experimental/Persistence/Redis#layerResult": - replacement: effect/unstable/persistence/Persistence#layerRedis + replacement: effect/persistence/Persistence#layerRedis note: The combined Redis persistence layer was merged into Persistence and now requires Redis.Redis. "@effect/experimental/Persistence/Redis#layerResultConfig": replacement: none diff --git a/repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml b/repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml index 32537e7580..efa6b8d909 100644 --- a/repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml +++ b/repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml @@ -1,21 +1,22 @@ "@effect/experimental/RateLimiter#layer": - replacement: effect/unstable/persistence/RateLimiter#layer + replacement: effect/persistence/RateLimiter#layer note: Import layer from the v4 unstable RateLimiter module. "@effect/experimental/RateLimiter#make": - replacement: effect/unstable/persistence/RateLimiter#make + replacement: effect/persistence/RateLimiter#make note: Import make from the v4 unstable RateLimiter module. "@effect/experimental/RateLimiter#makeSleep": - replacement: effect/unstable/persistence/RateLimiter#sleep + replacement: effect/persistence/RateLimiter#sleep note: The accessor Effect was replaced by sleep; obtain the RateLimiter service and pass it to sleep directly or with its curried overload. "@effect/experimental/RateLimiter#RateLimiterError": - replacement: effect/unstable/persistence/RateLimiter#RateLimiterError + replacement: effect/persistence/RateLimiter#RateLimiterError note: The retained name is now a wrapper error class whose reason is RateLimitExceeded or RateLimitStoreError. "@effect/experimental/RateLimiter#ErrorTypeId": - replacement: effect/unstable/persistence/RateLimiter#ErrorTypeId + replacement: effect/persistence/RateLimiter#ErrorTypeId note: Retained as a string brand; the runtime marker now uses the persistence module path. "@effect/experimental/RateLimiter#TypeId": - replacement: effect/unstable/persistence/RateLimiter#TypeId + replacement: effect/persistence/RateLimiter#TypeId note: Import TypeId from the v4 unstable RateLimiter module; it is now a string brand. "@effect/experimental/RateLimiter#RateLimiterStore": + include: true replacement: RateLimiter.RateLimiterStore - note: "Use the Context.Service class from effect/unstable/persistence/RateLimiter. Custom tokenBucket implementations must return [remaining, elapsedMillis] instead of a number, preserving fractional counts and elapsed refill time." + note: "Use the Context.Service class from effect/persistence/RateLimiter. Custom tokenBucket implementations must return [remaining, elapsedMillis] instead of a number, preserving fractional counts and elapsed refill time." diff --git a/repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml b/repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml index 27592db8a1..cbb78b75b9 100644 --- a/repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml +++ b/repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml @@ -1,9 +1,9 @@ "@effect/experimental/RateLimiter/Redis#layerStore": - replacement: effect/unstable/persistence/RateLimiter#layerStoreRedis + replacement: effect/persistence/RateLimiter#layerStoreRedis note: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. "@effect/experimental/RateLimiter/Redis#layerStoreConfig": - replacement: effect/unstable/persistence/RateLimiter#layerStoreRedisConfig + replacement: effect/persistence/RateLimiter#layerStoreRedisConfig note: Use the merged Redis store config layer; connection configuration belongs to a separate Redis provider. "@effect/experimental/RateLimiter/Redis#make": - replacement: effect/unstable/persistence/RateLimiter#makeStoreRedis + replacement: effect/persistence/RateLimiter#makeStoreRedis note: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. diff --git a/repos/effect/migration/annotations/effect__experimental__Reactivity.yaml b/repos/effect/migration/annotations/effect__experimental__Reactivity.yaml index d224797f06..853d1bee29 100644 --- a/repos/effect/migration/annotations/effect__experimental__Reactivity.yaml +++ b/repos/effect/migration/annotations/effect__experimental__Reactivity.yaml @@ -1,15 +1,15 @@ "@effect/experimental/Reactivity#layer": - replacement: effect/unstable/reactivity/Reactivity#layer + replacement: effect/reactivity/Reactivity#layer note: Import layer from the v4 unstable Reactivity module. "@effect/experimental/Reactivity#make": - replacement: effect/unstable/reactivity/Reactivity#make + replacement: effect/reactivity/Reactivity#make note: Import make from the v4 unstable Reactivity module. "@effect/experimental/Reactivity#Reactivity": - replacement: effect/unstable/reactivity/Reactivity#Reactivity + replacement: effect/reactivity/Reactivity#Reactivity note: Use the Reactivity interface for implementations and the Reactivity Context.Service value as the service key. Implementations require the TypeId brand; prefer Reactivity.make. Unsafe methods were renamed with an Unsafe suffix. "@effect/experimental/Reactivity#Reactivity.Service": - replacement: effect/unstable/reactivity/Reactivity#Reactivity + replacement: effect/reactivity/Reactivity#Reactivity note: "The named namespace member was removed; use the branded Reactivity interface directly. Prefer Reactivity.make, or include [Reactivity.TypeId]: Reactivity.TypeId in a custom implementation." "@effect/experimental/Reactivity#stream": - replacement: effect/unstable/reactivity/Reactivity#stream + replacement: effect/reactivity/Reactivity#stream note: Import stream from the v4 unstable Reactivity module. diff --git a/repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml b/repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml index 69fe62a7b3..d97380bf19 100644 --- a/repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml +++ b/repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml @@ -2,10 +2,10 @@ replacement: effect/RequestResolver#setDelay + effect/RequestResolver#batchN note: Pipe the resolver through setDelay(options.window) and batchN(options.maxBatchSize ?? Infinity); the transformation is now pure. "@effect/experimental/RequestResolver#PersistedRequest": - replacement: effect/Request#Request & effect/unstable/persistence/Persistable#Persistable + replacement: effect/Request#Request & effect/persistence/Persistable#Persistable note: Intersect a Request with Persistable or define it with Persistable.Class; there is no combined named export. "@effect/experimental/RequestResolver#PersistedRequest.Any": - replacement: effect/Request#Any & effect/unstable/persistence/Persistable#Any + replacement: effect/Request#Any & effect/persistence/Persistable#Any note: Intersect the Request and Persistable helper types for an arbitrary persisted request. "@effect/experimental/RequestResolver#persisted": replacement: effect/RequestResolver#persisted diff --git a/repos/effect/migration/annotations/effect__experimental__Sse.yaml b/repos/effect/migration/annotations/effect__experimental__Sse.yaml index 5f233fe6cf..55e47b75b1 100644 --- a/repos/effect/migration/annotations/effect__experimental__Sse.yaml +++ b/repos/effect/migration/annotations/effect__experimental__Sse.yaml @@ -1,3 +1,3 @@ "@effect/experimental/Sse#RetryTypeId": replacement: none - note: The Retry identifier is private in v4; use effect/unstable/encoding/Sse#Retry and Retry.is instead of inspecting the brand. + note: The Retry identifier is private in v4; use effect/encoding/Sse#Retry and Retry.is instead of inspecting the brand. diff --git a/repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml b/repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml index 865c3f4d04..9dcf74aad4 100644 --- a/repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml +++ b/repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml @@ -1,11 +1,11 @@ "@effect/experimental/VariantSchema#Field.Any": - replacement: effect/unstable/schema/VariantSchema#Field.Any + replacement: effect/schema/VariantSchema#Field.Any note: Import the retained Field.Any helper type from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#Field.Config": - replacement: effect/unstable/schema/VariantSchema#Field.Config + replacement: effect/schema/VariantSchema#Field.Config note: Import the retained Field.Config helper type from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#Field.Fields": - replacement: effect/unstable/schema/VariantSchema#Field.Fields + replacement: effect/schema/VariantSchema#Field.Fields note: Import the retained Field.Fields helper type from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#Field.ValueAny": replacement: effect/Schema#Top @@ -20,20 +20,20 @@ replacement: none note: The fromKey rename helper was not ported; for whole-struct encoded-key renaming consider Schema.encodeKeys. "@effect/experimental/VariantSchema#isField": - replacement: effect/unstable/schema/VariantSchema#isField + replacement: effect/schema/VariantSchema#isField note: Import isField from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#make": - replacement: effect/unstable/schema/VariantSchema#make + replacement: effect/schema/VariantSchema#make note: Import make from the v4 module; FieldOnly and FieldExcept take one key array and Union takes one member array. "@effect/experimental/VariantSchema#Override": - replacement: effect/unstable/schema/VariantSchema#Override + replacement: effect/schema/VariantSchema#Override note: Import Override from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#Struct.Fields": - replacement: effect/unstable/schema/VariantSchema#Struct.Fields + replacement: effect/schema/VariantSchema#Struct.Fields note: Import the retained Struct.Fields helper type from the v4 unstable VariantSchema module. "@effect/experimental/VariantSchema#TypeId": - replacement: effect/unstable/schema/VariantSchema#TypeId + replacement: effect/schema/VariantSchema#TypeId note: Use the retained runtime value; in type position use typeof VariantSchema.TypeId. "@effect/experimental/VariantSchema#Extract": - replacement: effect/unstable/schema/VariantSchema#Extract + replacement: effect/schema/VariantSchema#Extract note: Import the retained helper from the v4 module; its erased schema constraint is Schema.Top. diff --git a/repos/effect/migration/annotations/effect__experimental__index.yaml b/repos/effect/migration/annotations/effect__experimental__index.yaml index ca42fdd69d..6b26213d65 100644 --- a/repos/effect/migration/annotations/effect__experimental__index.yaml +++ b/repos/effect/migration/annotations/effect__experimental__index.yaml @@ -1,3 +1,3 @@ "@effect/experimental/index": replacement: "none" - note: "The package barrel was removed along with the package; import each module from its new effect/unstable/* location per the Import Map." + note: "The package barrel was removed along with the package; import each module from its new effect/* location per the Import Map." diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml b/repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml index 6737fd2d45..667d53b367 100644 --- a/repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml +++ b/repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml @@ -1,3 +1,3 @@ "@effect/opentelemetry/Otlp#layer": replacement: "Otlp.layer" - note: "Moved to effect/unstable/observability/Otlp; replaceLogger was replaced by loggerMergeWithExisting, and metricsTemporality is now configurable." + note: "Moved to effect/observability/Otlp; replaceLogger was replaced by loggerMergeWithExisting, and metricsTemporality is now configurable." diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml b/repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml index abe3f8563c..2a36093a2e 100644 --- a/repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml +++ b/repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml @@ -1,6 +1,6 @@ "@effect/opentelemetry/OtlpLogger#layer": replacement: "OtlpLogger.layer" - note: "Moved to effect/unstable/observability/OtlpLogger; use mergeWithExisting instead of passing replaceLogger." + note: "Moved to effect/observability/OtlpLogger; use mergeWithExisting instead of passing replaceLogger." "@effect/opentelemetry/OtlpLogger#make": replacement: "OtlpLogger.make" - note: "The constructor remains in the module moved to effect/unstable/observability/OtlpLogger." + note: "The constructor remains in the module moved to effect/observability/OtlpLogger." diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml b/repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml index c6b7b28b8d..52bdd29840 100644 --- a/repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml +++ b/repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml @@ -1,6 +1,6 @@ "@effect/opentelemetry/OtlpMetrics#layer": replacement: "OtlpMetrics.layer" - note: "Moved to effect/unstable/observability/OtlpMetrics; the layer now also accepts optional cumulative or delta temporality." + note: "Moved to effect/observability/OtlpMetrics; the layer now also accepts optional cumulative or delta temporality." "@effect/opentelemetry/OtlpMetrics#make": replacement: "OtlpMetrics.make" - note: "Moved to effect/unstable/observability/OtlpMetrics; the constructor now also accepts optional cumulative or delta temporality." + note: "Moved to effect/observability/OtlpMetrics; the constructor now also accepts optional cumulative or delta temporality." diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml b/repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml index a649a5fb41..1126c84216 100644 --- a/repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml +++ b/repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml @@ -1,6 +1,6 @@ "@effect/opentelemetry/OtlpResource#make": replacement: "OtlpResource.make" - note: "The constructor remains in the module moved to effect/unstable/observability/OtlpResource." + note: "The constructor remains in the module moved to effect/observability/OtlpResource." "@effect/opentelemetry/OtlpResource#unsafeServiceName": replacement: "OtlpResource.serviceNameUnsafe" - note: "Moved to effect/unstable/observability/OtlpResource and renamed to follow the v4 unsafe-suffix convention." + note: "Moved to effect/observability/OtlpResource and renamed to follow the v4 unsafe-suffix convention." diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml b/repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml index 2487741a91..b3a896f2b0 100644 --- a/repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml +++ b/repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml @@ -1,6 +1,6 @@ "@effect/opentelemetry/OtlpTracer#layer": replacement: "OtlpTracer.layer" - note: "The layer remains in the module moved to effect/unstable/observability/OtlpTracer." + note: "The layer remains in the module moved to effect/observability/OtlpTracer." "@effect/opentelemetry/OtlpTracer#make": replacement: "OtlpTracer.make" - note: "The constructor remains in the module moved to effect/unstable/observability/OtlpTracer." + note: "The constructor remains in the module moved to effect/observability/OtlpTracer." diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml b/repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml index ac93b76bd0..dfbf18d58d 100644 --- a/repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml +++ b/repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml @@ -1,3 +1,3 @@ "@effect/platform-bun/BunCommandExecutor#layer": replacement: "BunChildProcessSpawner.layer" - note: "CommandExecutor became effect/unstable/process/ChildProcessSpawner; the Bun adapter was renamed and still requires FileSystem and Path." + note: "CommandExecutor became effect/process/ChildProcessSpawner; the Bun adapter was renamed and still requires FileSystem and Path." diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml b/repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml index 000285be37..22d88bbb62 100644 --- a/repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml +++ b/repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml @@ -1,3 +1,3 @@ "@effect/platform-bun/BunKeyValueStore": - replacement: "effect/unstable/persistence/KeyValueStore" + replacement: "effect/persistence/KeyValueStore" note: "layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via BunServices.layer or BunFileSystem.layer with BunPath.layer." diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml b/repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml index 87da421895..dde4f7e529 100644 --- a/repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml +++ b/repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml @@ -1,3 +1,3 @@ "@effect/platform-node-shared/NodeCommandExecutor#layer": replacement: "NodeChildProcessSpawner.layer" - note: "CommandExecutor became effect/unstable/process/ChildProcessSpawner; the Node adapter was renamed and still requires FileSystem and Path." + note: "CommandExecutor became effect/process/ChildProcessSpawner; the Node adapter was renamed and still requires FileSystem and Path." diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml b/repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml index 8215e91d11..2ebf13d5eb 100644 --- a/repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml +++ b/repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml @@ -1,3 +1,3 @@ "@effect/platform-node-shared/NodeKeyValueStore": - replacement: "effect/unstable/persistence/KeyValueStore" + replacement: "effect/persistence/KeyValueStore" note: "layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer." diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml b/repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml index 64cc6a8b59..141214fd04 100644 --- a/repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml +++ b/repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml @@ -1,3 +1,3 @@ "@effect/platform-node/NodeKeyValueStore": - replacement: "effect/unstable/persistence/KeyValueStore" + replacement: "effect/persistence/KeyValueStore" note: "layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer." diff --git a/repos/effect/migration/annotations/effect__platform.yaml b/repos/effect/migration/annotations/effect__platform.yaml index 4653d0b895..eb74b0bdaa 100644 --- a/repos/effect/migration/annotations/effect__platform.yaml +++ b/repos/effect/migration/annotations/effect__platform.yaml @@ -1,3 +1,3 @@ "@effect/platform": replacement: "none" - note: "The @effect/platform package was folded into the effect package: core services live in effect root modules (e.g. effect/FileSystem, effect/Path) and HTTP in effect/unstable/http; follow the Import Map for each module." + note: "The @effect/platform package was folded into the effect package: core services live in effect root modules (e.g. effect/FileSystem, effect/Path) and HTTP in effect/http; follow the Import Map for each module." diff --git a/repos/effect/migration/annotations/effect__platform__Command.yaml b/repos/effect/migration/annotations/effect__platform__Command.yaml index af9a59b08a..6978f5742e 100644 --- a/repos/effect/migration/annotations/effect__platform__Command.yaml +++ b/repos/effect/migration/annotations/effect__platform__Command.yaml @@ -1,6 +1,6 @@ "@effect/platform/Command#Command": replacement: "ChildProcess.Command" - note: "Commands moved to effect/unstable/process/ChildProcess and are now directly Effectable to spawn a ChildProcessHandle." + note: "Commands moved to effect/process/ChildProcess and are now directly Effectable to spawn a ChildProcessHandle." "@effect/platform/Command#Command.Input": replacement: "ChildProcess.CommandInput" note: "The standard-input configuration type was flattened out of the Command namespace." diff --git a/repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml b/repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml index 976d72eb27..3386575192 100644 --- a/repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml +++ b/repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml @@ -1,6 +1,6 @@ "@effect/platform/CommandExecutor#CommandExecutor": replacement: "ChildProcessSpawner.ChildProcessSpawner" - note: "The executor service moved to effect/unstable/process/ChildProcessSpawner and was renamed." + note: "The executor service moved to effect/process/ChildProcessSpawner and was renamed." "@effect/platform/CommandExecutor#makeExecutor": replacement: "ChildProcessSpawner.make" note: "Use the renamed constructor; it derives output helpers from a spawn implementation." diff --git a/repos/effect/migration/annotations/effect__platform__Error.yaml b/repos/effect/migration/annotations/effect__platform__Error.yaml index 61763d683f..3be2dd6225 100644 --- a/repos/effect/migration/annotations/effect__platform__Error.yaml +++ b/repos/effect/migration/annotations/effect__platform__Error.yaml @@ -1,6 +1,3 @@ -"@effect/platform/Error#isPlatformError": - replacement: "value instanceof PlatformError.PlatformError" - note: "PlatformError is a class in v4; use an instanceof check or match its PlatformError tag." "@effect/platform/Error#Module": replacement: "string" note: "The closed module-name Schema was removed; PlatformError reason records accept any module string." diff --git a/repos/effect/migration/annotations/effect__platform__FileSystem.yaml b/repos/effect/migration/annotations/effect__platform__FileSystem.yaml index 7dafaa3727..a2b6995952 100644 --- a/repos/effect/migration/annotations/effect__platform__FileSystem.yaml +++ b/repos/effect/migration/annotations/effect__platform__FileSystem.yaml @@ -63,7 +63,7 @@ note: "Use ByteSize.bytes or unit constructors for file sizes. Truncation lengths, buffer sizes, and read/write counts use number. File.seek takes and returns signed bigint positions; it can fail with PlatformError, including BadArgument when seeking before the start." "@effect/platform/FileSystem#SizeInput": replacement: "ByteSize.Input" - note: "File-size and path-backed range inputs use ByteSize.Input. Truncation lengths, Web File ranges, and buffer sizes use number." + note: "File-size and path-backed range inputs use ByteSize.Input. String inputs must be non-negative decimal integers without leading zeros, followed by a canonical unit symbol or lowercase unit name, with at most one separating space. Parse external strings or fractional quantities with ByteSize.fromString (Option) or ByteSize.fromStringUnsafe (throws), then pass the resulting ByteSize. Truncation lengths, Web File ranges, and buffer sizes use number." "@effect/platform/FileSystem#StreamOptions": replacement: "NonNullable[1]>" note: "Stream options are inline; bufferSize was removed, bytesToRead and offset accept ByteSize inputs, and chunkSize uses number." diff --git a/repos/effect/migration/annotations/effect__platform__Headers.yaml b/repos/effect/migration/annotations/effect__platform__Headers.yaml index 2cd2ffa57a..5c7a20c53d 100644 --- a/repos/effect/migration/annotations/effect__platform__Headers.yaml +++ b/repos/effect/migration/annotations/effect__platform__Headers.yaml @@ -15,7 +15,7 @@ note: "Retained with the same dual, case-insensitive signature." "@effect/platform/Headers#Headers": replacement: "Headers.Headers" - note: "Import Headers from effect/unstable/http; the immutable string-record interface is retained with its v4 TypeId brand." + note: "Import Headers from effect/http; the immutable string-record interface is retained with its v4 TypeId brand." "@effect/platform/Headers#HeadersTypeId": replacement: "Headers.TypeId" note: "The public Headers type-id symbol was renamed from HeadersTypeId to TypeId." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApi.yaml b/repos/effect/migration/annotations/effect__platform__HttpApi.yaml index d761c45e2a..bd28b9f4b2 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApi.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApi.yaml @@ -2,13 +2,13 @@ replacement: "none" note: "The Context tag carrying the API was removed. Pass the HttpApi value explicitly to builders and clients." "@effect/platform/HttpApi#HttpApi.Any": - replacement: "effect/unstable/httpapi/HttpApi#Constraint" + replacement: "effect/http-api/HttpApi#Constraint" note: "Use the erased marker constraint when only HttpApi identity is needed." "@effect/platform/HttpApi#HttpApi.AnyWithProps": - replacement: "effect/unstable/httpapi/HttpApi#Top" + replacement: "effect/http-api/HttpApi#Top" note: "Use the widened HttpApi type that retains runtime properties." "@effect/platform/HttpApi#make": - replacement: "effect/unstable/httpapi/HttpApi#make" + replacement: "effect/http-api/HttpApi#make" note: "The constructor remains, but API-wide error and service parameters were removed; declare errors on endpoints and attach middleware." "@effect/platform/HttpApi#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml index 35231d5870..bb661b3aee 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml @@ -1,26 +1,26 @@ "@effect/platform/HttpApiBuilder#api": - replacement: "effect/unstable/httpapi/HttpApiBuilder#layer" + replacement: "effect/http-api/HttpApiBuilder#layer" note: "Use layer(api) and provide the group layers; it registers the completed API with HttpRouter." "@effect/platform/HttpApiBuilder#buildMiddleware": replacement: "none" note: "API-wide middleware assembly was removed. Declared HttpApiMiddleware services are applied while routes are built; use HttpRouter.middleware for additional middleware." "@effect/platform/HttpApiBuilder#group": - replacement: "effect/unstable/httpapi/HttpApiBuilder#group" + replacement: "effect/http-api/HttpApiBuilder#group" note: "The group layer remains; names are now identifiers and API/group global error channels are gone." "@effect/platform/HttpApiBuilder#handler": - replacement: "effect/unstable/httpapi/HttpApiBuilder#handler" + replacement: "effect/http-api/HttpApiBuilder#handler" note: "The typed callback helper remains; names are now identifiers and API/group global error channels are gone. Pass the returned callback to handlers.handle." "@effect/platform/HttpApiBuilder#Handlers": - replacement: "effect/unstable/httpapi/HttpApiBuilder#Handlers" + replacement: "effect/http-api/HttpApiBuilder#Handlers" note: "Handlers now tracks an endpoint map and handled identifiers. Prefer Handlers.FromGroup." "@effect/platform/HttpApiBuilder#Handlers.Error": - replacement: "effect/unstable/httpapi/HttpApiBuilder#Handlers.Error" + replacement: "effect/http-api/HttpApiBuilder#Handlers.Error" note: "The helper remains and extracts the error channel of an effectful group-builder return." "@effect/platform/HttpApiBuilder#Handlers.Middleware": replacement: "none" note: "The handler-internal HttpApp middleware alias was removed. Use HttpRouter.middleware inference or HttpRouter.middleware.Fn." "@effect/platform/HttpApiBuilder#Handlers.ValidateReturn": - replacement: "effect/unstable/httpapi/HttpApiBuilder#Handlers.ValidateReturn" + replacement: "effect/http-api/HttpApiBuilder#Handlers.ValidateReturn" note: "The validator remains and now checks the endpoint map against handled identifiers." "@effect/platform/HttpApiBuilder#HandlersTypeId": replacement: "none" @@ -29,23 +29,23 @@ replacement: "none" note: "The API-specific middleware service tag was removed. Declared HttpApiMiddleware services are applied while routes are built; use HttpRouter.middleware for additional global middleware." "@effect/platform/HttpApiBuilder#httpApp": - replacement: "effect/unstable/http/HttpRouter#toHttpEffect" + replacement: "effect/http/HttpRouter#toHttpEffect" note: "Build the application from the assembled API route layer; HTTP apps are Effects in v4." "@effect/platform/HttpApiBuilder#middleware": - replacement: "effect/unstable/http/HttpRouter#middleware" + replacement: "effect/http/HttpRouter#middleware" note: "Use router effect middleware and provide its layer to the API route layer; global middleware can target all router routes." "@effect/platform/HttpApiBuilder#middlewareCors": - replacement: "effect/unstable/http/HttpRouter#cors" + replacement: "effect/http/HttpRouter#cors" note: "Use the router CORS layer, or provide route-scoped HttpMiddleware.cors through HttpRouter.middleware." "@effect/platform/HttpApiBuilder#MiddlewareFn": - replacement: "effect/unstable/http/HttpRouter#middleware.Fn" + replacement: "effect/http/HttpRouter#middleware.Fn" note: "HTTP apps are Effects in v4; use the router middleware function type or infer it through HttpRouter.middleware." "@effect/platform/HttpApiBuilder#middlewareOpenApi": - replacement: "effect/unstable/httpapi/HttpApiBuilder#layer" + replacement: "effect/http-api/HttpApiBuilder#layer" note: "Set openapiPath in layer(api, options). The additionalPropertiesStrategy option was removed." "@effect/platform/HttpApiBuilder#Router": - replacement: "effect/unstable/http/HttpRouter#HttpRouter" + replacement: "effect/http/HttpRouter#HttpRouter" note: "The API-specific router tag was removed; API and group layers register with the shared HttpRouter service." "@effect/platform/HttpApiBuilder#toWebHandler": - replacement: "effect/unstable/http/HttpRouter#toWebHandler" + replacement: "effect/http/HttpRouter#toWebHandler" note: "Pass the assembled API route layer to HttpRouter.toWebHandler; the handler and dispose lifecycle is retained." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml index 92cf124c3b..3d0fcbe365 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml @@ -1,12 +1,12 @@ "@effect/platform/HttpApiClient#Client.Method": - replacement: "effect/unstable/httpapi/HttpApiClient#Client.Method" + replacement: "effect/http-api/HttpApiClient#Client.Method" note: "The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse." "@effect/platform/HttpApiClient#endpoint": - replacement: "effect/unstable/httpapi/HttpApiClient#endpoint" + replacement: "effect/http-api/HttpApiClient#endpoint" note: "The endpoint client remains, selected by group and endpoint identifiers and using v4 request and responseMode fields." "@effect/platform/HttpApiClient#make": - replacement: "effect/unstable/httpapi/HttpApiClient#make" + replacement: "effect/http-api/HttpApiClient#make" note: "The generated client remains; errors and services are now derived per endpoint and middleware." "@effect/platform/HttpApiClient#makeWith": - replacement: "effect/unstable/httpapi/HttpApiClient#makeWith" + replacement: "effect/http-api/HttpApiClient#makeWith" note: "The supplied-HttpClient constructor remains and now requires endpoint client-middleware services." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml index 5c8f3b0be1..ca1773e004 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml @@ -1,14 +1,14 @@ "@effect/platform/HttpApiEndpoint#get": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#get" + replacement: "effect/http-api/HttpApiEndpoint#get" note: "Use get(identifier, path, options?); tagged templates and fluent schema setters were removed." "@effect/platform/HttpApiEndpoint#head": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#head" + replacement: "effect/http-api/HttpApiEndpoint#head" note: "Use head(identifier, path, options?); tagged templates and fluent schema setters were removed." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#HttpApiEndpoint" + replacement: "effect/http-api/HttpApiEndpoint#HttpApiEndpoint" note: "The model remains, but its generics now carry path literals, schemas, middleware, and middleware services." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.AddContext": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#AddMiddleware" + replacement: "effect/http-api/HttpApiEndpoint#AddMiddleware" note: "Use AddMiddleware to add a middleware identifier and compute its service transformation." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.AddError": replacement: "none" @@ -17,34 +17,34 @@ replacement: "none" note: "The tagged-template constructor type was removed; use HttpApiEndpoint.make(method)(identifier, path, options?)." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.Context": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ServerServices" + replacement: "effect/http-api/HttpApiEndpoint#ServerServices" note: "Use ServerServices for handler requirements; middleware IDs and extra requirements have separate extractors." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ContextWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ServerServicesWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#ServerServicesWithIdentifier" note: "Name became Identifier; combine with middleware extractors when the complete handler requirement union is needed." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.Error": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Errors" + replacement: "effect/http-api/HttpApiEndpoint#Errors" note: "Use Errors for the decoded endpoint and middleware error union; v4 Error extracts the schema." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ErrorContext": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/http-api/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode" note: "The single schema context split into server encoding and client decoding services." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ErrorContextWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/http-api/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode" note: "Select the endpoint with WithIdentifier, then apply the encode or decode service extractor." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ErrorWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ErrorsWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#ErrorsWithIdentifier" note: "Renamed for identifier and returns the decoded endpoint plus middleware error union." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ExcludeName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ExcludeIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#ExcludeIdentifier" note: "Direct rename from name to identifier." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ExtractPath": replacement: "none" note: "Tagged-template path extraction was removed. Put a params schema or field record in constructor option params." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.HandlerRawWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#HandlerRawWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#HandlerRawWithIdentifier" note: "Direct rename; raw request fields are now params and query." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.HandlerWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#HandlerWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#HandlerWithIdentifier" note: "Direct rename from name to identifier." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.OptionalTypePropertySignature": replacement: "none" @@ -53,52 +53,52 @@ replacement: "none" note: "Removed with tagged-template path extraction; declare endpoint params explicitly." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.PathParsed": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Params" + replacement: "effect/http-api/HttpApiEndpoint#Params" note: "Path data became params; Params extracts the schema, so use Params[\"Type\"] for decoded data." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.Payload": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Payload" + replacement: "effect/http-api/HttpApiEndpoint#Payload" note: "The name remains but now extracts the schema; use Payload[\"Type\"] for buffered decoded data." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.Success": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#SuccessWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#SuccessWithIdentifier" note: "Use SuccessWithIdentifier for the decoded, stream-aware result; v4 Success extracts the schema." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.SuccessWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#SuccessWithIdentifier" + replacement: "effect/http-api/HttpApiEndpoint#SuccessWithIdentifier" note: "Direct rename from name to identifier; the result remains decoded and stream-aware." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.UrlParams": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Query" + replacement: "effect/http-api/HttpApiEndpoint#Query" note: "urlParams became query; Query extracts the schema, so use Query[\"Type\"] for decoded data." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ValidateHeaders": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#HeadersConstraint" + replacement: "effect/http-api/HttpApiEndpoint#HeadersConstraint" note: "Validation moved from an intersection helper to a constructor generic constraint." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ValidateParams": replacement: "none" note: "Tagged-template interpolation validation was removed; params are declared explicitly in options.params." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ValidatePath": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#ParamsConstraint" + replacement: "effect/http-api/HttpApiEndpoint#ParamsConstraint" note: "path became params and validation is now a constructor constraint." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ValidatePayload": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#PayloadConstraint" + replacement: "effect/http-api/HttpApiEndpoint#PayloadConstraint" note: "Payload validation is now a method-sensitive constructor constraint." "@effect/platform/HttpApiEndpoint#HttpApiEndpoint.ValidateUrlParams": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#QueryConstraint" + replacement: "effect/http-api/HttpApiEndpoint#QueryConstraint" note: "urlParams became query and validation is now a constructor constraint." "@effect/platform/HttpApiEndpoint#make": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#make" + replacement: "effect/http-api/HttpApiEndpoint#make" note: "The factory remains but now requires identifier, path, and options and applies codecs unless disabled." "@effect/platform/HttpApiEndpoint#options": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#options" + replacement: "effect/http-api/HttpApiEndpoint#options" note: "Same HTTP method constructor with the new identifier, path, and options signature." "@effect/platform/HttpApiEndpoint#patch": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#patch" + replacement: "effect/http-api/HttpApiEndpoint#patch" note: "Same HTTP method constructor with the new identifier, path, and options signature." "@effect/platform/HttpApiEndpoint#PathSegment": - replacement: "effect/unstable/http/HttpRouter#PathInput" + replacement: "effect/http/HttpRouter#PathInput" note: "Path input moved to the shared router and is generalized to slash-prefixed paths or wildcard." "@effect/platform/HttpApiEndpoint#post": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#post" + replacement: "effect/http-api/HttpApiEndpoint#post" note: "Same HTTP method constructor with the new identifier, path, and options signature." "@effect/platform/HttpApiEndpoint#put": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#put" + replacement: "effect/http-api/HttpApiEndpoint#put" note: "Same HTTP method constructor with the new identifier, path, and options signature." "@effect/platform/HttpApiEndpoint#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiError.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiError.yaml index 2d35327770..04ae0df617 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiError.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiError.yaml @@ -1,12 +1,12 @@ "@effect/platform/HttpApiError#Forbidden": - replacement: "effect/unstable/httpapi/HttpApiError#ForbiddenNoContent" + replacement: "effect/http-api/HttpApiError#ForbiddenNoContent" note: "Use ForbiddenNoContent to preserve the empty 403 wire schema; Forbidden now has a JSON-tagged body." "@effect/platform/HttpApiError#HttpApiDecodeError": - replacement: "effect/unstable/httpapi/HttpApiError#HttpApiSchemaError" + replacement: "effect/http-api/HttpApiError#HttpApiSchemaError" note: "Validation now stores kind and a SchemaError cause and is a defect unless transformed by schema-error middleware." "@effect/platform/HttpApiError#Issue": replacement: "effect/SchemaIssue#Issue" note: "Structured failures now live at HttpApiSchemaError.cause.issue; format them explicitly when a flat external list is needed." "@effect/platform/HttpApiError#TypeId": - replacement: "effect/unstable/httpapi/HttpApiError#HttpApiSchemaErrorTypeId" + replacement: "effect/http-api/HttpApiError#HttpApiSchemaErrorTypeId" note: "The old module symbol is gone; prefer HttpApiSchemaError.is for runtime narrowing." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml index 5297e522dd..28109a291b 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml @@ -1,17 +1,17 @@ "@effect/platform/HttpApiGroup#ApiGroup": - replacement: "effect/unstable/httpapi/HttpApiGroup#Service" + replacement: "effect/http-api/HttpApiGroup#Service" note: "Renamed; the service field and type parameter are now identifier rather than name." "@effect/platform/HttpApiGroup#HttpApiGroup.AddContext": replacement: "none" note: "Groups no longer carry arbitrary context. Use AddMiddleware for middleware service transformations." "@effect/platform/HttpApiGroup#HttpApiGroup.Any": - replacement: "effect/unstable/httpapi/HttpApiGroup#Constraint" + replacement: "effect/http-api/HttpApiGroup#Constraint" note: "Renamed widened structural constraint." "@effect/platform/HttpApiGroup#HttpApiGroup.AnyWithProps": - replacement: "effect/unstable/httpapi/HttpApiGroup#Top" + replacement: "effect/http-api/HttpApiGroup#Top" note: "Renamed widened runtime-property type." "@effect/platform/HttpApiGroup#HttpApiGroup.ClientContext": - replacement: "effect/unstable/httpapi/HttpApiGroup#ClientServices / ErrorServicesDecode / MiddlewareClient" + replacement: "effect/http-api/HttpApiGroup#ClientServices / ErrorServicesDecode / MiddlewareClient" note: "Client schema services and required client middleware are separate extractors in v4." "@effect/platform/HttpApiGroup#HttpApiGroup.Context": replacement: "none" @@ -20,34 +20,34 @@ replacement: "none" note: "Select with WithIdentifier and derive endpoint server requirements; groups no longer have a context generic." "@effect/platform/HttpApiGroup#HttpApiGroup.EndpointsWithName": - replacement: "effect/unstable/httpapi/HttpApiGroup#EndpointsWithIdentifier" + replacement: "effect/http-api/HttpApiGroup#EndpointsWithIdentifier" note: "Direct rename from name to identifier." "@effect/platform/HttpApiGroup#HttpApiGroup.Error": replacement: "none" note: "Group-level errors were removed. Declare shared errors on each endpoint or through middleware." "@effect/platform/HttpApiGroup#HttpApiGroup.ErrorContext": - replacement: "effect/unstable/httpapi/HttpApiGroup#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/http-api/HttpApiGroup#ErrorServicesEncode / ErrorServicesDecode" note: "The closest endpoint-error aggregate splits server encoding from client decoding services." "@effect/platform/HttpApiGroup#HttpApiGroup.ErrorWithName": replacement: "none" note: "Group-level errors were removed; select with WithIdentifier and inspect Errors over the selected endpoints." "@effect/platform/HttpApiGroup#HttpApiGroup.Middleware": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Middleware" + replacement: "effect/http-api/HttpApiEndpoint#Middleware" note: "Middleware is attached to the endpoints present when group.middleware is called; extract it from group endpoints." "@effect/platform/HttpApiGroup#HttpApiGroup.MiddlewareWithName": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#Middleware" + replacement: "effect/http-api/HttpApiEndpoint#Middleware" note: "Select the group with WithIdentifier, get its endpoints, then apply the endpoint Middleware extractor." "@effect/platform/HttpApiGroup#HttpApiGroup.Provides": - replacement: "effect/unstable/httpapi/HttpApiGroup#MiddlewareProvides" + replacement: "effect/http-api/HttpApiGroup#MiddlewareProvides" note: "Renamed; derives provided services from endpoint middleware." "@effect/platform/HttpApiGroup#HttpApiGroup.ToService": - replacement: "effect/unstable/httpapi/HttpApiGroup#ToService" + replacement: "effect/http-api/HttpApiGroup#ToService" note: "Same role and now produces Service." "@effect/platform/HttpApiGroup#HttpApiGroup.WithName": - replacement: "effect/unstable/httpapi/HttpApiGroup#WithIdentifier" + replacement: "effect/http-api/HttpApiGroup#WithIdentifier" note: "Direct rename from name to identifier." "@effect/platform/HttpApiGroup#make": - replacement: "effect/unstable/httpapi/HttpApiGroup#make" + replacement: "effect/http-api/HttpApiGroup#make" note: "The constructor remains; group error and context generics are gone and add is variadic." "@effect/platform/HttpApiGroup#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml index d0389a5545..d99d884cbf 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml @@ -1,59 +1,59 @@ "@effect/platform/HttpApiMiddleware#HttpApiMiddleware": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#HttpApiMiddleware" + replacement: "effect/http-api/HttpApiMiddleware#HttpApiMiddleware" note: "The model remains but now wraps the response effect and carries provided services, an error schema, and required services." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.Any": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#AnyService" + replacement: "effect/http-api/HttpApiMiddleware#AnyService" note: "Renamed widened middleware service-key shape." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.AnyId": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#AnyId" + replacement: "effect/http-api/HttpApiMiddleware#AnyId" note: "Same name; metadata now includes provided and required services, error schema, client error, and client requirement." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.Error": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Error" + replacement: "effect/http-api/HttpApiMiddleware#Error" note: "Same name and now derives the decoded type from the configured error schema." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.ErrorContext": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/http-api/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode" note: "The single schema context split into server encoding and client decoding services." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.Only": replacement: "Extract" note: "The helper was removed because middleware IDs are explicit; use Extract when the direct filter is still needed." "@effect/platform/HttpApiMiddleware#HttpApiMiddleware.Provides": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Provides" + replacement: "effect/http-api/HttpApiMiddleware#Provides" note: "Same name and reads the expanded v4 middleware ID metadata." "@effect/platform/HttpApiMiddleware#SecurityTypeId": replacement: "none" note: "The marker is private; use HttpApiMiddleware.isSecurity." "@effect/platform/HttpApiMiddleware#Tag": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Service" + replacement: "effect/http-api/HttpApiMiddleware#Service" note: "Renamed and redesigned; use error, requires, provides, clientError, and requiredForClient configuration." "@effect/platform/HttpApiMiddleware#TagClass": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#ServiceClass" + replacement: "effect/http-api/HttpApiMiddleware#ServiceClass" note: "Renamed class type with the new two-stage type configuration and wrapping service shape." "@effect/platform/HttpApiMiddleware#TagClass.BaseSecurity": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#ServiceClass" + replacement: "effect/http-api/HttpApiMiddleware#ServiceClass" note: "Security is conditional metadata on ServiceClass; there is no separate public base interface." "@effect/platform/HttpApiMiddleware#TagClass.Failure": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Error" + replacement: "effect/http-api/HttpApiMiddleware#Error" note: "failure terminology became error; apply the extractor to the middleware ID." "@effect/platform/HttpApiMiddleware#TagClass.FailureContext": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/http-api/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode" note: "Failure schema services split by server encoding and client decoding direction." "@effect/platform/HttpApiMiddleware#TagClass.FailureSchema": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#ErrorSchema" + replacement: "effect/http-api/HttpApiMiddleware#ErrorSchema" note: "Renamed and applied to the middleware ID rather than constructor options." "@effect/platform/HttpApiMiddleware#TagClass.FailureService": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Error" + replacement: "effect/http-api/HttpApiMiddleware#Error" note: "Use the decoded error extractor; optional middleware fallback was removed." "@effect/platform/HttpApiMiddleware#TagClass.Optional": replacement: "none" note: "Optional declaration and fallback-on-failure behavior were removed; model fallback in the wrapping middleware." "@effect/platform/HttpApiMiddleware#TagClass.Provides": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#Provides" + replacement: "effect/http-api/HttpApiMiddleware#Provides" note: "Moved to the module level and applied to the middleware ID." "@effect/platform/HttpApiMiddleware#TagClassAny": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#AnyService" + replacement: "effect/http-api/HttpApiMiddleware#AnyService" note: "Renamed widened service-key type." "@effect/platform/HttpApiMiddleware#TagClassSecurityAny": - replacement: "effect/unstable/httpapi/HttpApiMiddleware#AnyServiceSecurity" + replacement: "effect/http-api/HttpApiMiddleware#AnyServiceSecurity" note: "Renamed widened security service-key type." "@effect/platform/HttpApiMiddleware#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml index a6853fcafc..31b13089c7 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml @@ -1,9 +1,9 @@ "@effect/platform/HttpApiScalar#layer": - replacement: "effect/unstable/httpapi/HttpApiScalar#layer" + replacement: "effect/http-api/HttpApiScalar#layer" note: "Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter." "@effect/platform/HttpApiScalar#layerHttpLayerRouter": - replacement: "effect/unstable/httpapi/HttpApiScalar#layer" + replacement: "effect/http-api/HttpApiScalar#layer" note: "The duplicate was removed. Pass options.api as the first layer argument and the remaining Scalar options second." "@effect/platform/HttpApiScalar#layerHttpLayerRouterCdn": - replacement: "effect/unstable/httpapi/HttpApiScalar#layerCdn" + replacement: "effect/http-api/HttpApiScalar#layerCdn" note: "Use the explicit-api CDN layer with path, version, and Scalar options." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml index 3f0cc5009d..c8611c28dd 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml @@ -1,32 +1,32 @@ "@effect/platform/HttpApiSchema#AnnotationEmptyDecodeable": - replacement: "effect/unstable/httpapi/HttpApiSchema#asNoContent" + replacement: "effect/http-api/HttpApiSchema#asNoContent" note: "The public marker was removed; represent no-content decoding structurally with asNoContent({ decode })." "@effect/platform/HttpApiSchema#AnnotationEncoding": - replacement: "effect/unstable/httpapi/HttpApiSchema#asJson / asFormUrlEncoded / asText / asUint8Array" + replacement: "effect/http-api/HttpApiSchema#asJson / asFormUrlEncoded / asText / asUint8Array" note: "The key is internal; select encoding with a public combinator." "@effect/platform/HttpApiSchema#AnnotationMultipart": - replacement: "effect/unstable/httpapi/HttpApiSchema#asMultipart" + replacement: "effect/http-api/HttpApiSchema#asMultipart" note: "The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator." "@effect/platform/HttpApiSchema#AnnotationMultipartStream": - replacement: "effect/unstable/httpapi/HttpApiSchema#asMultipartStream" + replacement: "effect/http-api/HttpApiSchema#asMultipartStream" note: "The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator." "@effect/platform/HttpApiSchema#AnnotationParam": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#params" + replacement: "effect/http-api/HttpApiEndpoint#params" note: "Path names now live in the router path and schemas in endpoint option params, not schema annotations." "@effect/platform/HttpApiSchema#annotations": replacement: "effect/Schema#annotate" note: "Schema annotations became annotate; set httpApiStatus directly or prefer HttpApiSchema.status for status only." "@effect/platform/HttpApiSchema#AnnotationStatus": - replacement: "effect/unstable/httpapi/HttpApiSchema#status" + replacement: "effect/http-api/HttpApiSchema#status" note: "The public symbol was removed; apply status(code), which uses the httpApiStatus schema annotation." "@effect/platform/HttpApiSchema#asEmpty": - replacement: "effect/unstable/httpapi/HttpApiSchema#asNoContent" + replacement: "effect/http-api/HttpApiSchema#asNoContent" note: "Use schema.pipe(asNoContent({ decode }), status(code)); status is now a separate combinator." "@effect/platform/HttpApiSchema#deunionize": replacement: "none" note: "Pass schema arrays to endpoint success, error, and body alternatives so each member retains status and content type." "@effect/platform/HttpApiSchema#Empty": - replacement: "effect/unstable/httpapi/HttpApiSchema#Empty" + replacement: "effect/http-api/HttpApiSchema#Empty" note: "The API remains and returns Schema.Void annotated with the supplied status." "@effect/platform/HttpApiSchema#EmptyError": replacement: "effect/Schema#Error" @@ -41,22 +41,22 @@ replacement: "none" note: "Removed with EmptyError; do not recreate the old Unify marker." "@effect/platform/HttpApiSchema#Encoding": - replacement: "effect/unstable/httpapi/HttpApiSchema#Encoding" + replacement: "effect/http-api/HttpApiSchema#Encoding" note: "The name remains but is now a discriminated PayloadEncoding or ResponseEncoding union; prefer public as* combinators." "@effect/platform/HttpApiSchema#extractAnnotations": replacement: "none" note: "The internal symbol-copy helper was removed; HTTP metadata is schema-native and resolved through AST traversal." "@effect/platform/HttpApiSchema#getEmptyDecodeable": - replacement: "effect/unstable/httpapi/HttpApiSchema#isNoContent" + replacement: "effect/http-api/HttpApiSchema#isNoContent" note: "Use isNoContent only to test bodylessness; decodeability is structural and has no exact query replacement." "@effect/platform/HttpApiSchema#getEncoding": - replacement: "effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding / getResponseEncoding" + replacement: "effect/http-api/HttpApiSchema#getPayloadEncoding / getResponseEncoding" note: "Encoding lookup split by direction; application code should normally use public as* combinators." "@effect/platform/HttpApiSchema#getMultipart": - replacement: "effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding" + replacement: "effect/http-api/HttpApiSchema#getPayloadEncoding" note: "Narrow the payload encoding to Multipart with buffered mode; multipart limits are on the encoding value." "@effect/platform/HttpApiSchema#getMultipartStream": - replacement: "effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding" + replacement: "effect/http-api/HttpApiSchema#getPayloadEncoding" note: "Narrow the payload encoding to Multipart with stream mode; multipart limits are on the encoding value." "@effect/platform/HttpApiSchema#getParam": replacement: "none" @@ -65,35 +65,35 @@ replacement: "effect/SchemaAST#resolveAt" note: "Resolve the httpApiStatus annotation directly, or prefer getStatusSuccess and getStatusError for response logic." "@effect/platform/HttpApiSchema#getStatusError": - replacement: "effect/unstable/httpapi/HttpApiSchema#getStatusError" + replacement: "effect/http-api/HttpApiSchema#getStatusError" note: "The helper remains but accepts an AST and defaults to 500." "@effect/platform/HttpApiSchema#getStatusErrorAST": - replacement: "effect/unstable/httpapi/HttpApiSchema#getStatusError" + replacement: "effect/http-api/HttpApiSchema#getStatusError" note: "The AST suffix collapsed into the sole helper, which defaults to 500." "@effect/platform/HttpApiSchema#getStatusSuccess": - replacement: "effect/unstable/httpapi/HttpApiSchema#getStatusSuccess" + replacement: "effect/http-api/HttpApiSchema#getStatusSuccess" note: "The helper remains but accepts an AST; bare Schema.Void now defaults to 200, so use Empty(204) for 204." "@effect/platform/HttpApiSchema#getStatusSuccessAST": - replacement: "effect/unstable/httpapi/HttpApiSchema#getStatusSuccess" + replacement: "effect/http-api/HttpApiSchema#getStatusSuccess" note: "The AST suffix collapsed into the sole helper; bare Schema.Void no longer implies 204." "@effect/platform/HttpApiSchema#Multipart": - replacement: "effect/unstable/httpapi/HttpApiSchema#asMultipart" + replacement: "effect/http-api/HttpApiSchema#asMultipart" note: "The type and constructor became a curried schema combinator: schema.pipe(asMultipart(options))." "@effect/platform/HttpApiSchema#MultipartStream": - replacement: "effect/unstable/httpapi/HttpApiSchema#asMultipartStream" + replacement: "effect/http-api/HttpApiSchema#asMultipartStream" note: "The type and constructor became a curried schema combinator." "@effect/platform/HttpApiSchema#param": - replacement: "effect/unstable/httpapi/HttpApiEndpoint#params" + replacement: "effect/http-api/HttpApiEndpoint#params" note: "Use a literal /:name path and the matching field in endpoint constructor option params." "@effect/platform/HttpApiSchema#Text": - replacement: "effect/unstable/httpapi/HttpApiSchema#asText" + replacement: "effect/http-api/HttpApiSchema#asText" note: "Apply the encoding combinator to Schema.String instead of using a dedicated constructor." "@effect/platform/HttpApiSchema#Uint8Array": - replacement: "effect/unstable/httpapi/HttpApiSchema#asUint8Array" + replacement: "effect/http-api/HttpApiSchema#asUint8Array" note: "Apply the encoding combinator to Schema.Uint8Array instead of using a dedicated constructor." "@effect/platform/HttpApiSchema#UnionUnify": replacement: "effect/Schema#Union" note: "Use Schema.Union([self, that]); for endpoint alternatives, pass the schema array directly to preserve metadata." "@effect/platform/HttpApiSchema#withEncoding": - replacement: "effect/unstable/httpapi/HttpApiSchema#asJson / asFormUrlEncoded / asUint8Array / asText" + replacement: "effect/http-api/HttpApiSchema#asJson / asFormUrlEncoded / asUint8Array / asText" note: "Replace the generic kind with the matching public curried encoding combinator." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml index f29b17c5a5..f850f4eb36 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml @@ -1,14 +1,14 @@ "@effect/platform/HttpApiSecurity#annotate": - replacement: "effect/unstable/httpapi/HttpApiSecurity#annotate" + replacement: "effect/http-api/HttpApiSecurity#annotate" note: "The combinator remains; its key is now the v4 Context.Key abstraction." "@effect/platform/HttpApiSecurity#annotateContext": - replacement: "effect/unstable/httpapi/HttpApiSecurity#annotateMerge" + replacement: "effect/http-api/HttpApiSecurity#annotateMerge" note: "Renamed; it still merges a Context into existing OpenAPI annotations." "@effect/platform/HttpApiSecurity#Bearer": - replacement: "effect/unstable/httpapi/HttpApiSecurity#Http" + replacement: "effect/http-api/HttpApiSecurity#Http" note: "Bearer was generalized to Http with scheme Bearer; the value-level bearer singleton remains." "@effect/platform/HttpApiSecurity#HttpApiSecurity.Type": - replacement: "effect/unstable/httpapi/HttpApiSecurity#HttpApiSecurity.Type" + replacement: "effect/http-api/HttpApiSecurity#HttpApiSecurity.Type" note: "Unchanged after the module move; still extracts the credential type." "@effect/platform/HttpApiSecurity#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml b/repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml index 170c2c4148..f6722e9964 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml @@ -1,6 +1,6 @@ "@effect/platform/HttpApiSwagger#layer": - replacement: "effect/unstable/httpapi/HttpApiSwagger#layer" + replacement: "effect/http-api/HttpApiSwagger#layer" note: "Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter." "@effect/platform/HttpApiSwagger#layerHttpLayerRouter": - replacement: "effect/unstable/httpapi/HttpApiSwagger#layer" + replacement: "effect/http-api/HttpApiSwagger#layer" note: "The duplicate was removed. Pass options.api first and the path option second." diff --git a/repos/effect/migration/annotations/effect__platform__HttpClient.yaml b/repos/effect/migration/annotations/effect__platform__HttpClient.yaml index 6b7033dbb8..0abf1a9e98 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpClient.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpClient.yaml @@ -44,8 +44,8 @@ replacement: "HttpClient.transformResponse(Effect.provideService(HttpClient.TracerPropagationEnabled, enabled))" note: "Provide the renamed propagation reference around response effects." "@effect/platform/HttpClient#tap": - replacement: "effect/unstable/http/HttpClient#tap" + replacement: "effect/http/HttpClient#tap" note: "Moved to the v4 HTTP module with the same response-effect callback and client error/service widening." "@effect/platform/HttpClient#transform": - replacement: "effect/unstable/http/HttpClient#transform" + replacement: "effect/http/HttpClient#transform" note: "Moved to the v4 HTTP module with the same request-aware transformation shape." diff --git a/repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml b/repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml index ef12c88d04..c77bd7663f 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml @@ -1,15 +1,15 @@ "@effect/platform/HttpLayerRouter#addHttpApi": replacement: "HttpApiBuilder.layer" - note: "HTTP API registration moved to effect/unstable/httpapi." + note: "HTTP API registration moved to effect/http-api." "@effect/platform/HttpLayerRouter#cors": replacement: "HttpRouter.cors" note: "HttpLayerRouter was consolidated into v4 HttpRouter." "@effect/platform/HttpLayerRouter#FindMyWay.make": replacement: "FindMyWay.make" - note: "Import FindMyWay from effect/unstable/http." + note: "Import FindMyWay from effect/http." "@effect/platform/HttpLayerRouter#FindMyWay.PathInput": replacement: "FindMyWay.PathInput" - note: "Import FindMyWay from effect/unstable/http." + note: "Import FindMyWay from effect/http." "@effect/platform/HttpLayerRouter#make": replacement: "HttpRouter.make" note: "The layer-oriented router became the sole v4 HttpRouter implementation." diff --git a/repos/effect/migration/annotations/effect__platform__HttpServer.yaml b/repos/effect/migration/annotations/effect__platform__HttpServer.yaml index 12076eaf5c..8f295f9d8d 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpServer.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpServer.yaml @@ -1,5 +1,5 @@ "@effect/platform/HttpServer#Address": - replacement: "effect/unstable/net/NetAddress#SocketAddress" + replacement: "effect/net/NetAddress#SocketAddress" note: "Replaced by the shared concrete internet-or-Unix socket address union." "@effect/platform/HttpServer#addressWith": replacement: "HttpServer.HttpServer.use(({ address }) => effect(address))" @@ -20,14 +20,14 @@ replacement: "none" note: "The unused respond option model was removed with no shared v4 counterpart." "@effect/platform/HttpServer#TcpAddress": - replacement: "effect/unstable/net/NetAddress#InetAddress" + replacement: "effect/net/NetAddress#InetAddress" note: "Replaced by the shared resolved internet-address model; use address and port instead of hostname and port." "@effect/platform/HttpServer#TypeId": replacement: "none" note: "The public TypeId was removed; HttpServer is now a Context.Service class." "@effect/platform/HttpServer#UnixAddress": - replacement: "effect/unstable/net/NetAddress#UnixPathAddress" + replacement: "effect/net/NetAddress#UnixPathAddress" note: "Replaced by the shared Unix filesystem-path address model." "@effect/platform/HttpServer#serve": - replacement: "effect/unstable/http/HttpServer#serve" + replacement: "effect/http/HttpServer#serve" note: "Moved to the v4 HTTP module; the application is now an Effect producing HttpServerResponse rather than the separate HttpApp model." diff --git a/repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml b/repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml index e91e0818b2..f7b2202539 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml +++ b/repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml @@ -42,7 +42,7 @@ note: "Moved unchanged." "@effect/platform/HttpServerResponse#toWeb": replacement: "HttpServerResponse.toWeb" - note: "Retained, but the optional Runtime became an optional Context for stream execution." + note: "Retained, but the optional Runtime became an optional Context for stream execution. For a raw Web Response, outer headers override native headers and cookies append, including when the body is omitted. With withoutBody, the raw status and statusText are preserved unless the outer status is 204, 205, or 304, which takes precedence." "@effect/platform/HttpServerResponse#TypeId": replacement: "HttpServerResponse.isHttpServerResponse" note: "The response brand is private in v4; use the public runtime refinement." diff --git a/repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml b/repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml index ba36c699f7..cc6dd5e2b8 100644 --- a/repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml +++ b/repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml @@ -1,6 +1,6 @@ "@effect/platform/KeyValueStore#KeyValueStore": replacement: "KeyValueStore.KeyValueStore" - note: "The service moved to effect/unstable/persistence/KeyValueStore; missing values now use undefined and operations fail with KeyValueStoreError." + note: "The service moved to effect/persistence/KeyValueStore; missing values now use undefined and operations fail with KeyValueStoreError." "@effect/platform/KeyValueStore#KeyValueStore.AnyStore": replacement: "KeyValueStore.KeyValueStore | KeyValueStore.SchemaStore" note: "The convenience namespace alias was removed; write the store union explicitly when needed." diff --git a/repos/effect/migration/annotations/effect__platform__MsgPack.yaml b/repos/effect/migration/annotations/effect__platform__MsgPack.yaml index e6ff8e67ce..36f7b17b2c 100644 --- a/repos/effect/migration/annotations/effect__platform__MsgPack.yaml +++ b/repos/effect/migration/annotations/effect__platform__MsgPack.yaml @@ -1,5 +1,5 @@ "@effect/platform/MsgPack": - replacement: "effect/unstable/encoding/SchemaBinary" + replacement: "effect/encoding/SchemaBinary" note: "MessagePack support was removed. Schema-aware encode, decode, and duplex now live on SchemaBinary. Untyped MessagePack of unknown values has no replacement." "@effect/platform/MsgPack#duplex": replacement: "SchemaBinary.duplex" diff --git a/repos/effect/migration/annotations/effect__platform__Multipart.yaml b/repos/effect/migration/annotations/effect__platform__Multipart.yaml index 3e1a863458..22e8e79e81 100644 --- a/repos/effect/migration/annotations/effect__platform__Multipart.yaml +++ b/repos/effect/migration/annotations/effect__platform__Multipart.yaml @@ -9,13 +9,13 @@ note: "The schema for persisted multipart files was renamed." "@effect/platform/Multipart#isField": replacement: "Multipart.isField" - note: "The guard remains in effect/unstable/http/Multipart." + note: "The guard remains in effect/http/Multipart." "@effect/platform/Multipart#isFile": replacement: "Multipart.isFile" - note: "The guard remains in effect/unstable/http/Multipart." + note: "The guard remains in effect/http/Multipart." "@effect/platform/Multipart#isPart": replacement: "Multipart.isPart" - note: "The guard remains in effect/unstable/http/Multipart." + note: "The guard remains in effect/http/Multipart." "@effect/platform/Multipart#MaxFieldSize": replacement: "Multipart.MaxFieldSize" note: "Now a Context.Reference; provide a value such as ByteSize.bytes(100)." @@ -27,7 +27,7 @@ note: "The setting remains as a Context.Reference; use undefined rather than Option.none for no limit." "@effect/platform/Multipart#schemaJson": replacement: "Multipart.schemaJson" - note: "The JSON-field decoder remains in effect/unstable/http/Multipart and uses v4 Schema constraints." + note: "The JSON-field decoder remains in effect/http/Multipart and uses v4 Schema constraints." "@effect/platform/Multipart#TypeId": replacement: "typeof Multipart.TypeId" note: "The runtime marker remains exported, but the separate type alias was removed." @@ -50,7 +50,7 @@ replacement: "Effect.provideService(Multipart.MaxParts, count)" note: "Provide the v4 Context.Reference around the effect, converting Option.none to undefined." "@effect/platform/Multipart#makeChannel": - replacement: "effect/unstable/http/Multipart#makeChannel" + replacement: "effect/http/Multipart#makeChannel" note: "The channel constructor moved and no longer accepts bufferSize; input and output chunks use non-empty readonly arrays." "@effect/platform/Multipart#withLimits.Options": replacement: "Multipart.withLimits.Options" diff --git a/repos/effect/migration/annotations/effect__platform__Ndjson.yaml b/repos/effect/migration/annotations/effect__platform__Ndjson.yaml index 9786ff676c..3ceaa0bbd9 100644 --- a/repos/effect/migration/annotations/effect__platform__Ndjson.yaml +++ b/repos/effect/migration/annotations/effect__platform__Ndjson.yaml @@ -1,9 +1,9 @@ "@effect/platform/Ndjson#duplex": replacement: "Ndjson.duplex" - note: "The API moved to effect/unstable/encoding/Ndjson." + note: "The API moved to effect/encoding/Ndjson." "@effect/platform/Ndjson#duplexSchema": replacement: "Ndjson.duplexSchema" - note: "The API moved to effect/unstable/encoding/Ndjson and uses v4 Schema constraints." + note: "The API moved to effect/encoding/Ndjson and uses v4 Schema constraints." "@effect/platform/Ndjson#ErrorTypeId": replacement: "Ndjson.NdjsonError" note: "The public error marker was removed; use the NdjsonError class." diff --git a/repos/effect/migration/annotations/effect__platform__OpenApi.yaml b/repos/effect/migration/annotations/effect__platform__OpenApi.yaml index fe9cb44cf8..1ea80b71e8 100644 --- a/repos/effect/migration/annotations/effect__platform__OpenApi.yaml +++ b/repos/effect/migration/annotations/effect__platform__OpenApi.yaml @@ -2,17 +2,17 @@ replacement: "none" note: "OpenApi.fromApi no longer accepts generation options; standalone JSON Schema generation has a separate additionalProperties option." "@effect/platform/OpenApi#annotations": - replacement: "effect/unstable/httpapi/OpenApi#annotations" + replacement: "effect/http-api/OpenApi#annotations" note: "Same annotation-context helper after the module move." "@effect/platform/OpenApi#Exclude": - replacement: "effect/unstable/httpapi/OpenApi#Exclude" + replacement: "effect/http-api/OpenApi#Exclude" note: "Same annotation key and default; it is now a Context.Reference value." "@effect/platform/OpenApi#fromApi": - replacement: "effect/unstable/httpapi/OpenApi#fromApi" + replacement: "effect/http-api/OpenApi#fromApi" note: "The operation remains and returns OpenAPI 3.1, but the signature is now only fromApi(api)." "@effect/platform/OpenApi#OpenApiSpecContentType": replacement: "string" note: "The closed media-type union was removed so custom and streaming media types are supported." "@effect/platform/OpenApi#Title": - replacement: "effect/unstable/httpapi/OpenApi#Title" + replacement: "effect/http-api/OpenApi#Title" note: "Same annotation role, now implemented as a v4 Context.Service." diff --git a/repos/effect/migration/annotations/effect__platform__Socket.yaml b/repos/effect/migration/annotations/effect__platform__Socket.yaml index ecfcfec2da..f84f28a2d6 100644 --- a/repos/effect/migration/annotations/effect__platform__Socket.yaml +++ b/repos/effect/migration/annotations/effect__platform__Socket.yaml @@ -6,13 +6,13 @@ note: "The send queue was removed. The v4 Socket is pull-based: acquire socket.reader in a scope and pull frame batches; writes apply the transport's native backpressure." "@effect/platform/Socket#layerWebSocket": replacement: "Socket.layerWebSocket" - note: "The constructor remains in effect/unstable/socket/Socket; its URL may now also be an Effect." + note: "The constructor remains in effect/socket/Socket; its URL may now also be an Effect." "@effect/platform/Socket#SocketError": replacement: "Socket.SocketError" note: "The old union became a tagged wrapper around SocketReadError, SocketWriteError, SocketOpenError, or SocketCloseError." "@effect/platform/Socket#SocketErrorTypeId": replacement: "Socket.SocketErrorTypeId" - note: "The error marker remains in effect/unstable/socket/Socket." + note: "The error marker remains in effect/socket/Socket." "@effect/platform/Socket#SocketGenericError": replacement: "Socket.SocketReadError | Socket.SocketWriteError | Socket.SocketOpenError" note: "The generic reason discriminator was replaced by dedicated read, write, and open error classes." @@ -21,16 +21,16 @@ note: "The socket marker remains exported, but the separate type alias was removed." "@effect/platform/Socket#WebSocket": replacement: "Socket.WebSocket" - note: "The opaque service moved to effect/unstable/socket/Socket and is now a Context.Service class for globalThis.WebSocket." + note: "The opaque service moved to effect/socket/Socket and is now a Context.Service class for globalThis.WebSocket." "@effect/platform/Socket#WebSocketConstructor": replacement: "Socket.WebSocketConstructor" - note: "The service moved to effect/unstable/socket/Socket and is now a Context.Service class." + note: "The service moved to effect/socket/Socket and is now a Context.Service class." "@effect/platform/Socket#defaultCloseCodeIsError": replacement: none note: "Sockets no longer classify close codes; every close fails the reader's pull with a SocketError wrapping SocketCloseError. Consumers that treat a close as normal catch the error." "@effect/platform/Socket#fromTransformStream": replacement: "Socket.fromTransformStream" - note: "The constructor remains in effect/unstable/socket/Socket but drops closeCodeIsError; every close fails the reader's pull with a SocketError wrapping SocketCloseError." + note: "The constructor remains in effect/socket/Socket but drops closeCodeIsError; every close fails the reader's pull with a SocketError wrapping SocketCloseError." "@effect/platform/Socket#toChannelMap": replacement: none note: "The v4 Socket read side is an Effect that never completes via Cause.Done; map frames by acquiring Socket.readerBytes or Socket.readerString, or Effect.map the reader from socket.reader, and use Socket.toChannel or Socket.toChannelString for duplex channels." diff --git a/repos/effect/migration/annotations/effect__platform__SocketServer.yaml b/repos/effect/migration/annotations/effect__platform__SocketServer.yaml index 6367ff8e11..a5eef65931 100644 --- a/repos/effect/migration/annotations/effect__platform__SocketServer.yaml +++ b/repos/effect/migration/annotations/effect__platform__SocketServer.yaml @@ -1,12 +1,12 @@ "@effect/platform/SocketServer#Address": - replacement: "effect/unstable/net/NetAddress#SocketAddress" + replacement: "effect/net/NetAddress#SocketAddress" note: "Replaced by the shared concrete internet-or-Unix socket address union." "@effect/platform/SocketServer#ErrorTypeId": replacement: "SocketServer.ErrorTypeId" - note: "The API moved to effect/unstable/socket/SocketServer and retains this name." + note: "The API moved to effect/socket/SocketServer and retains this name." "@effect/platform/SocketServer#TcpAddress": - replacement: "effect/unstable/net/NetAddress#InetAddress" + replacement: "effect/net/NetAddress#InetAddress" note: "Replaced by the shared resolved internet-address model; use address and port instead of hostname and port." "@effect/platform/SocketServer#UnixAddress": - replacement: "effect/unstable/net/NetAddress#UnixPathAddress" + replacement: "effect/net/NetAddress#UnixPathAddress" note: "Replaced by the shared Unix filesystem-path address model." diff --git a/repos/effect/migration/annotations/effect__platform__Template.yaml b/repos/effect/migration/annotations/effect__platform__Template.yaml index 92bdf007f2..edebdb5b00 100644 --- a/repos/effect/migration/annotations/effect__platform__Template.yaml +++ b/repos/effect/migration/annotations/effect__platform__Template.yaml @@ -1,9 +1,9 @@ "@effect/platform/Template#Interpolated.Context": replacement: "Template.Interpolated.Context" - note: "The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values." + note: "The API moved to effect/http/Template; v4 interpolation types also account for Effect values." "@effect/platform/Template#Interpolated.Error": replacement: "Template.Interpolated.Error" - note: "The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values." + note: "The API moved to effect/http/Template; v4 interpolation types also account for Effect values." "@effect/platform/Template#make": replacement: "Template.make" - note: "The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values." + note: "The API moved to effect/http/Template; v4 interpolation types also account for Effect values." diff --git a/repos/effect/migration/annotations/effect__platform__Transferable.yaml b/repos/effect/migration/annotations/effect__platform__Transferable.yaml index 02a5415ee7..a7bdeeced8 100644 --- a/repos/effect/migration/annotations/effect__platform__Transferable.yaml +++ b/repos/effect/migration/annotations/effect__platform__Transferable.yaml @@ -3,7 +3,7 @@ note: "The collector interface is now the service type of the Transferable.Collector Context.Service class." "@effect/platform/Transferable#schema": replacement: "Transferable.schema" - note: "The schema wrapper moved to effect/unstable/workers/Transferable and uses the v4 Schema model." + note: "The schema wrapper moved to effect/workers/Transferable and uses the v4 Schema model." "@effect/platform/Transferable#Uint8Array": replacement: "Transferable.Uint8Array" note: "The transferable Uint8Array schema remains in the moved module." diff --git a/repos/effect/migration/annotations/effect__platform__UrlParams.yaml b/repos/effect/migration/annotations/effect__platform__UrlParams.yaml index 2e4a856dfe..83fa4c75e9 100644 --- a/repos/effect/migration/annotations/effect__platform__UrlParams.yaml +++ b/repos/effect/migration/annotations/effect__platform__UrlParams.yaml @@ -51,4 +51,4 @@ note: "Retained and broadened to accept any UrlParams.Input." "@effect/platform/UrlParams#UrlParams": replacement: "UrlParams.UrlParams" - note: "Import UrlParams from effect/unstable/http. It is now a branded iterable object with a params field rather than a ReadonlyArray; construct it with UrlParams.make or UrlParams.fromInput." + note: "Import UrlParams from effect/http. It is now a branded iterable object with a params field rather than a ReadonlyArray; construct it with UrlParams.make or UrlParams.fromInput." diff --git a/repos/effect/migration/annotations/effect__platform__Worker.yaml b/repos/effect/migration/annotations/effect__platform__Worker.yaml index 21e8d6ae23..96f541b342 100644 --- a/repos/effect/migration/annotations/effect__platform__Worker.yaml +++ b/repos/effect/migration/annotations/effect__platform__Worker.yaml @@ -42,7 +42,7 @@ note: "Pool sizing options moved to the worker RPC protocol; initial messages are provided separately with RpcWorker.layerInitialMessage." "@effect/platform/Worker#Worker": replacement: "Worker.Worker" - note: "The name remains in effect/unstable/workers/Worker, but it is now the low-level send/run abstraction rather than execute/executeEffect." + note: "The name remains in effect/workers/Worker, but it is now the low-level send/run abstraction rather than execute/executeEffect." "@effect/platform/Worker#Worker.Options": replacement: "Worker.Worker[\"run\"] options" note: "Encoding moved to RPC schemas; the low-level run operation only accepts an optional onSpawn effect." diff --git a/repos/effect/migration/annotations/effect__platform__index.yaml b/repos/effect/migration/annotations/effect__platform__index.yaml index ae474455a5..ed574266a9 100644 --- a/repos/effect/migration/annotations/effect__platform__index.yaml +++ b/repos/effect/migration/annotations/effect__platform__index.yaml @@ -1,3 +1,3 @@ "@effect/platform/index": replacement: "none" - note: "The package barrel was removed along with the package; import each module from its new effect location (e.g. effect/FileSystem, effect/unstable/http/HttpClient) per the Import Map." + note: "The package barrel was removed along with the package; import each module from its new effect location (e.g. effect/FileSystem, effect/http/HttpClient) per the Import Map." diff --git a/repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml b/repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml index 6b8aa9c5c0..be6da68a11 100644 --- a/repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml +++ b/repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml @@ -1,3 +1,3 @@ "@effect/printer-ansi/AnsiDoc": replacement: none - note: The @effect/printer-ansi package and its annotated document algebra were removed in v4. Use strings or another pretty-printing library; for Effect CLI help only, use HelpDoc with CliOutput from effect/unstable/cli. + note: The @effect/printer-ansi package and its annotated document algebra were removed in v4. Use strings or another pretty-printing library; for Effect CLI help only, use HelpDoc with CliOutput from effect/cli. diff --git a/repos/effect/migration/annotations/effect__printer__Layout.yaml b/repos/effect/migration/annotations/effect__printer__Layout.yaml index a393f185b4..9e1c3fae66 100644 --- a/repos/effect/migration/annotations/effect__printer__Layout.yaml +++ b/repos/effect/migration/annotations/effect__printer__Layout.yaml @@ -1,3 +1,3 @@ "@effect/printer/Layout": replacement: none - note: The @effect/printer layout pipeline was removed in v4 with no general replacement. Use another pretty-printing library; for Effect CLI output only, use CliOutput from effect/unstable/cli. + note: The @effect/printer layout pipeline was removed in v4 with no general replacement. Use another pretty-printing library; for Effect CLI output only, use CliOutput from effect/cli. diff --git a/repos/effect/migration/annotations/effect__rpc.yaml b/repos/effect/migration/annotations/effect__rpc.yaml index c20087b590..841a8419bd 100644 --- a/repos/effect/migration/annotations/effect__rpc.yaml +++ b/repos/effect/migration/annotations/effect__rpc.yaml @@ -1,3 +1,3 @@ "@effect/rpc": - replacement: "effect/unstable/rpc" - note: "The @effect/rpc package was merged into the effect package; import the effect/unstable/rpc barrel or import specific modules directly (e.g. effect/unstable/rpc/)." + replacement: "effect/rpc" + note: "The @effect/rpc package was merged into the effect package; import the effect/rpc barrel or import specific modules directly (e.g. effect/rpc/)." diff --git a/repos/effect/migration/annotations/effect__rpc__Rpc.yaml b/repos/effect/migration/annotations/effect__rpc__Rpc.yaml index afbdd05e95..5512e9b082 100644 --- a/repos/effect/migration/annotations/effect__rpc__Rpc.yaml +++ b/repos/effect/migration/annotations/effect__rpc__Rpc.yaml @@ -1,11 +1,11 @@ "@effect/rpc/Rpc#AddError": - replacement: "effect/unstable/rpc/Rpc#AddError" + replacement: "effect/rpc/Rpc#AddError" note: "Retained; the added error must now be a Schema.Top and the resulting RPC also preserves its explicit service requirements." "@effect/rpc/Rpc#AddMiddleware": - replacement: "effect/unstable/rpc/Rpc#AddMiddleware" + replacement: "effect/rpc/Rpc#AddMiddleware" note: "Retained; middleware is now an RpcMiddleware.AnyService and its provides/requires metadata updates the RPC service requirements." "@effect/rpc/Rpc#Any": - replacement: "effect/unstable/rpc/Rpc#Any" + replacement: "effect/rpc/Rpc#Any" note: "Retained as the erased RPC shape; use AnyWithProps when schema and middleware fields are required." "@effect/rpc/Rpc#AnySchema": replacement: "Schema.Top" @@ -14,10 +14,10 @@ replacement: "none" note: "RpcGroup no longer converts Schema.TaggedRequest classes into RPCs; declare the contract explicitly with Rpc.make." "@effect/rpc/Rpc#Context": - replacement: "effect/unstable/rpc/Rpc#Services" + replacement: "effect/rpc/Rpc#Services" note: "Schema Context became decoding and encoding services; use Services, or ServicesClient / ServicesServer at the corresponding boundary." "@effect/rpc/Rpc#Error": - replacement: "effect/unstable/rpc/Rpc#Error" + replacement: "effect/rpc/Rpc#Error" note: "Retained; it includes decoded errors contributed by attached middleware." "@effect/rpc/Rpc#ErrorEncoded": replacement: "Rpc.ErrorSchema[\"Encoded\"]" @@ -26,28 +26,28 @@ replacement: "Rpc.ErrorExitSchema[\"Encoded\"]" note: "Use the new exit error schema, which includes stream and middleware errors, then select its Encoded member." "@effect/rpc/Rpc#ErrorSchema": - replacement: "effect/unstable/rpc/Rpc#ErrorSchema" + replacement: "effect/rpc/Rpc#ErrorSchema" note: "Retained; middleware errors now come from each service's error metadata." "@effect/rpc/Rpc#fromTaggedRequest": replacement: "Rpc.make" note: "Automatic TaggedRequest conversion was removed; pass the tag, payload, success, and error schemas explicitly to Rpc.make." "@effect/rpc/Rpc#Handler": - replacement: "effect/unstable/rpc/Rpc#Handler" + replacement: "effect/rpc/Rpc#Handler" note: "Retained; handler metadata now supplies ServerClient, RequestId, headers, and the concrete RPC." "@effect/rpc/Rpc#make": - replacement: "effect/unstable/rpc/Rpc#make" + replacement: "effect/rpc/Rpc#make" note: "Retained; schemas use v4 Schema.Top constraints and the defect option accepts Rpc.DefectSchema." "@effect/rpc/Rpc#Middleware": - replacement: "effect/unstable/rpc/Rpc#Middleware" + replacement: "effect/rpc/Rpc#Middleware" note: "Retained and extracts Context.Service identifiers from the attached middleware services." "@effect/rpc/Rpc#MiddlewareClient": - replacement: "effect/unstable/rpc/Rpc#MiddlewareClient" + replacement: "effect/rpc/Rpc#MiddlewareClient" note: "Retained; required client middleware is derived from services configured with requiredForClient." "@effect/rpc/Rpc#Payload": - replacement: "effect/unstable/rpc/Rpc#Payload" + replacement: "effect/rpc/Rpc#Payload" note: "Retained as the decoded payload type; use PayloadConstructor for the input accepted by generated clients." "@effect/rpc/Rpc#Success": - replacement: "effect/unstable/rpc/Rpc#Success" + replacement: "effect/rpc/Rpc#Success" note: "Retained as the decoded success type." "@effect/rpc/Rpc#SuccessChunkEncoded": replacement: "Rpc.SuccessExitSchema[\"Encoded\"]" @@ -56,10 +56,10 @@ replacement: "Rpc.SuccessExitSchema[\"Encoded\"]" note: "Use the new exit success schema and select its Encoded member; streaming RPC exits use the element schema separately from the terminal void exit." "@effect/rpc/Rpc#SuccessSchema": - replacement: "effect/unstable/rpc/Rpc#SuccessSchema" + replacement: "effect/rpc/Rpc#SuccessSchema" note: "Retained and uses the v4 Schema.Top constraint." "@effect/rpc/Rpc#Tag": - replacement: "effect/unstable/rpc/Rpc#Tag" + replacement: "effect/rpc/Rpc#Tag" note: "Retained and also accounts for the v4 RPC service-requirement parameter." "@effect/rpc/Rpc#TypeId": replacement: "none" @@ -68,8 +68,8 @@ replacement: "none" note: "The wrapper marker is private in v4; use Rpc.isWrapper and the public Wrapper type." "@effect/rpc/Rpc#wrap": - replacement: "effect/unstable/rpc/Rpc#wrap" + replacement: "effect/rpc/Rpc#wrap" note: "Retained after the module move; it still applies fork and uninterruptible handler options, while the return type is now uniformly Rpc.Wrapper." "@effect/rpc/Rpc#SuccessEncoded": - replacement: "effect/unstable/rpc/Rpc#SuccessEncoded" + replacement: "effect/rpc/Rpc#SuccessEncoded" note: "Retained after the module move and now accounts for the RPC's explicit service-requirement parameter." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcClient.yaml b/repos/effect/migration/annotations/effect__rpc__RpcClient.yaml index 99a8723324..0a996d7bce 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcClient.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcClient.yaml @@ -1,17 +1,17 @@ "@effect/rpc/RpcClient#currentHeaders": - replacement: "effect/unstable/rpc/RpcClient#CurrentHeaders" + replacement: "effect/rpc/RpcClient#CurrentHeaders" note: "Renamed and changed from FiberRef to Context.Reference; prefer RpcClient.withHeaders for scoped overrides." "@effect/rpc/RpcClient#layerProtocolHttp": - replacement: "effect/unstable/rpc/RpcClient#layerProtocolHttp" + replacement: "effect/rpc/RpcClient#layerProtocolHttp" note: "Retained; it provides the v4 Protocol from HttpClient and RpcSerialization." "@effect/rpc/RpcClient#make": - replacement: "effect/unstable/rpc/RpcClient#make" + replacement: "effect/rpc/RpcClient#make" note: "Retained; generated calls can now take per-request headers and Context, and include RpcClientError in their error channel." "@effect/rpc/RpcClient#makeProtocolHttp": - replacement: "effect/unstable/rpc/RpcClient#makeProtocolHttp" + replacement: "effect/rpc/RpcClient#makeProtocolHttp" note: "Retained; it creates the Protocol service implementation from an HttpClient." "@effect/rpc/RpcClient#Protocol": - replacement: "effect/unstable/rpc/RpcClient#Protocol" + replacement: "effect/rpc/RpcClient#Protocol" note: "Retained as a Context.Service; custom transports now route multiple client ids through run and send." "@effect/rpc/RpcClient#RpcClient.NonPrefixed": replacement: "none" @@ -23,5 +23,5 @@ replacement: "Effect.flatMap(headers, (value) => RpcClient.withHeaders(effect, value))" note: "withHeaders now accepts Headers.Input synchronously; evaluate effectful headers first and then scope the client effect." "@effect/rpc/RpcClient#RpcClient.From": - replacement: "effect/unstable/rpc/RpcClient#RpcClient.From" + replacement: "effect/rpc/RpcClient#RpcClient.From" note: "Generated clients now preserve full RPC tags as property names, remove the Prefix type parameter, and expose streaming results through the asQueue option instead of asMailbox." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml b/repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml index b26b2af227..05685c726e 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml @@ -1,15 +1,15 @@ "@effect/rpc/RpcGroup#HandlerContext": - replacement: "effect/unstable/rpc/RpcGroup#HandlerServices" + replacement: "effect/rpc/RpcGroup#HandlerServices" note: "Renamed for v4 service terminology and now includes explicit RPC requirements after removing middleware-provided services." "@effect/rpc/RpcGroup#HandlersContext": - replacement: "effect/unstable/rpc/RpcGroup#HandlersServices" + replacement: "effect/rpc/RpcGroup#HandlersServices" note: "Renamed; it unions HandlerServices across the handler object." "@effect/rpc/RpcGroup#make": - replacement: "effect/unstable/rpc/RpcGroup#make" + replacement: "effect/rpc/RpcGroup#make" note: "Retained for explicit Rpc definitions; passing TaggedRequest schema classes for implicit conversion is no longer supported." "@effect/rpc/RpcGroup#TypeId": replacement: "none" note: "The group marker is private in v4; use RpcGroup.Any for an erased group constraint." "@effect/rpc/RpcGroup#Any": - replacement: "effect/unstable/rpc/RpcGroup#Any" + replacement: "effect/rpc/RpcGroup#Any" note: "Moved unchanged as the erased RpcGroup constraint." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml b/repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml index 6694182b52..2e8747920d 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml @@ -1,9 +1,9 @@ "@effect/rpc/RpcMessage#FromServerEncoded": - replacement: "effect/unstable/rpc/RpcMessage#FromServerEncoded" + replacement: "effect/rpc/RpcMessage#FromServerEncoded" note: "The union is retained and now also includes RequestEncoded for server-originated requests and notifications. Handle _tag: \"Request\" when matching exhaustively; isNotification identifies notifications." "@effect/rpc/RpcMessage#RequestIdTypeId": - replacement: "effect/unstable/rpc/RpcMessage#RequestId" + replacement: "effect/rpc/RpcMessage#RequestId" note: "The public symbol marker was removed; use the branded RequestId type and RequestId constructor rather than inspecting its brand." "@effect/rpc/RpcMessage#RequestId": - replacement: "effect/unstable/rpc/RpcMessage#RequestId" + replacement: "effect/rpc/RpcMessage#RequestId" note: "Request ids are now branded string or number values; convert bigint ids before calling the retained RequestId constructor." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml b/repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml index 3fad622b6c..1f70aea0a3 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml @@ -1,45 +1,45 @@ "@effect/rpc/RpcMiddleware#ForClient": - replacement: "effect/unstable/rpc/RpcMiddleware#ForClient" + replacement: "effect/rpc/RpcMiddleware#ForClient" note: "Retained as the marker requirement provided by a client middleware layer." "@effect/rpc/RpcMiddleware#layerClient": - replacement: "effect/unstable/rpc/RpcMiddleware#layerClient" + replacement: "effect/rpc/RpcMiddleware#layerClient" note: "Retained; the client function can now modify the typed Request passed to next and carry a client-only error type." "@effect/rpc/RpcMiddleware#RpcMiddlewareWrap": - replacement: "effect/unstable/rpc/RpcMiddleware#RpcMiddleware" + replacement: "effect/rpc/RpcMiddleware#RpcMiddleware" note: "The wrap and non-wrap shapes were unified; implement a function receiving the handler effect and request options." "@effect/rpc/RpcMiddleware#Tag": - replacement: "effect/unstable/rpc/RpcMiddleware#Service" + replacement: "effect/rpc/RpcMiddleware#Service" note: "Renamed and redesigned with explicit requires, provides, clientError, error, and requiredForClient configuration." "@effect/rpc/RpcMiddleware#TagClass": - replacement: "effect/unstable/rpc/RpcMiddleware#ServiceClass" + replacement: "effect/rpc/RpcMiddleware#ServiceClass" note: "Renamed class type for the v4 Context.Service-based middleware declaration." "@effect/rpc/RpcMiddleware#TagClass.Failure": - replacement: "effect/unstable/rpc/RpcMiddleware#Error" + replacement: "effect/rpc/RpcMiddleware#Error" note: "Failure terminology became error; apply the extractor to the middleware ID." "@effect/rpc/RpcMiddleware#TagClass.FailureContext": - replacement: "effect/unstable/rpc/RpcMiddleware#ErrorServicesEncode / ErrorServicesDecode" + replacement: "effect/rpc/RpcMiddleware#ErrorServicesEncode / ErrorServicesDecode" note: "The single schema context split into server encoding and client decoding services." "@effect/rpc/RpcMiddleware#TagClass.FailureSchema": - replacement: "effect/unstable/rpc/RpcMiddleware#ErrorSchema" + replacement: "effect/rpc/RpcMiddleware#ErrorSchema" note: "Renamed and applied to the middleware ID rather than constructor options." "@effect/rpc/RpcMiddleware#TagClass.FailureService": - replacement: "effect/unstable/rpc/RpcMiddleware#Error" + replacement: "effect/rpc/RpcMiddleware#Error" note: "Use the decoded error extractor; optional middleware fallback was removed." "@effect/rpc/RpcMiddleware#TagClass.Optional": replacement: "none" note: "Optional declaration and fallback-on-failure behavior were removed; model fallback inside the middleware effect." "@effect/rpc/RpcMiddleware#TagClass.Provides": - replacement: "effect/unstable/rpc/RpcMiddleware#Provides" + replacement: "effect/rpc/RpcMiddleware#Provides" note: "Moved to the module level and applied to the middleware ID metadata." "@effect/rpc/RpcMiddleware#TagClass.RequiredForClient": replacement: "RpcMiddleware.ServiceClass[\"requiredForClient\"]" note: "The standalone options extractor was removed; the boolean is exposed directly by the resulting service class." "@effect/rpc/RpcMiddleware#TagClassAny": - replacement: "effect/unstable/rpc/RpcMiddleware#AnyService" + replacement: "effect/rpc/RpcMiddleware#AnyService" note: "Renamed widened middleware service-key shape." "@effect/rpc/RpcMiddleware#TagClassAnyWithProps": - replacement: "effect/unstable/rpc/RpcMiddleware#AnyServiceWithProps" + replacement: "effect/rpc/RpcMiddleware#AnyServiceWithProps" note: "Renamed erased service key whose value has the unified server middleware function shape." "@effect/rpc/RpcMiddleware#TypeId": - replacement: "effect/unstable/rpc/RpcMiddleware#TypeId" + replacement: "effect/rpc/RpcMiddleware#TypeId" note: "Retained as the public middleware metadata marker and now has a string-literal type." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml b/repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml index 6164045dfd..25f4aa9df0 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml @@ -1,14 +1,14 @@ "@effect/rpc/RpcSchema#getStreamSchemas": - replacement: "effect/unstable/rpc/RpcSchema#getStreamSchemas" + replacement: "effect/rpc/RpcSchema#getStreamSchemas" note: "Retained for internal-style schema inspection; pass the schema itself rather than its AST." "@effect/rpc/RpcSchema#isStreamSchema": - replacement: "effect/unstable/rpc/RpcSchema#isStreamSchema" + replacement: "effect/rpc/RpcSchema#isStreamSchema" note: "Retained; it accepts a v4 Schema.Constraint." "@effect/rpc/RpcSchema#isStreamSerializable": replacement: "RpcSchema.isStreamSchema(schema)" note: "The separate WithResult serializability predicate was removed; v4 RPC streaming is identified by its explicit Stream schema." "@effect/rpc/RpcSchema#Stream": - replacement: "effect/unstable/rpc/RpcSchema#Stream" + replacement: "effect/rpc/RpcSchema#Stream" note: "Retained as both the stream schema interface and constructor; error is the second argument and schema services are split by direction." "@effect/rpc/RpcSchema#StreamSchemaId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml b/repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml index fae2c1f87b..6067a9ea71 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml @@ -1,15 +1,15 @@ "@effect/rpc/RpcSerialization#RpcSerializationError": - replacement: "effect/unstable/rpc/RpcSerialization#MaxBufferSizeExceeded" + replacement: "effect/rpc/RpcSerialization#MaxBufferSizeExceeded" note: "Buffer-limit failures now use MaxBufferSizeExceeded. MessagePack-specific decode errors have no counterpart." "@effect/rpc/RpcSerialization#layerMsgPack": - replacement: "effect/unstable/rpc/RpcSerialization#layerSchemaBinary" + replacement: "effect/rpc/RpcSerialization#layerSchemaBinary" note: "MessagePack RPC serialization was removed. Use SchemaBinary, or layerNdjson when you need newline-delimited JSON framing." "@effect/rpc/RpcSerialization#layerMsgPackWith": - replacement: "effect/unstable/rpc/RpcSerialization#layerSchemaBinary" + replacement: "effect/rpc/RpcSerialization#layerSchemaBinary" note: "MessagePack RPC serialization was removed. Pass maxFrameSize to layerSchemaBinary; NDJSON buffer limits remain on layerNdjsonWith." "@effect/rpc/RpcSerialization#makeMsgPack": - replacement: "effect/unstable/rpc/RpcSerialization#layerSchemaBinary" + replacement: "effect/rpc/RpcSerialization#layerSchemaBinary" note: "MessagePack RPC serialization was removed. Construct SchemaBinary serialization with layerSchemaBinary." "@effect/rpc/RpcSerialization#msgPack": - replacement: "effect/unstable/rpc/RpcSerialization#layerSchemaBinary" + replacement: "effect/rpc/RpcSerialization#layerSchemaBinary" note: "The MessagePack RpcSerialization service value was removed. Provide layerSchemaBinary instead." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcServer.yaml b/repos/effect/migration/annotations/effect__rpc__RpcServer.yaml index fdba786855..ff11cd3b74 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcServer.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcServer.yaml @@ -1,51 +1,51 @@ "@effect/rpc/RpcServer#fiberIdClientInterrupt": - replacement: "effect/unstable/rpc/RpcSchema#ClientAbort" + replacement: "effect/rpc/RpcSchema#ClientAbort" note: "The sentinel FiberId was replaced by a Cause annotation; inspect ClientAbort in the interruption cause when client cancellation must be distinguished." "@effect/rpc/RpcServer#fiberIdTransientInterrupt": replacement: "none" note: "The internal transient sentinel was removed; protocol shutdown and disconnect now interrupt with the active parent fiber identity." "@effect/rpc/RpcServer#layerHttpRouter": - replacement: "effect/unstable/rpc/RpcServer#layerHttp" + replacement: "effect/rpc/RpcServer#layerHttp" note: "Renamed; it installs an HTTP or WebSocket RPC route into the v4 HttpRouter service." "@effect/rpc/RpcServer#layerProtocolHttp": - replacement: "effect/unstable/rpc/RpcServer#layerProtocolHttp" + replacement: "effect/rpc/RpcServer#layerProtocolHttp" note: "Retained; v4 has one HttpRouter service and no router tag option." "@effect/rpc/RpcServer#layerProtocolHttpRouter": - replacement: "effect/unstable/rpc/RpcServer#layerProtocolHttp" + replacement: "effect/rpc/RpcServer#layerProtocolHttp" note: "The separate layer-router variant was unified with layerProtocolHttp." "@effect/rpc/RpcServer#layerProtocolWebsocketRouter": - replacement: "effect/unstable/rpc/RpcServer#layerProtocolWebsocket" + replacement: "effect/rpc/RpcServer#layerProtocolWebsocket" note: "Renamed after the HTTP router services were unified." "@effect/rpc/RpcServer#make": - replacement: "effect/unstable/rpc/RpcServer#make" + replacement: "effect/rpc/RpcServer#make" note: "Retained; schema encoding services are now explicit server requirements." "@effect/rpc/RpcServer#makeProtocolHttp": - replacement: "effect/unstable/rpc/RpcServer#makeProtocolHttp" + replacement: "effect/rpc/RpcServer#makeProtocolHttp" note: "Retained; it registers a POST route in the current v4 HttpRouter." "@effect/rpc/RpcServer#makeProtocolHttpRouter": - replacement: "effect/unstable/rpc/RpcServer#makeProtocolHttp" + replacement: "effect/rpc/RpcServer#makeProtocolHttp" note: "The separate router constructor was unified with makeProtocolHttp." "@effect/rpc/RpcServer#makeProtocolWebsocketRouter": - replacement: "effect/unstable/rpc/RpcServer#makeProtocolWebsocket" + replacement: "effect/rpc/RpcServer#makeProtocolWebsocket" note: "Renamed after the HTTP router services were unified." "@effect/rpc/RpcServer#makeProtocolWithHttpApp": - replacement: "effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffect" + replacement: "effect/rpc/RpcServer#makeProtocolWithHttpEffect" note: "HttpApp became HttpEffect; the result contains protocol and httpEffect." "@effect/rpc/RpcServer#makeProtocolWithHttpAppWebsocket": - replacement: "effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket" + replacement: "effect/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket" note: "HttpApp became HttpEffect; the result contains the WebSocket protocol and upgrade effect." "@effect/rpc/RpcServer#Protocol": - replacement: "effect/unstable/rpc/RpcServer#Protocol" + replacement: "effect/rpc/RpcServer#Protocol" note: "Retained as a Context.Service; custom transports now expose a disconnect queue, explicit capability flags, and codecFor for schema-aware payload and exit encoding." "@effect/rpc/RpcServer#toHttpApp": - replacement: "effect/unstable/rpc/RpcServer#toHttpEffect" + replacement: "effect/rpc/RpcServer#toHttpEffect" note: "Renamed for the v4 HTTP effect model; it starts the RPC server and returns the request effect." "@effect/rpc/RpcServer#toHttpAppWebsocket": - replacement: "effect/unstable/rpc/RpcServer#toHttpEffectWebsocket" + replacement: "effect/rpc/RpcServer#toHttpEffectWebsocket" note: "Renamed for the v4 HTTP effect model; it returns the WebSocket upgrade effect." "@effect/rpc/RpcServer#toWebHandler": replacement: "HttpRouter.toWebHandler(RpcServer.layerHttp(options).pipe(Layer.provide(options.layer)))" note: "The RPC convenience wrapper was removed; build the RPC route layer and convert it with the generic v4 HttpRouter web-handler adapter." "@effect/rpc/RpcServer#layer": - replacement: "effect/unstable/rpc/RpcServer#layer" + replacement: "effect/rpc/RpcServer#layer" note: "Moved to core Effect; server requirements are now derived with Rpc.ServicesServer rather than the former combined Rpc.Context alias." diff --git a/repos/effect/migration/annotations/effect__rpc__RpcTest.yaml b/repos/effect/migration/annotations/effect__rpc__RpcTest.yaml index caa66472b6..356f67bf00 100644 --- a/repos/effect/migration/annotations/effect__rpc__RpcTest.yaml +++ b/repos/effect/migration/annotations/effect__rpc__RpcTest.yaml @@ -1,3 +1,3 @@ "@effect/rpc/RpcTest#makeClient": - replacement: "effect/unstable/rpc/RpcTest#makeClient" + replacement: "effect/rpc/RpcTest#makeClient" note: "Retained; it uses the v4 no-serialization client/server path and requires handlers plus any server and client middleware services." diff --git a/repos/effect/migration/annotations/effect__rpc__index.yaml b/repos/effect/migration/annotations/effect__rpc__index.yaml index 3387ce0481..7fb50cdc51 100644 --- a/repos/effect/migration/annotations/effect__rpc__index.yaml +++ b/repos/effect/migration/annotations/effect__rpc__index.yaml @@ -1,3 +1,3 @@ "@effect/rpc/index": - replacement: "effect/unstable/rpc" - note: "The package barrel was removed; import the same namespaces from the effect/unstable/rpc barrel or import specific modules directly." + replacement: "effect/rpc" + note: "The package barrel was removed; import the same namespaces from the effect/rpc barrel or import specific modules directly." diff --git a/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml b/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml index c773d443f4..f897fb72e5 100644 --- a/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml +++ b/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml @@ -1,3 +1,3 @@ "@effect/sql-clickhouse/ClickhouseMigrator#MigrationError": replacement: "@effect/sql-clickhouse/ClickhouseMigrator#MigrationError" - note: "Retained via effect/unstable/sql/Migrator; migrate reason and its lowercase values to kind with PascalCase values." + note: "Retained via effect/sql/Migrator; migrate reason and its lowercase values to kind with PascalCase values." diff --git a/repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml b/repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml index d9ae6977b3..8c497bbb23 100644 --- a/repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml +++ b/repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml @@ -6,10 +6,11 @@ note: "Wrapping an existing node-pg Pool was removed. Provide connection settings to PgClient.layer instead." "@effect/sql-pg/PgClient#PgClient": replacement: "@effect/sql-pg/PgClient#PgClient" - note: "Retained; the service value is now a Context.Service." + note: "Retained; the service value is now a Context.Service. The listen method returns a scoped Effect acquiring a Queue.Dequeue; consume it with Queue operations or Stream.fromQueue and read each notification's payload. Connection failures after registration fail the queue with the original SqlError; scope closure interrupts consumers." "@effect/sql-pg/PgClient#PgClientConfig": + include: true replacement: "@effect/sql-pg/PgClient#PgClientConfig / PgPoolConfig" - note: "Use PgClientConfig for base settings and PgPoolConfig for make/layer; pool sizing, idle timeout, and connection TTL moved to PgPoolConfig." + note: "Use PgClientConfig for base settings and PgPoolConfig for make/layer; pool sizing, idle timeout, and connection TTL moved to PgPoolConfig. The types option takes a PgTypes.Registry rather than node-pg custom types. Timestamp and timestamptz results (including array elements) are Date values with millisecond precision; infinity, -infinity, and out-of-range values become invalid Dates. Numeric readers can use getTime() or register numeric codecs. Date parameters bind as timestamptz; use PgTypes.timestamp(value) to preserve UTC fields when writing a timestamp column instead of applying the session TimeZone." "@effect/sql-pg/PgClient#PgClientFromPoolOptions": replacement: none note: "The node-pg Pool wrapper options were removed with fromPool. Use PgClient.PgPoolConfig with PgClient.make or PgClient.layer." diff --git a/repos/effect/migration/annotations/effect__sql.yaml b/repos/effect/migration/annotations/effect__sql.yaml index 613890f1de..e5c40ac1da 100644 --- a/repos/effect/migration/annotations/effect__sql.yaml +++ b/repos/effect/migration/annotations/effect__sql.yaml @@ -1,3 +1,3 @@ "@effect/sql": - replacement: "effect/unstable/sql" - note: "The @effect/sql package was merged into the effect package; import the effect/unstable/sql barrel or import specific modules directly (e.g. effect/unstable/sql/)." + replacement: "effect/sql" + note: "The @effect/sql package was merged into the effect package; import the effect/sql barrel or import specific modules directly (e.g. effect/sql/)." diff --git a/repos/effect/migration/annotations/effect__sql__Migrator.yaml b/repos/effect/migration/annotations/effect__sql__Migrator.yaml index 3ca711ee6d..ae0b5cc906 100644 --- a/repos/effect/migration/annotations/effect__sql__Migrator.yaml +++ b/repos/effect/migration/annotations/effect__sql__Migrator.yaml @@ -1,3 +1,3 @@ "@effect/sql/Migrator#make": - replacement: "effect/unstable/sql/Migrator#make" + replacement: "effect/sql/Migrator#make" note: "Moved with the same curried make({ dumpSchema })(options) pattern." diff --git a/repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml b/repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml index 10a624a976..56a5fdd151 100644 --- a/repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml +++ b/repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml @@ -1,3 +1,3 @@ "@effect/sql/Migrator/FileSystem": - replacement: "effect/unstable/sql/Migrator" + replacement: "effect/sql/Migrator" note: "fromFileSystem was merged into the main Migrator module with the same (directory) signature; use Migrator.fromFileSystem as the loader." diff --git a/repos/effect/migration/annotations/effect__sql__Model.yaml b/repos/effect/migration/annotations/effect__sql__Model.yaml index 6c82eda852..c679d6071c 100644 --- a/repos/effect/migration/annotations/effect__sql__Model.yaml +++ b/repos/effect/migration/annotations/effect__sql__Model.yaml @@ -1,36 +1,36 @@ "@effect/sql/Model#Any": - replacement: "effect/unstable/schema/Model#Any" + replacement: "effect/schema/Model#Any" note: "Moved; v4 schemas track DecodingServices and EncodingServices separately instead of one Context type." "@effect/sql/Model#AnyNoContext": - replacement: "effect/unstable/schema/Model#Any" + replacement: "effect/schema/Model#Any" note: "The distinct no-context alias was removed; Model.Any propagates decoding and encoding services. Constrain both service types to never when required." "@effect/sql/Model#BooleanFromNumber": replacement: "effect/Schema#BooleanFromBit" note: "Use the core 0 | 1 to boolean schema; Model.BooleanSqlite is the ready-made model field." "@effect/sql/Model#Class": - replacement: "effect/unstable/schema/Model#Class" + replacement: "effect/schema/Model#Class" note: "Moved; model variants remain select, insert, update, json, jsonCreate, and jsonUpdate." "@effect/sql/Model#Date": - replacement: "effect/unstable/schema/Model#Date" + replacement: "effect/schema/Model#Date" note: "Moved; still serializes DateTime.Utc as a YYYY-MM-DD string." "@effect/sql/Model#DateTimeFromDate": replacement: "effect/Schema#DateTimeUtcFromDate" note: "Moved to core Schema and retains Date to DateTime.Utc conversion." "@effect/sql/Model#extract": - replacement: "effect/unstable/schema/Model#extract" + replacement: "effect/schema/Model#extract" note: "Retained after moving the model variant helpers into core Effect's unstable schema package." "@effect/sql/Model#fieldFromKey": replacement: "effect/Schema#encodeKeys" note: "The field helper was removed; apply encodeKeys to each concrete struct or model-variant schema that crosses the naming boundary." "@effect/sql/Model#Generated": - replacement: "effect/unstable/schema/Model#GeneratedByDb" + replacement: "effect/schema/Model#GeneratedByDb" note: "Renamed and now read-only, with select and json variants only. Use Model.Field with select, update, and json to preserve writable v3 behavior." "@effect/sql/Model#makeDataLoaders": - replacement: "effect/unstable/sql/SqlModel#makeResolvers" + replacement: "effect/sql/SqlModel#makeResolvers" note: "Returns RequestResolvers instead of callable loaders; execute with SqlResolver.request and use RequestResolver delay/batch combinators for batching controls. The insert resolver requires model decoding services as well as insert-schema encoding services." "@effect/sql/Model#Override": - replacement: "effect/unstable/schema/Model#Override" + replacement: "effect/schema/Model#Override" note: "Moved with the same explicit-default override purpose." "@effect/sql/Model#fields": - replacement: "effect/unstable/schema/Model#fields" + replacement: "effect/schema/Model#fields" note: "Moved with the variant-model helpers into core Effect's unstable schema package." diff --git a/repos/effect/migration/annotations/effect__sql__SqlClient.yaml b/repos/effect/migration/annotations/effect__sql__SqlClient.yaml index e97901a03f..e1daa30e39 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlClient.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlClient.yaml @@ -1,11 +1,11 @@ "@effect/sql/SqlClient#make": - replacement: "effect/unstable/sql/SqlClient#make" + replacement: "effect/sql/SqlClient#make" note: "Moved; custom clients rename MakeOptions.reactiveMailbox to reactiveQueue and may supply transactionService." "@effect/sql/SqlClient#SafeIntegers": - replacement: "effect/unstable/sql/SqlClient#SafeIntegers" + replacement: "effect/sql/SqlClient#SafeIntegers" note: "Moved and changed from a Reference subclass to a Context.Reference value; provide it as a service." "@effect/sql/SqlClient#TransactionConnection": - replacement: "effect/unstable/sql/SqlClient#TransactionConnection" + replacement: "effect/sql/SqlClient#TransactionConnection" note: "Now a factory keyed by client id, not a singleton tag. Prefer the client's transactionService; the payload type is TransactionConnection.Service." "@effect/sql/SqlClient#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__sql__SqlConnection.yaml b/repos/effect/migration/annotations/effect__sql__SqlConnection.yaml index 56a7889ac6..3f995b5e49 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlConnection.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlConnection.yaml @@ -1,3 +1,3 @@ "@effect/sql/SqlConnection#Connection": - replacement: "effect/unstable/sql/SqlConnection#Connection" + replacement: "effect/sql/SqlConnection#Connection" note: "Moved; Connection.Acquirer is now top-level SqlConnection.Acquirer, and custom connections must implement executeValuesUnprepared." diff --git a/repos/effect/migration/annotations/effect__sql__SqlError.yaml b/repos/effect/migration/annotations/effect__sql__SqlError.yaml index 50b119fac1..157c13cf1d 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlError.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlError.yaml @@ -1,3 +1,3 @@ "@effect/sql/SqlError#SqlErrorTypeId": - replacement: "effect/unstable/sql/SqlError#isSqlError" + replacement: "effect/sql/SqlError#isSqlError" note: "The type id is private; use isSqlError for runtime narrowing or isSqlErrorReason for structured reason values." diff --git a/repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml b/repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml index 60b3b2d96a..2dabf2ef77 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml @@ -1,6 +1,6 @@ "@effect/sql/SqlEventJournal#layer": - replacement: "effect/unstable/eventlog/SqlEventJournal#layer" + replacement: "effect/eventlog/SqlEventJournal#layer" note: "Moved; rename the eventLogTable layer option to entryTable." "@effect/sql/SqlEventJournal#make": - replacement: "effect/unstable/eventlog/SqlEventJournal#make" + replacement: "effect/eventlog/SqlEventJournal#make" note: "Moved with the same entryTable and remotesTable options." diff --git a/repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml b/repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml index 53e298176e..bfe0a5c47c 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml @@ -1,6 +1,6 @@ "@effect/sql/SqlEventLogServer#layerStorage": - replacement: "effect/unstable/eventlog/SqlEventLogServerEncrypted#layerStorage" + replacement: "effect/eventlog/SqlEventLogServerEncrypted#layerStorage" note: "Moved to the encrypted server module with the same options and EventLogEncryption requirement." "@effect/sql/SqlEventLogServer#makeStorage": - replacement: "effect/unstable/eventlog/SqlEventLogServerEncrypted#makeStorage" + replacement: "effect/eventlog/SqlEventLogServerEncrypted#makeStorage" note: "Moved to the encrypted server module with the same SQL, encryption, and scope requirements." diff --git a/repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml b/repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml index fadb083369..45087057d6 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml @@ -1,6 +1,6 @@ "@effect/sql/SqlPersistedQueue#layerStore": - replacement: "effect/unstable/persistence/PersistedQueue#layerStoreSql" + replacement: "effect/persistence/PersistedQueue#layerStoreSql" note: "Moved into PersistedQueue and renamed with the Sql suffix; options are unchanged." "@effect/sql/SqlPersistedQueue#make": - replacement: "effect/unstable/persistence/PersistedQueue#makeStoreSql" + replacement: "effect/persistence/PersistedQueue#makeStoreSql" note: "Use the SQL store constructor; PersistedQueue.make creates a typed queue from a store factory and is not equivalent." diff --git a/repos/effect/migration/annotations/effect__sql__SqlResolver.yaml b/repos/effect/migration/annotations/effect__sql__SqlResolver.yaml index a87b23afd6..19f30843a3 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlResolver.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlResolver.yaml @@ -1,6 +1,6 @@ "@effect/sql/SqlResolver#SqlResolver": replacement: "RequestResolver.RequestResolver>" - note: "The wrapper interface was removed; constructors return RequestResolvers. Execute them with effect/unstable/sql/SqlResolver#request." + note: "The wrapper interface was removed; constructors return RequestResolvers. Execute them with effect/sql/SqlResolver#request." "@effect/sql/SqlResolver#void": - replacement: "effect/unstable/sql/SqlResolver#void" + replacement: "effect/sql/SqlResolver#void" note: "Moved, but remove the leading tag and withContext arguments; it now returns a RequestResolver synchronously and runs through SqlResolver.request." diff --git a/repos/effect/migration/annotations/effect__sql__SqlSchema.yaml b/repos/effect/migration/annotations/effect__sql__SqlSchema.yaml index 4159f622b1..4f7b8511d8 100644 --- a/repos/effect/migration/annotations/effect__sql__SqlSchema.yaml +++ b/repos/effect/migration/annotations/effect__sql__SqlSchema.yaml @@ -1,6 +1,6 @@ "@effect/sql/SqlSchema#single": - replacement: "effect/unstable/sql/SqlSchema#findOne" + replacement: "effect/sql/SqlSchema#findOne" note: "Renamed with the same first-row-or-fail behavior; empty results use Cause.NoSuchElementError and schema failures use Schema.SchemaError." "@effect/sql/SqlSchema#void": - replacement: "effect/unstable/sql/SqlSchema#void" + replacement: "effect/sql/SqlSchema#void" note: "Moved with the same encode, execute, and discard-result pattern; schema failures now use Schema.SchemaError." diff --git a/repos/effect/migration/annotations/effect__sql__Statement.yaml b/repos/effect/migration/annotations/effect__sql__Statement.yaml index 353d499c6f..a7284fd6aa 100644 --- a/repos/effect/migration/annotations/effect__sql__Statement.yaml +++ b/repos/effect/migration/annotations/effect__sql__Statement.yaml @@ -1,29 +1,29 @@ "@effect/sql/Statement#currentTransformer": - replacement: "effect/unstable/sql/Statement#CurrentTransformer" + replacement: "effect/sql/Statement#CurrentTransformer" note: "Capitalized and changed from FiberRef> to Context.Reference." "@effect/sql/Statement#custom": - replacement: "effect/unstable/sql/Statement#custom" + replacement: "effect/sql/Statement#custom" note: "Retained, but returns a Custom segment and uses paramA/paramB/paramC; wrap it with Statement.fragment when a Fragment is required." "@effect/sql/Statement#defaultEscape": - replacement: "effect/unstable/sql/Statement#defaultEscape" + replacement: "effect/sql/Statement#defaultEscape" note: "Moved with the same signature." "@effect/sql/Statement#FragmentId": replacement: "none" note: "The v4 fragment brand is private; use Fragment, fragment, and isFragment instead of direct type-id access." "@effect/sql/Statement#join": - replacement: "effect/unstable/sql/Statement#join" + replacement: "effect/sql/Statement#join" note: "Moved with the same empty, single, and multiple-clause behavior." "@effect/sql/Statement#make": - replacement: "effect/unstable/sql/Statement#make" + replacement: "effect/sql/Statement#make" note: "Moved with the same constructor inputs." "@effect/sql/Statement#or": - replacement: "effect/unstable/sql/Statement#or" + replacement: "effect/sql/Statement#or" note: "Moved unchanged." "@effect/sql/Statement#setTransformer": replacement: "Layer.succeed(Statement.CurrentTransformer, transformer)" note: "The helper was removed; provide the CurrentTransformer reference as a layer." "@effect/sql/Statement#Statement": - replacement: "effect/unstable/sql/Statement#Statement" + replacement: "effect/sql/Statement#Statement" note: "Moved; the nested Transformer type is now top-level and its callback receives Fiber.Fiber rather than FiberRefs.FiberRefs." "@effect/sql/Statement#unsafeFragment": replacement: "Statement.fragment([Statement.literal(sql, params)])" @@ -35,5 +35,5 @@ replacement: "Effect.provideService(Statement.CurrentTransformer, undefined)" note: "The helper was removed; locally provide undefined for the transformer reference." "@effect/sql/Statement#makeCompiler": - replacement: "effect/unstable/sql/Statement#makeCompiler" + replacement: "effect/sql/Statement#makeCompiler" note: "Moved to core Effect; the constructor options are exposed as Statement.CompilerOptions and retain the dialect-specific callbacks." diff --git a/repos/effect/migration/annotations/effect__sql__index.yaml b/repos/effect/migration/annotations/effect__sql__index.yaml index 045e8637eb..48980c615c 100644 --- a/repos/effect/migration/annotations/effect__sql__index.yaml +++ b/repos/effect/migration/annotations/effect__sql__index.yaml @@ -1,3 +1,3 @@ "@effect/sql/index": - replacement: "effect/unstable/sql" - note: "The package barrel was removed; import the same namespaces from the effect/unstable/sql barrel or import specific modules directly." + replacement: "effect/sql" + note: "The package barrel was removed; import the same namespaces from the effect/sql barrel or import specific modules directly." diff --git a/repos/effect/migration/annotations/effect__workflow.yaml b/repos/effect/migration/annotations/effect__workflow.yaml index 0995bdb5e4..9483ca7544 100644 --- a/repos/effect/migration/annotations/effect__workflow.yaml +++ b/repos/effect/migration/annotations/effect__workflow.yaml @@ -1,3 +1,3 @@ "@effect/workflow": - replacement: "effect/unstable/workflow" - note: "The @effect/workflow package was merged into the effect package; import the effect/unstable/workflow barrel or import specific modules directly (e.g. effect/unstable/workflow/)." + replacement: "effect/workflow" + note: "The @effect/workflow package was merged into the effect package; import the effect/workflow barrel or import specific modules directly (e.g. effect/workflow/)." diff --git a/repos/effect/migration/annotations/effect__workflow__Activity.yaml b/repos/effect/migration/annotations/effect__workflow__Activity.yaml index cc4b675963..8d9bb1ad88 100644 --- a/repos/effect/migration/annotations/effect__workflow__Activity.yaml +++ b/repos/effect/migration/annotations/effect__workflow__Activity.yaml @@ -1,17 +1,17 @@ "@effect/workflow/Activity#Any": - replacement: "effect/unstable/workflow/Activity#Any" + replacement: "effect/workflow/Activity#Any" note: "Moved into core Effect. V4 Any is minimal; use AnyWithProps when schemas or execution properties are required." "@effect/workflow/Activity#CurrentAttempt": - replacement: "effect/unstable/workflow/Activity#CurrentAttempt" + replacement: "effect/workflow/Activity#CurrentAttempt" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same default of 1." "@effect/workflow/Activity#make": - replacement: "effect/unstable/workflow/Activity#make" + replacement: "effect/workflow/Activity#make" note: "Moved into core Effect with the same constructor shape, v4 Schema.Constraint service directions, and optional annotations." "@effect/workflow/Activity#raceAll": - replacement: "effect/unstable/workflow/Activity#raceAll" + replacement: "effect/workflow/Activity#raceAll" note: "Moved into core Effect with the same named durable race behavior." "@effect/workflow/Activity#retry": - replacement: "effect/unstable/workflow/Activity#retry" + replacement: "effect/workflow/Activity#retry" note: "Moved into core Effect and updated to v4 Effect.retry option types." "@effect/workflow/Activity#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__workflow__DurableClock.yaml b/repos/effect/migration/annotations/effect__workflow__DurableClock.yaml index adb1bf416e..9bfdf416bd 100644 --- a/repos/effect/migration/annotations/effect__workflow__DurableClock.yaml +++ b/repos/effect/migration/annotations/effect__workflow__DurableClock.yaml @@ -1,5 +1,5 @@ "@effect/workflow/DurableClock#make": - replacement: "effect/unstable/workflow/DurableClock#make" + replacement: "effect/workflow/DurableClock#make" note: "Moved into core Effect; Duration.DurationInput is now Duration.Input." "@effect/workflow/DurableClock#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml b/repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml index 6e01c63c9a..dfb8a92609 100644 --- a/repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml +++ b/repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml @@ -1,29 +1,29 @@ "@effect/workflow/DurableDeferred#Any": - replacement: "effect/unstable/workflow/DurableDeferred#Any" + replacement: "effect/workflow/DurableDeferred#Any" note: "Moved into core Effect. V4 Any is minimal; use AnyWithProps when success, error, or exit schemas are required." "@effect/workflow/DurableDeferred#await": - replacement: "effect/unstable/workflow/DurableDeferred#await" + replacement: "effect/workflow/DurableDeferred#await" note: "Moved into core Effect with the same persisted-result and workflow-suspension behavior." "@effect/workflow/DurableDeferred#done": - replacement: "effect/unstable/workflow/DurableDeferred#done" + replacement: "effect/workflow/DurableDeferred#done" note: "Moved into core Effect; schema requirements now use explicit directional encoding services." "@effect/workflow/DurableDeferred#fail": - replacement: "effect/unstable/workflow/DurableDeferred#fail" + replacement: "effect/workflow/DurableDeferred#fail" note: "Moved into core Effect and now requires the error schema encoding services." "@effect/workflow/DurableDeferred#failCause": - replacement: "effect/unstable/workflow/DurableDeferred#failCause" + replacement: "effect/workflow/DurableDeferred#failCause" note: "Moved into core Effect and now requires the error schema encoding services." "@effect/workflow/DurableDeferred#into": - replacement: "effect/unstable/workflow/DurableDeferred#into" + replacement: "effect/workflow/DurableDeferred#into" note: "Moved into core Effect with the same exit recording and suspension propagation behavior. Provide both decoding and encoding services for the success and error schemas; recording the exit requires encoding services." "@effect/workflow/DurableDeferred#make": - replacement: "effect/unstable/workflow/DurableDeferred#make" + replacement: "effect/workflow/DurableDeferred#make" note: "Moved into core Effect with the same name and optional schemas, expressed through v4 Schema.Constraint." "@effect/workflow/DurableDeferred#raceAll": - replacement: "effect/unstable/workflow/DurableDeferred#raceAll" + replacement: "effect/workflow/DurableDeferred#raceAll" note: "Moved into core Effect with the same persisted-winner behavior." "@effect/workflow/DurableDeferred#succeed": - replacement: "effect/unstable/workflow/DurableDeferred#succeed" + replacement: "effect/workflow/DurableDeferred#succeed" note: "Moved into core Effect and now requires the success schema encoding services." "@effect/workflow/DurableDeferred#TypeId": replacement: "none" diff --git a/repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml b/repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml index 5c2dbb0f05..bbc39a8043 100644 --- a/repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml +++ b/repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml @@ -1,6 +1,6 @@ "@effect/workflow/DurableQueue#make": - replacement: "effect/unstable/workflow/DurableQueue#make" - note: "Moved into core Effect; queue persistence now comes from effect/unstable/persistence." + replacement: "effect/workflow/DurableQueue#make" + note: "Moved into core Effect; queue persistence now comes from effect/persistence." "@effect/workflow/DurableQueue#TypeId": - replacement: "effect/unstable/workflow/DurableQueue#TypeId" + replacement: "effect/workflow/DurableQueue#TypeId" note: "Moved into core Effect; the marker literal changed to ~effect/workflow/DurableQueue. Use typeof DurableQueue.TypeId in type position." diff --git a/repos/effect/migration/annotations/effect__workflow__Workflow.yaml b/repos/effect/migration/annotations/effect__workflow__Workflow.yaml index 941cb9f256..16f7d0ac58 100644 --- a/repos/effect/migration/annotations/effect__workflow__Workflow.yaml +++ b/repos/effect/migration/annotations/effect__workflow__Workflow.yaml @@ -1,44 +1,44 @@ "@effect/workflow/Workflow#Any": - replacement: "effect/unstable/workflow/Workflow#Any" + replacement: "effect/workflow/Workflow#Any" note: "Moved into core Effect. Workflow identity changed from name to _tag and definitions are now class-compatible constructors." "@effect/workflow/Workflow#AnyTaggedRequestSchema": replacement: "none" note: "The TaggedRequest adapter constraint was removed. Define the workflow explicitly with Workflow.make and the request payload, success, error, and PrimaryKey schemas." "@effect/workflow/Workflow#CaptureDefects": - replacement: "effect/unstable/workflow/Workflow#CaptureDefects" + replacement: "effect/workflow/Workflow#CaptureDefects" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same true default." "@effect/workflow/Workflow#Execution": - replacement: "effect/unstable/workflow/Workflow#Execution" + replacement: "effect/workflow/Workflow#Execution" note: "Moved into core Effect; its workflow discriminator changed from name to _tag." "@effect/workflow/Workflow#fromTaggedRequest": replacement: "none" note: "Removed. Expand to Workflow.make(schema._tag, { payload: schema, success: schema.success, error: schema.failure, idempotencyKey: PrimaryKey.value })." "@effect/workflow/Workflow#isResult": - replacement: "effect/unstable/workflow/Workflow#isResult" + replacement: "effect/workflow/Workflow#isResult" note: "Moved into core Effect with the same result refinement behavior." "@effect/workflow/Workflow#make": - replacement: "effect/unstable/workflow/Workflow#make" + replacement: "effect/workflow/Workflow#make" note: "The signature changed from make({ name, ... }) to make(tag, { ... }); definitions expose _tag and are class-compatible constructors." "@effect/workflow/Workflow#Requirements": replacement: "Workflow.RequirementsClient / Workflow.RequirementsHandler" note: "The schema Context union split by direction: client payload encoding and result decoding versus handler payload decoding and result encoding." "@effect/workflow/Workflow#Result": - replacement: "effect/unstable/workflow/Workflow#Result" + replacement: "effect/workflow/Workflow#Result" note: "Moved into core Effect and remains the Complete or Suspended result type and schema constructor." "@effect/workflow/Workflow#ResultEncoded": - replacement: "effect/unstable/workflow/Workflow#ResultEncoded" + replacement: "effect/workflow/Workflow#ResultEncoded" note: "Moved into core Effect and remains both the encoded result type and generic encoded-result codec." "@effect/workflow/Workflow#ResultTypeId": replacement: "none" note: "The result marker is private in v4. Use Workflow.isResult for narrowing." "@effect/workflow/Workflow#SuspendOnFailure": - replacement: "effect/unstable/workflow/Workflow#SuspendOnFailure" + replacement: "effect/workflow/Workflow#SuspendOnFailure" note: "Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default." "@effect/workflow/Workflow#TypeId": replacement: "none" note: "The workflow marker is private in v4. Use Workflow.Any or Workflow.Workflow constraints." "@effect/workflow/Workflow#Workflow": - replacement: "effect/unstable/workflow/Workflow#Workflow" + replacement: "effect/workflow/Workflow#Workflow" note: "Name and name became Tag and _tag, schemas use directional services, definitions are constructable, and poll returns Option." "@effect/workflow/Workflow#Workflow.Error": replacement: "W[\"errorSchema\"][\"Type\"]" diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml b/repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml index 543c7002cd..1d0f3c7fb0 100644 --- a/repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml +++ b/repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml @@ -1,6 +1,6 @@ "@effect/workflow/WorkflowEngine#layerMemory": - replacement: "effect/unstable/workflow/WorkflowEngine#layerMemory" + replacement: "effect/workflow/WorkflowEngine#layerMemory" note: "Moved into core Effect and remains the non-durable engine for tests and local development." "@effect/workflow/WorkflowEngine#makeUnsafe": - replacement: "effect/unstable/workflow/WorkflowEngine#makeUnsafe" + replacement: "effect/workflow/WorkflowEngine#makeUnsafe" note: "Moved into core Effect. Context service projections now use Service instead of Type, and absent encoded results use Option." diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml b/repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml index bc1d27b4ad..55405b5cef 100644 --- a/repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml +++ b/repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml @@ -1,6 +1,6 @@ "@effect/workflow/WorkflowProxy#ConvertHttpApi": - replacement: "effect/unstable/workflow/WorkflowProxy#ConvertHttpApi" + replacement: "effect/workflow/WorkflowProxy#ConvertHttpApi" note: "Moved into core Effect and updated to v4 HttpApiEndpoint types and the consolidated HttpApi architecture." "@effect/workflow/WorkflowProxy#ConvertRpcs": - replacement: "effect/unstable/workflow/WorkflowProxy#ConvertRpcs" + replacement: "effect/workflow/WorkflowProxy#ConvertRpcs" note: "Moved into core Effect; generated execute, discard, and resume RPCs are now keyed from workflow _tag." diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml b/repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml index b604847efb..676c4f6a55 100644 --- a/repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml +++ b/repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml @@ -1,9 +1,9 @@ "@effect/workflow/WorkflowProxyServer#layerHttpApi": - replacement: "effect/unstable/workflow/WorkflowProxyServer#layerHttpApi" + replacement: "effect/workflow/WorkflowProxyServer#layerHttpApi" note: "Moved into core Effect. Use v4 HttpApi group identifiers and Workflow.RequirementsHandler schema services." "@effect/workflow/WorkflowProxyServer#layerRpcHandlers": - replacement: "effect/unstable/workflow/WorkflowProxyServer#layerRpcHandlers" + replacement: "effect/workflow/WorkflowProxyServer#layerRpcHandlers" note: "Moved into core Effect; generated handlers require Workflow.RequirementsHandler rather than the undirected Requirements union." "@effect/workflow/WorkflowProxyServer#RpcHandlers": - replacement: "effect/unstable/workflow/WorkflowProxyServer#RpcHandlers" + replacement: "effect/workflow/WorkflowProxyServer#RpcHandlers" note: "Moved into core Effect; handler names derive from workflow _tag and the optional prefix." diff --git a/repos/effect/migration/schema.md b/repos/effect/migration/schema.md index ad7b6df216..2d87cc3803 100644 --- a/repos/effect/migration/schema.md +++ b/repos/effect/migration/schema.md @@ -115,13 +115,13 @@ const DateFromIsoString = Schema.DateFromString All filters have been renamed with an `is` prefix and now use `check(...)` or `pipe(Schema.check(...))`: -`greaterThan` → `isGreaterThan`, `greaterThanOrEqualTo` → `isGreaterThanOrEqualTo`, `lessThan` → `isLessThan`, `lessThanOrEqualTo` → `isLessThanOrEqualTo`, `between` → `isBetween`, `int` → `isInt`, `multipleOf` → `isMultipleOf`, `finite` → `isFinite`, `minLength` → `isMinLength`, `maxLength` → `isMaxLength`, `length` → `isLengthBetween` +`greaterThan` → `isGreaterThan`, `greaterThanOrEqualTo` → `isGreaterThanOrEqualTo`, `lessThan` → `isLessThan`, `lessThanOrEqualTo` → `isLessThanOrEqualTo`, `between` → `isBetween`, `int` → `isInt`, `multipleOf` → `isMultipleOf`, `finite` → `isFinite`, `minLength` → `isMinLength`, `maxLength` → `isMaxLength`, `length` → `isBetweenLength` Note: `positive`, `negative`, `nonNegative`, `nonPositive` have been removed in v4. ### Utility renames -`equivalence` → `toEquivalence`, `arbitrary` → `toArbitrary`, `pretty` → `toFormatter`, `standardSchemaV1` → `toStandardSchemaV1` +`equivalence` → `toEquivalence`, `arbitrary` → [`Arbitrary.schema`](#migrating-from-the-fast-check-bridge-to-native-arbitrary) from `effect/Arbitrary`, `pretty` → `toFormatter`, `standardSchemaV1` → `toStandardSchemaV1` ## Detailed migrations @@ -1093,3 +1093,345 @@ function split(separator: string) { ) } ``` + +## Migrating from the fast-check bridge to native Arbitrary + +This section covers migration from the fast-check bridge published in `effect@4.0.0-rc.109` to the native, +Schema-first module at `effect/Arbitrary`. + +The new module removes fast-check from the `effect` package. Applications may still install and use fast-check +directly, but Effect Schema generation and `@effect/vitest` property tests no longer depend on it. + +For the new API and its semantics, see [Arbitrary in Effect](../packages/effect/ARBITRARY.md). + +### Import changes + +The following APIs have been removed: + +- `effect/testing/FastCheck`; +- `Schema.toArbitrary`; +- `Schema.Arbitrary`; +- the legacy `Schema.Annotations.ToArbitrary` contract and declaration-level `toArbitrary` annotation; +- the legacy `arbitrary` filter annotation; +- raw fast-check arbitrary inputs and `fastCheck` options in `@effect/vitest`. + +Import the native module explicitly: + +```ts +import * as Arbitrary from "effect/Arbitrary" +``` + +If other tests still use fast-check-specific APIs, add fast-check as a direct development dependency and import it +from `"fast-check"`. Do not import it through Effect. + +### Generating samples + +Previously, `Schema.toArbitrary` returned a factory that needed the fast-check module: + +```ts +import { Schema } from "effect" +import { FastCheck } from "effect/testing" + +const Person = Schema.Struct({ + name: Schema.String, + age: Schema.Int +}) + +const personArbitrary = Schema.toArbitrary(Person)(FastCheck) +const samples = FastCheck.sample(personArbitrary, { numRuns: 20, seed: 42 }) +``` + +Now derive and sample through the Effect-native module: + +```ts +import { Effect, Schema } from "effect" +import * as Arbitrary from "effect/Arbitrary" + +const Person = Schema.Struct({ + name: Schema.String, + age: Schema.Int +}) + +const personArbitrary = Arbitrary.schema(Person) +const samples = await Effect.runPromise( + Arbitrary.sampleEffect(personArbitrary, { count: 20, seed: 42 }) +) +``` + +`Arbitrary.sampleEffect` returns an `Effect` because sampling is interruptible, uses Effect `Random` when no seed is +provided, and reports bounded generation exhaustion as a typed `SampleError`. + +The generated values still use the decoded Schema `Type`. The sequence and distribution are not compatible with +fast-check, even when the same numeric seed is used. + +### Checking properties + +Previously, fast-check owned both the property and the runner: + +```ts +import { Schema } from "effect" +import { FastCheck } from "effect/testing" + +const integer = Schema.toArbitrary(Schema.Int)(FastCheck) + +FastCheck.assert( + FastCheck.property(integer, (value) => Number.isInteger(value)), + { numRuns: 100, seed: 42 } +) +``` + +Now `Arbitrary.checkEffect` runs a pure or Effectful property and returns a structured result: + +```ts +import { Effect, Schema } from "effect" +import * as Arbitrary from "effect/Arbitrary" + +const result = await Effect.runPromise( + Arbitrary.checkEffect( + Arbitrary.schema(Schema.Int), + (value) => Number.isInteger(value), + { runs: 100, seed: 42 } + ) +) +``` + +Unlike `FastCheck.assert`, `Arbitrary.checkEffect` does not throw for an ordinary falsification. Handle `Passed`, +`Falsified`, `Exhausted`, and `ReplayMismatch` explicitly, or use `@effect/vitest`, which converts non-passing results +into test failures. + +Typed failures from Effectful properties are preserved in `Falsified.failure`. Defects and interruption continue +through the returned Effect. + +### Option mapping + +The most common options map as follows: + +| Previous fast-check option | Native option | Migration note | +| -------------------------- | -------------------- | ------------------------------------------------------------------ | +| `numRuns` | `count` or `runs` | Use `count` for `sampleEffect` and `runs` for `checkEffect`. | +| `seed` | `seed` | The type is compatible, but generated sequences are not. | +| `path` | `replay` | Existing fast-check paths cannot be converted. | +| `maxSkipsPerRun` | `maxDiscards` | Native uses one absolute discard budget, not a multiplier per run. | +| `examples` | No direct equivalent | Keep explicit regression cases as ordinary tests. | +| `endOnFailure` | `maxShrinks` | Use `maxShrinks: 0` to stop at the initial failure. | +| `interruptAfterTimeLimit` | Effect interruption | Apply an Effect or test timeout around the check. | +| `skipAllAfterTimeLimit` | No direct equivalent | Prefer explicit run and discard bounds. | +| `verbose` | No direct equivalent | Inspect `CheckResult` or use `@effect/vitest` failure output. | + +Review any less common fast-check runner option manually. The native API deliberately does not reproduce the complete +`fc.Parameters` surface. + +### Replay migration + +Fast-check replay used a seed plus a shrink `path`. Native replay uses one opaque token returned by a `Falsified` +result: + +```ts +const replayed = Arbitrary.checkEffect(arbitrary, property, { + replay: previousFailure.replay +}) +``` + +There is no conversion from a fast-check seed and path to a native replay token. Re-run the property with the native +engine, then record the new token from its `Falsified` result. + +Replay tokens are intended for reproducing and diagnosing a current failure. Because the module is unstable, they are +not guaranteed to survive upgrades. Preserve important failing inputs as explicit regression tests. + +Array shrinking now tries deleting prefixes and interior blocks as well as suffixes. Products and objects retain child +shrink candidates when exploring other branches. Shrunk outputs and replay paths can therefore differ from earlier +native releases, including RC.115. Re-run affected properties to record new tokens. +For arrays of composed Arbitraries, use `Arbitrary.array(item, { maxLength: 50 })` instead of shrinking a generated +length through `flatMap` and `all`; deletions then preserve the remaining generated elements. + +### Migrating declaration annotations + +The old `toArbitrary` annotation directly constructed a fast-check arbitrary and exposed fast-check recursion and +constraint details: + +```ts +import { Schema } from "effect" + +class UserId { + readonly value: number + constructor(value: number) { + this.value = value + } +} + +const UserIdSchema = Schema.instanceOf(UserId, { + toArbitrary: () => (fc) => fc.integer({ min: 1, max: 1_000_000 }).map((value) => new UserId(value)) +}) +``` + +The native `toCodecArbitrary` annotation describes a generatable representation as a Schema `Link`: + +```ts +import { Schema, SchemaTransformation } from "effect" + +class UserId { + readonly value: number + constructor(value: number) { + this.value = value + } +} + +const UserIdSchema = Schema.instanceOf(UserId, { + toCodecArbitrary: () => + Schema.link()( + Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_000_000 })), + SchemaTransformation.transform({ + decode: (value) => new UserId(value), + encode: (id) => id.value + }) + ) +}) +``` + +Before adding `toCodecArbitrary`, check whether the declaration already has a useful `toCodecJson` or `toCodec`. +Native derivation falls back to those canonical codecs automatically. Add an arbitrary-specific Link only when the +canonical representation is opaque or generates valid values too rarely. + +The migration changes where generation logic lives: + +| Legacy contract | Native contract | +| ---------------------------------------------- | -------------------------------------------------------------- | +| Returns a `fast-check.Arbitrary` | Returns a `SchemaAST.Link` through `Schema.link`. | +| Receives generated arbitrary type parameters | Receives decoded Schema type parameters. | +| Receives fast-check constraints and recursion | Receives normalized constraints. | +| Manages terminal recursive branches explicitly | Leaves recursion analysis and budgets to the native compiler. | +| Uses arbitrary combinators | Uses Schema constructors, checks, and a Schema transformation. | + +The original declaration remains authoritative. Values decoded by the Link are checked against it. Failed decodes and +rejected values become bounded discards. + +#### Custom filter metadata + +The old `arbitrary` filter annotation has been replaced by `arbitraryConstraint`. Ordinary custom filters continue to +work as residual filters without generation metadata: + +```ts +import { Schema } from "effect" + +const Even = Schema.Int.check( + Schema.makeFilter((value) => value % 2 === 0) +) +``` + +Residual filtering is bounded, so a very selective or impossible predicate may produce `SampleError` or `Exhausted`. + +If the previous annotation supplied a recognized constructive constraint, move it to `arbitraryConstraint` and adapt +its shape. The predicate remains authoritative: + +```ts +import { Order, Schema } from "effect" + +const Positive = Schema.Number.check( + Schema.makeFilter( + (value) => value > 0, + { + arbitraryConstraint: { + order: Order.Number, + minimum: 0, + exclusiveMinimum: true + } + } + ) +) +``` + +The main constraint-shape changes are: + +| Previous field | Native field | +| ------------------------------------ | ------------------------------------------------------------------- | +| `ordered.order` | `order` | +| `ordered.minimum` / `maximum` | `minimum` / `maximum` | +| `ordered.exclusiveMinimum` / maximum | `exclusiveMinimum: true` / `exclusiveMaximum: true` | +| `integer: true` | `number: "integer"` | +| `noNaN` and `noInfinity` | `number: "finite"` when both restrictions apply | +| collection `minLength` / `maxLength` | `minLength`, `minSize`, or `minProperties` and its matching maximum | +| string pattern | `{ source, flags }` in `patterns` | +| `unique: true` | `uniqueBy: identity` | +| `candidate` | No direct equivalent | + +Choose the cardinality field that matches the Schema domain: `minLength` and `maxLength` for strings and arrays, +`minSize` and `maxSize` for sized collections, and `minProperties` and `maxProperties` for object properties. + +For an opaque declaration that needs a reusable statistically better source domain, express that source as a Schema +Link with `toCodecArbitrary`. + +### Migrating `@effect/vitest` + +Property inputs may be Schemas, native Arbitraries, or mixtures of both. + +Schema-only properties need only an option rename: + +```ts +// Before +it.prop( + "commutative", + [Schema.Int, Schema.Int], + ([a, b]) => a + b === b + a, + { fastCheck: { numRuns: 200, seed: 42 } } +) + +// After +it.prop( + "commutative", + [Schema.Int, Schema.Int], + ([a, b]) => a + b === b + a, + { arbitrary: { runs: 200, seed: 42 } } +) +``` + +Raw or mixed fast-check inputs are no longer accepted: + +```ts +// No longer supported +it.prop("raw arbitrary", [fc.integer()], ([value]) => Number.isInteger(value)) +it.prop("mixed", [Schema.String, fc.integer()], ([text, value]) => true) +``` + +Replace those inputs with Schemas when they describe a domain supported by Schema, or compose a native Arbitrary: + +```ts +import * as Arbitrary from "effect/Arbitrary" + +const integer = Arbitrary.schema(Schema.Int) + +it.prop("native arbitrary", [integer], ([value]) => Number.isInteger(value)) +it.prop("mixed", [Schema.String, integer], ([text, value]) => typeof text === "string" && Number.isInteger(value)) +``` + +If a test genuinely needs a fast-check-specific arbitrary or runner feature, use fast-check directly with Vitest +rather than passing it through `@effect/vitest`. + +`it.prop`, `it.effect.prop`, and `it.live.prop` all accept native check options under `arbitrary`. + +### Behavioral differences to review + +Migration is not only an import rename. Review the following differences: + +- native generation and shrinking have different distributions and may find different shrunk inputs; +- native checking returns structured results instead of using fast-check's assertion exceptions; +- generation that cannot find enough valid samples is bounded and reports `SampleError` or `Exhausted`; +- pure and Effectful properties share one interruptible runner; +- recursive and mutually recursive Schemas are analyzed as a graph and must have a finite generation path; +- replay tokens, seeds, and shrink paths are not compatible with fast-check; +- generated values are the decoded Schema `Type`; +- properties must not mutate generated values. + +### Migration checklist + +1. Replace `effect/testing/FastCheck` imports. Use the native Arbitrary module for Schema generation and import + `"fast-check"` directly only where it is still independently required. +2. Replace `Schema.toArbitrary(schema)(FastCheck)` with `Arbitrary.schema(schema)`. +3. Replace `FastCheck.sample` with `Arbitrary.sampleEffect` and run the returned Effect. +4. Replace `FastCheck.check` or `FastCheck.assert` for Schema-derived inputs with `Arbitrary.checkEffect`, then handle its + structured result. +5. Rename `@effect/vitest` options from `fastCheck` to `arbitrary` and convert `numRuns` to `runs`. +6. Replace raw fast-check inputs in `@effect/vitest` with Schemas or native Arbitraries. +7. Migrate declaration-level `toArbitrary` callbacks to the `toCodecArbitrary` Link-returning contract and replace old + filter-level `arbitrary` annotations with `arbitraryConstraint`. +8. Re-run properties with the native engine and record new replay tokens or explicit regression examples. +9. Review discard limits for selective custom filters. diff --git a/repos/effect/migration/v3-to-v4.md b/repos/effect/migration/v3-to-v4.md index 0171f2ec69..8e617743b5 100644 --- a/repos/effect/migration/v3-to-v4.md +++ b/repos/effect/migration/v3-to-v4.md @@ -2,9 +2,9 @@ # v3 to v4 Migration Reference -Base: `origin/v3` (`1af4232fea7bc613e1dc68db9bec7b1f596d9e68`) +Base: `origin/v3` (`0963a1c17f5be8e26676ed5ffc9c542bd26f97f0`) -Head: `origin/main` (`91abe38c3797b1c290aedef3b01e8c96dd4fee79`) +Head: `HEAD` (`072cdc42a84421694593035d65d0f3375d1a17fd`) This file is generated from the API diff and `migration/annotations/*.yaml`. @@ -33,174 +33,174 @@ effect/TSemaphore -> effect/TxSemaphore (barrel: effect) effect/TSubscriptionRef -> effect/TxSubscriptionRef (barrel: effect) effect/FastCheck -> fast-check effect/TestClock -> effect/testing/TestClock (barrel: effect/testing) -@effect/cli/Args -> effect/unstable/cli/Argument (barrel: effect/unstable/cli) -@effect/cli/ValidationError -> effect/unstable/cli/CliError (barrel: effect/unstable/cli) -@effect/cli/Command -> effect/unstable/cli/Command (barrel: effect/unstable/cli) -@effect/cli/CommandDescriptor -> effect/unstable/cli/Completions (barrel: effect/unstable/cli) -@effect/cli/Options -> effect/unstable/cli/Flag (barrel: effect/unstable/cli) -@effect/cli/BuiltInOptions -> effect/unstable/cli/GlobalFlag (barrel: effect/unstable/cli) -@effect/cli/HelpDoc -> effect/unstable/cli/HelpDoc (barrel: effect/unstable/cli) -@effect/cli/Primitive -> effect/unstable/cli/Primitive (barrel: effect/unstable/cli) -@effect/cli/Prompt -> effect/unstable/cli/Prompt (barrel: effect/unstable/cli) -@effect/cluster/ClusterCron -> effect/unstable/cluster/ClusterCron (barrel: effect/unstable/cluster) -@effect/cluster/ClusterError -> effect/unstable/cluster/ClusterError (barrel: effect/unstable/cluster) -@effect/cluster/ClusterMetrics -> effect/unstable/cluster/ClusterMetrics (barrel: effect/unstable/cluster) -@effect/cluster/ClusterSchema -> effect/unstable/cluster/ClusterSchema (barrel: effect/unstable/cluster) -@effect/cluster/ClusterWorkflowEngine -> effect/unstable/cluster/ClusterWorkflowEngine (barrel: effect/unstable/cluster) -@effect/cluster/DeliverAt -> effect/unstable/cluster/DeliverAt (barrel: effect/unstable/cluster) -@effect/cluster/Entity -> effect/unstable/cluster/Entity (barrel: effect/unstable/cluster) -@effect/cluster/EntityAddress -> effect/unstable/cluster/EntityAddress (barrel: effect/unstable/cluster) -@effect/cluster/EntityId -> effect/unstable/cluster/EntityId (barrel: effect/unstable/cluster) -@effect/cluster/EntityProxy -> effect/unstable/cluster/EntityProxy (barrel: effect/unstable/cluster) -@effect/cluster/EntityProxyServer -> effect/unstable/cluster/EntityProxyServer (barrel: effect/unstable/cluster) -@effect/cluster/EntityResource -> effect/unstable/cluster/EntityResource (barrel: effect/unstable/cluster) -@effect/cluster/EntityType -> effect/unstable/cluster/EntityType (barrel: effect/unstable/cluster) -@effect/cluster/Envelope -> effect/unstable/cluster/Envelope (barrel: effect/unstable/cluster) -@effect/cluster/HttpRunner -> effect/unstable/cluster/HttpRunner (barrel: effect/unstable/cluster) -@effect/cluster/K8sHttpClient -> effect/unstable/cluster/K8sHttpClient (barrel: effect/unstable/cluster) -@effect/cluster/MachineId -> effect/unstable/cluster/MachineId (barrel: effect/unstable/cluster) -@effect/cluster/Message -> effect/unstable/cluster/Message (barrel: effect/unstable/cluster) -@effect/cluster/MessageStorage -> effect/unstable/cluster/MessageStorage (barrel: effect/unstable/cluster) -@effect/cluster/Reply -> effect/unstable/cluster/Reply (barrel: effect/unstable/cluster) -@effect/cluster/Runner -> effect/unstable/cluster/Runner (barrel: effect/unstable/cluster) -@effect/cluster/RunnerAddress -> effect/unstable/cluster/RunnerAddress (barrel: effect/unstable/cluster) -@effect/cluster/RunnerHealth -> effect/unstable/cluster/RunnerHealth (barrel: effect/unstable/cluster) -@effect/cluster/RunnerServer -> effect/unstable/cluster/RunnerServer (barrel: effect/unstable/cluster) -@effect/cluster/RunnerStorage -> effect/unstable/cluster/RunnerStorage (barrel: effect/unstable/cluster) -@effect/cluster/Runners -> effect/unstable/cluster/Runners (barrel: effect/unstable/cluster) -@effect/cluster/ShardId -> effect/unstable/cluster/ShardId (barrel: effect/unstable/cluster) -@effect/cluster/Sharding -> effect/unstable/cluster/Sharding (barrel: effect/unstable/cluster) -@effect/cluster/ShardingConfig -> effect/unstable/cluster/ShardingConfig (barrel: effect/unstable/cluster) -@effect/cluster/ShardingRegistrationEvent -> effect/unstable/cluster/ShardingRegistrationEvent (barrel: effect/unstable/cluster) -@effect/cluster/SingleRunner -> effect/unstable/cluster/SingleRunner (barrel: effect/unstable/cluster) -@effect/cluster/Singleton -> effect/unstable/cluster/Singleton (barrel: effect/unstable/cluster) -@effect/cluster/SingletonAddress -> effect/unstable/cluster/SingletonAddress (barrel: effect/unstable/cluster) -@effect/cluster/Snowflake -> effect/unstable/cluster/Snowflake (barrel: effect/unstable/cluster) -@effect/cluster/SocketRunner -> effect/unstable/cluster/SocketRunner (barrel: effect/unstable/cluster) -@effect/cluster/SqlMessageStorage -> effect/unstable/cluster/SqlMessageStorage (barrel: effect/unstable/cluster) -@effect/cluster/SqlRunnerStorage -> effect/unstable/cluster/SqlRunnerStorage (barrel: effect/unstable/cluster) -@effect/cluster/TestRunner -> effect/unstable/cluster/TestRunner (barrel: effect/unstable/cluster) -@effect/experimental/DevTools -> effect/unstable/devtools/DevTools (barrel: effect/unstable/devtools) -@effect/experimental/DevTools/Client -> effect/unstable/devtools/DevToolsClient (barrel: effect/unstable/devtools) -@effect/experimental/DevTools/Domain -> effect/unstable/devtools/DevToolsSchema (barrel: effect/unstable/devtools) -@effect/experimental/DevTools/Server -> effect/unstable/devtools/DevToolsServer (barrel: effect/unstable/devtools) -@effect/platform/MsgPack -> effect/unstable/encoding/SchemaBinary (barrel: effect/unstable/encoding) -@effect/platform/Ndjson -> effect/unstable/encoding/Ndjson (barrel: effect/unstable/encoding) -@effect/experimental/Sse -> effect/unstable/encoding/Sse (barrel: effect/unstable/encoding) -@effect/ai/AiError -> effect/unstable/ai/AiError (barrel: effect/unstable/ai) -@effect/ai/Chat -> effect/unstable/ai/Chat (barrel: effect/unstable/ai) -@effect/ai/EmbeddingModel -> effect/unstable/ai/EmbeddingModel (barrel: effect/unstable/ai) -@effect/ai/IdGenerator -> effect/unstable/ai/IdGenerator (barrel: effect/unstable/ai) -@effect/ai/LanguageModel -> effect/unstable/ai/LanguageModel (barrel: effect/unstable/ai) -@effect/ai/McpSchema -> effect/unstable/ai/McpSchema (barrel: effect/unstable/ai) -@effect/ai/McpServer -> effect/unstable/ai/McpServer (barrel: effect/unstable/ai) -@effect/ai/Model -> effect/unstable/ai/Model (barrel: effect/unstable/ai) -@effect/ai/Prompt -> effect/unstable/ai/Prompt (barrel: effect/unstable/ai) -@effect/ai/Response -> effect/unstable/ai/Response (barrel: effect/unstable/ai) -@effect/ai/Telemetry -> effect/unstable/ai/Telemetry (barrel: effect/unstable/ai) -@effect/ai/Tokenizer -> effect/unstable/ai/Tokenizer (barrel: effect/unstable/ai) -@effect/ai/Tool -> effect/unstable/ai/Tool (barrel: effect/unstable/ai) -@effect/ai/Toolkit -> effect/unstable/ai/Toolkit (barrel: effect/unstable/ai) -@effect/experimental/Event -> effect/unstable/eventlog/Event (barrel: effect/unstable/eventlog) -@effect/experimental/EventGroup -> effect/unstable/eventlog/EventGroup (barrel: effect/unstable/eventlog) -@effect/experimental/EventJournal -> effect/unstable/eventlog/EventJournal (barrel: effect/unstable/eventlog) -@effect/experimental/EventLog -> effect/unstable/eventlog/EventLog (barrel: effect/unstable/eventlog) -@effect/experimental/EventLogEncryption -> effect/unstable/eventlog/EventLogEncryption (barrel: effect/unstable/eventlog) -@effect/experimental/EventLogRemote -> effect/unstable/eventlog/EventLogMessage (barrel: effect/unstable/eventlog) -@effect/experimental/EventLogRemote -> effect/unstable/eventlog/EventLogRemote (barrel: effect/unstable/eventlog) -@effect/experimental/EventLogServer -> effect/unstable/eventlog/EventLogServer (barrel: effect/unstable/eventlog) -@effect/experimental/EventLogServer -> effect/unstable/eventlog/EventLogServerEncrypted (barrel: effect/unstable/eventlog) -@effect/sql/SqlEventJournal -> effect/unstable/eventlog/SqlEventJournal (barrel: effect/unstable/eventlog) -@effect/sql/SqlEventLogServer -> effect/unstable/eventlog/SqlEventLogServerEncrypted (barrel: effect/unstable/eventlog) -@effect/platform/Cookies -> effect/unstable/http/Cookies (barrel: effect/unstable/http) -@effect/platform/Etag -> effect/unstable/http/Etag (barrel: effect/unstable/http) -@effect/platform/FetchHttpClient -> effect/unstable/http/FetchHttpClient (barrel: effect/unstable/http) -@effect/platform/Headers -> effect/unstable/http/Headers (barrel: effect/unstable/http) -@effect/platform/HttpBody -> effect/unstable/http/HttpBody (barrel: effect/unstable/http) -@effect/platform/HttpClient -> effect/unstable/http/HttpClient (barrel: effect/unstable/http) -@effect/platform/HttpClientError -> effect/unstable/http/HttpClientError (barrel: effect/unstable/http) -@effect/platform/HttpClientRequest -> effect/unstable/http/HttpClientRequest (barrel: effect/unstable/http) -@effect/platform/HttpClientResponse -> effect/unstable/http/HttpClientResponse (barrel: effect/unstable/http) -@effect/platform/HttpApp -> effect/unstable/http/HttpEffect (barrel: effect/unstable/http) -@effect/platform/HttpIncomingMessage -> effect/unstable/http/HttpIncomingMessage (barrel: effect/unstable/http) -@effect/platform/HttpMethod -> effect/unstable/http/HttpMethod (barrel: effect/unstable/http) -@effect/platform/HttpMiddleware -> effect/unstable/http/HttpMiddleware (barrel: effect/unstable/http) -@effect/platform/HttpPlatform -> effect/unstable/http/HttpPlatform (barrel: effect/unstable/http) -@effect/platform/HttpRouter -> effect/unstable/http/HttpRouter (barrel: effect/unstable/http) -@effect/platform/HttpServer -> effect/unstable/http/HttpServer (barrel: effect/unstable/http) -@effect/platform/HttpServerError -> effect/unstable/http/HttpServerError (barrel: effect/unstable/http) -@effect/platform/HttpServerRequest -> effect/unstable/http/HttpServerRequest (barrel: effect/unstable/http) -@effect/platform/HttpServerRespondable -> effect/unstable/http/HttpServerRespondable (barrel: effect/unstable/http) -@effect/platform/HttpServerResponse -> effect/unstable/http/HttpServerResponse (barrel: effect/unstable/http) -@effect/platform/HttpTraceContext -> effect/unstable/http/HttpTraceContext (barrel: effect/unstable/http) -@effect/platform/Multipart -> effect/unstable/http/Multipart (barrel: effect/unstable/http) -@effect/platform/Template -> effect/unstable/http/Template (barrel: effect/unstable/http) -@effect/platform/Url -> effect/unstable/http/Url (barrel: effect/unstable/http) -@effect/platform/UrlParams -> effect/unstable/http/UrlParams (barrel: effect/unstable/http) -@effect/platform/HttpApi -> effect/unstable/httpapi/HttpApi (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiBuilder -> effect/unstable/httpapi/HttpApiBuilder (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiClient -> effect/unstable/httpapi/HttpApiClient (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiEndpoint -> effect/unstable/httpapi/HttpApiEndpoint (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiError -> effect/unstable/httpapi/HttpApiError (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiGroup -> effect/unstable/httpapi/HttpApiGroup (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiMiddleware -> effect/unstable/httpapi/HttpApiMiddleware (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiScalar -> effect/unstable/httpapi/HttpApiScalar (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiSchema -> effect/unstable/httpapi/HttpApiSchema (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiSecurity -> effect/unstable/httpapi/HttpApiSecurity (barrel: effect/unstable/httpapi) -@effect/platform/HttpApiSwagger -> effect/unstable/httpapi/HttpApiSwagger (barrel: effect/unstable/httpapi) -@effect/platform/OpenApi -> effect/unstable/httpapi/OpenApi (barrel: effect/unstable/httpapi) -@effect/opentelemetry/Otlp -> effect/unstable/observability/Otlp (barrel: effect/unstable/observability) -@effect/opentelemetry/internal/otlpExporter -> effect/unstable/observability/OtlpExporter (barrel: effect/unstable/observability) -@effect/opentelemetry/OtlpLogger -> effect/unstable/observability/OtlpLogger (barrel: effect/unstable/observability) -@effect/opentelemetry/OtlpMetrics -> effect/unstable/observability/OtlpMetrics (barrel: effect/unstable/observability) -@effect/opentelemetry/OtlpResource -> effect/unstable/observability/OtlpResource (barrel: effect/unstable/observability) -@effect/opentelemetry/OtlpSerialization -> effect/unstable/observability/OtlpSerialization (barrel: effect/unstable/observability) -@effect/opentelemetry/OtlpTracer -> effect/unstable/observability/OtlpTracer (barrel: effect/unstable/observability) -@effect/platform/KeyValueStore -> effect/unstable/persistence/KeyValueStore (barrel: effect/unstable/persistence) -@effect/experimental/Persistence -> effect/unstable/persistence/Persistable (barrel: effect/unstable/persistence) -@effect/experimental/PersistedCache -> effect/unstable/persistence/PersistedCache (barrel: effect/unstable/persistence) -@effect/experimental/PersistedQueue -> effect/unstable/persistence/PersistedQueue (barrel: effect/unstable/persistence) -@effect/experimental/Persistence -> effect/unstable/persistence/Persistence (barrel: effect/unstable/persistence) -@effect/experimental/RateLimiter -> effect/unstable/persistence/RateLimiter (barrel: effect/unstable/persistence) -@effect/platform/Command -> effect/unstable/process/ChildProcess (barrel: effect/unstable/process) -@effect/platform/CommandExecutor -> effect/unstable/process/ChildProcessSpawner (barrel: effect/unstable/process) -@effect/experimental/Reactivity -> effect/unstable/reactivity/Reactivity (barrel: effect/unstable/reactivity) -@effect/rpc/Rpc -> effect/unstable/rpc/Rpc (barrel: effect/unstable/rpc) -@effect/rpc/RpcClient -> effect/unstable/rpc/RpcClient (barrel: effect/unstable/rpc) -@effect/rpc/RpcClientError -> effect/unstable/rpc/RpcClientError (barrel: effect/unstable/rpc) -@effect/rpc/RpcGroup -> effect/unstable/rpc/RpcGroup (barrel: effect/unstable/rpc) -@effect/rpc/RpcMessage -> effect/unstable/rpc/RpcMessage (barrel: effect/unstable/rpc) -@effect/rpc/RpcMiddleware -> effect/unstable/rpc/RpcMiddleware (barrel: effect/unstable/rpc) -@effect/rpc/RpcSchema -> effect/unstable/rpc/RpcSchema (barrel: effect/unstable/rpc) -@effect/rpc/RpcSerialization -> effect/unstable/rpc/RpcSerialization (barrel: effect/unstable/rpc) -@effect/rpc/RpcServer -> effect/unstable/rpc/RpcServer (barrel: effect/unstable/rpc) -@effect/rpc/RpcTest -> effect/unstable/rpc/RpcTest (barrel: effect/unstable/rpc) -@effect/rpc/RpcWorker -> effect/unstable/rpc/RpcWorker (barrel: effect/unstable/rpc) -@effect/sql/Model -> effect/unstable/schema/Model (barrel: effect/unstable/schema) -@effect/experimental/VariantSchema -> effect/unstable/schema/VariantSchema (barrel: effect/unstable/schema) -@effect/platform/Socket -> effect/unstable/socket/Socket (barrel: effect/unstable/socket) -@effect/platform/SocketServer -> effect/unstable/socket/SocketServer (barrel: effect/unstable/socket) -@effect/sql/Migrator -> effect/unstable/sql/Migrator (barrel: effect/unstable/sql) -@effect/sql/SqlClient -> effect/unstable/sql/SqlClient (barrel: effect/unstable/sql) -@effect/sql/SqlConnection -> effect/unstable/sql/SqlConnection (barrel: effect/unstable/sql) -@effect/sql/SqlError -> effect/unstable/sql/SqlError (barrel: effect/unstable/sql) -@effect/sql/Model -> effect/unstable/sql/SqlModel (barrel: effect/unstable/sql) -@effect/sql/SqlResolver -> effect/unstable/sql/SqlResolver (barrel: effect/unstable/sql) -@effect/sql/SqlSchema -> effect/unstable/sql/SqlSchema (barrel: effect/unstable/sql) -@effect/sql/SqlStream -> effect/unstable/sql/SqlStream (barrel: effect/unstable/sql) -@effect/sql/Statement -> effect/unstable/sql/Statement (barrel: effect/unstable/sql) -@effect/platform/Transferable -> effect/unstable/workers/Transferable (barrel: effect/unstable/workers) -@effect/platform/Worker -> effect/unstable/workers/Worker (barrel: effect/unstable/workers) -@effect/platform/WorkerError -> effect/unstable/workers/WorkerError (barrel: effect/unstable/workers) -@effect/platform/WorkerRunner -> effect/unstable/workers/WorkerRunner (barrel: effect/unstable/workers) -@effect/workflow/Activity -> effect/unstable/workflow/Activity (barrel: effect/unstable/workflow) -@effect/workflow/DurableClock -> effect/unstable/workflow/DurableClock (barrel: effect/unstable/workflow) -@effect/workflow/DurableDeferred -> effect/unstable/workflow/DurableDeferred (barrel: effect/unstable/workflow) -@effect/workflow/DurableQueue -> effect/unstable/workflow/DurableQueue (barrel: effect/unstable/workflow) -@effect/workflow/Workflow -> effect/unstable/workflow/Workflow (barrel: effect/unstable/workflow) -@effect/workflow/WorkflowEngine -> effect/unstable/workflow/WorkflowEngine (barrel: effect/unstable/workflow) -@effect/workflow/WorkflowProxy -> effect/unstable/workflow/WorkflowProxy (barrel: effect/unstable/workflow) -@effect/workflow/WorkflowProxyServer -> effect/unstable/workflow/WorkflowProxyServer (barrel: effect/unstable/workflow) +@effect/cli/Args -> effect/cli/Argument (barrel: effect/cli) +@effect/cli/ValidationError -> effect/cli/CliError (barrel: effect/cli) +@effect/cli/Command -> effect/cli/Command (barrel: effect/cli) +@effect/cli/CommandDescriptor -> effect/cli/Completions (barrel: effect/cli) +@effect/cli/Options -> effect/cli/Flag (barrel: effect/cli) +@effect/cli/BuiltInOptions -> effect/cli/GlobalFlag (barrel: effect/cli) +@effect/cli/HelpDoc -> effect/cli/HelpDoc (barrel: effect/cli) +@effect/cli/Primitive -> effect/cli/Primitive (barrel: effect/cli) +@effect/cli/Prompt -> effect/cli/Prompt (barrel: effect/cli) +@effect/cluster/ClusterCron -> effect/cluster/ClusterCron (barrel: effect/cluster) +@effect/cluster/ClusterError -> effect/cluster/ClusterError (barrel: effect/cluster) +@effect/cluster/ClusterMetrics -> effect/cluster/ClusterMetrics (barrel: effect/cluster) +@effect/cluster/ClusterSchema -> effect/cluster/ClusterSchema (barrel: effect/cluster) +@effect/cluster/ClusterWorkflowEngine -> effect/cluster/ClusterWorkflowEngine (barrel: effect/cluster) +@effect/cluster/DeliverAt -> effect/cluster/DeliverAt (barrel: effect/cluster) +@effect/cluster/Entity -> effect/cluster/Entity (barrel: effect/cluster) +@effect/cluster/EntityAddress -> effect/cluster/EntityAddress (barrel: effect/cluster) +@effect/cluster/EntityId -> effect/cluster/EntityId (barrel: effect/cluster) +@effect/cluster/EntityProxy -> effect/cluster/EntityProxy (barrel: effect/cluster) +@effect/cluster/EntityProxyServer -> effect/cluster/EntityProxyServer (barrel: effect/cluster) +@effect/cluster/EntityResource -> effect/cluster/EntityResource (barrel: effect/cluster) +@effect/cluster/EntityType -> effect/cluster/EntityType (barrel: effect/cluster) +@effect/cluster/Envelope -> effect/cluster/Envelope (barrel: effect/cluster) +@effect/cluster/HttpRunner -> effect/cluster/HttpRunner (barrel: effect/cluster) +@effect/cluster/K8sHttpClient -> effect/cluster/K8sHttpClient (barrel: effect/cluster) +@effect/cluster/MachineId -> effect/cluster/MachineId (barrel: effect/cluster) +@effect/cluster/Message -> effect/cluster/Message (barrel: effect/cluster) +@effect/cluster/MessageStorage -> effect/cluster/MessageStorage (barrel: effect/cluster) +@effect/cluster/Reply -> effect/cluster/Reply (barrel: effect/cluster) +@effect/cluster/Runner -> effect/cluster/Runner (barrel: effect/cluster) +@effect/cluster/RunnerAddress -> effect/cluster/RunnerAddress (barrel: effect/cluster) +@effect/cluster/RunnerHealth -> effect/cluster/RunnerHealth (barrel: effect/cluster) +@effect/cluster/RunnerServer -> effect/cluster/RunnerServer (barrel: effect/cluster) +@effect/cluster/RunnerStorage -> effect/cluster/RunnerStorage (barrel: effect/cluster) +@effect/cluster/Runners -> effect/cluster/Runners (barrel: effect/cluster) +@effect/cluster/ShardId -> effect/cluster/ShardId (barrel: effect/cluster) +@effect/cluster/Sharding -> effect/cluster/Sharding (barrel: effect/cluster) +@effect/cluster/ShardingConfig -> effect/cluster/ShardingConfig (barrel: effect/cluster) +@effect/cluster/ShardingRegistrationEvent -> effect/cluster/ShardingRegistrationEvent (barrel: effect/cluster) +@effect/cluster/SingleRunner -> effect/cluster/SingleRunner (barrel: effect/cluster) +@effect/cluster/Singleton -> effect/cluster/Singleton (barrel: effect/cluster) +@effect/cluster/SingletonAddress -> effect/cluster/SingletonAddress (barrel: effect/cluster) +@effect/cluster/Snowflake -> effect/cluster/Snowflake (barrel: effect/cluster) +@effect/cluster/SocketRunner -> effect/cluster/SocketRunner (barrel: effect/cluster) +@effect/cluster/SqlMessageStorage -> effect/cluster/SqlMessageStorage (barrel: effect/cluster) +@effect/cluster/SqlRunnerStorage -> effect/cluster/SqlRunnerStorage (barrel: effect/cluster) +@effect/cluster/TestRunner -> effect/cluster/TestRunner (barrel: effect/cluster) +@effect/experimental/DevTools -> effect/devtools/DevTools (barrel: effect/devtools) +@effect/experimental/DevTools/Client -> effect/devtools/DevToolsClient (barrel: effect/devtools) +@effect/experimental/DevTools/Domain -> effect/devtools/DevToolsSchema (barrel: effect/devtools) +@effect/experimental/DevTools/Server -> effect/devtools/DevToolsServer (barrel: effect/devtools) +@effect/platform/MsgPack -> effect/encoding/SchemaBinary (barrel: effect/encoding) +@effect/platform/Ndjson -> effect/encoding/Ndjson (barrel: effect/encoding) +@effect/experimental/Sse -> effect/encoding/Sse (barrel: effect/encoding) +@effect/ai/AiError -> effect/ai/AiError (barrel: effect/ai) +@effect/ai/Chat -> effect/ai/Chat (barrel: effect/ai) +@effect/ai/EmbeddingModel -> effect/ai/EmbeddingModel (barrel: effect/ai) +@effect/ai/IdGenerator -> effect/ai/IdGenerator (barrel: effect/ai) +@effect/ai/LanguageModel -> effect/ai/LanguageModel (barrel: effect/ai) +@effect/ai/McpSchema -> effect/ai/McpSchema (barrel: effect/ai) +@effect/ai/McpServer -> effect/ai/McpServer (barrel: effect/ai) +@effect/ai/Model -> effect/ai/Model (barrel: effect/ai) +@effect/ai/Prompt -> effect/ai/Prompt (barrel: effect/ai) +@effect/ai/Response -> effect/ai/Response (barrel: effect/ai) +@effect/ai/Telemetry -> effect/ai/Telemetry (barrel: effect/ai) +@effect/ai/Tokenizer -> effect/ai/Tokenizer (barrel: effect/ai) +@effect/ai/Tool -> effect/ai/Tool (barrel: effect/ai) +@effect/ai/Toolkit -> effect/ai/Toolkit (barrel: effect/ai) +@effect/experimental/Event -> effect/eventlog/Event (barrel: effect/eventlog) +@effect/experimental/EventGroup -> effect/eventlog/EventGroup (barrel: effect/eventlog) +@effect/experimental/EventJournal -> effect/eventlog/EventJournal (barrel: effect/eventlog) +@effect/experimental/EventLog -> effect/eventlog/EventLog (barrel: effect/eventlog) +@effect/experimental/EventLogEncryption -> effect/eventlog/EventLogEncryption (barrel: effect/eventlog) +@effect/experimental/EventLogRemote -> effect/eventlog/EventLogMessage (barrel: effect/eventlog) +@effect/experimental/EventLogRemote -> effect/eventlog/EventLogRemote (barrel: effect/eventlog) +@effect/experimental/EventLogServer -> effect/eventlog/EventLogServer (barrel: effect/eventlog) +@effect/experimental/EventLogServer -> effect/eventlog/EventLogServerEncrypted (barrel: effect/eventlog) +@effect/sql/SqlEventJournal -> effect/eventlog/SqlEventJournal (barrel: effect/eventlog) +@effect/sql/SqlEventLogServer -> effect/eventlog/SqlEventLogServerEncrypted (barrel: effect/eventlog) +@effect/platform/Cookies -> effect/http/Cookies (barrel: effect/http) +@effect/platform/Etag -> effect/http/Etag (barrel: effect/http) +@effect/platform/FetchHttpClient -> effect/http/FetchHttpClient (barrel: effect/http) +@effect/platform/Headers -> effect/http/Headers (barrel: effect/http) +@effect/platform/HttpBody -> effect/http/HttpBody (barrel: effect/http) +@effect/platform/HttpClient -> effect/http/HttpClient (barrel: effect/http) +@effect/platform/HttpClientError -> effect/http/HttpClientError (barrel: effect/http) +@effect/platform/HttpClientRequest -> effect/http/HttpClientRequest (barrel: effect/http) +@effect/platform/HttpClientResponse -> effect/http/HttpClientResponse (barrel: effect/http) +@effect/platform/HttpApp -> effect/http/HttpEffect (barrel: effect/http) +@effect/platform/HttpIncomingMessage -> effect/http/HttpIncomingMessage (barrel: effect/http) +@effect/platform/HttpMethod -> effect/http/HttpMethod (barrel: effect/http) +@effect/platform/HttpMiddleware -> effect/http/HttpMiddleware (barrel: effect/http) +@effect/platform/HttpPlatform -> effect/http/HttpPlatform (barrel: effect/http) +@effect/platform/HttpRouter -> effect/http/HttpRouter (barrel: effect/http) +@effect/platform/HttpServer -> effect/http/HttpServer (barrel: effect/http) +@effect/platform/HttpServerError -> effect/http/HttpServerError (barrel: effect/http) +@effect/platform/HttpServerRequest -> effect/http/HttpServerRequest (barrel: effect/http) +@effect/platform/HttpServerRespondable -> effect/http/HttpServerRespondable (barrel: effect/http) +@effect/platform/HttpServerResponse -> effect/http/HttpServerResponse (barrel: effect/http) +@effect/platform/HttpTraceContext -> effect/http/HttpTraceContext (barrel: effect/http) +@effect/platform/Multipart -> effect/http/Multipart (barrel: effect/http) +@effect/platform/Template -> effect/http/Template (barrel: effect/http) +@effect/platform/Url -> effect/http/Url (barrel: effect/http) +@effect/platform/UrlParams -> effect/http/UrlParams (barrel: effect/http) +@effect/platform/HttpApi -> effect/http-api/HttpApi (barrel: effect/http-api) +@effect/platform/HttpApiBuilder -> effect/http-api/HttpApiBuilder (barrel: effect/http-api) +@effect/platform/HttpApiClient -> effect/http-api/HttpApiClient (barrel: effect/http-api) +@effect/platform/HttpApiEndpoint -> effect/http-api/HttpApiEndpoint (barrel: effect/http-api) +@effect/platform/HttpApiError -> effect/http-api/HttpApiError (barrel: effect/http-api) +@effect/platform/HttpApiGroup -> effect/http-api/HttpApiGroup (barrel: effect/http-api) +@effect/platform/HttpApiMiddleware -> effect/http-api/HttpApiMiddleware (barrel: effect/http-api) +@effect/platform/HttpApiScalar -> effect/http-api/HttpApiScalar (barrel: effect/http-api) +@effect/platform/HttpApiSchema -> effect/http-api/HttpApiSchema (barrel: effect/http-api) +@effect/platform/HttpApiSecurity -> effect/http-api/HttpApiSecurity (barrel: effect/http-api) +@effect/platform/HttpApiSwagger -> effect/http-api/HttpApiSwagger (barrel: effect/http-api) +@effect/platform/OpenApi -> effect/http-api/OpenApi (barrel: effect/http-api) +@effect/opentelemetry/Otlp -> effect/observability/Otlp (barrel: effect/observability) +@effect/opentelemetry/internal/otlpExporter -> effect/observability/OtlpExporter (barrel: effect/observability) +@effect/opentelemetry/OtlpLogger -> effect/observability/OtlpLogger (barrel: effect/observability) +@effect/opentelemetry/OtlpMetrics -> effect/observability/OtlpMetrics (barrel: effect/observability) +@effect/opentelemetry/OtlpResource -> effect/observability/OtlpResource (barrel: effect/observability) +@effect/opentelemetry/OtlpSerialization -> effect/observability/OtlpSerialization (barrel: effect/observability) +@effect/opentelemetry/OtlpTracer -> effect/observability/OtlpTracer (barrel: effect/observability) +@effect/platform/KeyValueStore -> effect/persistence/KeyValueStore (barrel: effect/persistence) +@effect/experimental/Persistence -> effect/persistence/Persistable (barrel: effect/persistence) +@effect/experimental/PersistedCache -> effect/persistence/PersistedCache (barrel: effect/persistence) +@effect/experimental/PersistedQueue -> effect/persistence/PersistedQueue (barrel: effect/persistence) +@effect/experimental/Persistence -> effect/persistence/Persistence (barrel: effect/persistence) +@effect/experimental/RateLimiter -> effect/persistence/RateLimiter (barrel: effect/persistence) +@effect/platform/Command -> effect/process/ChildProcess (barrel: effect/process) +@effect/platform/CommandExecutor -> effect/process/ChildProcessSpawner (barrel: effect/process) +@effect/experimental/Reactivity -> effect/reactivity/Reactivity (barrel: effect/reactivity) +@effect/rpc/Rpc -> effect/rpc/Rpc (barrel: effect/rpc) +@effect/rpc/RpcClient -> effect/rpc/RpcClient (barrel: effect/rpc) +@effect/rpc/RpcClientError -> effect/rpc/RpcClientError (barrel: effect/rpc) +@effect/rpc/RpcGroup -> effect/rpc/RpcGroup (barrel: effect/rpc) +@effect/rpc/RpcMessage -> effect/rpc/RpcMessage (barrel: effect/rpc) +@effect/rpc/RpcMiddleware -> effect/rpc/RpcMiddleware (barrel: effect/rpc) +@effect/rpc/RpcSchema -> effect/rpc/RpcSchema (barrel: effect/rpc) +@effect/rpc/RpcSerialization -> effect/rpc/RpcSerialization (barrel: effect/rpc) +@effect/rpc/RpcServer -> effect/rpc/RpcServer (barrel: effect/rpc) +@effect/rpc/RpcTest -> effect/rpc/RpcTest (barrel: effect/rpc) +@effect/rpc/RpcWorker -> effect/rpc/RpcWorker (barrel: effect/rpc) +@effect/sql/Model -> effect/schema/Model (barrel: effect/schema) +@effect/experimental/VariantSchema -> effect/schema/VariantSchema (barrel: effect/schema) +@effect/platform/Socket -> effect/socket/Socket (barrel: effect/socket) +@effect/platform/SocketServer -> effect/socket/SocketServer (barrel: effect/socket) +@effect/sql/Migrator -> effect/sql/Migrator (barrel: effect/sql) +@effect/sql/SqlClient -> effect/sql/SqlClient (barrel: effect/sql) +@effect/sql/SqlConnection -> effect/sql/SqlConnection (barrel: effect/sql) +@effect/sql/SqlError -> effect/sql/SqlError (barrel: effect/sql) +@effect/sql/Model -> effect/sql/SqlModel (barrel: effect/sql) +@effect/sql/SqlResolver -> effect/sql/SqlResolver (barrel: effect/sql) +@effect/sql/SqlSchema -> effect/sql/SqlSchema (barrel: effect/sql) +@effect/sql/SqlStream -> effect/sql/SqlStream (barrel: effect/sql) +@effect/sql/Statement -> effect/sql/Statement (barrel: effect/sql) +@effect/platform/Transferable -> effect/workers/Transferable (barrel: effect/workers) +@effect/platform/Worker -> effect/workers/Worker (barrel: effect/workers) +@effect/platform/WorkerError -> effect/workers/WorkerError (barrel: effect/workers) +@effect/platform/WorkerRunner -> effect/workers/WorkerRunner (barrel: effect/workers) +@effect/workflow/Activity -> effect/workflow/Activity (barrel: effect/workflow) +@effect/workflow/DurableClock -> effect/workflow/DurableClock (barrel: effect/workflow) +@effect/workflow/DurableDeferred -> effect/workflow/DurableDeferred (barrel: effect/workflow) +@effect/workflow/DurableQueue -> effect/workflow/DurableQueue (barrel: effect/workflow) +@effect/workflow/Workflow -> effect/workflow/Workflow (barrel: effect/workflow) +@effect/workflow/WorkflowEngine -> effect/workflow/WorkflowEngine (barrel: effect/workflow) +@effect/workflow/WorkflowProxy -> effect/workflow/WorkflowProxy (barrel: effect/workflow) +@effect/workflow/WorkflowProxyServer -> effect/workflow/WorkflowProxyServer (barrel: effect/workflow) effect/Array -> effect/Array (barrel: effect) effect/BigDecimal -> effect/BigDecimal (barrel: effect) effect/BigInt -> effect/BigInt (barrel: effect) @@ -224,7 +224,7 @@ effect/Differ -> effect/Differ (barrel: effect) effect/Duration -> effect/Duration (barrel: effect) effect/Effect -> effect/Effect (barrel: effect) effect/Effectable -> effect/Effectable (barrel: effect) -effect/Encoding -> effect/Encoding (barrel: effect) +effect/Encoding -> none effect/Equal -> effect/Equal (barrel: effect) effect/Equivalence -> effect/Equivalence (barrel: effect) effect/ExecutionPlan -> effect/ExecutionPlan (barrel: effect) @@ -329,35 +329,35 @@ effect/TxChunk (barrel: effect) effect/UndefinedOr (barrel: effect) effect/testing/TestConsole (barrel: effect/testing) effect/testing/TestSchema (barrel: effect/testing) -effect/unstable/ai/AnthropicStructuredOutput (barrel: effect/unstable/ai) -effect/unstable/ai/OpenAiStructuredOutput (barrel: effect/unstable/ai) -effect/unstable/ai/ResponseIdTracker (barrel: effect/unstable/ai) -effect/unstable/cli/CliOutput (barrel: effect/unstable/cli) -effect/unstable/cli/Param (barrel: effect/unstable/cli) -effect/unstable/eventlog/EventLogServerUnencrypted (barrel: effect/unstable/eventlog) -effect/unstable/eventlog/EventLogSessionAuth (barrel: effect/unstable/eventlog) -effect/unstable/eventlog/SqlEventLogServerUnencrypted (barrel: effect/unstable/eventlog) -effect/unstable/http/FindMyWay (barrel: effect/unstable/http) -effect/unstable/http/HttpStaticServer (barrel: effect/unstable/http) -effect/unstable/http/MultipartParser (barrel: effect/unstable/http) -effect/unstable/http/MultipartParser/HeadersParser (barrel: effect/unstable/http) -effect/unstable/http/MultipartParser/Search (barrel: effect/unstable/http) -effect/unstable/httpapi/HttpApiTest (barrel: effect/unstable/httpapi) -effect/unstable/observability/PrometheusMetrics (barrel: effect/unstable/observability) -effect/unstable/persistence/Redis (barrel: effect/unstable/persistence) -effect/unstable/reactivity/AsyncResult (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/Atom (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/AtomHttpApi (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/AtomRef (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/AtomRegistry (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/AtomRpc (barrel: effect/unstable/reactivity) -effect/unstable/reactivity/Hydration (barrel: effect/unstable/reactivity) -effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) +effect/ai/AnthropicStructuredOutput (barrel: effect/ai) +effect/ai/OpenAiStructuredOutput (barrel: effect/ai) +effect/ai/ResponseIdTracker (barrel: effect/ai) +effect/cli/CliOutput (barrel: effect/cli) +effect/cli/Param (barrel: effect/cli) +effect/eventlog/EventLogServerUnencrypted (barrel: effect/eventlog) +effect/eventlog/EventLogSessionAuth (barrel: effect/eventlog) +effect/eventlog/SqlEventLogServerUnencrypted (barrel: effect/eventlog) +effect/http/FindMyWay (barrel: effect/http) +effect/http/HttpStaticServer (barrel: effect/http) +effect/http/MultipartParser (barrel: effect/http) +effect/http/MultipartParser/HeadersParser (barrel: effect/http) +effect/http/MultipartParser/Search (barrel: effect/http) +effect/http-api/HttpApiTest (barrel: effect/http-api) +effect/observability/PrometheusMetrics (barrel: effect/observability) +effect/persistence/Redis (barrel: effect/persistence) +effect/reactivity/AsyncResult (barrel: effect/reactivity) +effect/reactivity/Atom (barrel: effect/reactivity) +effect/reactivity/AtomHttpApi (barrel: effect/reactivity) +effect/reactivity/AtomRef (barrel: effect/reactivity) +effect/reactivity/AtomRegistry (barrel: effect/reactivity) +effect/reactivity/AtomRpc (barrel: effect/reactivity) +effect/reactivity/Hydration (barrel: effect/reactivity) +effect/rpc/Utils (barrel: effect/rpc) ``` ## Removed Modules -- `@effect/ai` -> `effect/unstable/ai`: The @effect/ai package was merged into the effect package; import the effect/unstable/ai barrel or import specific modules directly (e.g. effect/unstable/ai/\). +- `@effect/ai` -> `effect/ai`: The @effect/ai package was merged into the effect package; import the effect/ai barrel or import specific modules directly (e.g. effect/ai/\). - `@effect/ai-amazon-bedrock` -> `none`: The @effect/ai-amazon-bedrock provider package was removed from v4 with no direct replacement. Use @aws-sdk/client-bedrock-runtime directly or build a custom v4 provider integration. - `@effect/ai-amazon-bedrock/AmazonBedrockClient` -> `none`: The @effect/ai-amazon-bedrock provider package was removed from v4, so AmazonBedrockClient, layer, layerConfig, make, and Service have no direct replacements. Use @aws-sdk/client-bedrock-runtime directly or build a custom v4 provider integration. - `@effect/ai-amazon-bedrock/AmazonBedrockConfig` -> `none`: The @effect/ai-amazon-bedrock provider package was removed from v4, so AmazonBedrockConfig has no direct replacement. Use @aws-sdk/client-bedrock-runtime directly or build a custom v4 provider integration. @@ -378,224 +378,224 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `@effect/ai-openai/OpenAiTokenizer`: No single module replacement; follow the curated per-API guidance below. - `@effect/ai-openai/index` -> `@effect/ai-openai`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/ai-openai package root or import specific modules directly. - `@effect/ai-openrouter/index` -> `@effect/ai-openrouter`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/ai-openrouter package root or import specific modules directly. -- `@effect/ai/AiError` -> `effect/unstable/ai/AiError` -- `@effect/ai/Chat` -> `effect/unstable/ai/Chat` -- `@effect/ai/EmbeddingModel` -> `effect/unstable/ai/EmbeddingModel` -- `@effect/ai/IdGenerator` -> `effect/unstable/ai/IdGenerator` -- `@effect/ai/LanguageModel` -> `effect/unstable/ai/LanguageModel` -- `@effect/ai/McpSchema` -> `effect/unstable/ai/McpSchema` -- `@effect/ai/McpServer` -> `effect/unstable/ai/McpServer` -- `@effect/ai/Model` -> `effect/unstable/ai/Model` -- `@effect/ai/Prompt` -> `effect/unstable/ai/Prompt` -- `@effect/ai/Response` -> `effect/unstable/ai/Response` -- `@effect/ai/Telemetry` -> `effect/unstable/ai/Telemetry` -- `@effect/ai/Tokenizer` -> `effect/unstable/ai/Tokenizer` -- `@effect/ai/Tool` -> `effect/unstable/ai/Tool` -- `@effect/ai/Toolkit` -> `effect/unstable/ai/Toolkit` -- `@effect/ai/index` -> `effect/unstable/ai`: The package barrel was removed; import the same namespaces from the effect/unstable/ai barrel or import specific modules directly. -- `@effect/cli` -> `effect/unstable/cli`: The @effect/cli package was merged into the effect package; import the effect/unstable/cli barrel or import specific modules directly (e.g. effect/unstable/cli/\). -- `@effect/cli/Args` -> `effect/unstable/cli/Argument` +- `@effect/ai/AiError` -> `effect/ai/AiError` +- `@effect/ai/Chat` -> `effect/ai/Chat` +- `@effect/ai/EmbeddingModel` -> `effect/ai/EmbeddingModel` +- `@effect/ai/IdGenerator` -> `effect/ai/IdGenerator` +- `@effect/ai/LanguageModel` -> `effect/ai/LanguageModel` +- `@effect/ai/McpSchema` -> `effect/ai/McpSchema` +- `@effect/ai/McpServer` -> `effect/ai/McpServer` +- `@effect/ai/Model` -> `effect/ai/Model` +- `@effect/ai/Prompt` -> `effect/ai/Prompt` +- `@effect/ai/Response` -> `effect/ai/Response` +- `@effect/ai/Telemetry` -> `effect/ai/Telemetry` +- `@effect/ai/Tokenizer` -> `effect/ai/Tokenizer` +- `@effect/ai/Tool` -> `effect/ai/Tool` +- `@effect/ai/Toolkit` -> `effect/ai/Toolkit` +- `@effect/ai/index` -> `effect/ai`: The package barrel was removed; import the same namespaces from the effect/ai barrel or import specific modules directly. +- `@effect/cli` -> `effect/cli`: The @effect/cli package was merged into the effect package; import the effect/cli barrel or import specific modules directly (e.g. effect/cli/\). +- `@effect/cli/Args` -> `effect/cli/Argument` - `@effect/cli/AutoCorrect` -> `none`: V4 suggestion distance is internal and fixed; the public configurable distance helper was removed. -- `@effect/cli/BuiltInOptions` -> `effect/unstable/cli/GlobalFlag` +- `@effect/cli/BuiltInOptions` -> `effect/cli/GlobalFlag` - `@effect/cli/CliApp`: No single module replacement; follow the curated per-API guidance below. - `@effect/cli/CliConfig`: No single module replacement; follow the curated per-API guidance below. -- `@effect/cli/Command` -> `effect/unstable/cli/Command` -- `@effect/cli/CommandDescriptor` -> `effect/unstable/cli/Completions` +- `@effect/cli/Command` -> `effect/cli/Command` +- `@effect/cli/CommandDescriptor` -> `effect/cli/Completions` - `@effect/cli/CommandDirective`: No single module replacement; follow the curated per-API guidance below. - `@effect/cli/ConfigFile`: No single module replacement; follow the curated per-API guidance below. -- `@effect/cli/HelpDoc` -> `effect/unstable/cli/HelpDoc` +- `@effect/cli/HelpDoc` -> `effect/cli/HelpDoc` - `@effect/cli/HelpDoc/Span` -> `none`: The Span ADT was removed; v4 help fields are strings and terminal styling is owned by CliOutput. -- `@effect/cli/Options` -> `effect/unstable/cli/Flag` -- `@effect/cli/Primitive` -> `effect/unstable/cli/Primitive` -- `@effect/cli/Prompt` -> `effect/unstable/cli/Prompt` +- `@effect/cli/Options` -> `effect/cli/Flag` +- `@effect/cli/Primitive` -> `effect/cli/Primitive` +- `@effect/cli/Prompt` -> `effect/cli/Prompt` - `@effect/cli/Usage` -> `none`: The Usage ADT was removed; Command builds a plain HelpDoc.usage string internally. -- `@effect/cli/ValidationError` -> `effect/unstable/cli/CliError` -- `@effect/cli/index` -> `effect/unstable/cli`: The package barrel was removed; import the same namespaces from the effect/unstable/cli barrel or import specific modules directly. -- `@effect/cluster` -> `effect/unstable/cluster`: The @effect/cluster package was merged into the effect package; import the effect/unstable/cluster barrel or import specific modules directly (e.g. effect/unstable/cluster/\). -- `@effect/cluster/ClusterCron` -> `effect/unstable/cluster/ClusterCron` -- `@effect/cluster/ClusterError` -> `effect/unstable/cluster/ClusterError` -- `@effect/cluster/ClusterMetrics` -> `effect/unstable/cluster/ClusterMetrics` -- `@effect/cluster/ClusterSchema` -> `effect/unstable/cluster/ClusterSchema` -- `@effect/cluster/ClusterWorkflowEngine` -> `effect/unstable/cluster/ClusterWorkflowEngine` -- `@effect/cluster/DeliverAt` -> `effect/unstable/cluster/DeliverAt` -- `@effect/cluster/Entity` -> `effect/unstable/cluster/Entity` -- `@effect/cluster/EntityAddress` -> `effect/unstable/cluster/EntityAddress` -- `@effect/cluster/EntityId` -> `effect/unstable/cluster/EntityId` -- `@effect/cluster/EntityProxy` -> `effect/unstable/cluster/EntityProxy` -- `@effect/cluster/EntityProxyServer` -> `effect/unstable/cluster/EntityProxyServer` -- `@effect/cluster/EntityResource` -> `effect/unstable/cluster/EntityResource` -- `@effect/cluster/EntityType` -> `effect/unstable/cluster/EntityType` -- `@effect/cluster/Envelope` -> `effect/unstable/cluster/Envelope` -- `@effect/cluster/HttpRunner` -> `effect/unstable/cluster/HttpRunner` -- `@effect/cluster/K8sHttpClient` -> `effect/unstable/cluster/K8sHttpClient` -- `@effect/cluster/MachineId` -> `effect/unstable/cluster/MachineId` -- `@effect/cluster/Message` -> `effect/unstable/cluster/Message` -- `@effect/cluster/MessageStorage` -> `effect/unstable/cluster/MessageStorage` -- `@effect/cluster/Reply` -> `effect/unstable/cluster/Reply` -- `@effect/cluster/Runner` -> `effect/unstable/cluster/Runner` -- `@effect/cluster/RunnerAddress` -> `effect/unstable/cluster/RunnerAddress` -- `@effect/cluster/RunnerHealth` -> `effect/unstable/cluster/RunnerHealth` -- `@effect/cluster/RunnerServer` -> `effect/unstable/cluster/RunnerServer` -- `@effect/cluster/RunnerStorage` -> `effect/unstable/cluster/RunnerStorage` -- `@effect/cluster/Runners` -> `effect/unstable/cluster/Runners` -- `@effect/cluster/ShardId` -> `effect/unstable/cluster/ShardId` -- `@effect/cluster/Sharding` -> `effect/unstable/cluster/Sharding` -- `@effect/cluster/ShardingConfig` -> `effect/unstable/cluster/ShardingConfig` -- `@effect/cluster/ShardingRegistrationEvent` -> `effect/unstable/cluster/ShardingRegistrationEvent` -- `@effect/cluster/SingleRunner` -> `effect/unstable/cluster/SingleRunner` -- `@effect/cluster/Singleton` -> `effect/unstable/cluster/Singleton` -- `@effect/cluster/SingletonAddress` -> `effect/unstable/cluster/SingletonAddress` -- `@effect/cluster/Snowflake` -> `effect/unstable/cluster/Snowflake` -- `@effect/cluster/SocketRunner` -> `effect/unstable/cluster/SocketRunner` -- `@effect/cluster/SqlMessageStorage` -> `effect/unstable/cluster/SqlMessageStorage` -- `@effect/cluster/SqlRunnerStorage` -> `effect/unstable/cluster/SqlRunnerStorage` -- `@effect/cluster/TestRunner` -> `effect/unstable/cluster/TestRunner` -- `@effect/cluster/index` -> `effect/unstable/cluster`: The package barrel was removed; import the same namespaces from the effect/unstable/cluster barrel or import specific modules directly. -- `@effect/experimental` -> `none`: The @effect/experimental package was folded into the effect package, split across effect/unstable/\* (devtools, eventlog, persistence, reactivity, ...); follow the Import Map for each module. -- `@effect/experimental/DevTools` -> `effect/unstable/devtools/DevTools` -- `@effect/experimental/DevTools/Client` -> `effect/unstable/devtools/DevToolsClient` -- `@effect/experimental/DevTools/Domain` -> `effect/unstable/devtools/DevToolsSchema` -- `@effect/experimental/DevTools/Server` -> `effect/unstable/devtools/DevToolsServer` -- `@effect/experimental/Event` -> `effect/unstable/eventlog/Event` -- `@effect/experimental/EventGroup` -> `effect/unstable/eventlog/EventGroup` -- `@effect/experimental/EventJournal` -> `effect/unstable/eventlog/EventJournal` -- `@effect/experimental/EventLog` -> `effect/unstable/eventlog/EventLog` -- `@effect/experimental/EventLogEncryption` -> `effect/unstable/eventlog/EventLogEncryption` -- `@effect/experimental/EventLogRemote` -> `effect/unstable/eventlog/EventLogMessage`, `effect/unstable/eventlog/EventLogRemote` -- `@effect/experimental/EventLogServer` -> `effect/unstable/eventlog/EventLogServer`, `effect/unstable/eventlog/EventLogServerEncrypted` +- `@effect/cli/ValidationError` -> `effect/cli/CliError` +- `@effect/cli/index` -> `effect/cli`: The package barrel was removed; import the same namespaces from the effect/cli barrel or import specific modules directly. +- `@effect/cluster` -> `effect/cluster`: The @effect/cluster package was merged into the effect package; import the effect/cluster barrel or import specific modules directly (e.g. effect/cluster/\). +- `@effect/cluster/ClusterCron` -> `effect/cluster/ClusterCron` +- `@effect/cluster/ClusterError` -> `effect/cluster/ClusterError` +- `@effect/cluster/ClusterMetrics` -> `effect/cluster/ClusterMetrics` +- `@effect/cluster/ClusterSchema` -> `effect/cluster/ClusterSchema` +- `@effect/cluster/ClusterWorkflowEngine` -> `effect/cluster/ClusterWorkflowEngine` +- `@effect/cluster/DeliverAt` -> `effect/cluster/DeliverAt` +- `@effect/cluster/Entity` -> `effect/cluster/Entity` +- `@effect/cluster/EntityAddress` -> `effect/cluster/EntityAddress` +- `@effect/cluster/EntityId` -> `effect/cluster/EntityId` +- `@effect/cluster/EntityProxy` -> `effect/cluster/EntityProxy` +- `@effect/cluster/EntityProxyServer` -> `effect/cluster/EntityProxyServer` +- `@effect/cluster/EntityResource` -> `effect/cluster/EntityResource` +- `@effect/cluster/EntityType` -> `effect/cluster/EntityType` +- `@effect/cluster/Envelope` -> `effect/cluster/Envelope` +- `@effect/cluster/HttpRunner` -> `effect/cluster/HttpRunner` +- `@effect/cluster/K8sHttpClient` -> `effect/cluster/K8sHttpClient` +- `@effect/cluster/MachineId` -> `effect/cluster/MachineId` +- `@effect/cluster/Message` -> `effect/cluster/Message` +- `@effect/cluster/MessageStorage` -> `effect/cluster/MessageStorage` +- `@effect/cluster/Reply` -> `effect/cluster/Reply` +- `@effect/cluster/Runner` -> `effect/cluster/Runner` +- `@effect/cluster/RunnerAddress` -> `effect/cluster/RunnerAddress` +- `@effect/cluster/RunnerHealth` -> `effect/cluster/RunnerHealth` +- `@effect/cluster/RunnerServer` -> `effect/cluster/RunnerServer` +- `@effect/cluster/RunnerStorage` -> `effect/cluster/RunnerStorage` +- `@effect/cluster/Runners` -> `effect/cluster/Runners` +- `@effect/cluster/ShardId` -> `effect/cluster/ShardId` +- `@effect/cluster/Sharding` -> `effect/cluster/Sharding` +- `@effect/cluster/ShardingConfig` -> `effect/cluster/ShardingConfig` +- `@effect/cluster/ShardingRegistrationEvent` -> `effect/cluster/ShardingRegistrationEvent` +- `@effect/cluster/SingleRunner` -> `effect/cluster/SingleRunner` +- `@effect/cluster/Singleton` -> `effect/cluster/Singleton` +- `@effect/cluster/SingletonAddress` -> `effect/cluster/SingletonAddress` +- `@effect/cluster/Snowflake` -> `effect/cluster/Snowflake` +- `@effect/cluster/SocketRunner` -> `effect/cluster/SocketRunner` +- `@effect/cluster/SqlMessageStorage` -> `effect/cluster/SqlMessageStorage` +- `@effect/cluster/SqlRunnerStorage` -> `effect/cluster/SqlRunnerStorage` +- `@effect/cluster/TestRunner` -> `effect/cluster/TestRunner` +- `@effect/cluster/index` -> `effect/cluster`: The package barrel was removed; import the same namespaces from the effect/cluster barrel or import specific modules directly. +- `@effect/experimental` -> `none`: The @effect/experimental package was folded into the effect package, split across effect/\* (devtools, eventlog, persistence, reactivity, ...); follow the Import Map for each module. +- `@effect/experimental/DevTools` -> `effect/devtools/DevTools` +- `@effect/experimental/DevTools/Client` -> `effect/devtools/DevToolsClient` +- `@effect/experimental/DevTools/Domain` -> `effect/devtools/DevToolsSchema` +- `@effect/experimental/DevTools/Server` -> `effect/devtools/DevToolsServer` +- `@effect/experimental/Event` -> `effect/eventlog/Event` +- `@effect/experimental/EventGroup` -> `effect/eventlog/EventGroup` +- `@effect/experimental/EventJournal` -> `effect/eventlog/EventJournal` +- `@effect/experimental/EventLog` -> `effect/eventlog/EventLog` +- `@effect/experimental/EventLogEncryption` -> `effect/eventlog/EventLogEncryption` +- `@effect/experimental/EventLogRemote` -> `effect/eventlog/EventLogMessage`, `effect/eventlog/EventLogRemote` +- `@effect/experimental/EventLogServer` -> `effect/eventlog/EventLogServer`, `effect/eventlog/EventLogServerEncrypted` - `@effect/experimental/EventLogServer/Cloudflare` -> `none`: The Cloudflare adapter was not ported; combine EventLogServerEncrypted.layer with a custom Durable Object RpcServer.Protocol adapter. - `@effect/experimental/Machine` -> `none`: The experimental local Machine actor runtime, model, boot process, constructors, brands, and serializable variants were not ported to v4. Redesign request contracts with Rpc/RpcGroup and choose Cluster Entity, Workflow, or a local actor built from Queue, Ref, PubSub, and scoped fibers according to the required semantics; ClusterWorkflowEngine is a different durable Workflow abstraction. For serializable actors, define schemas with Rpc/RpcGroup and choose Cluster Entity or Workflow explicitly. Context and initialization helpers (including the serializable initialization contract), input/private/public/state extractors, and the Machine-specific handler context were also removed, so request handling and state management must be explicit. Use ordinary Effect tracing controls and Effect.retry instead of the removed Machine-specific wrappers; its defect wrapper was also removed. Snapshot restoration was not ported, so implement persistence explicitly for the replacement architecture. - `@effect/experimental/Machine/Procedure` -> `none`: The stateful Machine Procedure model, its serializable variant and guard, and both Procedure brands were not ported to v4. Define request contracts with Rpc (using schemas for serializable procedures) and implement state handling in an explicit actor architecture, because Rpc provides only the request contract. The handler context, context and request extractors, and no-reply sentinel were removed; use the corresponding Rpc request types after redesigning the contract. Replace the removed tagged-request base and helpers with schema-backed Rpc requests and Rpc helper types where appropriate. - `@effect/experimental/Machine/ProcedureList` -> `none`: The stateful Machine ProcedureList abstraction and brand were not ported to v4. RpcGroup is the closest protocol collection for its schema-backed operations, but it has no initial state or public/private visibility split. Implement state handling and initialization in the replacement actor or workflow, and enforce visibility in that architecture. - `@effect/experimental/Machine/SerializableProcedureList` -> `none`: The serializable stateful ProcedureList abstraction was not ported to v4. RpcGroup is the closest protocol collection for its schema-backed operations, but it has no initial state or public/private visibility split. Implement state handling and initialization in the replacement actor or workflow, and enforce visibility in that architecture. -- `@effect/experimental/PersistedCache` -> `effect/unstable/persistence/PersistedCache` -- `@effect/experimental/PersistedQueue` -> `effect/unstable/persistence/PersistedQueue` +- `@effect/experimental/PersistedCache` -> `effect/persistence/PersistedCache` +- `@effect/experimental/PersistedQueue` -> `effect/persistence/PersistedQueue` - `@effect/experimental/PersistedQueue/Redis`: No single module replacement; follow the curated per-API guidance below. -- `@effect/experimental/Persistence` -> `effect/unstable/persistence/Persistable`, `effect/unstable/persistence/Persistence` +- `@effect/experimental/Persistence` -> `effect/persistence/Persistable`, `effect/persistence/Persistence` - `@effect/experimental/Persistence/Lmdb` -> `none`: The LMDB backend was not ported; implement a custom BackingPersistence layer or use a supported Kvs, Redis, or SQL backend. - `@effect/experimental/Persistence/Redis`: No single module replacement; follow the curated per-API guidance below. -- `@effect/experimental/RateLimiter` -> `effect/unstable/persistence/RateLimiter` +- `@effect/experimental/RateLimiter` -> `effect/persistence/RateLimiter` - `@effect/experimental/RateLimiter/Redis`: No single module replacement; follow the curated per-API guidance below. -- `@effect/experimental/Reactivity` -> `effect/unstable/reactivity/Reactivity` +- `@effect/experimental/Reactivity` -> `effect/reactivity/Reactivity` - `@effect/experimental/RequestResolver`: No single module replacement; follow the curated per-API guidance below. -- `@effect/experimental/Sse` -> `effect/unstable/encoding/Sse` -- `@effect/experimental/VariantSchema` -> `effect/unstable/schema/VariantSchema` -- `@effect/experimental/index` -> `none`: The package barrel was removed along with the package; import each module from its new effect/unstable/\* location per the Import Map. +- `@effect/experimental/Sse` -> `effect/encoding/Sse` +- `@effect/experimental/VariantSchema` -> `effect/schema/VariantSchema` +- `@effect/experimental/index` -> `none`: The package barrel was removed along with the package; import each module from its new effect/\* location per the Import Map. - `@effect/opentelemetry/Logger`: No single module replacement; follow the curated per-API guidance below. - `@effect/opentelemetry/Metrics`: No single module replacement; follow the curated per-API guidance below. -- `@effect/opentelemetry/Otlp` -> `effect/unstable/observability/Otlp` -- `@effect/opentelemetry/OtlpLogger` -> `effect/unstable/observability/OtlpLogger` -- `@effect/opentelemetry/OtlpMetrics` -> `effect/unstable/observability/OtlpMetrics` -- `@effect/opentelemetry/OtlpResource` -> `effect/unstable/observability/OtlpResource` -- `@effect/opentelemetry/OtlpSerialization` -> `effect/unstable/observability/OtlpSerialization` -- `@effect/opentelemetry/OtlpTracer` -> `effect/unstable/observability/OtlpTracer` +- `@effect/opentelemetry/Otlp` -> `effect/observability/Otlp` +- `@effect/opentelemetry/OtlpLogger` -> `effect/observability/OtlpLogger` +- `@effect/opentelemetry/OtlpMetrics` -> `effect/observability/OtlpMetrics` +- `@effect/opentelemetry/OtlpResource` -> `effect/observability/OtlpResource` +- `@effect/opentelemetry/OtlpSerialization` -> `effect/observability/OtlpSerialization` +- `@effect/opentelemetry/OtlpTracer` -> `effect/observability/OtlpTracer` - `@effect/opentelemetry/Tracer`: No single module replacement; follow the curated per-API guidance below. - `@effect/opentelemetry/index` -> `@effect/opentelemetry`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/opentelemetry package root or import specific modules directly. -- `@effect/platform` -> `none`: The @effect/platform package was folded into the effect package: core services live in effect root modules (e.g. effect/FileSystem, effect/Path) and HTTP in effect/unstable/http; follow the Import Map for each module. +- `@effect/platform` -> `none`: The @effect/platform package was folded into the effect package: core services live in effect root modules (e.g. effect/FileSystem, effect/Path) and HTTP in effect/http; follow the Import Map for each module. - `@effect/platform-browser/index` -> `@effect/platform-browser`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/platform-browser package root or import specific modules directly. - `@effect/platform-bun/BunCommandExecutor`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-bun/BunContext`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-bun/BunFileSystem/ParcelWatcher`: No single module replacement; follow the curated per-API guidance below. -- `@effect/platform-bun/BunKeyValueStore` -> `effect/unstable/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via BunServices.layer or BunFileSystem.layer with BunPath.layer. +- `@effect/platform-bun/BunKeyValueStore` -> `effect/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via BunServices.layer or BunFileSystem.layer with BunPath.layer. - `@effect/platform-bun/index` -> `@effect/platform-bun`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/platform-bun package root or import specific modules directly. - `@effect/platform-node-shared/NodeCommandExecutor`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-node-shared/NodeFileSystem/ParcelWatcher`: No single module replacement; follow the curated per-API guidance below. -- `@effect/platform-node-shared/NodeKeyValueStore` -> `effect/unstable/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer. +- `@effect/platform-node-shared/NodeKeyValueStore` -> `effect/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer. - `@effect/platform-node-shared/NodeMultipart`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-node/NodeCommandExecutor`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-node/NodeContext`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform-node/NodeFileSystem/ParcelWatcher`: No single module replacement; follow the curated per-API guidance below. -- `@effect/platform-node/NodeKeyValueStore` -> `effect/unstable/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer. +- `@effect/platform-node/NodeKeyValueStore` -> `effect/persistence/KeyValueStore`: layerFileSystem is now platform-neutral as KeyValueStore.layerFileSystem(directory); provide FileSystem and Path via NodeServices.layer or NodeFileSystem.layer with NodePath.layer. - `@effect/platform-node/index` -> `@effect/platform-node`: The explicit /index entrypoint was removed; import Node-prefixed namespaces from the @effect/platform-node package root or import specific modules directly. Undici is no longer in the root barrel; import it from @effect/platform-node/Undici or directly from undici. - `@effect/platform/ChannelSchema` -> `effect/ChannelSchema` -- `@effect/platform/Command` -> `effect/unstable/process/ChildProcess` -- `@effect/platform/CommandExecutor` -> `effect/unstable/process/ChildProcessSpawner` -- `@effect/platform/Cookies` -> `effect/unstable/http/Cookies` +- `@effect/platform/Command` -> `effect/process/ChildProcess` +- `@effect/platform/CommandExecutor` -> `effect/process/ChildProcessSpawner` +- `@effect/platform/Cookies` -> `effect/http/Cookies` - `@effect/platform/Effectify` -> `effect/Effect`: effectify moved into the Effect module as Effect.effectify; the Effectify and EffectifyError type helpers live in the Effect namespace as well. - `@effect/platform/Error` -> `effect/PlatformError` -- `@effect/platform/Etag` -> `effect/unstable/http/Etag` -- `@effect/platform/FetchHttpClient` -> `effect/unstable/http/FetchHttpClient` +- `@effect/platform/Etag` -> `effect/http/Etag` +- `@effect/platform/FetchHttpClient` -> `effect/http/FetchHttpClient` - `@effect/platform/FileSystem` -> `effect/FileSystem` -- `@effect/platform/Headers` -> `effect/unstable/http/Headers` -- `@effect/platform/HttpApi` -> `effect/unstable/httpapi/HttpApi` -- `@effect/platform/HttpApiBuilder` -> `effect/unstable/httpapi/HttpApiBuilder` -- `@effect/platform/HttpApiClient` -> `effect/unstable/httpapi/HttpApiClient` -- `@effect/platform/HttpApiEndpoint` -> `effect/unstable/httpapi/HttpApiEndpoint` -- `@effect/platform/HttpApiError` -> `effect/unstable/httpapi/HttpApiError` -- `@effect/platform/HttpApiGroup` -> `effect/unstable/httpapi/HttpApiGroup` -- `@effect/platform/HttpApiMiddleware` -> `effect/unstable/httpapi/HttpApiMiddleware` -- `@effect/platform/HttpApiScalar` -> `effect/unstable/httpapi/HttpApiScalar` -- `@effect/platform/HttpApiSchema` -> `effect/unstable/httpapi/HttpApiSchema` -- `@effect/platform/HttpApiSecurity` -> `effect/unstable/httpapi/HttpApiSecurity` -- `@effect/platform/HttpApiSwagger` -> `effect/unstable/httpapi/HttpApiSwagger` -- `@effect/platform/HttpApp` -> `effect/unstable/http/HttpEffect` -- `@effect/platform/HttpBody` -> `effect/unstable/http/HttpBody` -- `@effect/platform/HttpClient` -> `effect/unstable/http/HttpClient` -- `@effect/platform/HttpClientError` -> `effect/unstable/http/HttpClientError` -- `@effect/platform/HttpClientRequest` -> `effect/unstable/http/HttpClientRequest` -- `@effect/platform/HttpClientResponse` -> `effect/unstable/http/HttpClientResponse` -- `@effect/platform/HttpIncomingMessage` -> `effect/unstable/http/HttpIncomingMessage` +- `@effect/platform/Headers` -> `effect/http/Headers` +- `@effect/platform/HttpApi` -> `effect/http-api/HttpApi` +- `@effect/platform/HttpApiBuilder` -> `effect/http-api/HttpApiBuilder` +- `@effect/platform/HttpApiClient` -> `effect/http-api/HttpApiClient` +- `@effect/platform/HttpApiEndpoint` -> `effect/http-api/HttpApiEndpoint` +- `@effect/platform/HttpApiError` -> `effect/http-api/HttpApiError` +- `@effect/platform/HttpApiGroup` -> `effect/http-api/HttpApiGroup` +- `@effect/platform/HttpApiMiddleware` -> `effect/http-api/HttpApiMiddleware` +- `@effect/platform/HttpApiScalar` -> `effect/http-api/HttpApiScalar` +- `@effect/platform/HttpApiSchema` -> `effect/http-api/HttpApiSchema` +- `@effect/platform/HttpApiSecurity` -> `effect/http-api/HttpApiSecurity` +- `@effect/platform/HttpApiSwagger` -> `effect/http-api/HttpApiSwagger` +- `@effect/platform/HttpApp` -> `effect/http/HttpEffect` +- `@effect/platform/HttpBody` -> `effect/http/HttpBody` +- `@effect/platform/HttpClient` -> `effect/http/HttpClient` +- `@effect/platform/HttpClientError` -> `effect/http/HttpClientError` +- `@effect/platform/HttpClientRequest` -> `effect/http/HttpClientRequest` +- `@effect/platform/HttpClientResponse` -> `effect/http/HttpClientResponse` +- `@effect/platform/HttpIncomingMessage` -> `effect/http/HttpIncomingMessage` - `@effect/platform/HttpLayerRouter`: No single module replacement; follow the curated per-API guidance below. -- `@effect/platform/HttpMethod` -> `effect/unstable/http/HttpMethod` -- `@effect/platform/HttpMiddleware` -> `effect/unstable/http/HttpMiddleware` +- `@effect/platform/HttpMethod` -> `effect/http/HttpMethod` +- `@effect/platform/HttpMiddleware` -> `effect/http/HttpMiddleware` - `@effect/platform/HttpMultiplex` -> `none`: The HttpMultiplex module, value, constructor, and nominal type id were removed with no v4 counterpart. Replace them with a custom first-match Effect dispatcher requiring HttpServerRequest; initialize an empty dispatcher, then add or fold predicate/app pairs into it. Recreate header helpers with predicates over lower-cased request header values using exact equality, String.startsWith, String.endsWith, or RegExp.test; recreate host helpers with the same comparisons over request.headers.host. -- `@effect/platform/HttpPlatform` -> `effect/unstable/http/HttpPlatform` -- `@effect/platform/HttpRouter` -> `effect/unstable/http/HttpRouter` -- `@effect/platform/HttpServer` -> `effect/unstable/http/HttpServer` -- `@effect/platform/HttpServerError` -> `effect/unstable/http/HttpServerError` -- `@effect/platform/HttpServerRequest` -> `effect/unstable/http/HttpServerRequest` -- `@effect/platform/HttpServerRespondable` -> `effect/unstable/http/HttpServerRespondable` -- `@effect/platform/HttpServerResponse` -> `effect/unstable/http/HttpServerResponse` -- `@effect/platform/HttpTraceContext` -> `effect/unstable/http/HttpTraceContext` -- `@effect/platform/KeyValueStore` -> `effect/unstable/persistence/KeyValueStore` -- `@effect/platform/MsgPack` -> `effect/unstable/encoding/SchemaBinary`: MessagePack support was removed. Schema-aware encode, decode, and duplex now live on SchemaBinary. Untyped MessagePack of unknown values has no replacement. -- `@effect/platform/Multipart` -> `effect/unstable/http/Multipart` -- `@effect/platform/Ndjson` -> `effect/unstable/encoding/Ndjson` -- `@effect/platform/OpenApi` -> `effect/unstable/httpapi/OpenApi` +- `@effect/platform/HttpPlatform` -> `effect/http/HttpPlatform` +- `@effect/platform/HttpRouter` -> `effect/http/HttpRouter` +- `@effect/platform/HttpServer` -> `effect/http/HttpServer` +- `@effect/platform/HttpServerError` -> `effect/http/HttpServerError` +- `@effect/platform/HttpServerRequest` -> `effect/http/HttpServerRequest` +- `@effect/platform/HttpServerRespondable` -> `effect/http/HttpServerRespondable` +- `@effect/platform/HttpServerResponse` -> `effect/http/HttpServerResponse` +- `@effect/platform/HttpTraceContext` -> `effect/http/HttpTraceContext` +- `@effect/platform/KeyValueStore` -> `effect/persistence/KeyValueStore` +- `@effect/platform/MsgPack` -> `effect/encoding/SchemaBinary`: MessagePack support was removed. Schema-aware encode, decode, and duplex now live on SchemaBinary. Untyped MessagePack of unknown values has no replacement. +- `@effect/platform/Multipart` -> `effect/http/Multipart` +- `@effect/platform/Ndjson` -> `effect/encoding/Ndjson` +- `@effect/platform/OpenApi` -> `effect/http-api/OpenApi` - `@effect/platform/OpenApiJsonSchema`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform/Path` -> `effect/Path` - `@effect/platform/PlatformConfigProvider`: No single module replacement; follow the curated per-API guidance below. - `@effect/platform/PlatformLogger` -> `effect/Logger`: toFile moved to Logger.toFile; it still requires a FileSystem service (e.g. NodeFileSystem.layer) and Scope. - `@effect/platform/Runtime`: No single module replacement; follow the curated per-API guidance below. -- `@effect/platform/Socket` -> `effect/unstable/socket/Socket` -- `@effect/platform/SocketServer` -> `effect/unstable/socket/SocketServer` -- `@effect/platform/Template` -> `effect/unstable/http/Template` +- `@effect/platform/Socket` -> `effect/socket/Socket` +- `@effect/platform/SocketServer` -> `effect/socket/SocketServer` +- `@effect/platform/Template` -> `effect/http/Template` - `@effect/platform/Terminal` -> `effect/Terminal` -- `@effect/platform/Transferable` -> `effect/unstable/workers/Transferable` -- `@effect/platform/Url` -> `effect/unstable/http/Url` -- `@effect/platform/UrlParams` -> `effect/unstable/http/UrlParams` -- `@effect/platform/Worker` -> `effect/unstable/workers/Worker` -- `@effect/platform/WorkerError` -> `effect/unstable/workers/WorkerError` -- `@effect/platform/WorkerRunner` -> `effect/unstable/workers/WorkerRunner` -- `@effect/platform/index` -> `none`: The package barrel was removed along with the package; import each module from its new effect location (e.g. effect/FileSystem, effect/unstable/http/HttpClient) per the Import Map. +- `@effect/platform/Transferable` -> `effect/workers/Transferable` +- `@effect/platform/Url` -> `effect/http/Url` +- `@effect/platform/UrlParams` -> `effect/http/UrlParams` +- `@effect/platform/Worker` -> `effect/workers/Worker` +- `@effect/platform/WorkerError` -> `effect/workers/WorkerError` +- `@effect/platform/WorkerRunner` -> `effect/workers/WorkerRunner` +- `@effect/platform/index` -> `none`: The package barrel was removed along with the package; import each module from its new effect location (e.g. effect/FileSystem, effect/http/HttpClient) per the Import Map. - `@effect/printer` -> `none`: The @effect/printer document algebra was removed in v4 with no direct replacement. Use strings and joins for simple output, or adopt another pretty-printing library when adaptive layout is required. - `@effect/printer-ansi` -> `none`: The @effect/printer document algebra was removed in v4 with no direct replacement. Use strings and joins for simple output, or adopt another pretty-printing library when adaptive layout is required. - `@effect/printer-ansi/Ansi` -> `none`: The @effect/printer-ansi package was removed in v4 with no public replacement. Use a maintained ANSI library or local escape-string helpers; the v4 CLI ANSI helpers are internal and cannot be imported. -- `@effect/printer-ansi/AnsiDoc` -> `none`: The @effect/printer-ansi package and its annotated document algebra were removed in v4. Use strings or another pretty-printing library; for Effect CLI help only, use HelpDoc with CliOutput from effect/unstable/cli. +- `@effect/printer-ansi/AnsiDoc` -> `none`: The @effect/printer-ansi package and its annotated document algebra were removed in v4. Use strings or another pretty-printing library; for Effect CLI help only, use HelpDoc with CliOutput from effect/cli. - `@effect/printer-ansi/Color` -> `none`: The @effect/printer-ansi package was removed in v4, and Effect no longer provides a public ANSI color ADT. Use a maintained ANSI library or local escape-string helpers. - `@effect/printer-ansi/index` -> `none`: The @effect/printer document algebra was removed in v4 with no direct replacement. Use strings and joins for simple output, or adopt another pretty-printing library when adaptive layout is required. - `@effect/printer/Doc` -> `none`: The @effect/printer document algebra was removed in v4 with no direct replacement. Use strings and joins for simple output, or adopt another pretty-printing library when adaptive layout is required. - `@effect/printer/DocStream` -> `none`: The @effect/printer package and its laid-out DocStream intermediate representation were removed in v4. Use a target-specific renderer or another pretty-printing library. - `@effect/printer/DocTree` -> `none`: The @effect/printer package and its structured DocTree rendering representation were removed in v4. Use a target-specific tree and renderer or another pretty-printing library. - `@effect/printer/Flatten` -> `none`: This printer-specific flattening result was removed with the @effect/printer document algebra in v4 and has no direct replacement. -- `@effect/printer/Layout` -> `none`: The @effect/printer layout pipeline was removed in v4 with no general replacement. Use another pretty-printing library; for Effect CLI output only, use CliOutput from effect/unstable/cli. +- `@effect/printer/Layout` -> `none`: The @effect/printer layout pipeline was removed in v4 with no general replacement. Use another pretty-printing library; for Effect CLI output only, use CliOutput from effect/cli. - `@effect/printer/Optimize` -> `none`: The @effect/printer document optimizer was removed with the document algebra in v4. String-based output needs no equivalent optimization stage. - `@effect/printer/PageWidth` -> `none`: The @effect/printer page-width layout model was removed in v4 with no direct replacement. Use Terminal.columns for terminal dimensions, or another pretty-printing library for page-width-aware layout. - `@effect/printer/index` -> `none`: The @effect/printer document algebra was removed in v4 with no direct replacement. Use strings and joins for simple output, or adopt another pretty-printing library when adaptive layout is required. -- `@effect/rpc` -> `effect/unstable/rpc`: The @effect/rpc package was merged into the effect package; import the effect/unstable/rpc barrel or import specific modules directly (e.g. effect/unstable/rpc/\). -- `@effect/rpc/Rpc` -> `effect/unstable/rpc/Rpc` -- `@effect/rpc/RpcClient` -> `effect/unstable/rpc/RpcClient` -- `@effect/rpc/RpcClientError` -> `effect/unstable/rpc/RpcClientError` -- `@effect/rpc/RpcGroup` -> `effect/unstable/rpc/RpcGroup` -- `@effect/rpc/RpcMessage` -> `effect/unstable/rpc/RpcMessage` -- `@effect/rpc/RpcMiddleware` -> `effect/unstable/rpc/RpcMiddleware` -- `@effect/rpc/RpcSchema` -> `effect/unstable/rpc/RpcSchema` -- `@effect/rpc/RpcSerialization` -> `effect/unstable/rpc/RpcSerialization` -- `@effect/rpc/RpcServer` -> `effect/unstable/rpc/RpcServer` -- `@effect/rpc/RpcTest` -> `effect/unstable/rpc/RpcTest` -- `@effect/rpc/RpcWorker` -> `effect/unstable/rpc/RpcWorker` -- `@effect/rpc/index` -> `effect/unstable/rpc`: The package barrel was removed; import the same namespaces from the effect/unstable/rpc barrel or import specific modules directly. -- `@effect/sql` -> `effect/unstable/sql`: The @effect/sql package was merged into the effect package; import the effect/unstable/sql barrel or import specific modules directly (e.g. effect/unstable/sql/\). +- `@effect/rpc` -> `effect/rpc`: The @effect/rpc package was merged into the effect package; import the effect/rpc barrel or import specific modules directly (e.g. effect/rpc/\). +- `@effect/rpc/Rpc` -> `effect/rpc/Rpc` +- `@effect/rpc/RpcClient` -> `effect/rpc/RpcClient` +- `@effect/rpc/RpcClientError` -> `effect/rpc/RpcClientError` +- `@effect/rpc/RpcGroup` -> `effect/rpc/RpcGroup` +- `@effect/rpc/RpcMessage` -> `effect/rpc/RpcMessage` +- `@effect/rpc/RpcMiddleware` -> `effect/rpc/RpcMiddleware` +- `@effect/rpc/RpcSchema` -> `effect/rpc/RpcSchema` +- `@effect/rpc/RpcSerialization` -> `effect/rpc/RpcSerialization` +- `@effect/rpc/RpcServer` -> `effect/rpc/RpcServer` +- `@effect/rpc/RpcTest` -> `effect/rpc/RpcTest` +- `@effect/rpc/RpcWorker` -> `effect/rpc/RpcWorker` +- `@effect/rpc/index` -> `effect/rpc`: The package barrel was removed; import the same namespaces from the effect/rpc barrel or import specific modules directly. +- `@effect/sql` -> `effect/sql`: The @effect/sql package was merged into the effect package; import the effect/sql barrel or import specific modules directly (e.g. effect/sql/\). - `@effect/sql-clickhouse/index` -> `@effect/sql-clickhouse`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/sql-clickhouse package root or import specific modules directly. - `@effect/sql-d1/index` -> `@effect/sql-d1`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/sql-d1 package root or import specific modules directly. - `@effect/sql-drizzle/Mysql`: No single module replacement; follow the curated per-API guidance below. @@ -616,20 +616,20 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `@effect/sql-sqlite-node/index` -> `@effect/sql-sqlite-node`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/sql-sqlite-node package root or import specific modules directly. - `@effect/sql-sqlite-react-native/index` -> `@effect/sql-sqlite-react-native`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/sql-sqlite-react-native package root or import specific modules directly. - `@effect/sql-sqlite-wasm/index` -> `@effect/sql-sqlite-wasm`: The explicit /index entrypoint was removed; import the same namespaces from the @effect/sql-sqlite-wasm package root or import specific modules directly. -- `@effect/sql/Migrator` -> `effect/unstable/sql/Migrator` -- `@effect/sql/Migrator/FileSystem` -> `effect/unstable/sql/Migrator`: fromFileSystem was merged into the main Migrator module with the same (directory) signature; use Migrator.fromFileSystem as the loader. -- `@effect/sql/Model` -> `effect/unstable/schema/Model`, `effect/unstable/sql/SqlModel` -- `@effect/sql/SqlClient` -> `effect/unstable/sql/SqlClient` -- `@effect/sql/SqlConnection` -> `effect/unstable/sql/SqlConnection` -- `@effect/sql/SqlError` -> `effect/unstable/sql/SqlError` -- `@effect/sql/SqlEventJournal` -> `effect/unstable/eventlog/SqlEventJournal` -- `@effect/sql/SqlEventLogServer` -> `effect/unstable/eventlog/SqlEventLogServerEncrypted` +- `@effect/sql/Migrator` -> `effect/sql/Migrator` +- `@effect/sql/Migrator/FileSystem` -> `effect/sql/Migrator`: fromFileSystem was merged into the main Migrator module with the same (directory) signature; use Migrator.fromFileSystem as the loader. +- `@effect/sql/Model` -> `effect/schema/Model`, `effect/sql/SqlModel` +- `@effect/sql/SqlClient` -> `effect/sql/SqlClient` +- `@effect/sql/SqlConnection` -> `effect/sql/SqlConnection` +- `@effect/sql/SqlError` -> `effect/sql/SqlError` +- `@effect/sql/SqlEventJournal` -> `effect/eventlog/SqlEventJournal` +- `@effect/sql/SqlEventLogServer` -> `effect/eventlog/SqlEventLogServerEncrypted` - `@effect/sql/SqlPersistedQueue`: No single module replacement; follow the curated per-API guidance below. -- `@effect/sql/SqlResolver` -> `effect/unstable/sql/SqlResolver` -- `@effect/sql/SqlSchema` -> `effect/unstable/sql/SqlSchema` -- `@effect/sql/SqlStream` -> `effect/unstable/sql/SqlStream` -- `@effect/sql/Statement` -> `effect/unstable/sql/Statement` -- `@effect/sql/index` -> `effect/unstable/sql`: The package barrel was removed; import the same namespaces from the effect/unstable/sql barrel or import specific modules directly. +- `@effect/sql/SqlResolver` -> `effect/sql/SqlResolver` +- `@effect/sql/SqlSchema` -> `effect/sql/SqlSchema` +- `@effect/sql/SqlStream` -> `effect/sql/SqlStream` +- `@effect/sql/Statement` -> `effect/sql/Statement` +- `@effect/sql/index` -> `effect/sql`: The package barrel was removed; import the same namespaces from the effect/sql barrel or import specific modules directly. - `@effect/typeclass` -> `none`: The @effect/typeclass package was removed in v4 with no generic typeclass layer replacement. Rewrite abstractions against the concrete v4 data type and its module functions. - `@effect/typeclass/Alternative` -> `none`: The @effect/typeclass package was removed in v4 with no generic typeclass layer replacement. Rewrite this abstraction against the concrete v4 data type and its module functions. - `@effect/typeclass/Applicative` -> `none`: The @effect/typeclass package was removed in v4 with no generic typeclass layer replacement. Rewrite this abstraction against the concrete v4 data type and its module functions. @@ -672,24 +672,24 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `@effect/typeclass/data/Tuple`: No single module replacement; follow the curated per-API guidance below. - `@effect/typeclass/index` -> `none`: The @effect/typeclass package was removed in v4 with no generic typeclass layer replacement. Rewrite abstractions against the concrete v4 data type and its module functions. - `@effect/vitest/index`: No single module replacement; follow the curated per-API guidance below. -- `@effect/workflow` -> `effect/unstable/workflow`: The @effect/workflow package was merged into the effect package; import the effect/unstable/workflow barrel or import specific modules directly (e.g. effect/unstable/workflow/\). -- `@effect/workflow/Activity` -> `effect/unstable/workflow/Activity` -- `@effect/workflow/DurableClock` -> `effect/unstable/workflow/DurableClock` -- `@effect/workflow/DurableDeferred` -> `effect/unstable/workflow/DurableDeferred` -- `@effect/workflow/DurableQueue` -> `effect/unstable/workflow/DurableQueue` +- `@effect/workflow` -> `effect/workflow`: The @effect/workflow package was merged into the effect package; import the effect/workflow barrel or import specific modules directly (e.g. effect/workflow/\). +- `@effect/workflow/Activity` -> `effect/workflow/Activity` +- `@effect/workflow/DurableClock` -> `effect/workflow/DurableClock` +- `@effect/workflow/DurableDeferred` -> `effect/workflow/DurableDeferred` +- `@effect/workflow/DurableQueue` -> `effect/workflow/DurableQueue` - `@effect/workflow/DurableRateLimiter` -> `none`: Not ported. Build an Activity whose execute uses persistence RateLimiter.consume with onExceeded: delay, then sleeps for the returned delay with DurableClock. -- `@effect/workflow/Workflow` -> `effect/unstable/workflow/Workflow` -- `@effect/workflow/WorkflowEngine` -> `effect/unstable/workflow/WorkflowEngine` -- `@effect/workflow/WorkflowProxy` -> `effect/unstable/workflow/WorkflowProxy` -- `@effect/workflow/WorkflowProxyServer` -> `effect/unstable/workflow/WorkflowProxyServer` -- `effect/Arbitrary` -> `effect/unstable/arbitrary/Arbitrary`: Schema-derived generation moved to the native Arbitrary module. Effect no longer bridges to fast-check. +- `@effect/workflow/Workflow` -> `effect/workflow/Workflow` +- `@effect/workflow/WorkflowEngine` -> `effect/workflow/WorkflowEngine` +- `@effect/workflow/WorkflowProxy` -> `effect/workflow/WorkflowProxy` +- `@effect/workflow/WorkflowProxyServer` -> `effect/workflow/WorkflowProxyServer` - `effect/ChildExecutorDecision` -> `none`: Removed with the v3 channel executor and Channel.concatMapWithCustom. Choose Channel.flatMap, Channel.switchMap, or Channel.mergeAll instead; v4 exposes no child-executor decision ADT. - `effect/ConfigError`: No single module replacement; follow the curated per-API guidance below. - `effect/ConfigProviderPathPatch`: No single module replacement; follow the curated per-API guidance below. - `effect/DefaultServices`: No single module replacement; follow the curated per-API guidance below. - `effect/Either` -> `effect/Result` +- `effect/Encoding` -> `none`: Import each format or the shared error API from its direct subpath. - `effect/ExecutionStrategy`: No single module replacement; follow the curated per-API guidance below. -- `effect/FastCheck` -> `fast-check`: Effect no longer re-exports fast-check. Depend on the fast-check package and import it directly. For Schema-derived generation, use Arbitrary.schema from effect/unstable/arbitrary. +- `effect/FastCheck` -> `fast-check`: Effect no longer re-exports fast-check. Depend on the fast-check package and import it directly. For Schema-derived generation, use Arbitrary.schema from effect/Arbitrary. - `effect/FiberId`: No single module replacement; follow the curated per-API guidance below. - `effect/FiberRef` -> `effect/References` - `effect/FiberRefs`: No single module replacement; follow the curated per-API guidance below. @@ -719,7 +719,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `effect/MutableQueue`: No single module replacement; follow the curated per-API guidance below. - `effect/ParseResult` -> `effect/SchemaIssue`, `effect/SchemaParser` - `effect/Pretty`: No single module replacement; follow the curated per-API guidance below. -- `effect/RateLimiter` -> `none`: The old limit, interval, and algorithm options belonged to the removed in-process limiter; choose and configure an application limiter explicitly. The scoped in-process callable limiter was not ported to v4; effect/unstable/persistence/RateLimiter is a keyed persistence service with different semantics, not a drop-in replacement. The FiberRef-based per-effect cost annotation was removed with the core RateLimiter; pass token cost explicitly to the replacement limiter. +- `effect/RateLimiter` -> `none`: The old limit, interval, and algorithm options belonged to the removed in-process limiter; choose and configure an application limiter explicitly. The scoped in-process callable limiter was not ported to v4; effect/persistence/RateLimiter is a keyed persistence service with different semantics, not a drop-in replacement. The FiberRef-based per-effect cost annotation was removed with the core RateLimiter; pass token cost explicitly to the replacement limiter. - `effect/Readable`: No single module replacement; follow the curated per-API guidance below. - `effect/RedBlackTree`: No single module replacement; follow the curated per-API guidance below. - `effect/Reloadable`: No single module replacement; follow the curated per-API guidance below. @@ -828,9 +828,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai-anthropic/AnthropicTokenizer` -- `AnthropicTokenizer.layer` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required. +- `AnthropicTokenizer.layer` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required. -- `AnthropicTokenizer.make` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required. +- `AnthropicTokenizer.make` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using @anthropic-ai/tokenizer if equivalent Anthropic counting is required. ### `@effect/ai-anthropic/AnthropicTool` @@ -4160,9 +4160,15 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai-openai/OpenAiTokenizer` -- `OpenAiTokenizer.layer` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required. +- `OpenAiTokenizer.layer` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required. -- `OpenAiTokenizer.make` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/unstable/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required. +- `OpenAiTokenizer.make` -> `Tokenizer.make`: The provider-specific tokenizer module was removed; build and provide an effect/ai/Tokenizer service explicitly, using gpt-tokenizer if equivalent OpenAI counting is required. + +### `@effect/ai-openai/OpenAiTool` + +- `OpenAiTool.WebSearch` -> `OpenAiTool.WebSearch`: Results now include action alongside status. Only completed is successful; other statuses produce failure results. Inspect isFailure before treating a result as a successful search. Inspect each search source by its type: URL sources have url, while API sources have name and no url. + +- `OpenAiTool.WebSearchPreview` -> `OpenAiTool.WebSearchPreview`: Results now include action alongside status. Only completed is successful; other statuses produce failure results. Inspect isFailure before treating a result as a successful search. Inspect each search source by its type: URL sources have url, while API sources have name and no url. ### `@effect/ai-openrouter/Generated` @@ -4878,7 +4884,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `OpenRouterClient.ChatStreamingResponseChunk` -> `OpenRouterClient.ChatStreamingResponseChunkData`: The standalone streaming chunk schema was replaced by the decoded data type from Generated.ChatStreamingResponse. -- `OpenRouterClient.Service` -> `OpenRouterClient.Service`: Still exported in v4; adapt to the regenerated client, revised request and response schemas, and the new streaming result tuple. +- `OpenRouterClient.Service` -> `OpenRouterClient.Service`: Still exported in v4; adapt to the regenerated client, revised request and response schemas, and the new streaming result tuple. Custom service implementations and mocks must also provide createDecisions, returning an Effect of [decoded DecisionsResponse, HttpClientResponse] with AiError failures. ### `@effect/ai-openrouter/OpenRouterConfig` @@ -4896,9 +4902,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai/AiError` -- `AiError.AiError` -> `AiError.AiError`: Moved to effect/unstable/ai/AiError and redesigned from a union of separately tagged errors into one AiError wrapper with a semantic reason. Construct it with AiError.make({ module, method, reason }) and match error.reason rather than the old top-level tags. +- `AiError.AiError` -> `AiError.AiError`: Moved to effect/ai/AiError and redesigned from a union of separately tagged errors into one AiError wrapper with a semantic reason. Construct it with AiError.make({ module, method, reason }) and match error.reason rather than the old top-level tags. -- `AiError.HttpRequestDetails` -> `AiError.HttpRequestDetails`: Retained in effect/unstable/ai/AiError and also re-exported as Response.HttpRequestDetails. Hash is an optional string instead of Option, headers may contain Redacted strings, and method includes TRACE. +- `AiError.HttpRequestDetails` -> `AiError.HttpRequestDetails`: Retained in effect/ai/AiError and also re-exported as Response.HttpRequestDetails. Hash is an optional string instead of Option, headers may contain Redacted strings, and method includes TRACE. - `AiError.HttpRequestError` -> `AiError.make + AiError.NetworkError`: Replace the old top-level request error with an AiError whose reason is NetworkError. NetworkError.fromRequestError converts a v4 HttpClientError.RequestError. @@ -4930,13 +4936,13 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai/IdGenerator` -- `IdGenerator.make` -> `IdGenerator.make`: Moved to effect/unstable/ai/IdGenerator with the same configurable alphabet, prefix, separator, and size behavior. Invalid configuration now fails with Cause.IllegalArgumentError. +- `IdGenerator.make` -> `IdGenerator.make`: Moved to effect/ai/IdGenerator with the same configurable alphabet, prefix, separator, and size behavior. Invalid configuration now fails with Cause.IllegalArgumentError. ### `@effect/ai/LanguageModel` - `LanguageModel.ConstructorParams` -> `none`: V4 inlines this provider-adapter shape in LanguageModel.make. Pass generateText and streamText directly to make, with optional codecTransformer, instead of naming a constructor-parameter type. -- `LanguageModel.ExtractContext` -> `LanguageModel.ExtractServices`: Renamed in effect/unstable/ai/LanguageModel. ExtractServices infers toolkit handler, result-decoding, and effectful-toolkit service requirements. +- `LanguageModel.ExtractContext` -> `LanguageModel.ExtractServices`: Renamed in effect/ai/LanguageModel. ExtractServices infers toolkit handler, result-decoding, and effectful-toolkit service requirements. - `LanguageModel.LanguageModel` -> `LanguageModel.LanguageModel`: Use the LanguageModel interface for implementations and the Context.Service value as the service key; it is no longer a class. Prefer LanguageModel.make to construct the branded implementation. @@ -4944,27 +4950,41 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai/McpSchema` -- `McpSchema.ContentBlock` -> `McpSchema.ContentBlock`: Moved to effect/unstable/ai/McpSchema. It remains the MCP content-block union, but v4 exports it as a const schema rather than a Schema.Union subclass. Binary image, audio, and blob data still use Uint8Array values with base64 wire encoding. +- `McpSchema.ContentBlock` -> `McpSchema.ContentBlock`: Moved to effect/ai/McpSchema. It remains the MCP content-block union, but v4 exports it as a const schema rather than a Schema.Union subclass. Binary image, audio, and blob data still use Uint8Array values with base64 wire encoding. -- `McpSchema.ElicitResult` -> `McpSchema.ElicitResult`: Moved to effect/unstable/ai/McpSchema. It remains discriminated by action, but accepted content is now optional and, when present, is a record of strings, finite numbers, booleans, or string arrays; declined and canceled responses still omit content. +- `McpSchema.ElicitResult` -> `McpSchema.ElicitResult`: Moved to effect/ai/McpSchema. It remains discriminated by action, but accepted content is now optional and, when present, is a record of strings, finite numbers, booleans, or string arrays; declined and canceled responses still omit content. - `McpSchema.McpError` -> `McpSchema.McpError`: Moved, but changed from a constructable base class to a union schema of standard tagged protocol errors plus McpErrorBase. Use McpErrorBase to construct a generic MCP error. - `McpSchema.ParamAnnotation` -> `McpSchema.isParam / Param.name`: The public symbol annotation was removed. Detect parameter wrappers with McpSchema.isParam and read the narrowed Param.name instead of inspecting AST annotations. -- `McpSchema.param` -> `McpSchema.param`: Moved to effect/unstable/ai/McpSchema. V4 wraps the schema and exposes Param.name and Param.schema instead of attaching a public symbol annotation. +- `McpSchema.param` -> `McpSchema.param`: Moved to effect/ai/McpSchema. V4 wraps the schema and exposes Param.name and Param.schema instead of attaching a public symbol annotation. ### `@effect/ai/McpServer` -- `McpServer.layer` -> `McpServer.layer`: Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/unstable/ai; it still runs over a caller-provided RpcServer.Protocol. +- `McpServer.McpServer` -> `McpServer.McpServer`: The service is now a Context.Service. initializedClients and notificationsMailbox are no longer exposed. Registration callbacks use McpSchema.McpRequestContext; McpServerClient is only supplied for initialized stateful requests. Prefer registerToolkit, registerResource, and registerPrompt over implementing the registry shape directly. + +- `McpServer.layer` -> `McpServer.layer`: Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai; it still runs over a caller-provided RpcServer.Protocol. + +- `McpServer.layerHttp` -> `McpServer.layerHttp`: Moved to effect/ai/McpServer and the unified HttpRouter. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai. + +- `McpServer.layerHttpRouter` -> `McpServer.layerHttp`: Renamed and consolidated. V4 layerHttp registers the Streamable HTTP endpoint in the unified HttpRouter; pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai. + +- `McpServer.layerStdio` -> `McpServer.layerStdio`: Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai. -- `McpServer.layerHttp` -> `McpServer.layerHttp`: Moved to effect/unstable/ai/McpServer and the unified HttpRouter. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/unstable/ai. +- `McpServer.prompt` -> `McpServer.prompt`: Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding and handler requirements instead of McpServerClient. -- `McpServer.layerHttpRouter` -> `McpServer.layerHttp`: Renamed and consolidated. V4 layerHttp registers the Streamable HTTP endpoint in the unified HttpRouter; pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/unstable/ai. +- `McpServer.registerPrompt` -> `McpServer.registerPrompt`: Moved to effect/ai/McpServer. Prompt decoding and handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata. -- `McpServer.layerStdio` -> `McpServer.layerStdio`: Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/unstable/ai. +- `McpServer.registerResource` -> `McpServer.registerResource`: Moved to effect/ai/McpServer. Resource and completion handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata. -- `McpServer.run` -> `McpServer.run`: Moved to effect/unstable/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/unstable/ai; it remains the Effect-level runner over RpcServer.Protocol. +- `McpServer.registerToolkit` -> `McpServer.registerToolkit`: Moved to effect/ai/McpServer. Handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams. + +- `McpServer.resource` -> `McpServer.resource`: Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from resource and completion handler requirements instead of McpServerClient. + +- `McpServer.run` -> `McpServer.run`: Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai; it remains the Effect-level runner over RpcServer.Protocol. + +- `McpServer.toolkit` -> `McpServer.toolkit`: Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema. ### `@effect/ai/Model` @@ -4972,47 +4992,47 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai/Prompt` -- `Prompt.FilePart` -> `Prompt.FilePart`: Moved to effect/unstable/ai/Prompt with the same file-part model and schema; update the module import. +- `Prompt.FilePart` -> `Prompt.FilePart`: Moved to effect/ai/Prompt with the same file-part model and schema; update the module import. - `Prompt.FromJson` -> `Schema.fromJsonString(Prompt.Prompt)`: The module-specific JSON schema was removed. Compose the general v4 JSON-string codec with the public Prompt codec. -- `Prompt.MessageContentFromString` -> `Prompt.ContentFromString`: Renamed in effect/unstable/ai/Prompt. It still decodes a string to a non-empty array containing one TextPart and encodes the first part's text. +- `Prompt.MessageContentFromString` -> `Prompt.ContentFromString`: Renamed in effect/ai/Prompt. It still decodes a string to a non-empty array containing one TextPart and encodes the first part's text. - `Prompt.MessageTypeId` -> `Prompt.isMessage`: The message type id is private in v4. Use the public isMessage guard for runtime refinement instead of importing or inspecting the marker. -- `Prompt.Part` -> `Prompt.Part`: Moved to effect/unstable/ai/Prompt. The union now also includes tool-approval request and response parts. +- `Prompt.Part` -> `Prompt.Part`: Moved to effect/ai/Prompt. The union now also includes tool-approval request and response parts. -- `Prompt.PartEncoded` -> `Prompt.PartEncoded`: Moved to effect/unstable/ai/Prompt. The encoded union now also includes tool-approval request and response parts. +- `Prompt.PartEncoded` -> `Prompt.PartEncoded`: Moved to effect/ai/Prompt. The encoded union now also includes tool-approval request and response parts. - `Prompt.PartTypeId` -> `Prompt.isPart`: The part type id is private in v4. Use the public isPart guard for runtime refinement instead of importing or inspecting the marker. - `Prompt.PromptFromSelf` -> `Prompt.Prompt`: The standalone declared from-self schema was removed. Use the public Prompt codec for prompt validation and encoding, or Prompt.isPrompt when only runtime refinement is needed. -- `Prompt.ReasoningPart` -> `Prompt.ReasoningPart`: Moved to effect/unstable/ai/Prompt with the same reasoning-part model and schema; update the module import. +- `Prompt.ReasoningPart` -> `Prompt.ReasoningPart`: Moved to effect/ai/Prompt with the same reasoning-part model and schema; update the module import. -- `Prompt.TextPart` -> `Prompt.TextPart`: Moved to effect/unstable/ai/Prompt with the same text-part model and schema; update the module import. +- `Prompt.TextPart` -> `Prompt.TextPart`: Moved to effect/ai/Prompt with the same text-part model and schema; update the module import. -- `Prompt.ToolCallPart` -> `Prompt.ToolCallPart`: Moved to effect/unstable/ai/Prompt with the same tool-call model and schema; update the module import. +- `Prompt.ToolCallPart` -> `Prompt.ToolCallPart`: Moved to effect/ai/Prompt with the same tool-call model and schema; update the module import. -- `Prompt.ToolResultPartEncoded` -> `Prompt.ToolResultPartEncoded`: Moved to effect/unstable/ai/Prompt, but providerExecuted was removed from the encoded prompt tool-result shape. +- `Prompt.ToolResultPartEncoded` -> `Prompt.ToolResultPartEncoded`: Moved to effect/ai/Prompt, but providerExecuted was removed from the encoded prompt tool-result shape. - `Prompt.TypeId` -> `Prompt.isPrompt`: The Prompt type id is private in v4 and its internal literal changed. Use the public isPrompt guard instead of importing or inspecting the marker. -- `Prompt.makePart` -> `Prompt.makePart`: Moved to effect/unstable/ai/Prompt. The generic constructor also supports the new tool-approval request and response part variants. +- `Prompt.makePart` -> `Prompt.makePart`: Moved to effect/ai/Prompt. The generic constructor also supports the new tool-approval request and response part variants. - `Prompt.merge` -> `Prompt.concat`: Renamed in v4. concat preserves the old dual API and concatenates the messages from a Prompt with additional raw input. ### `@effect/ai/Response` -- `Response.Part` -> `Response.Part`: Moved to effect/unstable/ai/Response. The non-streaming union now also includes ToolApprovalRequestPart. +- `Response.Part` -> `Response.Part`: Moved to effect/ai/Response. The non-streaming union now also includes ToolApprovalRequestPart. - `Response.PartTypeId` -> `Response.isPart`: The public PartTypeId was removed and the marker is internal in v4. Use Response.isPart for runtime refinement. -- `Response.ToolCallPartEncoded` -> `Response.ToolCallPartEncoded`: Moved to effect/unstable/ai/Response; providerName was removed while providerExecuted remains optional when encoded. +- `Response.ToolCallPartEncoded` -> `Response.ToolCallPartEncoded`: Moved to effect/ai/Response; providerName was removed while providerExecuted remains optional when encoded. - `Response.ToolResultPart` -> `Response.ToolResultPart`: The schema selects success or failure using isFailure rather than trying both result schemas. It returns Schema.Codec; supply decoding services when decoding and encoding services when encoding. -- `Response.ToolResultPartEncoded` -> `Response.ToolResultPartEncoded`: Moved to effect/unstable/ai/Response; providerName was removed and optional preliminary was added to the encoded shape. +- `Response.ToolResultPartEncoded` -> `Response.ToolResultPartEncoded`: Moved to effect/ai/Response; providerName was removed and optional preliminary was added to the encoded shape. - `Response.documentSourcePart` -> `Response.makePart("source", { ...params, sourceType: "document" })`: The lowercase convenience constructor was removed. The DocumentSourcePart model remains, and the generic constructor now requires the document source discriminator. @@ -5040,7 +5060,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Response.toolParamsStartPart` -> `Response.makePart("tool-params-start", params)`: The lowercase convenience constructor was removed; providerName was also removed from ToolParamsStartPart in v4. -- `Response.toolResultPart` -> `Response.toolResultPart`: Moved to effect/unstable/ai/Response; providerName was removed and decoded tool results now require preliminary, normally false. +- `Response.toolResultPart` -> `Response.toolResultPart`: Moved to effect/ai/Response; providerName was removed and decoded tool results now require preliminary, normally false. - `Response.urlSourcePart` -> `Response.makePart("source", { ...params, sourceType: "url" })`: The lowercase convenience constructor was removed. The UrlSourcePart model remains, and the generic constructor now requires the URL source discriminator. @@ -5050,19 +5070,19 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Tool.AnyTaggedRequestSchema` -> `none`: The TaggedRequest-specific Tool adapter contract was removed. Model the operation directly with Tool.make and ordinary v4 Schema.Constraint values. -- `Tool.Destructive` -> `Tool.Destructive`: Moved to effect/unstable/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains true. +- `Tool.Destructive` -> `Tool.Destructive`: Moved to effect/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains true. -- `Tool.Failure` -> `Tool.Failure`: Moved to effect/unstable/ai/Tool and remains the utility type that extracts a tool's decoded failure type. +- `Tool.Failure` -> `Tool.Failure`: Moved to effect/ai/Tool and remains the utility type that extracts a tool's decoded failure type. - `Tool.FromTaggedRequest` -> `Tool.Tool`: The dedicated derived alias was removed. Construct with Tool.make and let Tool.Tool infer the name, parameter, success, and failure schemas. -- `Tool.Idempotent` -> `Tool.Idempotent`: Moved to effect/unstable/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains false. +- `Tool.Idempotent` -> `Tool.Idempotent`: Moved to effect/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains false. -- `Tool.OpenWorld` -> `Tool.OpenWorld`: Moved to effect/unstable/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains true. +- `Tool.OpenWorld` -> `Tool.OpenWorld`: Moved to effect/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains true. -- `Tool.ProviderDefinedTypeId` -> `Tool.ProviderDefinedTypeId`: Moved to effect/unstable/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string. +- `Tool.ProviderDefinedTypeId` -> `Tool.ProviderDefinedTypeId`: Moved to effect/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string. -- `Tool.Readonly` -> `Tool.Readonly`: Moved to effect/unstable/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains false. +- `Tool.Readonly` -> `Tool.Readonly`: Moved to effect/ai/Tool. It is now a Context.Reference\ value rather than a Reference subclass; its default remains false. - `Tool.Requirements` -> `Tool.HandlerServices`: Renamed and refined. HandlerServices combines parameter-decoding, result-encoding, and request-level dependencies required by a tool handler. @@ -5070,7 +5090,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Tool.ResultEncoded` -> `Tool.ResultEncoded`: The encoded result includes execution-denied and execution-interrupted variants in both failure modes, plus encoded AiError in return mode. -- `Tool.Success` -> `Tool.Success`: Moved to effect/unstable/ai/Tool and remains the utility type that extracts a tool's decoded success type. +- `Tool.Success` -> `Tool.Success`: Moved to effect/ai/Tool and remains the utility type that extracts a tool's decoded success type. - `Tool.Tool.ProviderDefinedProto` -> `Tool.ProviderDefined`: This implementation-brand interface is no longer public. Use Tool.ProviderDefined for the model type and Tool.isProviderDefined for runtime narrowing. @@ -5078,7 +5098,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Tool.Tool.VarianceStruct` -> `Tool.Tool / Tool.Any`: This implementation variance payload is no longer public; the requirements marker is inline in Tool.Tool and should not be named independently. -- `Tool.TypeId` -> `Tool.TypeId`: Moved to effect/unstable/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string. +- `Tool.TypeId` -> `Tool.TypeId`: Moved to effect/ai/Tool and remains public. Its literal changed, so use the export rather than retaining the old hard-coded string. - `Tool.fromTaggedRequest` -> `Tool.make`: The adapter was removed. Rebuild the tool explicitly with Tool.make(name, { parameters, success, failure }); Toolkit.make no longer converts schema values automatically. @@ -5088,7 +5108,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/ai/Toolkit` -- `Toolkit.HandlersFrom` -> `Toolkit.HandlersFrom`: Moved to effect/unstable/ai/Toolkit. V4 handlers receive a HandlerContext argument and may fail with the declared failure, AiError, or AiErrorReason while requiring Tool.HandlerServices. +- `Toolkit.HandlersFrom` -> `Toolkit.HandlersFrom`: Moved to effect/ai/Toolkit. V4 handlers receive a HandlerContext argument and may fail with the declared failure, AiError, or AiErrorReason while requiring Tool.HandlerServices. - `Toolkit.TypeId` -> `Toolkit.Toolkit / Toolkit.Any`: The toolkit nominal id is private in v4. Use Toolkit.Toolkit or Toolkit.Any for typing instead of importing or inspecting the marker. @@ -5098,7 +5118,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Args.All.Return` -> `Command.Command.Config.Infer`: Infer the output of a command config record; standalone argument collections were removed. -- `Args.Args` -> `Argument.Argument`: Args was renamed to Argument in effect/unstable/cli. +- `Args.Args` -> `Argument.Argument`: Args was renamed to Argument in effect/cli. - `Args.Args.BaseArgsConfig` -> `name: string`: Argument constructors now take the name as a required first parameter. @@ -5408,7 +5428,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Options.All.Return` -> `Command.Command.Config.Infer`: Infer the output of a command config record; standalone flag collections were removed. -- `Options.Options` -> `Flag.Flag`: Options was renamed to Flag in effect/unstable/cli. +- `Options.Options` -> `Flag.Flag`: Options was renamed to Flag in effect/cli. - `Options.Options.BooleanOptionsConfig` -> `Flag.Boolean + Flag.withAlias + Flag.map`: The config object was removed; aliases and value inversion are combinators, while custom negation names need application logic. @@ -5540,7 +5560,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Prompt.All.Return` -> `Prompt.All.Return`: The collection result helper remains under Prompt.All. -- `Prompt.Prompt` -> `Prompt.Prompt`: The model moved to effect/unstable/cli; quitting now fails with Terminal.QuitError. +- `Prompt.Prompt` -> `Prompt.Prompt`: The model moved to effect/cli; quitting now fails with Terminal.QuitError. - `Prompt.Prompt.FloatOptions` -> `Prompt.NumberOptions`: Use the renamed public options type for Prompt.Number; it still extends the integer options type, now IntOptions. @@ -5656,7 +5676,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/cluster/ClusterCron` -- `ClusterCron.make` -> `effect/unstable/cluster/ClusterCron#make`: Moved into core Effect. The constructor remains; Duration.DurationInput is now Duration.Input. +- `ClusterCron.make` -> `effect/cluster/ClusterCron#make`: Moved into core Effect. The constructor remains; Duration.DurationInput is now Duration.Input. ### `@effect/cluster/ClusterError` @@ -5664,111 +5684,111 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/cluster/ClusterSchema` -- `ClusterSchema.ClientTracingEnabled` -> `effect/unstable/cluster/ClusterSchema#ClientTracingEnabled`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value. +- `ClusterSchema.ClientTracingEnabled` -> `effect/cluster/ClusterSchema#ClientTracingEnabled`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value. -- `ClusterSchema.Persisted` -> `effect/unstable/cluster/ClusterSchema#Persisted`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default. +- `ClusterSchema.Persisted` -> `effect/cluster/ClusterSchema#Persisted`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default. -- `ClusterSchema.ShardGroup` -> `effect/unstable/cluster/ClusterSchema#ShardGroup`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value. +- `ClusterSchema.ShardGroup` -> `effect/cluster/ClusterSchema#ShardGroup`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value. -- `ClusterSchema.Uninterruptible` -> `effect/unstable/cluster/ClusterSchema#Uninterruptible`: Now a Context.Reference value. Replace its static methods with ClusterSchema.isUninterruptibleForServer and isUninterruptibleForClient. +- `ClusterSchema.Uninterruptible` -> `effect/cluster/ClusterSchema#Uninterruptible`: Now a Context.Reference value. Replace its static methods with ClusterSchema.isUninterruptibleForServer and isUninterruptibleForClient. ### `@effect/cluster/ClusterWorkflowEngine` -- `ClusterWorkflowEngine.layer` -> `effect/unstable/cluster/ClusterWorkflowEngine#layer`: Moved into core Effect with the same cluster-backed WorkflowEngine layer composition. +- `ClusterWorkflowEngine.layer` -> `effect/cluster/ClusterWorkflowEngine#layer`: Moved into core Effect with the same cluster-backed WorkflowEngine layer composition. -- `ClusterWorkflowEngine.make` -> `effect/unstable/cluster/ClusterWorkflowEngine#make`: Moved into core Effect; the constructor still uses Sharding and MessageStorage. +- `ClusterWorkflowEngine.make` -> `effect/cluster/ClusterWorkflowEngine#make`: Moved into core Effect; the constructor still uses Sharding and MessageStorage. ### `@effect/cluster/DeliverAt` -- `DeliverAt.symbol` -> `effect/unstable/cluster/DeliverAt#symbol`: Moved into core Effect; the protocol key is now the string literal \~effect/cluster/DeliverAt rather than a global symbol. +- `DeliverAt.symbol` -> `effect/cluster/DeliverAt#symbol`: Moved into core Effect; the protocol key is now the string literal \~effect/cluster/DeliverAt rather than a global symbol. ### `@effect/cluster/Entity` -- `Entity.HandlersFrom` -> `effect/unstable/cluster/Entity#HandlersFrom`: Moved into core Effect; handler results now use Rpc.WrapperOr, which accepts either the raw RPC result or its wrapper. +- `Entity.HandlersFrom` -> `effect/cluster/Entity#HandlersFrom`: Moved into core Effect; handler results now use Rpc.WrapperOr, which accepts either the raw RPC result or its wrapper. -- `Entity.Replier.Success` -> `effect/unstable/cluster/Entity#Replier.Success`: Moved into core Effect; streaming replies may use Queue.Dequeue with Cause.Done instead of the removed Mailbox type. +- `Entity.Replier.Success` -> `effect/cluster/Entity#Replier.Success`: Moved into core Effect; streaming replies may use Queue.Dequeue with Cause.Done instead of the removed Mailbox type. - `Entity.TypeId` -> `none`: The entity marker is private in v4. Use Entity.isEntity for runtime refinement. ### `@effect/cluster/EntityAddress` -- `EntityAddress.EntityAddressFromSelf` -> `effect/unstable/cluster/EntityAddress#EntityAddress`: The separate self schema was removed; the v4 Schema.Class is itself the EntityAddress schema. +- `EntityAddress.EntityAddressFromSelf` -> `effect/cluster/EntityAddress#EntityAddress`: The separate self schema was removed; the v4 Schema.Class is itself the EntityAddress schema. - `EntityAddress.TypeId` -> `none`: The marker is private in v4. Use the exported EntityAddress class and schema. ### `@effect/cluster/EntityProxy` -- `EntityProxy.ConvertHttpApi` -> `effect/unstable/cluster/EntityProxy#ConvertHttpApi`: Moved into core Effect and updated to the v4 HttpApiEndpoint and Schema types. +- `EntityProxy.ConvertHttpApi` -> `effect/cluster/EntityProxy#ConvertHttpApi`: Moved into core Effect and updated to the v4 HttpApiEndpoint and Schema types. -- `EntityProxy.ConvertRpcs` -> `effect/unstable/cluster/EntityProxy#ConvertRpcs`: Moved into core Effect and updated to the v4 Rpc and Schema type parameters. +- `EntityProxy.ConvertRpcs` -> `effect/cluster/EntityProxy#ConvertRpcs`: Moved into core Effect and updated to the v4 Rpc and Schema type parameters. ### `@effect/cluster/EntityProxyServer` -- `EntityProxyServer.RpcHandlers` -> `effect/unstable/cluster/EntityProxyServer#RpcHandlers`: Moved into core Effect and updated for the additional v4 Rpc requirements type parameter. +- `EntityProxyServer.RpcHandlers` -> `effect/cluster/EntityProxyServer#RpcHandlers`: Moved into core Effect and updated for the additional v4 Rpc requirements type parameter. -- `EntityProxyServer.layerHttpApi` -> `effect/unstable/cluster/EntityProxyServer#layerHttpApi`: Moved into core Effect. Use v4 HttpApi identifiers and provide Sharding plus both Rpc.ServicesServer and Rpc.ServicesClient codec requirements. +- `EntityProxyServer.layerHttpApi` -> `effect/cluster/EntityProxyServer#layerHttpApi`: Moved into core Effect. Use v4 HttpApi identifiers and provide Sharding plus both Rpc.ServicesServer and Rpc.ServicesClient codec requirements. -- `EntityProxyServer.layerRpcHandlers` -> `effect/unstable/cluster/EntityProxyServer#layerRpcHandlers`: Moved into core Effect; replace Rpc.Context with both Rpc.ServicesServer and Rpc.ServicesClient codec requirements, alongside Sharding. +- `EntityProxyServer.layerRpcHandlers` -> `effect/cluster/EntityProxyServer#layerRpcHandlers`: Moved into core Effect; replace Rpc.Context with both Rpc.ServicesServer and Rpc.ServicesClient codec requirements, alongside Sharding. ### `@effect/cluster/EntityResource` -- `EntityResource.TypeId` -> `effect/unstable/cluster/EntityResource#TypeId`: Moved into core Effect; its literal changed to \~effect/cluster/EntityResource. +- `EntityResource.TypeId` -> `effect/cluster/EntityResource#TypeId`: Moved into core Effect; its literal changed to \~effect/cluster/EntityResource. -- `EntityResource.make` -> `effect/unstable/cluster/EntityResource#make`: Moved into core Effect. Acquisition is lazy by default in v4; set acquireEagerly: true to preserve v3 behavior. +- `EntityResource.make` -> `effect/cluster/EntityResource#make`: Moved into core Effect. Acquisition is lazy by default in v4; set acquireEagerly: true to preserve v3 behavior. ### `@effect/cluster/Envelope` -- `Envelope.Envelope.Encoded` -> `effect/unstable/cluster/Envelope#Encoded`: The encoded envelope union moved to the module-level Encoded type. +- `Envelope.Envelope.Encoded` -> `effect/cluster/Envelope#Encoded`: The encoded envelope union moved to the module-level Encoded type. -- `Envelope.Envelope.PartialEncoded` -> `effect/unstable/cluster/Envelope#Partial`: The partially decoded runtime union was renamed to Partial; use PartialJson for its JSON codec. +- `Envelope.Envelope.PartialEncoded` -> `effect/cluster/Envelope#Partial`: The partially decoded runtime union was renamed to Partial; use PartialJson for its JSON codec. -- `Envelope.EnvelopeFromSelf` -> `effect/unstable/cluster/Envelope#Envelope`: The self schema was renamed to Envelope and declaration-merges with the envelope type and namespace. +- `Envelope.EnvelopeFromSelf` -> `effect/cluster/Envelope#Envelope`: The self schema was renamed to Envelope and declaration-merges with the envelope type and namespace. -- `Envelope.PartialEncoded` -> `effect/unstable/cluster/Envelope#PartialJson`: The partially decoded envelope JSON codec was renamed to PartialJson. +- `Envelope.PartialEncoded` -> `effect/cluster/Envelope#PartialJson`: The partially decoded envelope JSON codec was renamed to PartialJson. -- `Envelope.PartialEncodedArray` -> `effect/unstable/cluster/Envelope#PartialArray`: The mutable array codec was renamed to PartialArray. +- `Envelope.PartialEncodedArray` -> `effect/cluster/Envelope#PartialArray`: The mutable array codec was renamed to PartialArray. -- `Envelope.PartialEncodedFromSelf` -> `effect/unstable/cluster/Envelope#Partial`: The separate self schema was folded into Partial; derive JSON encoding with PartialJson. +- `Envelope.PartialEncodedFromSelf` -> `effect/cluster/Envelope#Partial`: The separate self schema was folded into Partial; derive JSON encoding with PartialJson. - `Envelope.PartialEncodedRequest` -> `Schema.toCodecJson(Envelope.PartialRequest)`: V4 exports the self schema as PartialRequest and derives its JSON codec with Schema.toCodecJson. -- `Envelope.PartialEncodedRequestFromSelf` -> `effect/unstable/cluster/Envelope#PartialRequest`: The partially decoded request self schema was renamed to PartialRequest. +- `Envelope.PartialEncodedRequestFromSelf` -> `effect/cluster/Envelope#PartialRequest`: The partially decoded request self schema was renamed to PartialRequest. -- `Envelope.Request` -> `effect/unstable/cluster/Envelope#Request`: The request interface remains and declaration-merges with the exported Request schema. +- `Envelope.Request` -> `effect/cluster/Envelope#Request`: The request interface remains and declaration-merges with the exported Request schema. -- `Envelope.Request.Encoded` -> `effect/unstable/cluster/Envelope#PartialRequestEncoded`: The JSON request shape moved to the module-level PartialRequestEncoded interface. +- `Envelope.Request.Encoded` -> `effect/cluster/Envelope#PartialRequestEncoded`: The JSON request shape moved to the module-level PartialRequestEncoded interface. -- `Envelope.Request.PartialEncoded` -> `effect/unstable/cluster/Envelope#PartialRequest`: The partially decoded request shape moved to the module-level PartialRequest class and type. +- `Envelope.Request.PartialEncoded` -> `effect/cluster/Envelope#PartialRequest`: The partially decoded request shape moved to the module-level PartialRequest class and type. -- `Envelope.RequestFromSelf` -> `effect/unstable/cluster/Envelope#Request`: The request self schema was renamed to Request and declaration-merges with the runtime interface. +- `Envelope.RequestFromSelf` -> `effect/cluster/Envelope#Request`: The request self schema was renamed to Request and declaration-merges with the runtime interface. - `Envelope.TypeId` -> `typeof Envelope.TypeId`: The marker value remains, but the type alias was removed and the value is now a string literal; use typeof in type position. ### `@effect/cluster/MachineId` -- `MachineId.make` -> `effect/unstable/cluster/MachineId#make`: Moved into core Effect. The v4 helper is an unchecked cast; validate external input with the MachineId schema when needed. +- `MachineId.make` -> `effect/cluster/MachineId#make`: Moved into core Effect. The v4 helper is an unchecked cast; validate external input with the MachineId schema when needed. ### `@effect/cluster/Message` -- `Message.serialize` -> `effect/unstable/cluster/Message#serialize`: Moved into core Effect. Pass the transport's codecFor as the second argument; use serializeEnvelope for the JSON Envelope.Encoded form. +- `Message.serialize` -> `effect/cluster/Message#serialize`: Moved into core Effect. Pass the transport's codecFor as the second argument; use serializeEnvelope for the JSON Envelope.Encoded form. ### `@effect/cluster/MessageStorage` -- `MessageStorage.Encoded` -> `effect/unstable/cluster/MessageStorage#Encoded`: Moved into core Effect; use the v4 Envelope.Encoded and Reply.Encoded aliases. Custom drivers now implement batched resetAddresses, and unprocessedMessages receives optional limit and address filters. +- `MessageStorage.Encoded` -> `effect/cluster/MessageStorage#Encoded`: Moved into core Effect; use the v4 Envelope.Encoded and Reply.Encoded aliases. Custom drivers now implement batched resetAddresses, and unprocessedMessages receives optional limit and address filters. -- `MessageStorage.make` -> `effect/unstable/cluster/MessageStorage#make`: Moved into core Effect. Context service projections now use the Service property instead of Type. Custom service implementations must also provide resetAddresses for batched mailbox resets. +- `MessageStorage.make` -> `effect/cluster/MessageStorage#make`: Moved into core Effect. Context service projections now use the Service property instead of Type. Custom service implementations must also provide resetAddresses for batched mailbox resets. -- `MessageStorage.makeEncoded` -> `effect/unstable/cluster/MessageStorage#makeEncoded`: Moved into core Effect. Custom encoded drivers must replace resetAddress with resetAddresses and may use the new limit and addresses options passed to unprocessedMessages. +- `MessageStorage.makeEncoded` -> `effect/cluster/MessageStorage#makeEncoded`: Moved into core Effect. Custom encoded drivers must replace resetAddress with resetAddresses and may use the new limit and addresses options passed to unprocessedMessages. ### `@effect/cluster/Reply` - `Reply.Reply` -> `Reply.Reply(rpc, codecFor)`: Pass the transport codec. Decoding replies requires Rpc.ServicesClient; encoding replies requires Rpc.ServicesServer. -- `Reply.ReplyEncoded` -> `effect/unstable/cluster/Reply#Encoded`: Renamed to Encoded and no longer parameterized by an Rpc; payload fields are unknown and validated by Reply.Reply(rpc, codecFor) with the transport's codec. +- `Reply.ReplyEncoded` -> `effect/cluster/Reply#Encoded`: Renamed to Encoded and no longer parameterized by an Rpc; payload fields are unknown and validated by Reply.Reply(rpc, codecFor) with the transport's codec. - `Reply.TypeId` -> `none`: The reply marker is private in v4. Use Reply.isReply for runtime refinement. -- `Reply.serialize` -> `effect/unstable/cluster/Reply#serialize`: Moved into core Effect and now returns the non-generic Reply.Encoded wire union. Pass the transport's codecFor as the second argument. +- `Reply.serialize` -> `effect/cluster/Reply#serialize`: Moved into core Effect and now returns the non-generic Reply.Encoded wire union. Pass the transport's codecFor as the second argument. ### `@effect/cluster/Runner` @@ -5780,35 +5800,35 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/cluster/RunnerStorage` -- `RunnerStorage.makeMemory` -> `effect/unstable/cluster/RunnerStorage#makeMemory`: Moved into core Effect; it still constructs the in-memory RunnerStorage service implementation. +- `RunnerStorage.makeMemory` -> `effect/cluster/RunnerStorage#makeMemory`: Moved into core Effect; it still constructs the in-memory RunnerStorage service implementation. ### `@effect/cluster/Runners` -- `Runners.make` -> `effect/unstable/cluster/Runners#make`: Moved into core Effect. Its options now require codecFor; pass the codec used by the remote runner transport, such as RpcSerialization.json.codecFor for JSON. Context service projections now use Service instead of Type. +- `Runners.make` -> `effect/cluster/Runners#make`: Moved into core Effect. Its options now require codecFor; pass the codec used by the remote runner transport, such as RpcSerialization.json.codecFor for JSON. Context service projections now use Service instead of Type. -- `Runners.makeNoop` -> `effect/unstable/cluster/Runners#makeNoop`: Moved into core Effect; it returns the Context.Service implementation through the Service projection instead of Type. +- `Runners.makeNoop` -> `effect/cluster/Runners#makeNoop`: Moved into core Effect; it returns the Context.Service implementation through the Service projection instead of Type. ### `@effect/cluster/ShardId` -- `ShardId.ShardId` -> `effect/unstable/cluster/ShardId#ShardId`: The class became a merged interface and schema value. Use ShardId.make; former static parsers and printers are module functions. +- `ShardId.ShardId` -> `effect/cluster/ShardId#ShardId`: The class became a merged interface and schema value. Use ShardId.make; former static parsers and printers are module functions. - `ShardId.TypeId` -> `none`: The shard marker is private in v4. Use ShardId.isShardId for runtime refinement. ### `@effect/cluster/ShardingConfig` -- `ShardingConfig.config` -> `effect/unstable/cluster/ShardingConfig#config`: Moved into core Effect; its Context service value type now uses the Service property instead of Type. +- `ShardingConfig.config` -> `effect/cluster/ShardingConfig#config`: Moved into core Effect; its Context service value type now uses the Service property instead of Type. -- `ShardingConfig.defaults` -> `effect/unstable/cluster/ShardingConfig#defaults`: Moved into core Effect; service type projections now use Service instead of Type. V4 also defaults maxResidentEntities to 10,000 and unprocessedMessageBatchSize to 1,024. +- `ShardingConfig.defaults` -> `effect/cluster/ShardingConfig#defaults`: Moved into core Effect; service type projections now use Service instead of Type. V4 also defaults maxResidentEntities to 10,000 and unprocessedMessageBatchSize to 1,024. -- `ShardingConfig.layer` -> `effect/unstable/cluster/ShardingConfig#layer`: Moved into core Effect with the same shallow default merge; service type projections now use Service instead of Type. +- `ShardingConfig.layer` -> `effect/cluster/ShardingConfig#layer`: Moved into core Effect with the same shallow default merge; service type projections now use Service instead of Type. ### `@effect/cluster/ShardingRegistrationEvent` -- `ShardingRegistrationEvent.match` -> `effect/unstable/cluster/ShardingRegistrationEvent#match`: Moved into core Effect with the same tagged-enum matcher. +- `ShardingRegistrationEvent.match` -> `effect/cluster/ShardingRegistrationEvent#match`: Moved into core Effect with the same tagged-enum matcher. ### `@effect/cluster/SingleRunner` -- `SingleRunner.layer` -> `effect/unstable/cluster/SingleRunner#layer`: Moved into core Effect. V4 additionally requires Crypto.Crypto because SQL message storage hashes long deduplication keys. +- `SingleRunner.layer` -> `effect/cluster/SingleRunner#layer`: Moved into core Effect. V4 additionally requires Crypto.Crypto because SQL message storage hashes long deduplication keys. ### `@effect/cluster/SingletonAddress` @@ -5816,115 +5836,117 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/cluster/Snowflake` -- `Snowflake.TypeId` -> `effect/unstable/cluster/Snowflake#TypeId`: Moved into core Effect; the public marker is now the string literal \~effect/cluster/Snowflake. +- `Snowflake.Generator` -> `effect/cluster/Snowflake#Generator`: Moved into core Effect and changed to Context.Service; its unsafeNext method was renamed to nextUnsafe. + +- `Snowflake.TypeId` -> `effect/cluster/Snowflake#TypeId`: Moved into core Effect; the public marker is now the string literal \~effect/cluster/Snowflake. ### `@effect/cluster/SqlMessageStorage` -- `SqlMessageStorage.layer` -> `effect/unstable/cluster/SqlMessageStorage#layer`: Moved into core Effect. V4 adds a Crypto.Crypto requirement for hashing long deduplication keys. +- `SqlMessageStorage.layer` -> `effect/cluster/SqlMessageStorage#layer`: Moved into core Effect. V4 adds a Crypto.Crypto requirement for hashing long deduplication keys. -- `SqlMessageStorage.layerWith` -> `effect/unstable/cluster/SqlMessageStorage#layerWith`: Moved into core Effect with the same optional table prefix; v4 additionally requires Crypto.Crypto. +- `SqlMessageStorage.layerWith` -> `effect/cluster/SqlMessageStorage#layerWith`: Moved into core Effect with the same optional table prefix; v4 additionally requires Crypto.Crypto. -- `SqlMessageStorage.make` -> `effect/unstable/cluster/SqlMessageStorage#make`: Moved into core Effect with the same prefix option; v4 additionally requires Crypto.Crypto. +- `SqlMessageStorage.make` -> `effect/cluster/SqlMessageStorage#make`: Moved into core Effect with the same prefix option; v4 additionally requires Crypto.Crypto. ### `@effect/cluster/SqlRunnerStorage` -- `SqlRunnerStorage.make` -> `effect/unstable/cluster/SqlRunnerStorage#make`: Moved into core Effect with the same prefix option and service requirements. +- `SqlRunnerStorage.make` -> `effect/cluster/SqlRunnerStorage#make`: Moved into core Effect with the same prefix option and service requirements. ### `@effect/experimental/DevTools/Client` -- `Client.Client` -> `effect/unstable/devtools/DevToolsClient#DevToolsClient`: Client was renamed to the DevToolsClient Context.Service class. +- `Client.Client` -> `effect/devtools/DevToolsClient#DevToolsClient`: Client was renamed to the DevToolsClient Context.Service class. -- `Client.ClientImpl` -> `effect/unstable/devtools/DevToolsClient#DevToolsClient["Service"]`: Use the service shape from DevToolsClient; unsafeAddSpan was replaced by sendUnsafe. +- `Client.ClientImpl` -> `effect/devtools/DevToolsClient#DevToolsClient["Service"]`: Use the service shape from DevToolsClient; unsafeAddSpan was replaced by sendUnsafe. -- `Client.layer` -> `effect/unstable/devtools/DevToolsClient#layer`: Import layer from the v4 unstable DevToolsClient module. +- `Client.layer` -> `effect/devtools/DevToolsClient#layer`: Import layer from the v4 unstable DevToolsClient module. -- `Client.make` -> `effect/unstable/devtools/DevToolsClient#make`: Import make from the v4 unstable DevToolsClient module. +- `Client.make` -> `effect/devtools/DevToolsClient#make`: Import make from the v4 unstable DevToolsClient module. ### `@effect/experimental/DevTools/Domain` -- `Domain.ExternalSpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ExternalSpan. +- `Domain.ExternalSpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ExternalSpan. -- `Domain.MetricFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Metric. +- `Domain.MetricFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Metric. -- `Domain.MetricsSnapshotFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from MetricsSnapshot. +- `Domain.MetricsSnapshotFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from MetricsSnapshot. -- `Domain.ParentSpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ParentSpan. +- `Domain.ParentSpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from ParentSpan. -- `Domain.SpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Span. +- `Domain.SpanFrom` -> `effect/Schema#Codec.Encoded`: The named encoded alias was removed; derive it with Schema.Codec.Encoded from Span. - `Domain.metric` -> `none`: The metric schema helper is private in v4; use the exported Counter, Frequency, Gauge, Histogram, Summary, or Metric schemas, or build a Schema.Struct. ### `@effect/experimental/DevTools/Server` -- `Server.run` -> `effect/unstable/devtools/DevToolsServer#run`: Import run from the v4 unstable DevToolsServer module. +- `Server.run` -> `effect/devtools/DevToolsServer#run`: Import run from the v4 unstable DevToolsServer module. ### `@effect/experimental/Event` -- `Event.Event.AddError` -> `effect/unstable/eventlog/Event#AddError`: This type moved from the Event namespace to a top-level export. +- `Event.Event.AddError` -> `effect/eventlog/Event#AddError`: This type moved from the Event namespace to a top-level export. -- `Event.Event.Any` -> `effect/unstable/eventlog/Event#Any`: This type moved from the Event namespace to a top-level export. +- `Event.Event.Any` -> `effect/eventlog/Event#Any`: This type moved from the Event namespace to a top-level export. -- `Event.Event.Context` -> `effect/unstable/eventlog/Event#Services`: Event schema context is now represented by decoding and encoding Services. +- `Event.Event.Context` -> `effect/eventlog/Event#Services`: Event schema context is now represented by decoding and encoding Services. -- `Event.Event.ContextWithTag` -> `effect/unstable/eventlog/Event#Services>`: Filter with WithTag and derive its decoding and encoding Services. +- `Event.Event.ContextWithTag` -> `effect/eventlog/Event#Services>`: Filter with WithTag and derive its decoding and encoding Services. -- `Event.Event.Error` -> `effect/unstable/eventlog/Event#Error`: This type moved from the Event namespace to a top-level export. +- `Event.Event.Error` -> `effect/eventlog/Event#Error`: This type moved from the Event namespace to a top-level export. -- `Event.Event.ErrorSchema` -> `effect/unstable/eventlog/Event#ErrorSchema`: This type moved from the Event namespace to a top-level export. +- `Event.Event.ErrorSchema` -> `effect/eventlog/Event#ErrorSchema`: This type moved from the Event namespace to a top-level export. -- `Event.Event.Payload` -> `effect/unstable/eventlog/Event#Payload`: This type moved from the Event namespace to a top-level export. +- `Event.Event.Payload` -> `effect/eventlog/Event#Payload`: This type moved from the Event namespace to a top-level export. -- `Event.Event.PayloadSchema` -> `effect/unstable/eventlog/Event#PayloadSchema`: This type moved from the Event namespace to a top-level export. +- `Event.Event.PayloadSchema` -> `effect/eventlog/Event#PayloadSchema`: This type moved from the Event namespace to a top-level export. -- `Event.Event.Success` -> `effect/unstable/eventlog/Event#Success`: This type moved from the Event namespace to a top-level export. +- `Event.Event.Success` -> `effect/eventlog/Event#Success`: This type moved from the Event namespace to a top-level export. -- `Event.Event.SuccessSchema` -> `effect/unstable/eventlog/Event#SuccessSchema`: This type moved from the Event namespace to a top-level export. +- `Event.Event.SuccessSchema` -> `effect/eventlog/Event#SuccessSchema`: This type moved from the Event namespace to a top-level export. -- `Event.Event.Tag` -> `effect/unstable/eventlog/Event#Tag`: This type moved from the Event namespace to a top-level export. +- `Event.Event.Tag` -> `effect/eventlog/Event#Tag`: This type moved from the Event namespace to a top-level export. -- `Event.Event.ToService` -> `effect/unstable/eventlog/Event#ToService`: This type moved from the Event namespace to a top-level export. +- `Event.Event.ToService` -> `effect/eventlog/Event#ToService`: This type moved from the Event namespace to a top-level export. -- `Event.TypeId` -> `effect/unstable/eventlog/Event#TypeId`: Import TypeId from the v4 unstable Event module; its runtime representation is now a string brand. +- `Event.TypeId` -> `effect/eventlog/Event#TypeId`: Import TypeId from the v4 unstable Event module; its runtime representation is now a string brand. -- `Event.make` -> `effect/unstable/eventlog/Event#make`: Import make from the v4 unstable Event module. +- `Event.make` -> `effect/eventlog/Event#make`: Import make from the v4 unstable Event module. ### `@effect/experimental/EventGroup` -- `EventGroup.EventGroup.Any` -> `effect/unstable/eventlog/EventGroup#Any`: This type moved from the EventGroup namespace to a top-level export. +- `EventGroup.EventGroup.Any` -> `effect/eventlog/EventGroup#Any`: This type moved from the EventGroup namespace to a top-level export. -- `EventGroup.EventGroup.AnyWithProps` -> `effect/unstable/eventlog/EventGroup#AnyWithProps`: This type moved from the EventGroup namespace to a top-level export. +- `EventGroup.EventGroup.AnyWithProps` -> `effect/eventlog/EventGroup#AnyWithProps`: This type moved from the EventGroup namespace to a top-level export. -- `EventGroup.EventGroup.Context` -> `effect/unstable/eventlog/EventGroup#ServicesClient | effect/unstable/eventlog/EventGroup#ServicesServer`: Choose the client or server schema services for the required direction. +- `EventGroup.EventGroup.Context` -> `effect/eventlog/EventGroup#ServicesClient | effect/eventlog/EventGroup#ServicesServer`: Choose the client or server schema services for the required direction. -- `EventGroup.EventGroup.ToService` -> `effect/unstable/eventlog/EventGroup#ToService`: This type moved from the EventGroup namespace to a top-level export. +- `EventGroup.EventGroup.ToService` -> `effect/eventlog/EventGroup#ToService`: This type moved from the EventGroup namespace to a top-level export. -- `EventGroup.TypeId` -> `effect/unstable/eventlog/EventGroup#TypeId`: Import TypeId from the v4 unstable EventGroup module; its runtime representation is now a string brand. +- `EventGroup.TypeId` -> `effect/eventlog/EventGroup#TypeId`: Import TypeId from the v4 unstable EventGroup module; its runtime representation is now a string brand. ### `@effect/experimental/EventJournal` -- `EventJournal.EntryIdTypeId` -> `effect/unstable/eventlog/EventJournal#EntryIdTypeId`: Import EntryIdTypeId from the v4 EventJournal module; it is now a string brand. +- `EventJournal.EntryIdTypeId` -> `effect/eventlog/EventJournal#EntryIdTypeId`: Import EntryIdTypeId from the v4 EventJournal module; it is now a string brand. - `EventJournal.ErrorTypeId` -> `none`: The v4 error marker is private; narrow with EventJournalError instead. -- `EventJournal.RemoteIdTypeId` -> `effect/unstable/eventlog/EventJournal#RemoteIdTypeId`: Import RemoteIdTypeId from the v4 EventJournal module; it is now a string brand. +- `EventJournal.RemoteIdTypeId` -> `effect/eventlog/EventJournal#RemoteIdTypeId`: Import RemoteIdTypeId from the v4 EventJournal module; it is now a string brand. -- `EventJournal.makeEntryId` -> `effect/unstable/eventlog/EventJournal#makeEntryIdUnsafe`: The unchecked EntryId constructor was renamed to makeEntryIdUnsafe. +- `EventJournal.makeEntryId` -> `effect/eventlog/EventJournal#makeEntryIdUnsafe`: The unchecked EntryId constructor was renamed to makeEntryIdUnsafe. -- `EventJournal.makeMemory` -> `effect/unstable/eventlog/EventJournal#makeMemory`: The in-memory constructor moved into core Effect and now returns the Context.Service implementation through its Service projection. +- `EventJournal.makeMemory` -> `effect/eventlog/EventJournal#makeMemory`: The in-memory constructor moved into core Effect and now returns the Context.Service implementation through its Service projection. -- `EventJournal.makeRemoteId` -> `effect/unstable/eventlog/EventJournal#makeRemoteIdUnsafe`: The unchecked RemoteId constructor was renamed to makeRemoteIdUnsafe. +- `EventJournal.makeRemoteId` -> `effect/eventlog/EventJournal#makeRemoteIdUnsafe`: The unchecked RemoteId constructor was renamed to makeRemoteIdUnsafe. ### `@effect/experimental/EventLog` -- `EventLog.Handlers` -> `effect/unstable/eventlog/EventLog#Handlers`: Import Handlers from the v4 EventLog module; handlers now also receive storeId. +- `EventLog.Handlers` -> `effect/eventlog/EventLog#Handlers`: Import Handlers from the v4 EventLog module; handlers now also receive storeId. -- `EventLog.HandlersTypeId` -> `effect/unstable/eventlog/EventLog#HandlersTypeId`: Import HandlersTypeId from the v4 EventLog module. +- `EventLog.HandlersTypeId` -> `effect/eventlog/EventLog#HandlersTypeId`: Import HandlersTypeId from the v4 EventLog module. -- `EventLog.SchemaTypeId` -> `effect/unstable/eventlog/EventLog#SchemaTypeId`: Import SchemaTypeId from the v4 EventLog module. +- `EventLog.SchemaTypeId` -> `effect/eventlog/EventLog#SchemaTypeId`: Import SchemaTypeId from the v4 EventLog module. -- `EventLog.group` -> `effect/unstable/eventlog/EventLog#group`: Import group from the v4 EventLog module; it now requires the shared Registry service. +- `EventLog.group` -> `effect/eventlog/EventLog#group`: Import group from the v4 EventLog module; it now requires the shared Registry service. -- `EventLog.layer` -> `effect/unstable/eventlog/EventLog#layer`: The v4 layer takes both the schema and handler layer; use layerEventLog for runtime only. +- `EventLog.layer` -> `effect/eventlog/EventLog#layer`: The v4 layer takes both the schema and handler layer; use layerEventLog for runtime only. - `EventLog.layerIdentityKvs` -> `none`: Compose KeyValueStore.toSchemaStore, EventLog.IdentitySchema, EventLog.makeIdentity, and Layer.effect manually. @@ -5932,115 +5954,117 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `EventLogRemote.Ack` -> `none`: A write acknowledgement is now the void success of EventLogMessage.WriteSingleRpc or WriteChunkedRpc. -- `EventLogRemote.Changes` -> `effect/unstable/eventlog/EventLogMessage#ChangesRpc`: ChangesRpc replaces the separate request and response models with one streaming RPC. - -- `EventLogRemote.EventLogRemote` -> `effect/unstable/eventlog/EventLogRemote#EventLogRemote`: Use the v4 Context.Service; methods now take storeId-aware options. +- `EventLogRemote.Changes` -> `effect/eventlog/EventLogMessage#ChangesRpc`: ChangesRpc replaces the separate request and response models with one streaming RPC. -- `EventLogRemote.Hello` -> `effect/unstable/eventlog/EventLogMessage#HelloResponse`: HelloResponse replaces Hello and includes the v4 authentication challenge; HelloRpc defines the endpoint. +- `EventLogRemote.EventLogRemote` -> `effect/eventlog/EventLogRemote#EventLogRemote`: Use the v4 Context.Service; methods now take storeId-aware options. -- `EventLogRemote.Ping`: TODO: needs guidance +- `EventLogRemote.Hello` -> `effect/eventlog/EventLogMessage#HelloResponse`: HelloResponse replaces Hello and includes the v4 authentication challenge; HelloRpc defines the endpoint. - `EventLogRemote.Pong` -> `none`: The event-log Pong model was removed; heartbeats belong to the generic RPC socket protocol. -- `EventLogRemote.ProtocolRequest` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: EventLogRemoteRpcs and generic RPC serialization replace the old protocol request union. +- `EventLogRemote.ProtocolRequest` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: EventLogRemoteRpcs and generic RPC serialization replace the old protocol request union. -- `EventLogRemote.ProtocolRequestMsgPack` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: Use the generic SchemaBinary RPC serialization layer instead of a request-specific schema. +- `EventLogRemote.ProtocolRequestMsgPack` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: Use the generic SchemaBinary RPC serialization layer instead of a request-specific schema. -- `EventLogRemote.ProtocolResponse` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: EventLogRemoteRpcs and generic RPC serialization replace the old protocol response union. +- `EventLogRemote.ProtocolResponse` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: EventLogRemoteRpcs and generic RPC serialization replace the old protocol response union. -- `EventLogRemote.ProtocolResponseMsgPack` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: Use the generic SchemaBinary RPC serialization layer instead of a response-specific schema. +- `EventLogRemote.ProtocolResponseMsgPack` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: Use the generic SchemaBinary RPC serialization layer instead of a response-specific schema. - `EventLogRemote.RemoteAdditions` -> `none`: This unused protocol model has no v4 counterpart. -- `EventLogRemote.RequestChanges` -> `effect/unstable/eventlog/EventLogMessage#ChangesRpc`: ChangesRpc replaces the separate request model with one streaming RPC. +- `EventLogRemote.RequestChanges` -> `effect/eventlog/EventLogMessage#ChangesRpc`: ChangesRpc replaces the separate request model with one streaming RPC. - `EventLogRemote.StopChanges` -> `none`: Interrupt the ChangesRpc stream instead of sending a StopChanges message. -- `EventLogRemote.decodeRequest` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request decoder. +- `EventLogRemote.decodeRequest` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request decoder. -- `EventLogRemote.decodeResponse` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response decoder. +- `EventLogRemote.decodeResponse` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response decoder. -- `EventLogRemote.encodeRequest` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request encoder. +- `EventLogRemote.encodeRequest` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific request encoder. -- `EventLogRemote.encodeResponse` -> `effect/unstable/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response encoder. +- `EventLogRemote.encodeResponse` -> `effect/eventlog/EventLogMessage#EventLogRemoteRpcs`: Generic RPC framing and RpcSerialization.layerSchemaBinary replace the module-specific response encoder. -- `EventLogRemote.fromSocket` -> `effect/unstable/eventlog/EventLogRemote#makeEncrypted + effect/unstable/rpc/RpcClient#makeProtocolSocket`: Construct the encrypted remote separately from its generic RPC socket protocol. +- `EventLogRemote.fromSocket` -> `effect/eventlog/EventLogRemote#makeEncrypted + effect/rpc/RpcClient#makeProtocolSocket`: Construct the encrypted remote separately from its generic RPC socket protocol. -- `EventLogRemote.layerWebSocket` -> `effect/unstable/eventlog/EventLogRemote#layerEncrypted + effect/unstable/rpc/RpcClient#layerProtocolSocket`: Compose the encrypted remote with the generic socket protocol, SchemaBinary serialization, and a Socket provider. +- `EventLogRemote.layerWebSocket` -> `effect/eventlog/EventLogRemote#layerEncrypted + effect/rpc/RpcClient#layerProtocolSocket`: Compose the encrypted remote with the generic socket protocol, SchemaBinary serialization, and a Socket provider. -- `EventLogRemote.layerWebSocketBrowser` -> `effect/unstable/eventlog/EventLogRemote#layerEncrypted + effect/unstable/rpc/RpcClient#layerProtocolSocket + @effect/platform-browser/BrowserSocket#layerWebSocket`: Compose the encrypted remote and generic RPC socket protocol with the browser WebSocket layer. +- `EventLogRemote.layerWebSocketBrowser` -> `effect/eventlog/EventLogRemote#layerEncrypted + effect/rpc/RpcClient#layerProtocolSocket + @effect/platform-browser/BrowserSocket#layerWebSocket`: Compose the encrypted remote and generic RPC socket protocol with the browser WebSocket layer. ### `@effect/experimental/EventLogServer` -- `EventLogServer.makeHandler` -> `effect/unstable/eventlog/EventLogServerEncrypted#layer + effect/unstable/rpc/RpcServer#layerProtocolSocketServer`: Compose the encrypted server layer with the generic RPC socket server; there is no per-socket handler factory. +- `EventLogServer.Storage` -> `effect/eventlog/EventLogServerEncrypted#Storage`: Use the encrypted server Storage service, which is storeId- and session-aware. + +- `EventLogServer.layerStorageMemory` -> `effect/eventlog/EventLogServerEncrypted#layerStorageMemory`: Use the encrypted server memory storage layer; storage is now storeId- and session-aware. -- `EventLogServer.makeHandlerHttp` -> `effect/unstable/eventlog/EventLogServerEncrypted#layer + effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket`: Use the returned httpEffect for upgrades and provide its protocol to the encrypted server layer. +- `EventLogServer.makeHandler` -> `effect/eventlog/EventLogServerEncrypted#layer + effect/rpc/RpcServer#layerProtocolSocketServer`: Compose the encrypted server layer with the generic RPC socket server; there is no per-socket handler factory. -- `EventLogServer.makeStorageMemory` -> `effect/unstable/eventlog/EventLogServerEncrypted#makeStorageMemory`: Use the encrypted server memory storage constructor. +- `EventLogServer.makeHandlerHttp` -> `effect/eventlog/EventLogServerEncrypted#layer + effect/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket`: Use the returned httpEffect for upgrades and provide its protocol to the encrypted server layer. + +- `EventLogServer.makeStorageMemory` -> `effect/eventlog/EventLogServerEncrypted#makeStorageMemory`: Use the encrypted server memory storage constructor. ### `@effect/experimental/PersistedCache` -- `PersistedCache.make` -> `effect/unstable/persistence/PersistedCache#make`: Pass lookup as the first argument and options second; timeToLive now receives exit before request and the service is Persistence.Persistence. +- `PersistedCache.make` -> `effect/persistence/PersistedCache#make`: Pass lookup as the first argument and options second; timeToLive now receives exit before request and the service is Persistence.Persistence. ### `@effect/experimental/PersistedQueue` -- `PersistedQueue.ErrorTypeId` -> `effect/unstable/persistence/PersistedQueue#ErrorTypeId`: Retained as a string brand; the runtime marker now uses the persistence module path. +- `PersistedQueue.ErrorTypeId` -> `effect/persistence/PersistedQueue#ErrorTypeId`: Retained as a string brand; the runtime marker now uses the persistence module path. -- `PersistedQueue.TypeId` -> `effect/unstable/persistence/PersistedQueue#TypeId`: Import TypeId from the v4 unstable PersistedQueue module; it is now a string brand. +- `PersistedQueue.TypeId` -> `effect/persistence/PersistedQueue#TypeId`: Import TypeId from the v4 unstable PersistedQueue module; it is now a string brand. -- `PersistedQueue.make` -> `effect/unstable/persistence/PersistedQueue#make`: Import make from the v4 unstable PersistedQueue module. +- `PersistedQueue.make` -> `effect/persistence/PersistedQueue#make`: Import make from the v4 unstable PersistedQueue module. ### `@effect/experimental/PersistedQueue/Redis` -- `Redis.layerStore` -> `effect/unstable/persistence/PersistedQueue#layerStoreRedis`: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. +- `Redis.layerStore` -> `effect/persistence/PersistedQueue#layerStoreRedis`: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. - `Redis.layerStoreConfig` -> `none`: Configure a Redis provider such as NodeRedis.layerConfig separately, then compose it with PersistedQueue.layerStoreRedis. -- `Redis.make` -> `effect/unstable/persistence/PersistedQueue#makeStoreRedis`: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. +- `Redis.make` -> `effect/persistence/PersistedQueue#makeStoreRedis`: The Redis adapter was merged into PersistedQueue and now requires the generic Redis.Redis service. ### `@effect/experimental/Persistence` -- `Persistence.BackingPersistence` -> `effect/unstable/persistence/Persistence#BackingPersistence`: Use the v4 BackingPersistence Context.Service class. +- `Persistence.BackingPersistence` -> `effect/persistence/Persistence#BackingPersistence`: Use the v4 BackingPersistence Context.Service class. - `Persistence.BackingPersistenceTypeId` -> `none`: The BackingPersistence brand is no longer publicly exported in v4. - `Persistence.ErrorTypeId` -> `none`: The v4 persistence error identifier is private; narrow with the exported error classes. -- `Persistence.PersistenceBackingError` -> `effect/unstable/persistence/Persistence#PersistenceError`: PersistenceError now represents failures from the backing persistence implementation. +- `Persistence.PersistenceBackingError` -> `effect/persistence/Persistence#PersistenceError`: PersistenceError now represents failures from the backing persistence implementation. -- `Persistence.PersistenceError` -> `effect/unstable/persistence/Persistence#PersistenceError | effect/Schema#SchemaError`: The old combined alias was split into backing PersistenceError and schema SchemaError. +- `Persistence.PersistenceError` -> `effect/persistence/Persistence#PersistenceError | effect/Schema#SchemaError`: The old combined alias was split into backing PersistenceError and schema SchemaError. - `Persistence.PersistenceParseError` -> `effect/Schema#SchemaError`: Persistence parsing failures now use the core SchemaError type. -- `Persistence.ResultPersistence` -> `effect/unstable/persistence/Persistence#Persistence`: ResultPersistence was renamed to Persistence and is now a Context.Service class. +- `Persistence.ResultPersistence` -> `effect/persistence/Persistence#Persistence`: ResultPersistence was renamed to Persistence and is now a Context.Service class. -- `Persistence.ResultPersistence.Key` -> `effect/unstable/persistence/Persistable#Persistable`: Persistable is the v4 schema-backed persistence key contract. +- `Persistence.ResultPersistence.Key` -> `effect/persistence/Persistable#Persistable`: Persistable is the v4 schema-backed persistence key contract. -- `Persistence.ResultPersistence.KeyAny` -> `effect/unstable/persistence/Persistable#Any`: Use Persistable.Any for an arbitrary v4 persistence key contract. +- `Persistence.ResultPersistence.KeyAny` -> `effect/persistence/Persistable#Any`: Use Persistable.Any for an arbitrary v4 persistence key contract. -- `Persistence.ResultPersistence.TimeToLiveArgs` -> `Parameters>`: Derive the tuple from TimeToLiveFn; its order is now exit then request. +- `Persistence.ResultPersistence.TimeToLiveArgs` -> `Parameters>`: Derive the tuple from TimeToLiveFn; its order is now exit then request. -- `Persistence.ResultPersistenceStore` -> `effect/unstable/persistence/Persistence#PersistenceStore`: ResultPersistenceStore was renamed to PersistenceStore. +- `Persistence.ResultPersistenceStore` -> `effect/persistence/Persistence#PersistenceStore`: ResultPersistenceStore was renamed to PersistenceStore. - `Persistence.ResultPersistenceTypeId` -> `none`: The ResultPersistence brand is no longer publicly exported in v4. -- `Persistence.layerKeyValueStore` -> `effect/unstable/persistence/Persistence#layerBackingKvs`: The KeyValueStore backing layer was renamed to layerBackingKvs. +- `Persistence.layerKeyValueStore` -> `effect/persistence/Persistence#layerBackingKvs`: The KeyValueStore backing layer was renamed to layerBackingKvs. -- `Persistence.layerMemory` -> `effect/unstable/persistence/Persistence#layerBackingMemory`: Use layerBackingMemory for the old backing service; v4 layerMemory creates the higher-level Persistence service. +- `Persistence.layerMemory` -> `effect/persistence/Persistence#layerBackingMemory`: Use layerBackingMemory for the old backing service; v4 layerMemory creates the higher-level Persistence service. -- `Persistence.layerResult` -> `effect/unstable/persistence/Persistence#layer`: The ResultPersistence service layer was renamed to layer. +- `Persistence.layerResult` -> `effect/persistence/Persistence#layer`: The ResultPersistence service layer was renamed to layer. -- `Persistence.layerResultKeyValueStore` -> `effect/unstable/persistence/Persistence#layerKvs`: The combined KeyValueStore-backed result layer was renamed to layerKvs. +- `Persistence.layerResultKeyValueStore` -> `effect/persistence/Persistence#layerKvs`: The combined KeyValueStore-backed result layer was renamed to layerKvs. -- `Persistence.layerResultMemory` -> `effect/unstable/persistence/Persistence#layerMemory`: The combined memory-backed result layer was renamed to layerMemory. +- `Persistence.layerResultMemory` -> `effect/persistence/Persistence#layerMemory`: The combined memory-backed result layer was renamed to layerMemory. ### `@effect/experimental/Persistence/Redis` -- `Redis.layer` -> `effect/unstable/persistence/Persistence#layerBackingRedis`: The Redis backing adapter was merged into Persistence and now requires the generic Redis.Redis service. +- `Redis.layer` -> `effect/persistence/Persistence#layerBackingRedis`: The Redis backing adapter was merged into Persistence and now requires the generic Redis.Redis service. - `Redis.layerConfig` -> `none`: Compose Persistence.layerBackingRedis with a config-driven provider such as NodeRedis.layerConfig. -- `Redis.layerResult` -> `effect/unstable/persistence/Persistence#layerRedis`: The combined Redis persistence layer was merged into Persistence and now requires Redis.Redis. +- `Redis.layerResult` -> `effect/persistence/Persistence#layerRedis`: The combined Redis persistence layer was merged into Persistence and now requires Redis.Redis. - `Redis.layerResultConfig` -> `none`: Compose Persistence.layerRedis with a config-driven provider such as NodeRedis.layerConfig. @@ -6048,35 +6072,39 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/experimental/RateLimiter` -- `RateLimiter.ErrorTypeId` -> `effect/unstable/persistence/RateLimiter#ErrorTypeId`: Retained as a string brand; the runtime marker now uses the persistence module path. +- `RateLimiter.ErrorTypeId` -> `effect/persistence/RateLimiter#ErrorTypeId`: Retained as a string brand; the runtime marker now uses the persistence module path. + +- `RateLimiter.RateLimiterError` -> `effect/persistence/RateLimiter#RateLimiterError`: The retained name is now a wrapper error class whose reason is RateLimitExceeded or RateLimitStoreError. -- `RateLimiter.RateLimiterError` -> `effect/unstable/persistence/RateLimiter#RateLimiterError`: The retained name is now a wrapper error class whose reason is RateLimitExceeded or RateLimitStoreError. +- `RateLimiter.RateLimiterStore` -> `RateLimiter.RateLimiterStore`: Use the Context.Service class from effect/persistence/RateLimiter. Custom tokenBucket implementations must return [remaining, elapsedMillis] instead of a number, preserving fractional counts and elapsed refill time. -- `RateLimiter.TypeId` -> `effect/unstable/persistence/RateLimiter#TypeId`: Import TypeId from the v4 unstable RateLimiter module; it is now a string brand. +- `RateLimiter.TypeId` -> `effect/persistence/RateLimiter#TypeId`: Import TypeId from the v4 unstable RateLimiter module; it is now a string brand. -- `RateLimiter.makeSleep` -> `effect/unstable/persistence/RateLimiter#sleep`: The accessor Effect was replaced by sleep; obtain the RateLimiter service and pass it to sleep directly or with its curried overload. +- `RateLimiter.makeSleep` -> `effect/persistence/RateLimiter#sleep`: The accessor Effect was replaced by sleep; obtain the RateLimiter service and pass it to sleep directly or with its curried overload. ### `@effect/experimental/RateLimiter/Redis` -- `Redis.layerStore` -> `effect/unstable/persistence/RateLimiter#layerStoreRedis`: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. +- `Redis.layerStore` -> `effect/persistence/RateLimiter#layerStoreRedis`: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. -- `Redis.layerStoreConfig` -> `effect/unstable/persistence/RateLimiter#layerStoreRedisConfig`: Use the merged Redis store config layer; connection configuration belongs to a separate Redis provider. +- `Redis.layerStoreConfig` -> `effect/persistence/RateLimiter#layerStoreRedisConfig`: Use the merged Redis store config layer; connection configuration belongs to a separate Redis provider. -- `Redis.make` -> `effect/unstable/persistence/RateLimiter#makeStoreRedis`: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. +- `Redis.make` -> `effect/persistence/RateLimiter#makeStoreRedis`: The Redis adapter was merged into RateLimiter and now requires the generic Redis.Redis service. ### `@effect/experimental/Reactivity` -- `Reactivity.Reactivity` -> `effect/unstable/reactivity/Reactivity#Reactivity`: Use the Reactivity interface for implementations and the Reactivity Context.Service value as the service key. Implementations require the TypeId brand; prefer Reactivity.make. Unsafe methods were renamed with an Unsafe suffix. +- `Reactivity.Reactivity` -> `effect/reactivity/Reactivity#Reactivity`: Use the Reactivity interface for implementations and the Reactivity Context.Service value as the service key. Implementations require the TypeId brand; prefer Reactivity.make. Unsafe methods were renamed with an Unsafe suffix. -- `Reactivity.Reactivity.Service` -> `effect/unstable/reactivity/Reactivity#Reactivity`: The named namespace member was removed; use the branded Reactivity interface directly. Prefer Reactivity.make, or include [Reactivity.TypeId]: Reactivity.TypeId in a custom implementation. +- `Reactivity.Reactivity.Service` -> `effect/reactivity/Reactivity#Reactivity`: The named namespace member was removed; use the branded Reactivity interface directly. Prefer Reactivity.make, or include [Reactivity.TypeId]: Reactivity.TypeId in a custom implementation. -- `Reactivity.make` -> `effect/unstable/reactivity/Reactivity#make`: Import make from the v4 unstable Reactivity module. +- `Reactivity.make` -> `effect/reactivity/Reactivity#make`: Import make from the v4 unstable Reactivity module. + +- `Reactivity.query`: TODO: needs guidance ### `@effect/experimental/RequestResolver` -- `RequestResolver.PersistedRequest` -> `effect/Request#Request & effect/unstable/persistence/Persistable#Persistable`: Intersect a Request with Persistable or define it with Persistable.Class; there is no combined named export. +- `RequestResolver.PersistedRequest` -> `effect/Request#Request & effect/persistence/Persistable#Persistable`: Intersect a Request with Persistable or define it with Persistable.Class; there is no combined named export. -- `RequestResolver.PersistedRequest.Any` -> `effect/Request#Any & effect/unstable/persistence/Persistable#Any`: Intersect the Request and Persistable helper types for an arbitrary persisted request. +- `RequestResolver.PersistedRequest.Any` -> `effect/Request#Any & effect/persistence/Persistable#Any`: Intersect the Request and Persistable helper types for an arbitrary persisted request. - `RequestResolver.dataLoader` -> `effect/RequestResolver#setDelay + effect/RequestResolver#batchN`: Pipe the resolver through setDelay(options.window) and batchN(options.maxBatchSize ?? Infinity); the transformation is now pure. @@ -6084,31 +6112,31 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/experimental/Sse` -- `Sse.RetryTypeId` -> `none`: The Retry identifier is private in v4; use effect/unstable/encoding/Sse#Retry and Retry.is instead of inspecting the brand. +- `Sse.RetryTypeId` -> `none`: The Retry identifier is private in v4; use effect/encoding/Sse#Retry and Retry.is instead of inspecting the brand. ### `@effect/experimental/VariantSchema` -- `VariantSchema.Extract` -> `effect/unstable/schema/VariantSchema#Extract`: Import the retained helper from the v4 module; its erased schema constraint is Schema.Top. +- `VariantSchema.Extract` -> `effect/schema/VariantSchema#Extract`: Import the retained helper from the v4 module; its erased schema constraint is Schema.Top. -- `VariantSchema.Field.Any` -> `effect/unstable/schema/VariantSchema#Field.Any`: Import the retained Field.Any helper type from the v4 unstable VariantSchema module. +- `VariantSchema.Field.Any` -> `effect/schema/VariantSchema#Field.Any`: Import the retained Field.Any helper type from the v4 unstable VariantSchema module. -- `VariantSchema.Field.Config` -> `effect/unstable/schema/VariantSchema#Field.Config`: Import the retained Field.Config helper type from the v4 unstable VariantSchema module. +- `VariantSchema.Field.Config` -> `effect/schema/VariantSchema#Field.Config`: Import the retained Field.Config helper type from the v4 unstable VariantSchema module. -- `VariantSchema.Field.Fields` -> `effect/unstable/schema/VariantSchema#Field.Fields`: Import the retained Field.Fields helper type from the v4 unstable VariantSchema module. +- `VariantSchema.Field.Fields` -> `effect/schema/VariantSchema#Field.Fields`: Import the retained Field.Fields helper type from the v4 unstable VariantSchema module. - `VariantSchema.Field.ValueAny` -> `effect/Schema#Top`: Use the core Schema.Top constraint for an arbitrary field value schema. - `VariantSchema.FieldTypeId` -> `none`: The Field brand is private in v4; use VariantSchema.isField for narrowing. -- `VariantSchema.Struct.Fields` -> `effect/unstable/schema/VariantSchema#Struct.Fields`: Import the retained Struct.Fields helper type from the v4 unstable VariantSchema module. +- `VariantSchema.Struct.Fields` -> `effect/schema/VariantSchema#Struct.Fields`: Import the retained Struct.Fields helper type from the v4 unstable VariantSchema module. -- `VariantSchema.TypeId` -> `effect/unstable/schema/VariantSchema#TypeId`: Use the retained runtime value; in type position use typeof VariantSchema.TypeId. +- `VariantSchema.TypeId` -> `effect/schema/VariantSchema#TypeId`: Use the retained runtime value; in type position use typeof VariantSchema.TypeId. - `VariantSchema.fromKey` -> `none`: Field-level fromKey was not ported; for whole-struct encoded-key renaming consider Schema.encodeKeys. - `VariantSchema.fromKey.Rename` -> `none`: The fromKey rename helper was not ported; for whole-struct encoded-key renaming consider Schema.encodeKeys. -- `VariantSchema.make` -> `effect/unstable/schema/VariantSchema#make`: Import make from the v4 module; FieldOnly and FieldExcept take one key array and Union takes one member array. +- `VariantSchema.make` -> `effect/schema/VariantSchema#make`: Import make from the v4 module; FieldOnly and FieldExcept take one key array and Union takes one member array. ### `@effect/opentelemetry/Logger` @@ -6128,29 +6156,29 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/opentelemetry/Otlp` -- `Otlp.layer` -> `Otlp.layer`: Moved to effect/unstable/observability/Otlp; replaceLogger was replaced by loggerMergeWithExisting, and metricsTemporality is now configurable. +- `Otlp.layer` -> `Otlp.layer`: Moved to effect/observability/Otlp; replaceLogger was replaced by loggerMergeWithExisting, and metricsTemporality is now configurable. ### `@effect/opentelemetry/OtlpLogger` -- `OtlpLogger.layer` -> `OtlpLogger.layer`: Moved to effect/unstable/observability/OtlpLogger; use mergeWithExisting instead of passing replaceLogger. +- `OtlpLogger.layer` -> `OtlpLogger.layer`: Moved to effect/observability/OtlpLogger; use mergeWithExisting instead of passing replaceLogger. -- `OtlpLogger.make` -> `OtlpLogger.make`: The constructor remains in the module moved to effect/unstable/observability/OtlpLogger. +- `OtlpLogger.make` -> `OtlpLogger.make`: The constructor remains in the module moved to effect/observability/OtlpLogger. ### `@effect/opentelemetry/OtlpMetrics` -- `OtlpMetrics.layer` -> `OtlpMetrics.layer`: Moved to effect/unstable/observability/OtlpMetrics; the layer now also accepts optional cumulative or delta temporality. +- `OtlpMetrics.layer` -> `OtlpMetrics.layer`: Moved to effect/observability/OtlpMetrics; the layer now also accepts optional cumulative or delta temporality. -- `OtlpMetrics.make` -> `OtlpMetrics.make`: Moved to effect/unstable/observability/OtlpMetrics; the constructor now also accepts optional cumulative or delta temporality. +- `OtlpMetrics.make` -> `OtlpMetrics.make`: Moved to effect/observability/OtlpMetrics; the constructor now also accepts optional cumulative or delta temporality. ### `@effect/opentelemetry/OtlpResource` -- `OtlpResource.unsafeServiceName` -> `OtlpResource.serviceNameUnsafe`: Moved to effect/unstable/observability/OtlpResource and renamed to follow the v4 unsafe-suffix convention. +- `OtlpResource.unsafeServiceName` -> `OtlpResource.serviceNameUnsafe`: Moved to effect/observability/OtlpResource and renamed to follow the v4 unsafe-suffix convention. ### `@effect/opentelemetry/OtlpTracer` -- `OtlpTracer.layer` -> `OtlpTracer.layer`: The layer remains in the module moved to effect/unstable/observability/OtlpTracer. +- `OtlpTracer.layer` -> `OtlpTracer.layer`: The layer remains in the module moved to effect/observability/OtlpTracer. -- `OtlpTracer.make` -> `OtlpTracer.make`: The constructor remains in the module moved to effect/unstable/observability/OtlpTracer. +- `OtlpTracer.make` -> `OtlpTracer.make`: The constructor remains in the module moved to effect/observability/OtlpTracer. ### `@effect/opentelemetry/Resource` @@ -6220,7 +6248,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform-bun/BunCommandExecutor` -- `BunCommandExecutor.layer` -> `BunChildProcessSpawner.layer`: CommandExecutor became effect/unstable/process/ChildProcessSpawner; the Bun adapter was renamed and still requires FileSystem and Path. +- `BunCommandExecutor.layer` -> `BunChildProcessSpawner.layer`: CommandExecutor became effect/process/ChildProcessSpawner; the Bun adapter was renamed and still requires FileSystem and Path. ### `@effect/platform-bun/BunContext` @@ -6282,7 +6310,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform-node-shared/NodeCommandExecutor` -- `NodeCommandExecutor.layer` -> `NodeChildProcessSpawner.layer`: CommandExecutor became effect/unstable/process/ChildProcessSpawner; the Node adapter was renamed and still requires FileSystem and Path. +- `NodeCommandExecutor.layer` -> `NodeChildProcessSpawner.layer`: CommandExecutor became effect/process/ChildProcessSpawner; the Node adapter was renamed and still requires FileSystem and Path. ### `@effect/platform-node-shared/NodeFileSystem/ParcelWatcher` @@ -6388,6 +6416,8 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Undici.DiagnosticsChannel.RequestErrorMessage` -> `undici.DiagnosticsChannel.RequestErrorMessage`: Import the same type-only namespace member; its error field is the built-in Error type in Undici 8. +- `Undici.DiagnosticsChannel.Response`: TODO: needs guidance + - `Undici.Dispatcher` -> `undici.Dispatcher`: Import the upstream Dispatcher directly; custom dispatchers must adopt Undici 8's controller-based v2 handler API. - `Undici.Dispatcher.ConnectOptions` -> `undici.Dispatcher.ConnectOptions`: Import the same Dispatcher namespace type; Undici 8 removes maxRedirections and redirectionLimitReached. @@ -6458,7 +6488,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/Command` -- `Command.Command` -> `ChildProcess.Command`: Commands moved to effect/unstable/process/ChildProcess and are now directly Effectable to spawn a ChildProcessHandle. +- `Command.Command` -> `ChildProcess.Command`: Commands moved to effect/process/ChildProcess and are now directly Effectable to spawn a ChildProcessHandle. - `Command.Command.Input` -> `ChildProcess.CommandInput`: The standard-input configuration type was flattened out of the Command namespace. @@ -6496,7 +6526,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/CommandExecutor` -- `CommandExecutor.CommandExecutor` -> `ChildProcessSpawner.ChildProcessSpawner`: The executor service moved to effect/unstable/process/ChildProcessSpawner and was renamed. +- `CommandExecutor.CommandExecutor` -> `ChildProcessSpawner.ChildProcessSpawner`: The executor service moved to effect/process/ChildProcessSpawner and was renamed. - `CommandExecutor.Process` -> `ChildProcessSpawner.ChildProcessHandle`: Running-process handles were renamed and moved to ChildProcessSpawner. @@ -6536,8 +6566,6 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Error.TypeIdError` -> `Data.TaggedError or Schema.Error`: The platform-specific error-class factory was removed; define tagged data errors or schema-backed error classes directly. -- `Error.isPlatformError` -> `value instanceof PlatformError.PlatformError`: PlatformError is a class in v4; use an instanceof check or match its PlatformError tag. - ### `@effect/platform/Etag` - `Etag.GeneratorTypeId` -> `Etag.Generator`: The standalone generator brand was removed; Generator is now a Context.Service class. @@ -6586,7 +6614,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `FileSystem.Size` -> `ByteSize.ByteSize`: Use ByteSize.bytes or unit constructors for file sizes. Truncation lengths, buffer sizes, and read/write counts use number. File.seek takes and returns signed bigint positions; it can fail with PlatformError, including BadArgument when seeking before the start. -- `FileSystem.SizeInput` -> `ByteSize.Input`: File-size and path-backed range inputs use ByteSize.Input. Truncation lengths, Web File ranges, and buffer sizes use number. +- `FileSystem.SizeInput` -> `ByteSize.Input`: File-size and path-backed range inputs use ByteSize.Input. String inputs must be non-negative decimal integers without leading zeros, followed by a canonical unit symbol or lowercase unit name, with at most one separating space. Parse external strings or fractional quantities with ByteSize.fromString (Option) or ByteSize.fromStringUnsafe (throws), then pass the resulting ByteSize. Truncation lengths, Web File ranges, and buffer sizes use number. - `FileSystem.StreamOptions` -> `NonNullable[1]>`: Stream options are inline; bufferSize was removed, bytesToRead and offset accept ByteSize inputs, and chunkSize uses number. @@ -6610,7 +6638,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/Headers` -- `Headers.Headers` -> `Headers.Headers`: Import Headers from effect/unstable/http; the immutable string-record interface is retained with its v4 TypeId brand. +- `Headers.Headers` -> `Headers.Headers`: Import Headers from effect/http; the immutable string-record interface is retained with its v4 TypeId brand. - `Headers.HeadersTypeId` -> `Headers.TypeId`: The public Headers type-id symbol was renamed from HeadersTypeId to TypeId. @@ -6628,245 +6656,245 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpApi.Api` -> `none`: The Context tag carrying the API was removed. Pass the HttpApi value explicitly to builders and clients. -- `HttpApi.HttpApi.Any` -> `effect/unstable/httpapi/HttpApi#Constraint`: Use the erased marker constraint when only HttpApi identity is needed. +- `HttpApi.HttpApi.Any` -> `effect/http-api/HttpApi#Constraint`: Use the erased marker constraint when only HttpApi identity is needed. -- `HttpApi.HttpApi.AnyWithProps` -> `effect/unstable/httpapi/HttpApi#Top`: Use the widened HttpApi type that retains runtime properties. +- `HttpApi.HttpApi.AnyWithProps` -> `effect/http-api/HttpApi#Top`: Use the widened HttpApi type that retains runtime properties. - `HttpApi.TypeId` -> `none`: The marker is private in v4; use HttpApi.isHttpApi for runtime narrowing and Constraint or Top for types. -- `HttpApi.make` -> `effect/unstable/httpapi/HttpApi#make`: The constructor remains, but API-wide error and service parameters were removed; declare errors on endpoints and attach middleware. +- `HttpApi.make` -> `effect/http-api/HttpApi#make`: The constructor remains, but API-wide error and service parameters were removed; declare errors on endpoints and attach middleware. ### `@effect/platform/HttpApiBuilder` -- `HttpApiBuilder.Handlers` -> `effect/unstable/httpapi/HttpApiBuilder#Handlers`: Handlers now tracks an endpoint map and handled identifiers. Prefer Handlers.FromGroup\. +- `HttpApiBuilder.Handlers` -> `effect/http-api/HttpApiBuilder#Handlers`: Handlers now tracks an endpoint map and handled identifiers. Prefer Handlers.FromGroup\. -- `HttpApiBuilder.Handlers.Error` -> `effect/unstable/httpapi/HttpApiBuilder#Handlers.Error`: The helper remains and extracts the error channel of an effectful group-builder return. +- `HttpApiBuilder.Handlers.Error` -> `effect/http-api/HttpApiBuilder#Handlers.Error`: The helper remains and extracts the error channel of an effectful group-builder return. - `HttpApiBuilder.Handlers.Middleware` -> `none`: The handler-internal HttpApp middleware alias was removed. Use HttpRouter.middleware inference or HttpRouter.middleware.Fn. -- `HttpApiBuilder.Handlers.ValidateReturn` -> `effect/unstable/httpapi/HttpApiBuilder#Handlers.ValidateReturn`: The validator remains and now checks the endpoint map against handled identifiers. +- `HttpApiBuilder.Handlers.ValidateReturn` -> `effect/http-api/HttpApiBuilder#Handlers.ValidateReturn`: The validator remains and now checks the endpoint map against handled identifiers. - `HttpApiBuilder.HandlersTypeId` -> `none`: The exported symbol was removed; do not inspect or construct the private Handlers marker. - `HttpApiBuilder.Middleware` -> `none`: The API-specific middleware service tag was removed. Declared HttpApiMiddleware services are applied while routes are built; use HttpRouter.middleware for additional global middleware. -- `HttpApiBuilder.MiddlewareFn` -> `effect/unstable/http/HttpRouter#middleware.Fn`: HTTP apps are Effects in v4; use the router middleware function type or infer it through HttpRouter.middleware. +- `HttpApiBuilder.MiddlewareFn` -> `effect/http/HttpRouter#middleware.Fn`: HTTP apps are Effects in v4; use the router middleware function type or infer it through HttpRouter.middleware. -- `HttpApiBuilder.Router` -> `effect/unstable/http/HttpRouter#HttpRouter`: The API-specific router tag was removed; API and group layers register with the shared HttpRouter service. +- `HttpApiBuilder.Router` -> `effect/http/HttpRouter#HttpRouter`: The API-specific router tag was removed; API and group layers register with the shared HttpRouter service. -- `HttpApiBuilder.api` -> `effect/unstable/httpapi/HttpApiBuilder#layer`: Use layer(api) and provide the group layers; it registers the completed API with HttpRouter. +- `HttpApiBuilder.api` -> `effect/http-api/HttpApiBuilder#layer`: Use layer(api) and provide the group layers; it registers the completed API with HttpRouter. - `HttpApiBuilder.buildMiddleware` -> `none`: API-wide middleware assembly was removed. Declared HttpApiMiddleware services are applied while routes are built; use HttpRouter.middleware for additional middleware. -- `HttpApiBuilder.group` -> `effect/unstable/httpapi/HttpApiBuilder#group`: The group layer remains; names are now identifiers and API/group global error channels are gone. +- `HttpApiBuilder.group` -> `effect/http-api/HttpApiBuilder#group`: The group layer remains; names are now identifiers and API/group global error channels are gone. -- `HttpApiBuilder.handler` -> `effect/unstable/httpapi/HttpApiBuilder#handler`: The typed callback helper remains; names are now identifiers and API/group global error channels are gone. Pass the returned callback to handlers.handle. +- `HttpApiBuilder.handler` -> `effect/http-api/HttpApiBuilder#handler`: The typed callback helper remains; names are now identifiers and API/group global error channels are gone. Pass the returned callback to handlers.handle. -- `HttpApiBuilder.httpApp` -> `effect/unstable/http/HttpRouter#toHttpEffect`: Build the application from the assembled API route layer; HTTP apps are Effects in v4. +- `HttpApiBuilder.httpApp` -> `effect/http/HttpRouter#toHttpEffect`: Build the application from the assembled API route layer; HTTP apps are Effects in v4. -- `HttpApiBuilder.middleware` -> `effect/unstable/http/HttpRouter#middleware`: Use router effect middleware and provide its layer to the API route layer; global middleware can target all router routes. +- `HttpApiBuilder.middleware` -> `effect/http/HttpRouter#middleware`: Use router effect middleware and provide its layer to the API route layer; global middleware can target all router routes. -- `HttpApiBuilder.middlewareCors` -> `effect/unstable/http/HttpRouter#cors`: Use the router CORS layer, or provide route-scoped HttpMiddleware.cors through HttpRouter.middleware. +- `HttpApiBuilder.middlewareCors` -> `effect/http/HttpRouter#cors`: Use the router CORS layer, or provide route-scoped HttpMiddleware.cors through HttpRouter.middleware. -- `HttpApiBuilder.middlewareOpenApi` -> `effect/unstable/httpapi/HttpApiBuilder#layer`: Set openapiPath in layer(api, options). The additionalPropertiesStrategy option was removed. +- `HttpApiBuilder.middlewareOpenApi` -> `effect/http-api/HttpApiBuilder#layer`: Set openapiPath in layer(api, options). The additionalPropertiesStrategy option was removed. -- `HttpApiBuilder.toWebHandler` -> `effect/unstable/http/HttpRouter#toWebHandler`: Pass the assembled API route layer to HttpRouter.toWebHandler; the handler and dispose lifecycle is retained. +- `HttpApiBuilder.toWebHandler` -> `effect/http/HttpRouter#toWebHandler`: Pass the assembled API route layer to HttpRouter.toWebHandler; the handler and dispose lifecycle is retained. ### `@effect/platform/HttpApiClient` -- `HttpApiClient.Client.Method` -> `effect/unstable/httpapi/HttpApiClient#Client.Method`: The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse. +- `HttpApiClient.Client.Method` -> `effect/http-api/HttpApiClient#Client.Method`: The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse. -- `HttpApiClient.endpoint` -> `effect/unstable/httpapi/HttpApiClient#endpoint`: The endpoint client remains, selected by group and endpoint identifiers and using v4 request and responseMode fields. +- `HttpApiClient.endpoint` -> `effect/http-api/HttpApiClient#endpoint`: The endpoint client remains, selected by group and endpoint identifiers and using v4 request and responseMode fields. -- `HttpApiClient.make` -> `effect/unstable/httpapi/HttpApiClient#make`: The generated client remains; errors and services are now derived per endpoint and middleware. +- `HttpApiClient.make` -> `effect/http-api/HttpApiClient#make`: The generated client remains; errors and services are now derived per endpoint and middleware. -- `HttpApiClient.makeWith` -> `effect/unstable/httpapi/HttpApiClient#makeWith`: The supplied-HttpClient constructor remains and now requires endpoint client-middleware services. +- `HttpApiClient.makeWith` -> `effect/http-api/HttpApiClient#makeWith`: The supplied-HttpClient constructor remains and now requires endpoint client-middleware services. ### `@effect/platform/HttpApiEndpoint` -- `HttpApiEndpoint.HttpApiEndpoint` -> `effect/unstable/httpapi/HttpApiEndpoint#HttpApiEndpoint`: The model remains, but its generics now carry path literals, schemas, middleware, and middleware services. +- `HttpApiEndpoint.HttpApiEndpoint` -> `effect/http-api/HttpApiEndpoint#HttpApiEndpoint`: The model remains, but its generics now carry path literals, schemas, middleware, and middleware services. -- `HttpApiEndpoint.HttpApiEndpoint.AddContext` -> `effect/unstable/httpapi/HttpApiEndpoint#AddMiddleware`: Use AddMiddleware to add a middleware identifier and compute its service transformation. +- `HttpApiEndpoint.HttpApiEndpoint.AddContext` -> `effect/http-api/HttpApiEndpoint#AddMiddleware`: Use AddMiddleware to add a middleware identifier and compute its service transformation. - `HttpApiEndpoint.HttpApiEndpoint.AddError` -> `none`: Declare error schemas in the endpoint constructor options; the type helper and fluent addError method were removed. - `HttpApiEndpoint.HttpApiEndpoint.Constructor` -> `none`: The tagged-template constructor type was removed; use HttpApiEndpoint.make(method)(identifier, path, options?). -- `HttpApiEndpoint.HttpApiEndpoint.Context` -> `effect/unstable/httpapi/HttpApiEndpoint#ServerServices`: Use ServerServices for handler requirements; middleware IDs and extra requirements have separate extractors. +- `HttpApiEndpoint.HttpApiEndpoint.Context` -> `effect/http-api/HttpApiEndpoint#ServerServices`: Use ServerServices for handler requirements; middleware IDs and extra requirements have separate extractors. -- `HttpApiEndpoint.HttpApiEndpoint.ContextWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#ServerServicesWithIdentifier`: Name became Identifier; combine with middleware extractors when the complete handler requirement union is needed. +- `HttpApiEndpoint.HttpApiEndpoint.ContextWithName` -> `effect/http-api/HttpApiEndpoint#ServerServicesWithIdentifier`: Name became Identifier; combine with middleware extractors when the complete handler requirement union is needed. -- `HttpApiEndpoint.HttpApiEndpoint.Error` -> `effect/unstable/httpapi/HttpApiEndpoint#Errors`: Use Errors for the decoded endpoint and middleware error union; v4 Error extracts the schema. +- `HttpApiEndpoint.HttpApiEndpoint.Error` -> `effect/http-api/HttpApiEndpoint#Errors`: Use Errors for the decoded endpoint and middleware error union; v4 Error extracts the schema. -- `HttpApiEndpoint.HttpApiEndpoint.ErrorContext` -> `effect/unstable/httpapi/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. +- `HttpApiEndpoint.HttpApiEndpoint.ErrorContext` -> `effect/http-api/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. -- `HttpApiEndpoint.HttpApiEndpoint.ErrorContextWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode`: Select the endpoint with WithIdentifier, then apply the encode or decode service extractor. +- `HttpApiEndpoint.HttpApiEndpoint.ErrorContextWithName` -> `effect/http-api/HttpApiEndpoint#ErrorServicesEncode / ErrorServicesDecode`: Select the endpoint with WithIdentifier, then apply the encode or decode service extractor. -- `HttpApiEndpoint.HttpApiEndpoint.ErrorWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#ErrorsWithIdentifier`: Renamed for identifier and returns the decoded endpoint plus middleware error union. +- `HttpApiEndpoint.HttpApiEndpoint.ErrorWithName` -> `effect/http-api/HttpApiEndpoint#ErrorsWithIdentifier`: Renamed for identifier and returns the decoded endpoint plus middleware error union. -- `HttpApiEndpoint.HttpApiEndpoint.ExcludeName` -> `effect/unstable/httpapi/HttpApiEndpoint#ExcludeIdentifier`: Direct rename from name to identifier. +- `HttpApiEndpoint.HttpApiEndpoint.ExcludeName` -> `effect/http-api/HttpApiEndpoint#ExcludeIdentifier`: Direct rename from name to identifier. - `HttpApiEndpoint.HttpApiEndpoint.ExtractPath` -> `none`: Tagged-template path extraction was removed. Put a params schema or field record in constructor option params. -- `HttpApiEndpoint.HttpApiEndpoint.HandlerRawWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#HandlerRawWithIdentifier`: Direct rename; raw request fields are now params and query. +- `HttpApiEndpoint.HttpApiEndpoint.HandlerRawWithName` -> `effect/http-api/HttpApiEndpoint#HandlerRawWithIdentifier`: Direct rename; raw request fields are now params and query. -- `HttpApiEndpoint.HttpApiEndpoint.HandlerWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#HandlerWithIdentifier`: Direct rename from name to identifier. +- `HttpApiEndpoint.HttpApiEndpoint.HandlerWithName` -> `effect/http-api/HttpApiEndpoint#HandlerWithIdentifier`: Direct rename from name to identifier. - `HttpApiEndpoint.HttpApiEndpoint.OptionalTypePropertySignature` -> `none`: Removed with the tagged-template path implementation. - `HttpApiEndpoint.HttpApiEndpoint.PathEntries` -> `none`: Removed with tagged-template path extraction; declare endpoint params explicitly. -- `HttpApiEndpoint.HttpApiEndpoint.PathParsed` -> `effect/unstable/httpapi/HttpApiEndpoint#Params`: Path data became params; Params extracts the schema, so use Params\["Type"] for decoded data. +- `HttpApiEndpoint.HttpApiEndpoint.PathParsed` -> `effect/http-api/HttpApiEndpoint#Params`: Path data became params; Params extracts the schema, so use Params\["Type"] for decoded data. -- `HttpApiEndpoint.HttpApiEndpoint.Payload` -> `effect/unstable/httpapi/HttpApiEndpoint#Payload`: The name remains but now extracts the schema; use Payload\["Type"] for buffered decoded data. +- `HttpApiEndpoint.HttpApiEndpoint.Payload` -> `effect/http-api/HttpApiEndpoint#Payload`: The name remains but now extracts the schema; use Payload\["Type"] for buffered decoded data. -- `HttpApiEndpoint.HttpApiEndpoint.Success` -> `effect/unstable/httpapi/HttpApiEndpoint#SuccessWithIdentifier`: Use SuccessWithIdentifier for the decoded, stream-aware result; v4 Success extracts the schema. +- `HttpApiEndpoint.HttpApiEndpoint.Success` -> `effect/http-api/HttpApiEndpoint#SuccessWithIdentifier`: Use SuccessWithIdentifier for the decoded, stream-aware result; v4 Success extracts the schema. -- `HttpApiEndpoint.HttpApiEndpoint.SuccessWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#SuccessWithIdentifier`: Direct rename from name to identifier; the result remains decoded and stream-aware. +- `HttpApiEndpoint.HttpApiEndpoint.SuccessWithName` -> `effect/http-api/HttpApiEndpoint#SuccessWithIdentifier`: Direct rename from name to identifier; the result remains decoded and stream-aware. -- `HttpApiEndpoint.HttpApiEndpoint.UrlParams` -> `effect/unstable/httpapi/HttpApiEndpoint#Query`: urlParams became query; Query extracts the schema, so use Query\["Type"] for decoded data. +- `HttpApiEndpoint.HttpApiEndpoint.UrlParams` -> `effect/http-api/HttpApiEndpoint#Query`: urlParams became query; Query extracts the schema, so use Query\["Type"] for decoded data. -- `HttpApiEndpoint.HttpApiEndpoint.ValidateHeaders` -> `effect/unstable/httpapi/HttpApiEndpoint#HeadersConstraint`: Validation moved from an intersection helper to a constructor generic constraint. +- `HttpApiEndpoint.HttpApiEndpoint.ValidateHeaders` -> `effect/http-api/HttpApiEndpoint#HeadersConstraint`: Validation moved from an intersection helper to a constructor generic constraint. - `HttpApiEndpoint.HttpApiEndpoint.ValidateParams` -> `none`: Tagged-template interpolation validation was removed; params are declared explicitly in options.params. -- `HttpApiEndpoint.HttpApiEndpoint.ValidatePath` -> `effect/unstable/httpapi/HttpApiEndpoint#ParamsConstraint`: path became params and validation is now a constructor constraint. +- `HttpApiEndpoint.HttpApiEndpoint.ValidatePath` -> `effect/http-api/HttpApiEndpoint#ParamsConstraint`: path became params and validation is now a constructor constraint. -- `HttpApiEndpoint.HttpApiEndpoint.ValidatePayload` -> `effect/unstable/httpapi/HttpApiEndpoint#PayloadConstraint`: Payload validation is now a method-sensitive constructor constraint. +- `HttpApiEndpoint.HttpApiEndpoint.ValidatePayload` -> `effect/http-api/HttpApiEndpoint#PayloadConstraint`: Payload validation is now a method-sensitive constructor constraint. -- `HttpApiEndpoint.HttpApiEndpoint.ValidateUrlParams` -> `effect/unstable/httpapi/HttpApiEndpoint#QueryConstraint`: urlParams became query and validation is now a constructor constraint. +- `HttpApiEndpoint.HttpApiEndpoint.ValidateUrlParams` -> `effect/http-api/HttpApiEndpoint#QueryConstraint`: urlParams became query and validation is now a constructor constraint. -- `HttpApiEndpoint.PathSegment` -> `effect/unstable/http/HttpRouter#PathInput`: Path input moved to the shared router and is generalized to slash-prefixed paths or wildcard. +- `HttpApiEndpoint.PathSegment` -> `effect/http/HttpRouter#PathInput`: Path input moved to the shared router and is generalized to slash-prefixed paths or wildcard. - `HttpApiEndpoint.TypeId` -> `none`: The endpoint type ID is private; use HttpApiEndpoint.isHttpApiEndpoint for runtime narrowing. -- `HttpApiEndpoint.get` -> `effect/unstable/httpapi/HttpApiEndpoint#get`: Use get(identifier, path, options?); tagged templates and fluent schema setters were removed. +- `HttpApiEndpoint.get` -> `effect/http-api/HttpApiEndpoint#get`: Use get(identifier, path, options?); tagged templates and fluent schema setters were removed. -- `HttpApiEndpoint.head` -> `effect/unstable/httpapi/HttpApiEndpoint#head`: Use head(identifier, path, options?); tagged templates and fluent schema setters were removed. +- `HttpApiEndpoint.head` -> `effect/http-api/HttpApiEndpoint#head`: Use head(identifier, path, options?); tagged templates and fluent schema setters were removed. -- `HttpApiEndpoint.make` -> `effect/unstable/httpapi/HttpApiEndpoint#make`: The factory remains but now requires identifier, path, and options and applies codecs unless disabled. +- `HttpApiEndpoint.make` -> `effect/http-api/HttpApiEndpoint#make`: The factory remains but now requires identifier, path, and options and applies codecs unless disabled. -- `HttpApiEndpoint.options` -> `effect/unstable/httpapi/HttpApiEndpoint#options`: Same HTTP method constructor with the new identifier, path, and options signature. +- `HttpApiEndpoint.options` -> `effect/http-api/HttpApiEndpoint#options`: Same HTTP method constructor with the new identifier, path, and options signature. -- `HttpApiEndpoint.patch` -> `effect/unstable/httpapi/HttpApiEndpoint#patch`: Same HTTP method constructor with the new identifier, path, and options signature. +- `HttpApiEndpoint.patch` -> `effect/http-api/HttpApiEndpoint#patch`: Same HTTP method constructor with the new identifier, path, and options signature. -- `HttpApiEndpoint.post` -> `effect/unstable/httpapi/HttpApiEndpoint#post`: Same HTTP method constructor with the new identifier, path, and options signature. +- `HttpApiEndpoint.post` -> `effect/http-api/HttpApiEndpoint#post`: Same HTTP method constructor with the new identifier, path, and options signature. -- `HttpApiEndpoint.put` -> `effect/unstable/httpapi/HttpApiEndpoint#put`: Same HTTP method constructor with the new identifier, path, and options signature. +- `HttpApiEndpoint.put` -> `effect/http-api/HttpApiEndpoint#put`: Same HTTP method constructor with the new identifier, path, and options signature. ### `@effect/platform/HttpApiError` -- `HttpApiError.Forbidden` -> `effect/unstable/httpapi/HttpApiError#ForbiddenNoContent`: Use ForbiddenNoContent to preserve the empty 403 wire schema; Forbidden now has a JSON-tagged body. +- `HttpApiError.Forbidden` -> `effect/http-api/HttpApiError#ForbiddenNoContent`: Use ForbiddenNoContent to preserve the empty 403 wire schema; Forbidden now has a JSON-tagged body. -- `HttpApiError.HttpApiDecodeError` -> `effect/unstable/httpapi/HttpApiError#HttpApiSchemaError`: Validation now stores kind and a SchemaError cause and is a defect unless transformed by schema-error middleware. +- `HttpApiError.HttpApiDecodeError` -> `effect/http-api/HttpApiError#HttpApiSchemaError`: Validation now stores kind and a SchemaError cause and is a defect unless transformed by schema-error middleware. - `HttpApiError.Issue` -> `effect/SchemaIssue#Issue`: Structured failures now live at HttpApiSchemaError.cause.issue; format them explicitly when a flat external list is needed. -- `HttpApiError.TypeId` -> `effect/unstable/httpapi/HttpApiError#HttpApiSchemaErrorTypeId`: The old module symbol is gone; prefer HttpApiSchemaError.is for runtime narrowing. +- `HttpApiError.TypeId` -> `effect/http-api/HttpApiError#HttpApiSchemaErrorTypeId`: The old module symbol is gone; prefer HttpApiSchemaError.is for runtime narrowing. ### `@effect/platform/HttpApiGroup` -- `HttpApiGroup.ApiGroup` -> `effect/unstable/httpapi/HttpApiGroup#Service`: Renamed; the service field and type parameter are now identifier rather than name. +- `HttpApiGroup.ApiGroup` -> `effect/http-api/HttpApiGroup#Service`: Renamed; the service field and type parameter are now identifier rather than name. - `HttpApiGroup.HttpApiGroup.AddContext` -> `none`: Groups no longer carry arbitrary context. Use AddMiddleware for middleware service transformations. -- `HttpApiGroup.HttpApiGroup.Any` -> `effect/unstable/httpapi/HttpApiGroup#Constraint`: Renamed widened structural constraint. +- `HttpApiGroup.HttpApiGroup.Any` -> `effect/http-api/HttpApiGroup#Constraint`: Renamed widened structural constraint. -- `HttpApiGroup.HttpApiGroup.AnyWithProps` -> `effect/unstable/httpapi/HttpApiGroup#Top`: Renamed widened runtime-property type. +- `HttpApiGroup.HttpApiGroup.AnyWithProps` -> `effect/http-api/HttpApiGroup#Top`: Renamed widened runtime-property type. -- `HttpApiGroup.HttpApiGroup.ClientContext` -> `effect/unstable/httpapi/HttpApiGroup#ClientServices / ErrorServicesDecode / MiddlewareClient`: Client schema services and required client middleware are separate extractors in v4. +- `HttpApiGroup.HttpApiGroup.ClientContext` -> `effect/http-api/HttpApiGroup#ClientServices / ErrorServicesDecode / MiddlewareClient`: Client schema services and required client middleware are separate extractors in v4. - `HttpApiGroup.HttpApiGroup.Context` -> `none`: Group error and context generics were removed; derive server requirements from the group's endpoints. - `HttpApiGroup.HttpApiGroup.ContextWithName` -> `none`: Select with WithIdentifier and derive endpoint server requirements; groups no longer have a context generic. -- `HttpApiGroup.HttpApiGroup.EndpointsWithName` -> `effect/unstable/httpapi/HttpApiGroup#EndpointsWithIdentifier`: Direct rename from name to identifier. +- `HttpApiGroup.HttpApiGroup.EndpointsWithName` -> `effect/http-api/HttpApiGroup#EndpointsWithIdentifier`: Direct rename from name to identifier. - `HttpApiGroup.HttpApiGroup.Error` -> `none`: Group-level errors were removed. Declare shared errors on each endpoint or through middleware. -- `HttpApiGroup.HttpApiGroup.ErrorContext` -> `effect/unstable/httpapi/HttpApiGroup#ErrorServicesEncode / ErrorServicesDecode`: The closest endpoint-error aggregate splits server encoding from client decoding services. +- `HttpApiGroup.HttpApiGroup.ErrorContext` -> `effect/http-api/HttpApiGroup#ErrorServicesEncode / ErrorServicesDecode`: The closest endpoint-error aggregate splits server encoding from client decoding services. - `HttpApiGroup.HttpApiGroup.ErrorWithName` -> `none`: Group-level errors were removed; select with WithIdentifier and inspect Errors over the selected endpoints. -- `HttpApiGroup.HttpApiGroup.Middleware` -> `effect/unstable/httpapi/HttpApiEndpoint#Middleware`: Middleware is attached to the endpoints present when group.middleware is called; extract it from group endpoints. +- `HttpApiGroup.HttpApiGroup.Middleware` -> `effect/http-api/HttpApiEndpoint#Middleware`: Middleware is attached to the endpoints present when group.middleware is called; extract it from group endpoints. -- `HttpApiGroup.HttpApiGroup.MiddlewareWithName` -> `effect/unstable/httpapi/HttpApiEndpoint#Middleware`: Select the group with WithIdentifier, get its endpoints, then apply the endpoint Middleware extractor. +- `HttpApiGroup.HttpApiGroup.MiddlewareWithName` -> `effect/http-api/HttpApiEndpoint#Middleware`: Select the group with WithIdentifier, get its endpoints, then apply the endpoint Middleware extractor. -- `HttpApiGroup.HttpApiGroup.Provides` -> `effect/unstable/httpapi/HttpApiGroup#MiddlewareProvides`: Renamed; derives provided services from endpoint middleware. +- `HttpApiGroup.HttpApiGroup.Provides` -> `effect/http-api/HttpApiGroup#MiddlewareProvides`: Renamed; derives provided services from endpoint middleware. -- `HttpApiGroup.HttpApiGroup.ToService` -> `effect/unstable/httpapi/HttpApiGroup#ToService`: Same role and now produces Service\. +- `HttpApiGroup.HttpApiGroup.ToService` -> `effect/http-api/HttpApiGroup#ToService`: Same role and now produces Service\. -- `HttpApiGroup.HttpApiGroup.WithName` -> `effect/unstable/httpapi/HttpApiGroup#WithIdentifier`: Direct rename from name to identifier. +- `HttpApiGroup.HttpApiGroup.WithName` -> `effect/http-api/HttpApiGroup#WithIdentifier`: Direct rename from name to identifier. - `HttpApiGroup.TypeId` -> `none`: The group type ID is private; use HttpApiGroup.isHttpApiGroup for runtime narrowing. -- `HttpApiGroup.make` -> `effect/unstable/httpapi/HttpApiGroup#make`: The constructor remains; group error and context generics are gone and add is variadic. +- `HttpApiGroup.make` -> `effect/http-api/HttpApiGroup#make`: The constructor remains; group error and context generics are gone and add is variadic. ### `@effect/platform/HttpApiMiddleware` -- `HttpApiMiddleware.HttpApiMiddleware` -> `effect/unstable/httpapi/HttpApiMiddleware#HttpApiMiddleware`: The model remains but now wraps the response effect and carries provided services, an error schema, and required services. +- `HttpApiMiddleware.HttpApiMiddleware` -> `effect/http-api/HttpApiMiddleware#HttpApiMiddleware`: The model remains but now wraps the response effect and carries provided services, an error schema, and required services. -- `HttpApiMiddleware.HttpApiMiddleware.Any` -> `effect/unstable/httpapi/HttpApiMiddleware#AnyService`: Renamed widened middleware service-key shape. +- `HttpApiMiddleware.HttpApiMiddleware.Any` -> `effect/http-api/HttpApiMiddleware#AnyService`: Renamed widened middleware service-key shape. -- `HttpApiMiddleware.HttpApiMiddleware.AnyId` -> `effect/unstable/httpapi/HttpApiMiddleware#AnyId`: Same name; metadata now includes provided and required services, error schema, client error, and client requirement. +- `HttpApiMiddleware.HttpApiMiddleware.AnyId` -> `effect/http-api/HttpApiMiddleware#AnyId`: Same name; metadata now includes provided and required services, error schema, client error, and client requirement. -- `HttpApiMiddleware.HttpApiMiddleware.Error` -> `effect/unstable/httpapi/HttpApiMiddleware#Error`: Same name and now derives the decoded type from the configured error schema. +- `HttpApiMiddleware.HttpApiMiddleware.Error` -> `effect/http-api/HttpApiMiddleware#Error`: Same name and now derives the decoded type from the configured error schema. -- `HttpApiMiddleware.HttpApiMiddleware.ErrorContext` -> `effect/unstable/httpapi/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. +- `HttpApiMiddleware.HttpApiMiddleware.ErrorContext` -> `effect/http-api/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. - `HttpApiMiddleware.HttpApiMiddleware.Only` -> `Extract`: The helper was removed because middleware IDs are explicit; use Extract when the direct filter is still needed. -- `HttpApiMiddleware.HttpApiMiddleware.Provides` -> `effect/unstable/httpapi/HttpApiMiddleware#Provides`: Same name and reads the expanded v4 middleware ID metadata. +- `HttpApiMiddleware.HttpApiMiddleware.Provides` -> `effect/http-api/HttpApiMiddleware#Provides`: Same name and reads the expanded v4 middleware ID metadata. - `HttpApiMiddleware.SecurityTypeId` -> `none`: The marker is private; use HttpApiMiddleware.isSecurity. -- `HttpApiMiddleware.Tag` -> `effect/unstable/httpapi/HttpApiMiddleware#Service`: Renamed and redesigned; use error, requires, provides, clientError, and requiredForClient configuration. +- `HttpApiMiddleware.Tag` -> `effect/http-api/HttpApiMiddleware#Service`: Renamed and redesigned; use error, requires, provides, clientError, and requiredForClient configuration. -- `HttpApiMiddleware.TagClass` -> `effect/unstable/httpapi/HttpApiMiddleware#ServiceClass`: Renamed class type with the new two-stage type configuration and wrapping service shape. +- `HttpApiMiddleware.TagClass` -> `effect/http-api/HttpApiMiddleware#ServiceClass`: Renamed class type with the new two-stage type configuration and wrapping service shape. -- `HttpApiMiddleware.TagClass.BaseSecurity` -> `effect/unstable/httpapi/HttpApiMiddleware#ServiceClass`: Security is conditional metadata on ServiceClass; there is no separate public base interface. +- `HttpApiMiddleware.TagClass.BaseSecurity` -> `effect/http-api/HttpApiMiddleware#ServiceClass`: Security is conditional metadata on ServiceClass; there is no separate public base interface. -- `HttpApiMiddleware.TagClass.Failure` -> `effect/unstable/httpapi/HttpApiMiddleware#Error`: failure terminology became error; apply the extractor to the middleware ID. +- `HttpApiMiddleware.TagClass.Failure` -> `effect/http-api/HttpApiMiddleware#Error`: failure terminology became error; apply the extractor to the middleware ID. -- `HttpApiMiddleware.TagClass.FailureContext` -> `effect/unstable/httpapi/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode`: Failure schema services split by server encoding and client decoding direction. +- `HttpApiMiddleware.TagClass.FailureContext` -> `effect/http-api/HttpApiMiddleware#ErrorServicesEncode / ErrorServicesDecode`: Failure schema services split by server encoding and client decoding direction. -- `HttpApiMiddleware.TagClass.FailureSchema` -> `effect/unstable/httpapi/HttpApiMiddleware#ErrorSchema`: Renamed and applied to the middleware ID rather than constructor options. +- `HttpApiMiddleware.TagClass.FailureSchema` -> `effect/http-api/HttpApiMiddleware#ErrorSchema`: Renamed and applied to the middleware ID rather than constructor options. -- `HttpApiMiddleware.TagClass.FailureService` -> `effect/unstable/httpapi/HttpApiMiddleware#Error`: Use the decoded error extractor; optional middleware fallback was removed. +- `HttpApiMiddleware.TagClass.FailureService` -> `effect/http-api/HttpApiMiddleware#Error`: Use the decoded error extractor; optional middleware fallback was removed. - `HttpApiMiddleware.TagClass.Optional` -> `none`: Optional declaration and fallback-on-failure behavior were removed; model fallback in the wrapping middleware. -- `HttpApiMiddleware.TagClass.Provides` -> `effect/unstable/httpapi/HttpApiMiddleware#Provides`: Moved to the module level and applied to the middleware ID. +- `HttpApiMiddleware.TagClass.Provides` -> `effect/http-api/HttpApiMiddleware#Provides`: Moved to the module level and applied to the middleware ID. -- `HttpApiMiddleware.TagClassAny` -> `effect/unstable/httpapi/HttpApiMiddleware#AnyService`: Renamed widened service-key type. +- `HttpApiMiddleware.TagClassAny` -> `effect/http-api/HttpApiMiddleware#AnyService`: Renamed widened service-key type. -- `HttpApiMiddleware.TagClassSecurityAny` -> `effect/unstable/httpapi/HttpApiMiddleware#AnyServiceSecurity`: Renamed widened security service-key type. +- `HttpApiMiddleware.TagClassSecurityAny` -> `effect/http-api/HttpApiMiddleware#AnyServiceSecurity`: Renamed widened security service-key type. - `HttpApiMiddleware.TypeId` -> `none`: The marker is private; use public guards and type extractors. ### `@effect/platform/HttpApiScalar` -- `HttpApiScalar.layer` -> `effect/unstable/httpapi/HttpApiScalar#layer`: Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter. +- `HttpApiScalar.layer` -> `effect/http-api/HttpApiScalar#layer`: Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter. -- `HttpApiScalar.layerHttpLayerRouter` -> `effect/unstable/httpapi/HttpApiScalar#layer`: The duplicate was removed. Pass options.api as the first layer argument and the remaining Scalar options second. +- `HttpApiScalar.layerHttpLayerRouter` -> `effect/http-api/HttpApiScalar#layer`: The duplicate was removed. Pass options.api as the first layer argument and the remaining Scalar options second. -- `HttpApiScalar.layerHttpLayerRouterCdn` -> `effect/unstable/httpapi/HttpApiScalar#layerCdn`: Use the explicit-api CDN layer with path, version, and Scalar options. +- `HttpApiScalar.layerHttpLayerRouterCdn` -> `effect/http-api/HttpApiScalar#layerCdn`: Use the explicit-api CDN layer with path, version, and Scalar options. ### `@effect/platform/HttpApiSchema` -- `HttpApiSchema.AnnotationEmptyDecodeable` -> `effect/unstable/httpapi/HttpApiSchema#asNoContent`: The public marker was removed; represent no-content decoding structurally with asNoContent({ decode }). +- `HttpApiSchema.AnnotationEmptyDecodeable` -> `effect/http-api/HttpApiSchema#asNoContent`: The public marker was removed; represent no-content decoding structurally with asNoContent({ decode }). -- `HttpApiSchema.AnnotationEncoding` -> `effect/unstable/httpapi/HttpApiSchema#asJson / asFormUrlEncoded / asText / asUint8Array`: The key is internal; select encoding with a public combinator. +- `HttpApiSchema.AnnotationEncoding` -> `effect/http-api/HttpApiSchema#asJson / asFormUrlEncoded / asText / asUint8Array`: The key is internal; select encoding with a public combinator. -- `HttpApiSchema.AnnotationMultipart` -> `effect/unstable/httpapi/HttpApiSchema#asMultipart`: The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator. +- `HttpApiSchema.AnnotationMultipart` -> `effect/http-api/HttpApiSchema#asMultipart`: The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator. -- `HttpApiSchema.AnnotationMultipartStream` -> `effect/unstable/httpapi/HttpApiSchema#asMultipartStream`: The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator. +- `HttpApiSchema.AnnotationMultipartStream` -> `effect/http-api/HttpApiSchema#asMultipartStream`: The symbol annotation became a brand plus internal encoding metadata; apply the schema combinator. -- `HttpApiSchema.AnnotationParam` -> `effect/unstable/httpapi/HttpApiEndpoint#params`: Path names now live in the router path and schemas in endpoint option params, not schema annotations. +- `HttpApiSchema.AnnotationParam` -> `effect/http-api/HttpApiEndpoint#params`: Path names now live in the router path and schemas in endpoint option params, not schema annotations. -- `HttpApiSchema.AnnotationStatus` -> `effect/unstable/httpapi/HttpApiSchema#status`: The public symbol was removed; apply status(code), which uses the httpApiStatus schema annotation. +- `HttpApiSchema.AnnotationStatus` -> `effect/http-api/HttpApiSchema#status`: The public symbol was removed; apply status(code), which uses the httpApiStatus schema annotation. -- `HttpApiSchema.Empty` -> `effect/unstable/httpapi/HttpApiSchema#Empty`: The API remains and returns Schema.Void annotated with the supplied status. +- `HttpApiSchema.Empty` -> `effect/http-api/HttpApiSchema#Empty`: The API remains and returns Schema.Void annotated with the supplied status. - `HttpApiSchema.EmptyError` -> `effect/Schema#Error`: Define a normal schema error with httpApiStatus, then derive its no-content wire schema with asNoContent. @@ -6876,65 +6904,65 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpApiSchema.EmptyErrorUnifyIgnore` -> `none`: Removed with EmptyError; do not recreate the old Unify marker. -- `HttpApiSchema.Encoding` -> `effect/unstable/httpapi/HttpApiSchema#Encoding`: The name remains but is now a discriminated PayloadEncoding or ResponseEncoding union; prefer public as\* combinators. +- `HttpApiSchema.Encoding` -> `effect/http-api/HttpApiSchema#Encoding`: The name remains but is now a discriminated PayloadEncoding or ResponseEncoding union; prefer public as\* combinators. -- `HttpApiSchema.Multipart` -> `effect/unstable/httpapi/HttpApiSchema#asMultipart`: The type and constructor became a curried schema combinator: schema.pipe(asMultipart(options)). +- `HttpApiSchema.Multipart` -> `effect/http-api/HttpApiSchema#asMultipart`: The type and constructor became a curried schema combinator: schema.pipe(asMultipart(options)). -- `HttpApiSchema.MultipartStream` -> `effect/unstable/httpapi/HttpApiSchema#asMultipartStream`: The type and constructor became a curried schema combinator. +- `HttpApiSchema.MultipartStream` -> `effect/http-api/HttpApiSchema#asMultipartStream`: The type and constructor became a curried schema combinator. -- `HttpApiSchema.Text` -> `effect/unstable/httpapi/HttpApiSchema#asText`: Apply the encoding combinator to Schema.String instead of using a dedicated constructor. +- `HttpApiSchema.Text` -> `effect/http-api/HttpApiSchema#asText`: Apply the encoding combinator to Schema.String instead of using a dedicated constructor. -- `HttpApiSchema.Uint8Array` -> `effect/unstable/httpapi/HttpApiSchema#asUint8Array`: Apply the encoding combinator to Schema.Uint8Array instead of using a dedicated constructor. +- `HttpApiSchema.Uint8Array` -> `effect/http-api/HttpApiSchema#asUint8Array`: Apply the encoding combinator to Schema.Uint8Array instead of using a dedicated constructor. - `HttpApiSchema.UnionUnify` -> `effect/Schema#Union`: Use Schema.Union([self, that]); for endpoint alternatives, pass the schema array directly to preserve metadata. - `HttpApiSchema.annotations` -> `effect/Schema#annotate`: Schema annotations became annotate; set httpApiStatus directly or prefer HttpApiSchema.status for status only. -- `HttpApiSchema.asEmpty` -> `effect/unstable/httpapi/HttpApiSchema#asNoContent`: Use schema.pipe(asNoContent({ decode }), status(code)); status is now a separate combinator. +- `HttpApiSchema.asEmpty` -> `effect/http-api/HttpApiSchema#asNoContent`: Use schema.pipe(asNoContent({ decode }), status(code)); status is now a separate combinator. - `HttpApiSchema.deunionize` -> `none`: Pass schema arrays to endpoint success, error, and body alternatives so each member retains status and content type. - `HttpApiSchema.extractAnnotations` -> `none`: The internal symbol-copy helper was removed; HTTP metadata is schema-native and resolved through AST traversal. -- `HttpApiSchema.getEmptyDecodeable` -> `effect/unstable/httpapi/HttpApiSchema#isNoContent`: Use isNoContent only to test bodylessness; decodeability is structural and has no exact query replacement. +- `HttpApiSchema.getEmptyDecodeable` -> `effect/http-api/HttpApiSchema#isNoContent`: Use isNoContent only to test bodylessness; decodeability is structural and has no exact query replacement. -- `HttpApiSchema.getEncoding` -> `effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding / getResponseEncoding`: Encoding lookup split by direction; application code should normally use public as\* combinators. +- `HttpApiSchema.getEncoding` -> `effect/http-api/HttpApiSchema#getPayloadEncoding / getResponseEncoding`: Encoding lookup split by direction; application code should normally use public as\* combinators. -- `HttpApiSchema.getMultipart` -> `effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding`: Narrow the payload encoding to Multipart with buffered mode; multipart limits are on the encoding value. +- `HttpApiSchema.getMultipart` -> `effect/http-api/HttpApiSchema#getPayloadEncoding`: Narrow the payload encoding to Multipart with buffered mode; multipart limits are on the encoding value. -- `HttpApiSchema.getMultipartStream` -> `effect/unstable/httpapi/HttpApiSchema#getPayloadEncoding`: Narrow the payload encoding to Multipart with stream mode; multipart limits are on the encoding value. +- `HttpApiSchema.getMultipartStream` -> `effect/http-api/HttpApiSchema#getPayloadEncoding`: Narrow the payload encoding to Multipart with stream mode; multipart limits are on the encoding value. - `HttpApiSchema.getParam` -> `none`: Param identity moved out of schema metadata; read endpoint.path and endpoint.params. - `HttpApiSchema.getStatus` -> `effect/SchemaAST#resolveAt`: Resolve the httpApiStatus annotation directly, or prefer getStatusSuccess and getStatusError for response logic. -- `HttpApiSchema.getStatusError` -> `effect/unstable/httpapi/HttpApiSchema#getStatusError`: The helper remains but accepts an AST and defaults to 500. +- `HttpApiSchema.getStatusError` -> `effect/http-api/HttpApiSchema#getStatusError`: The helper remains but accepts an AST and defaults to 500. -- `HttpApiSchema.getStatusErrorAST` -> `effect/unstable/httpapi/HttpApiSchema#getStatusError`: The AST suffix collapsed into the sole helper, which defaults to 500. +- `HttpApiSchema.getStatusErrorAST` -> `effect/http-api/HttpApiSchema#getStatusError`: The AST suffix collapsed into the sole helper, which defaults to 500. -- `HttpApiSchema.getStatusSuccess` -> `effect/unstable/httpapi/HttpApiSchema#getStatusSuccess`: The helper remains but accepts an AST; bare Schema.Void now defaults to 200, so use Empty(204) for 204. +- `HttpApiSchema.getStatusSuccess` -> `effect/http-api/HttpApiSchema#getStatusSuccess`: The helper remains but accepts an AST; bare Schema.Void now defaults to 200, so use Empty(204) for 204. -- `HttpApiSchema.getStatusSuccessAST` -> `effect/unstable/httpapi/HttpApiSchema#getStatusSuccess`: The AST suffix collapsed into the sole helper; bare Schema.Void no longer implies 204. +- `HttpApiSchema.getStatusSuccessAST` -> `effect/http-api/HttpApiSchema#getStatusSuccess`: The AST suffix collapsed into the sole helper; bare Schema.Void no longer implies 204. -- `HttpApiSchema.param` -> `effect/unstable/httpapi/HttpApiEndpoint#params`: Use a literal /:name path and the matching field in endpoint constructor option params. +- `HttpApiSchema.param` -> `effect/http-api/HttpApiEndpoint#params`: Use a literal /:name path and the matching field in endpoint constructor option params. -- `HttpApiSchema.withEncoding` -> `effect/unstable/httpapi/HttpApiSchema#asJson / asFormUrlEncoded / asUint8Array / asText`: Replace the generic kind with the matching public curried encoding combinator. +- `HttpApiSchema.withEncoding` -> `effect/http-api/HttpApiSchema#asJson / asFormUrlEncoded / asUint8Array / asText`: Replace the generic kind with the matching public curried encoding combinator. ### `@effect/platform/HttpApiSecurity` -- `HttpApiSecurity.Bearer` -> `effect/unstable/httpapi/HttpApiSecurity#Http`: Bearer was generalized to Http with scheme Bearer; the value-level bearer singleton remains. +- `HttpApiSecurity.Bearer` -> `effect/http-api/HttpApiSecurity#Http`: Bearer was generalized to Http with scheme Bearer; the value-level bearer singleton remains. - `HttpApiSecurity.TypeId` -> `none`: The marker is private; use the public union or specific Http, ApiKey, and Basic types. -- `HttpApiSecurity.annotate` -> `effect/unstable/httpapi/HttpApiSecurity#annotate`: The combinator remains; its key is now the v4 Context.Key abstraction. +- `HttpApiSecurity.annotate` -> `effect/http-api/HttpApiSecurity#annotate`: The combinator remains; its key is now the v4 Context.Key abstraction. -- `HttpApiSecurity.annotateContext` -> `effect/unstable/httpapi/HttpApiSecurity#annotateMerge`: Renamed; it still merges a Context into existing OpenAPI annotations. +- `HttpApiSecurity.annotateContext` -> `effect/http-api/HttpApiSecurity#annotateMerge`: Renamed; it still merges a Context into existing OpenAPI annotations. ### `@effect/platform/HttpApiSwagger` -- `HttpApiSwagger.layer` -> `effect/unstable/httpapi/HttpApiSwagger#layer`: Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter. +- `HttpApiSwagger.layer` -> `effect/http-api/HttpApiSwagger#layer`: Pass the HttpApi as the first argument; the layer now contributes directly to HttpRouter. -- `HttpApiSwagger.layerHttpLayerRouter` -> `effect/unstable/httpapi/HttpApiSwagger#layer`: The duplicate was removed. Pass options.api first and the path option second. +- `HttpApiSwagger.layerHttpLayerRouter` -> `effect/http-api/HttpApiSwagger#layer`: The duplicate was removed. Pass options.api first and the path option second. ### `@effect/platform/HttpApp` @@ -7004,9 +7032,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpClient.retry` -> `HttpClient.retry`: Retained; the Schedule error channel is included in the resulting client error type. -- `HttpClient.tap` -> `effect/unstable/http/HttpClient#tap`: Moved to the v4 HTTP module with the same response-effect callback and client error/service widening. +- `HttpClient.tap` -> `effect/http/HttpClient#tap`: Moved to the v4 HTTP module with the same response-effect callback and client error/service widening. -- `HttpClient.transform` -> `effect/unstable/http/HttpClient#transform`: Moved to the v4 HTTP module with the same request-aware transformation shape. +- `HttpClient.transform` -> `effect/http/HttpClient#transform`: Moved to the v4 HTTP module with the same request-aware transformation shape. - `HttpClient.withSpanNameGenerator` -> `HttpClient.transformResponse(Effect.provideService(HttpClient.SpanNameGenerator, f))`: The convenience combinator was removed; provide the reference around response effects. @@ -7074,9 +7102,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/HttpLayerRouter` -- `HttpLayerRouter.FindMyWay.PathInput` -> `FindMyWay.PathInput`: Import FindMyWay from effect/unstable/http. +- `HttpLayerRouter.FindMyWay.PathInput` -> `FindMyWay.PathInput`: Import FindMyWay from effect/http. -- `HttpLayerRouter.FindMyWay.make` -> `FindMyWay.make`: Import FindMyWay from effect/unstable/http. +- `HttpLayerRouter.FindMyWay.make` -> `FindMyWay.make`: Import FindMyWay from effect/http. - `HttpLayerRouter.MiddlewareTypeId` -> `none`: The middleware type id is internal in v4; use HttpRouter.Middleware. @@ -7102,7 +7130,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpLayerRouter.addAll` -> `HttpRouter.addAll`: Moved to the consolidated HttpRouter; it still registers route values through a Layer and supports a prefix option. -- `HttpLayerRouter.addHttpApi` -> `HttpApiBuilder.layer`: HTTP API registration moved to effect/unstable/httpapi. +- `HttpLayerRouter.addHttpApi` -> `HttpApiBuilder.layer`: HTTP API registration moved to effect/http-api. - `HttpLayerRouter.cors` -> `HttpRouter.cors`: HttpLayerRouter was consolidated into v4 HttpRouter. @@ -7218,17 +7246,17 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/HttpServer` -- `HttpServer.Address` -> `effect/unstable/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union. +- `HttpServer.Address` -> `effect/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union. - `HttpServer.HttpServer` -> `HttpServer.HttpServer`: The interface and tag became one Context.Service class; use its Service member for implementations. - `HttpServer.ServeOptions` -> `none`: The unused respond option model was removed with no shared v4 counterpart. -- `HttpServer.TcpAddress` -> `effect/unstable/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port. +- `HttpServer.TcpAddress` -> `effect/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port. - `HttpServer.TypeId` -> `none`: The public TypeId was removed; HttpServer is now a Context.Service class. -- `HttpServer.UnixAddress` -> `effect/unstable/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model. +- `HttpServer.UnixAddress` -> `effect/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model. - `HttpServer.addressWith` -> `HttpServer.HttpServer.use(({ address }) => effect(address))`: The accessor was removed; read the service and pass its Address to the callback. @@ -7238,7 +7266,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpServer.make` -> `HttpServer.make`: Retained; it returns the Context.Service implementation. Supply a NetAddress.SocketAddress for address, using NetAddress.inetAddressUnsafe for a parsed IP and port or NetAddress.unixPathAddress for a Unix path. Use NetAddress.inetAddress to validate the port through Result instead. -- `HttpServer.serve` -> `effect/unstable/http/HttpServer#serve`: Moved to the v4 HTTP module; the application is now an Effect producing HttpServerResponse rather than the separate HttpApp model. +- `HttpServer.serve` -> `effect/http/HttpServer#serve`: Moved to the v4 HTTP module; the application is now an Effect producing HttpServerResponse rather than the separate HttpApp model. ### `@effect/platform/HttpServerError` @@ -7294,7 +7322,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `HttpServerResponse.text` -> `HttpServerResponse.text`: Moved unchanged. -- `HttpServerResponse.toWeb` -> `HttpServerResponse.toWeb`: Retained, but the optional Runtime became an optional Context for stream execution. +- `HttpServerResponse.toWeb` -> `HttpServerResponse.toWeb`: Retained, but the optional Runtime became an optional Context for stream execution. For a raw Web Response, outer headers override native headers and cookies append, including when the body is omitted. With withoutBody, the raw status and statusText are preserved unless the outer status is 204, 205, or 304, which takes precedence. - `HttpServerResponse.uint8Array` -> `HttpServerResponse.uint8Array`: Moved unchanged. @@ -7306,7 +7334,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/platform/KeyValueStore` -- `KeyValueStore.KeyValueStore` -> `KeyValueStore.KeyValueStore`: The service moved to effect/unstable/persistence/KeyValueStore; missing values now use undefined and operations fail with KeyValueStoreError. +- `KeyValueStore.KeyValueStore` -> `KeyValueStore.KeyValueStore`: The service moved to effect/persistence/KeyValueStore; missing values now use undefined and operations fail with KeyValueStoreError. - `KeyValueStore.KeyValueStore.AnyStore` -> `KeyValueStore.KeyValueStore | KeyValueStore.SchemaStore`: The convenience namespace alias was removed; write the store union explicitly when needed. @@ -7356,9 +7384,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Multipart.TypeId` -> `typeof Multipart.TypeId`: The runtime marker remains exported, but the separate type alias was removed. -- `Multipart.makeChannel` -> `effect/unstable/http/Multipart#makeChannel`: The channel constructor moved and no longer accepts bufferSize; input and output chunks use non-empty readonly arrays. +- `Multipart.makeChannel` -> `effect/http/Multipart#makeChannel`: The channel constructor moved and no longer accepts bufferSize; input and output chunks use non-empty readonly arrays. -- `Multipart.schemaJson` -> `Multipart.schemaJson`: The JSON-field decoder remains in effect/unstable/http/Multipart and uses v4 Schema constraints. +- `Multipart.schemaJson` -> `Multipart.schemaJson`: The JSON-field decoder remains in effect/http/Multipart and uses v4 Schema constraints. - `Multipart.withFieldMimeTypes` -> `Effect.provideService(Multipart.FieldMimeTypes, mimeTypes)`: Provide the v4 Context.Reference around the effect. @@ -7382,9 +7410,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Ndjson.NdjsonOptions` -> `{ readonly ignoreEmptyLines?: boolean }`: The standalone options interface was removed; decoding and duplex APIs accept this inline shape. -- `Ndjson.duplex` -> `Ndjson.duplex`: The API moved to effect/unstable/encoding/Ndjson. +- `Ndjson.duplex` -> `Ndjson.duplex`: The API moved to effect/encoding/Ndjson. -- `Ndjson.duplexSchema` -> `Ndjson.duplexSchema`: The API moved to effect/unstable/encoding/Ndjson and uses v4 Schema constraints. +- `Ndjson.duplexSchema` -> `Ndjson.duplexSchema`: The API moved to effect/encoding/Ndjson and uses v4 Schema constraints. - `Ndjson.pack` -> `Ndjson.encode`: The NDJSON channel constructor was renamed from pack to encode. @@ -7406,11 +7434,11 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `OpenApi.AdditionalPropertiesStrategy` -> `none`: OpenApi.fromApi no longer accepts generation options; standalone JSON Schema generation has a separate additionalProperties option. -- `OpenApi.Exclude` -> `effect/unstable/httpapi/OpenApi#Exclude`: Same annotation key and default; it is now a Context.Reference value. +- `OpenApi.Exclude` -> `effect/http-api/OpenApi#Exclude`: Same annotation key and default; it is now a Context.Reference value. - `OpenApi.OpenApiSpecContentType` -> `string`: The closed media-type union was removed so custom and streaming media types are supported. -- `OpenApi.fromApi` -> `effect/unstable/httpapi/OpenApi#fromApi`: The operation remains and returns OpenAPI 3.1, but the signature is now only fromApi(api). +- `OpenApi.fromApi` -> `effect/http-api/OpenApi#fromApi`: The operation remains and returns OpenAPI 3.1, but the signature is now only fromApi(api). ### `@effect/platform/OpenApiJsonSchema` @@ -7482,41 +7510,41 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Socket.SocketError` -> `Socket.SocketError`: The old union became a tagged wrapper around SocketReadError, SocketWriteError, SocketOpenError, or SocketCloseError. -- `Socket.SocketErrorTypeId` -> `Socket.SocketErrorTypeId`: The error marker remains in effect/unstable/socket/Socket. +- `Socket.SocketErrorTypeId` -> `Socket.SocketErrorTypeId`: The error marker remains in effect/socket/Socket. - `Socket.SocketGenericError` -> `Socket.SocketReadError | Socket.SocketWriteError | Socket.SocketOpenError`: The generic reason discriminator was replaced by dedicated read, write, and open error classes. - `Socket.TypeId` -> `typeof Socket.TypeId`: The socket marker remains exported, but the separate type alias was removed. -- `Socket.WebSocket` -> `Socket.WebSocket`: The opaque service moved to effect/unstable/socket/Socket and is now a Context.Service class for globalThis.WebSocket. +- `Socket.WebSocket` -> `Socket.WebSocket`: The opaque service moved to effect/socket/Socket and is now a Context.Service class for globalThis.WebSocket. -- `Socket.WebSocketConstructor` -> `Socket.WebSocketConstructor`: The service moved to effect/unstable/socket/Socket and is now a Context.Service class. +- `Socket.WebSocketConstructor` -> `Socket.WebSocketConstructor`: The service moved to effect/socket/Socket and is now a Context.Service class. - `Socket.currentSendQueueCapacity` -> `none`: The send queue was removed. The v4 Socket is pull-based: acquire socket.reader in a scope and pull frame batches; writes apply the transport's native backpressure. - `Socket.defaultCloseCodeIsError` -> `none`: Sockets no longer classify close codes; every close fails the reader's pull with a SocketError wrapping SocketCloseError. Consumers that treat a close as normal catch the error. -- `Socket.fromTransformStream` -> `Socket.fromTransformStream`: The constructor remains in effect/unstable/socket/Socket but drops closeCodeIsError; every close fails the reader's pull with a SocketError wrapping SocketCloseError. +- `Socket.fromTransformStream` -> `Socket.fromTransformStream`: The constructor remains in effect/socket/Socket but drops closeCodeIsError; every close fails the reader's pull with a SocketError wrapping SocketCloseError. -- `Socket.layerWebSocket` -> `Socket.layerWebSocket`: The constructor remains in effect/unstable/socket/Socket; its URL may now also be an Effect. +- `Socket.layerWebSocket` -> `Socket.layerWebSocket`: The constructor remains in effect/socket/Socket; its URL may now also be an Effect. - `Socket.toChannelMap` -> `none`: The v4 Socket read side is an Effect that never completes via Cause.Done; map frames by acquiring Socket.readerBytes or Socket.readerString, or Effect.map the reader from socket.reader, and use Socket.toChannel or Socket.toChannelString for duplex channels. ### `@effect/platform/SocketServer` -- `SocketServer.Address` -> `effect/unstable/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union. +- `SocketServer.Address` -> `effect/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union. -- `SocketServer.ErrorTypeId` -> `SocketServer.ErrorTypeId`: The API moved to effect/unstable/socket/SocketServer and retains this name. +- `SocketServer.ErrorTypeId` -> `SocketServer.ErrorTypeId`: The API moved to effect/socket/SocketServer and retains this name. -- `SocketServer.TcpAddress` -> `effect/unstable/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port. +- `SocketServer.TcpAddress` -> `effect/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port. -- `SocketServer.UnixAddress` -> `effect/unstable/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model. +- `SocketServer.UnixAddress` -> `effect/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model. ### `@effect/platform/Template` -- `Template.Interpolated.Context` -> `Template.Interpolated.Context`: The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values. +- `Template.Interpolated.Context` -> `Template.Interpolated.Context`: The API moved to effect/http/Template; v4 interpolation types also account for Effect values. -- `Template.Interpolated.Error` -> `Template.Interpolated.Error`: The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values. +- `Template.Interpolated.Error` -> `Template.Interpolated.Error`: The API moved to effect/http/Template; v4 interpolation types also account for Effect values. ### `@effect/platform/Terminal` @@ -7530,7 +7558,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Transferable.Uint8Array` -> `Transferable.Uint8Array`: The transferable Uint8Array schema remains in the moved module. -- `Transferable.schema` -> `Transferable.schema`: The schema wrapper moved to effect/unstable/workers/Transferable and uses the v4 Schema model. +- `Transferable.schema` -> `Transferable.schema`: The schema wrapper moved to effect/workers/Transferable and uses the v4 Schema model. - `Transferable.unsafeMakeCollector` -> `Transferable.makeCollectorUnsafe`: The unsafe collector constructor was renamed. @@ -7546,7 +7574,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `UrlParams.Input` -> `UrlParams.Input`: Retained and broadened to include UrlParams itself. -- `UrlParams.UrlParams` -> `UrlParams.UrlParams`: Import UrlParams from effect/unstable/http. It is now a branded iterable object with a params field rather than a ReadonlyArray; construct it with UrlParams.make or UrlParams.fromInput. +- `UrlParams.UrlParams` -> `UrlParams.UrlParams`: Import UrlParams from effect/http. It is now a branded iterable object with a params field rather than a ReadonlyArray; construct it with UrlParams.make or UrlParams.fromInput. - `UrlParams.makeUrl` -> `Url.make`: Moved to Url, returns Result, and takes string | undefined for the hash. @@ -7580,7 +7608,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Worker.SerializedWorkerPool.Options` -> `Parameters[0]`: Pool sizing options moved to the worker RPC protocol; initial messages are provided separately with RpcWorker.layerInitialMessage. -- `Worker.Worker` -> `Worker.Worker`: The name remains in effect/unstable/workers/Worker, but it is now the low-level send/run abstraction rather than execute/executeEffect. +- `Worker.Worker` -> `Worker.Worker`: The name remains in effect/workers/Worker, but it is now the low-level send/run abstraction rather than execute/executeEffect. - `Worker.Worker.Options` -> `Worker.Worker["run"] options`: Encoding moved to RPC schemas; the low-level run operation only accepts an optional onSpawn effect. @@ -7656,43 +7684,43 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/rpc/Rpc` -- `Rpc.AddError` -> `effect/unstable/rpc/Rpc#AddError`: Retained; the added error must now be a Schema.Top and the resulting RPC also preserves its explicit service requirements. +- `Rpc.AddError` -> `effect/rpc/Rpc#AddError`: Retained; the added error must now be a Schema.Top and the resulting RPC also preserves its explicit service requirements. -- `Rpc.AddMiddleware` -> `effect/unstable/rpc/Rpc#AddMiddleware`: Retained; middleware is now an RpcMiddleware.AnyService and its provides/requires metadata updates the RPC service requirements. +- `Rpc.AddMiddleware` -> `effect/rpc/Rpc#AddMiddleware`: Retained; middleware is now an RpcMiddleware.AnyService and its provides/requires metadata updates the RPC service requirements. -- `Rpc.Any` -> `effect/unstable/rpc/Rpc#Any`: Retained as the erased RPC shape; use AnyWithProps when schema and middleware fields are required. +- `Rpc.Any` -> `effect/rpc/Rpc#Any`: Retained as the erased RPC shape; use AnyWithProps when schema and middleware fields are required. - `Rpc.AnySchema` -> `Schema.Top`: The RPC-specific erased schema alias was removed; use the v4 top schema constraint. - `Rpc.AnyTaggedRequestSchema` -> `none`: RpcGroup no longer converts Schema.TaggedRequest classes into RPCs; declare the contract explicitly with Rpc.make. -- `Rpc.Context` -> `effect/unstable/rpc/Rpc#Services`: Schema Context became decoding and encoding services; use Services, or ServicesClient / ServicesServer at the corresponding boundary. +- `Rpc.Context` -> `effect/rpc/Rpc#Services`: Schema Context became decoding and encoding services; use Services, or ServicesClient / ServicesServer at the corresponding boundary. - `Rpc.ErrorEncoded` -> `Rpc.ErrorSchema["Encoded"]`: The alias was removed; index the v4 error schema's Encoded member directly. - `Rpc.ErrorExitEncoded` -> `Rpc.ErrorExitSchema["Encoded"]`: Use the new exit error schema, which includes stream and middleware errors, then select its Encoded member. -- `Rpc.ErrorSchema` -> `effect/unstable/rpc/Rpc#ErrorSchema`: Retained; middleware errors now come from each service's error metadata. +- `Rpc.ErrorSchema` -> `effect/rpc/Rpc#ErrorSchema`: Retained; middleware errors now come from each service's error metadata. -- `Rpc.Handler` -> `effect/unstable/rpc/Rpc#Handler`: Retained; handler metadata now supplies ServerClient, RequestId, headers, and the concrete RPC. +- `Rpc.Handler` -> `effect/rpc/Rpc#Handler`: Retained; handler metadata now supplies ServerClient, RequestId, headers, and the concrete RPC. -- `Rpc.Middleware` -> `effect/unstable/rpc/Rpc#Middleware`: Retained and extracts Context.Service identifiers from the attached middleware services. +- `Rpc.Middleware` -> `effect/rpc/Rpc#Middleware`: Retained and extracts Context.Service identifiers from the attached middleware services. -- `Rpc.MiddlewareClient` -> `effect/unstable/rpc/Rpc#MiddlewareClient`: Retained; required client middleware is derived from services configured with requiredForClient. +- `Rpc.MiddlewareClient` -> `effect/rpc/Rpc#MiddlewareClient`: Retained; required client middleware is derived from services configured with requiredForClient. -- `Rpc.Payload` -> `effect/unstable/rpc/Rpc#Payload`: Retained as the decoded payload type; use PayloadConstructor for the input accepted by generated clients. +- `Rpc.Payload` -> `effect/rpc/Rpc#Payload`: Retained as the decoded payload type; use PayloadConstructor for the input accepted by generated clients. -- `Rpc.Success` -> `effect/unstable/rpc/Rpc#Success`: Retained as the decoded success type. +- `Rpc.Success` -> `effect/rpc/Rpc#Success`: Retained as the decoded success type. - `Rpc.SuccessChunkEncoded` -> `Rpc.SuccessExitSchema["Encoded"]`: The alias was removed; for a streaming RPC the exit success schema is the stream element schema. -- `Rpc.SuccessEncoded` -> `effect/unstable/rpc/Rpc#SuccessEncoded`: Retained after the module move and now accounts for the RPC's explicit service-requirement parameter. +- `Rpc.SuccessEncoded` -> `effect/rpc/Rpc#SuccessEncoded`: Retained after the module move and now accounts for the RPC's explicit service-requirement parameter. - `Rpc.SuccessExitEncoded` -> `Rpc.SuccessExitSchema["Encoded"]`: Use the new exit success schema and select its Encoded member; streaming RPC exits use the element schema separately from the terminal void exit. -- `Rpc.SuccessSchema` -> `effect/unstable/rpc/Rpc#SuccessSchema`: Retained and uses the v4 Schema.Top constraint. +- `Rpc.SuccessSchema` -> `effect/rpc/Rpc#SuccessSchema`: Retained and uses the v4 Schema.Top constraint. -- `Rpc.Tag` -> `effect/unstable/rpc/Rpc#Tag`: Retained and also accounts for the v4 RPC service-requirement parameter. +- `Rpc.Tag` -> `effect/rpc/Rpc#Tag`: Retained and also accounts for the v4 RPC service-requirement parameter. - `Rpc.TypeId` -> `none`: The RPC marker is private in v4; use Rpc.isRpc for runtime checks and Rpc.Any for type constraints. @@ -7700,23 +7728,23 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Rpc.fromTaggedRequest` -> `Rpc.make`: Automatic TaggedRequest conversion was removed; pass the tag, payload, success, and error schemas explicitly to Rpc.make. -- `Rpc.make` -> `effect/unstable/rpc/Rpc#make`: Retained; schemas use v4 Schema.Top constraints and the defect option accepts Rpc.DefectSchema. +- `Rpc.make` -> `effect/rpc/Rpc#make`: Retained; schemas use v4 Schema.Top constraints and the defect option accepts Rpc.DefectSchema. -- `Rpc.wrap` -> `effect/unstable/rpc/Rpc#wrap`: Retained after the module move; it still applies fork and uninterruptible handler options, while the return type is now uniformly Rpc.Wrapper. +- `Rpc.wrap` -> `effect/rpc/Rpc#wrap`: Retained after the module move; it still applies fork and uninterruptible handler options, while the return type is now uniformly Rpc.Wrapper. ### `@effect/rpc/RpcClient` -- `RpcClient.Protocol` -> `effect/unstable/rpc/RpcClient#Protocol`: Retained as a Context.Service; custom transports now route multiple client ids through run and send. +- `RpcClient.Protocol` -> `effect/rpc/RpcClient#Protocol`: Retained as a Context.Service; custom transports now route multiple client ids through run and send. -- `RpcClient.RpcClient.From` -> `effect/unstable/rpc/RpcClient#RpcClient.From`: Generated clients now preserve full RPC tags as property names, remove the Prefix type parameter, and expose streaming results through the asQueue option instead of asMailbox. +- `RpcClient.RpcClient.From` -> `effect/rpc/RpcClient#RpcClient.From`: Generated clients now preserve full RPC tags as property names, remove the Prefix type parameter, and expose streaming results through the asQueue option instead of asMailbox. - `RpcClient.RpcClient.NonPrefixed` -> `none`: The prefix-partition helper was removed; v4 clients map every RPC tag directly to an object property. - `RpcClient.RpcClient.Prefixes` -> `none`: Nested prefix client objects were removed; v4 preserves the full RPC tag as the generated client property. -- `RpcClient.currentHeaders` -> `effect/unstable/rpc/RpcClient#CurrentHeaders`: Renamed and changed from FiberRef to Context.Reference; prefer RpcClient.withHeaders for scoped overrides. +- `RpcClient.currentHeaders` -> `effect/rpc/RpcClient#CurrentHeaders`: Renamed and changed from FiberRef to Context.Reference; prefer RpcClient.withHeaders for scoped overrides. -- `RpcClient.makeProtocolHttp` -> `effect/unstable/rpc/RpcClient#makeProtocolHttp`: Retained; it creates the Protocol service implementation from an HttpClient. +- `RpcClient.makeProtocolHttp` -> `effect/rpc/RpcClient#makeProtocolHttp`: Retained; it creates the Protocol service implementation from an HttpClient. - `RpcClient.withHeadersEffect` -> `Effect.flatMap(headers, (value) => RpcClient.withHeaders(effect, value))`: withHeaders now accepts Headers.Input synchronously; evaluate effectful headers first and then scope the client effect. @@ -7726,115 +7754,115 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/rpc/RpcGroup` -- `RpcGroup.Any` -> `effect/unstable/rpc/RpcGroup#Any`: Moved unchanged as the erased RpcGroup constraint. +- `RpcGroup.Any` -> `effect/rpc/RpcGroup#Any`: Moved unchanged as the erased RpcGroup constraint. -- `RpcGroup.HandlerContext` -> `effect/unstable/rpc/RpcGroup#HandlerServices`: Renamed for v4 service terminology and now includes explicit RPC requirements after removing middleware-provided services. +- `RpcGroup.HandlerContext` -> `effect/rpc/RpcGroup#HandlerServices`: Renamed for v4 service terminology and now includes explicit RPC requirements after removing middleware-provided services. -- `RpcGroup.HandlersContext` -> `effect/unstable/rpc/RpcGroup#HandlersServices`: Renamed; it unions HandlerServices across the handler object. +- `RpcGroup.HandlersContext` -> `effect/rpc/RpcGroup#HandlersServices`: Renamed; it unions HandlerServices across the handler object. - `RpcGroup.TypeId` -> `none`: The group marker is private in v4; use RpcGroup.Any for an erased group constraint. ### `@effect/rpc/RpcMessage` -- `RpcMessage.FromServerEncoded` -> `effect/unstable/rpc/RpcMessage#FromServerEncoded`: The union is retained and now also includes RequestEncoded for server-originated requests and notifications. Handle \_tag: "Request" when matching exhaustively; isNotification identifies notifications. +- `RpcMessage.FromServerEncoded` -> `effect/rpc/RpcMessage#FromServerEncoded`: The union is retained and now also includes RequestEncoded for server-originated requests and notifications. Handle \_tag: "Request" when matching exhaustively; isNotification identifies notifications. -- `RpcMessage.RequestId` -> `effect/unstable/rpc/RpcMessage#RequestId`: Request ids are now branded string or number values; convert bigint ids before calling the retained RequestId constructor. +- `RpcMessage.RequestId` -> `effect/rpc/RpcMessage#RequestId`: Request ids are now branded string or number values; convert bigint ids before calling the retained RequestId constructor. -- `RpcMessage.RequestIdTypeId` -> `effect/unstable/rpc/RpcMessage#RequestId`: The public symbol marker was removed; use the branded RequestId type and RequestId constructor rather than inspecting its brand. +- `RpcMessage.RequestIdTypeId` -> `effect/rpc/RpcMessage#RequestId`: The public symbol marker was removed; use the branded RequestId type and RequestId constructor rather than inspecting its brand. ### `@effect/rpc/RpcMiddleware` -- `RpcMiddleware.RpcMiddlewareWrap` -> `effect/unstable/rpc/RpcMiddleware#RpcMiddleware`: The wrap and non-wrap shapes were unified; implement a function receiving the handler effect and request options. +- `RpcMiddleware.RpcMiddlewareWrap` -> `effect/rpc/RpcMiddleware#RpcMiddleware`: The wrap and non-wrap shapes were unified; implement a function receiving the handler effect and request options. -- `RpcMiddleware.Tag` -> `effect/unstable/rpc/RpcMiddleware#Service`: Renamed and redesigned with explicit requires, provides, clientError, error, and requiredForClient configuration. +- `RpcMiddleware.Tag` -> `effect/rpc/RpcMiddleware#Service`: Renamed and redesigned with explicit requires, provides, clientError, error, and requiredForClient configuration. -- `RpcMiddleware.TagClass` -> `effect/unstable/rpc/RpcMiddleware#ServiceClass`: Renamed class type for the v4 Context.Service-based middleware declaration. +- `RpcMiddleware.TagClass` -> `effect/rpc/RpcMiddleware#ServiceClass`: Renamed class type for the v4 Context.Service-based middleware declaration. -- `RpcMiddleware.TagClass.Failure` -> `effect/unstable/rpc/RpcMiddleware#Error`: Failure terminology became error; apply the extractor to the middleware ID. +- `RpcMiddleware.TagClass.Failure` -> `effect/rpc/RpcMiddleware#Error`: Failure terminology became error; apply the extractor to the middleware ID. -- `RpcMiddleware.TagClass.FailureContext` -> `effect/unstable/rpc/RpcMiddleware#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. +- `RpcMiddleware.TagClass.FailureContext` -> `effect/rpc/RpcMiddleware#ErrorServicesEncode / ErrorServicesDecode`: The single schema context split into server encoding and client decoding services. -- `RpcMiddleware.TagClass.FailureSchema` -> `effect/unstable/rpc/RpcMiddleware#ErrorSchema`: Renamed and applied to the middleware ID rather than constructor options. +- `RpcMiddleware.TagClass.FailureSchema` -> `effect/rpc/RpcMiddleware#ErrorSchema`: Renamed and applied to the middleware ID rather than constructor options. -- `RpcMiddleware.TagClass.FailureService` -> `effect/unstable/rpc/RpcMiddleware#Error`: Use the decoded error extractor; optional middleware fallback was removed. +- `RpcMiddleware.TagClass.FailureService` -> `effect/rpc/RpcMiddleware#Error`: Use the decoded error extractor; optional middleware fallback was removed. - `RpcMiddleware.TagClass.Optional` -> `none`: Optional declaration and fallback-on-failure behavior were removed; model fallback inside the middleware effect. -- `RpcMiddleware.TagClass.Provides` -> `effect/unstable/rpc/RpcMiddleware#Provides`: Moved to the module level and applied to the middleware ID metadata. +- `RpcMiddleware.TagClass.Provides` -> `effect/rpc/RpcMiddleware#Provides`: Moved to the module level and applied to the middleware ID metadata. - `RpcMiddleware.TagClass.RequiredForClient` -> `RpcMiddleware.ServiceClass["requiredForClient"]`: The standalone options extractor was removed; the boolean is exposed directly by the resulting service class. -- `RpcMiddleware.TagClassAny` -> `effect/unstable/rpc/RpcMiddleware#AnyService`: Renamed widened middleware service-key shape. +- `RpcMiddleware.TagClassAny` -> `effect/rpc/RpcMiddleware#AnyService`: Renamed widened middleware service-key shape. -- `RpcMiddleware.TagClassAnyWithProps` -> `effect/unstable/rpc/RpcMiddleware#AnyServiceWithProps`: Renamed erased service key whose value has the unified server middleware function shape. +- `RpcMiddleware.TagClassAnyWithProps` -> `effect/rpc/RpcMiddleware#AnyServiceWithProps`: Renamed erased service key whose value has the unified server middleware function shape. -- `RpcMiddleware.TypeId` -> `effect/unstable/rpc/RpcMiddleware#TypeId`: Retained as the public middleware metadata marker and now has a string-literal type. +- `RpcMiddleware.TypeId` -> `effect/rpc/RpcMiddleware#TypeId`: Retained as the public middleware metadata marker and now has a string-literal type. -- `RpcMiddleware.layerClient` -> `effect/unstable/rpc/RpcMiddleware#layerClient`: Retained; the client function can now modify the typed Request passed to next and carry a client-only error type. +- `RpcMiddleware.layerClient` -> `effect/rpc/RpcMiddleware#layerClient`: Retained; the client function can now modify the typed Request passed to next and carry a client-only error type. ### `@effect/rpc/RpcSchema` -- `RpcSchema.Stream` -> `effect/unstable/rpc/RpcSchema#Stream`: Retained as both the stream schema interface and constructor; error is the second argument and schema services are split by direction. +- `RpcSchema.Stream` -> `effect/rpc/RpcSchema#Stream`: Retained as both the stream schema interface and constructor; error is the second argument and schema services are split by direction. - `RpcSchema.StreamSchemaId` -> `none`: The stream marker is private in v4; use RpcSchema.isStreamSchema and getStreamSchemas. -- `RpcSchema.getStreamSchemas` -> `effect/unstable/rpc/RpcSchema#getStreamSchemas`: Retained for internal-style schema inspection; pass the schema itself rather than its AST. +- `RpcSchema.getStreamSchemas` -> `effect/rpc/RpcSchema#getStreamSchemas`: Retained for internal-style schema inspection; pass the schema itself rather than its AST. -- `RpcSchema.isStreamSchema` -> `effect/unstable/rpc/RpcSchema#isStreamSchema`: Retained; it accepts a v4 Schema.Constraint. +- `RpcSchema.isStreamSchema` -> `effect/rpc/RpcSchema#isStreamSchema`: Retained; it accepts a v4 Schema.Constraint. - `RpcSchema.isStreamSerializable` -> `RpcSchema.isStreamSchema(schema)`: The separate WithResult serializability predicate was removed; v4 RPC streaming is identified by its explicit Stream schema. ### `@effect/rpc/RpcSerialization` -- `RpcSerialization.RpcSerializationError` -> `effect/unstable/rpc/RpcSerialization#MaxBufferSizeExceeded`: Buffer-limit failures now use MaxBufferSizeExceeded. MessagePack-specific decode errors have no counterpart. +- `RpcSerialization.RpcSerializationError` -> `effect/rpc/RpcSerialization#MaxBufferSizeExceeded`: Buffer-limit failures now use MaxBufferSizeExceeded. MessagePack-specific decode errors have no counterpart. -- `RpcSerialization.layerMsgPack` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Use SchemaBinary, or layerNdjson when you need newline-delimited JSON framing. +- `RpcSerialization.layerMsgPack` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Use SchemaBinary, or layerNdjson when you need newline-delimited JSON framing. -- `RpcSerialization.layerMsgPackWith` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Pass maxFrameSize to layerSchemaBinary; NDJSON buffer limits remain on layerNdjsonWith. +- `RpcSerialization.layerMsgPackWith` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Pass maxFrameSize to layerSchemaBinary; NDJSON buffer limits remain on layerNdjsonWith. -- `RpcSerialization.makeMsgPack` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Construct SchemaBinary serialization with layerSchemaBinary. +- `RpcSerialization.makeMsgPack` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: MessagePack RPC serialization was removed. Construct SchemaBinary serialization with layerSchemaBinary. -- `RpcSerialization.msgPack` -> `effect/unstable/rpc/RpcSerialization#layerSchemaBinary`: The MessagePack RpcSerialization service value was removed. Provide layerSchemaBinary instead. +- `RpcSerialization.msgPack` -> `effect/rpc/RpcSerialization#layerSchemaBinary`: The MessagePack RpcSerialization service value was removed. Provide layerSchemaBinary instead. ### `@effect/rpc/RpcServer` -- `RpcServer.Protocol` -> `effect/unstable/rpc/RpcServer#Protocol`: Retained as a Context.Service; custom transports now expose a disconnect queue, explicit capability flags, and codecFor for schema-aware payload and exit encoding. +- `RpcServer.Protocol` -> `effect/rpc/RpcServer#Protocol`: Retained as a Context.Service; custom transports now expose a disconnect queue, explicit capability flags, and codecFor for schema-aware payload and exit encoding. -- `RpcServer.fiberIdClientInterrupt` -> `effect/unstable/rpc/RpcSchema#ClientAbort`: The sentinel FiberId was replaced by a Cause annotation; inspect ClientAbort in the interruption cause when client cancellation must be distinguished. +- `RpcServer.fiberIdClientInterrupt` -> `effect/rpc/RpcSchema#ClientAbort`: The sentinel FiberId was replaced by a Cause annotation; inspect ClientAbort in the interruption cause when client cancellation must be distinguished. - `RpcServer.fiberIdTransientInterrupt` -> `none`: The internal transient sentinel was removed; protocol shutdown and disconnect now interrupt with the active parent fiber identity. -- `RpcServer.layer` -> `effect/unstable/rpc/RpcServer#layer`: Moved to core Effect; server requirements are now derived with Rpc.ServicesServer rather than the former combined Rpc.Context alias. +- `RpcServer.layer` -> `effect/rpc/RpcServer#layer`: Moved to core Effect; server requirements are now derived with Rpc.ServicesServer rather than the former combined Rpc.Context alias. -- `RpcServer.layerHttpRouter` -> `effect/unstable/rpc/RpcServer#layerHttp`: Renamed; it installs an HTTP or WebSocket RPC route into the v4 HttpRouter service. +- `RpcServer.layerHttpRouter` -> `effect/rpc/RpcServer#layerHttp`: Renamed; it installs an HTTP or WebSocket RPC route into the v4 HttpRouter service. -- `RpcServer.layerProtocolHttp` -> `effect/unstable/rpc/RpcServer#layerProtocolHttp`: Retained; v4 has one HttpRouter service and no router tag option. +- `RpcServer.layerProtocolHttp` -> `effect/rpc/RpcServer#layerProtocolHttp`: Retained; v4 has one HttpRouter service and no router tag option. -- `RpcServer.layerProtocolHttpRouter` -> `effect/unstable/rpc/RpcServer#layerProtocolHttp`: The separate layer-router variant was unified with layerProtocolHttp. +- `RpcServer.layerProtocolHttpRouter` -> `effect/rpc/RpcServer#layerProtocolHttp`: The separate layer-router variant was unified with layerProtocolHttp. -- `RpcServer.layerProtocolWebsocketRouter` -> `effect/unstable/rpc/RpcServer#layerProtocolWebsocket`: Renamed after the HTTP router services were unified. +- `RpcServer.layerProtocolWebsocketRouter` -> `effect/rpc/RpcServer#layerProtocolWebsocket`: Renamed after the HTTP router services were unified. -- `RpcServer.make` -> `effect/unstable/rpc/RpcServer#make`: Retained; schema encoding services are now explicit server requirements. +- `RpcServer.make` -> `effect/rpc/RpcServer#make`: Retained; schema encoding services are now explicit server requirements. -- `RpcServer.makeProtocolHttp` -> `effect/unstable/rpc/RpcServer#makeProtocolHttp`: Retained; it registers a POST route in the current v4 HttpRouter. +- `RpcServer.makeProtocolHttp` -> `effect/rpc/RpcServer#makeProtocolHttp`: Retained; it registers a POST route in the current v4 HttpRouter. -- `RpcServer.makeProtocolHttpRouter` -> `effect/unstable/rpc/RpcServer#makeProtocolHttp`: The separate router constructor was unified with makeProtocolHttp. +- `RpcServer.makeProtocolHttpRouter` -> `effect/rpc/RpcServer#makeProtocolHttp`: The separate router constructor was unified with makeProtocolHttp. -- `RpcServer.makeProtocolWebsocketRouter` -> `effect/unstable/rpc/RpcServer#makeProtocolWebsocket`: Renamed after the HTTP router services were unified. +- `RpcServer.makeProtocolWebsocketRouter` -> `effect/rpc/RpcServer#makeProtocolWebsocket`: Renamed after the HTTP router services were unified. -- `RpcServer.makeProtocolWithHttpApp` -> `effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffect`: HttpApp became HttpEffect; the result contains protocol and httpEffect. +- `RpcServer.makeProtocolWithHttpApp` -> `effect/rpc/RpcServer#makeProtocolWithHttpEffect`: HttpApp became HttpEffect; the result contains protocol and httpEffect. -- `RpcServer.makeProtocolWithHttpAppWebsocket` -> `effect/unstable/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket`: HttpApp became HttpEffect; the result contains the WebSocket protocol and upgrade effect. +- `RpcServer.makeProtocolWithHttpAppWebsocket` -> `effect/rpc/RpcServer#makeProtocolWithHttpEffectWebsocket`: HttpApp became HttpEffect; the result contains the WebSocket protocol and upgrade effect. -- `RpcServer.toHttpApp` -> `effect/unstable/rpc/RpcServer#toHttpEffect`: Renamed for the v4 HTTP effect model; it starts the RPC server and returns the request effect. +- `RpcServer.toHttpApp` -> `effect/rpc/RpcServer#toHttpEffect`: Renamed for the v4 HTTP effect model; it starts the RPC server and returns the request effect. -- `RpcServer.toHttpAppWebsocket` -> `effect/unstable/rpc/RpcServer#toHttpEffectWebsocket`: Renamed for the v4 HTTP effect model; it returns the WebSocket upgrade effect. +- `RpcServer.toHttpAppWebsocket` -> `effect/rpc/RpcServer#toHttpEffectWebsocket`: Renamed for the v4 HTTP effect model; it returns the WebSocket upgrade effect. - `RpcServer.toWebHandler` -> `HttpRouter.toWebHandler(RpcServer.layerHttp(options).pipe(Layer.provide(options.layer)))`: The RPC convenience wrapper was removed; build the RPC route layer and convert it with the generic v4 HttpRouter web-handler adapter. ### `@effect/rpc/RpcTest` -- `RpcTest.makeClient` -> `effect/unstable/rpc/RpcTest#makeClient`: Retained; it uses the v4 no-serialization client/server path and requires handlers plus any server and client middleware services. +- `RpcTest.makeClient` -> `effect/rpc/RpcTest#makeClient`: Retained; it uses the v4 no-serialization client/server path and requires handlers plus any server and client middleware services. ### `@effect/sql-clickhouse/ClickhouseClient` @@ -7848,7 +7876,7 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/sql-clickhouse/ClickhouseMigrator` -- `ClickhouseMigrator.MigrationError` -> `@effect/sql-clickhouse/ClickhouseMigrator#MigrationError`: Retained via effect/unstable/sql/Migrator; migrate reason and its lowercase values to kind with PascalCase values. +- `ClickhouseMigrator.MigrationError` -> `@effect/sql-clickhouse/ClickhouseMigrator#MigrationError`: Retained via effect/sql/Migrator; migrate reason and its lowercase values to kind with PascalCase values. ### `@effect/sql-d1/D1Client` @@ -7914,9 +7942,9 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/sql-pg/PgClient` -- `PgClient.PgClient` -> `@effect/sql-pg/PgClient#PgClient`: Retained; the service value is now a Context.Service. +- `PgClient.PgClient` -> `@effect/sql-pg/PgClient#PgClient`: Retained; the service value is now a Context.Service. The listen method returns a scoped Effect acquiring a Queue.Dequeue\; consume it with Queue operations or Stream.fromQueue and read each notification's payload. Connection failures after registration fail the queue with the original SqlError; scope closure interrupts consumers. -- `PgClient.PgClientConfig` -> `@effect/sql-pg/PgClient#PgClientConfig / PgPoolConfig`: Use PgClientConfig for base settings and PgPoolConfig for make/layer; pool sizing, idle timeout, and connection TTL moved to PgPoolConfig. +- `PgClient.PgClientConfig` -> `@effect/sql-pg/PgClient#PgClientConfig / PgPoolConfig`: Use PgClientConfig for base settings and PgPoolConfig for make/layer; pool sizing, idle timeout, and connection TTL moved to PgPoolConfig. The types option takes a PgTypes.Registry rather than node-pg custom types. Timestamp and timestamptz results (including array elements) are Date values with millisecond precision; infinity, -infinity, and out-of-range values become invalid Dates. Numeric readers can use getTime() or register numeric codecs. Date parameters bind as timestamptz; use PgTypes.timestamp(value) to preserve UTC fields when writing a timestamp column instead of applying the session TimeZone. - `PgClient.PgClientFromPoolOptions` -> `none`: The node-pg Pool wrapper options were removed with fromPool. Use PgClient.PgPoolConfig with PgClient.make or PgClient.layer. @@ -7954,93 +7982,93 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/sql/Model` -- `Model.Any` -> `effect/unstable/schema/Model#Any`: Moved; v4 schemas track DecodingServices and EncodingServices separately instead of one Context type. +- `Model.Any` -> `effect/schema/Model#Any`: Moved; v4 schemas track DecodingServices and EncodingServices separately instead of one Context type. -- `Model.AnyNoContext` -> `effect/unstable/schema/Model#Any`: The distinct no-context alias was removed; Model.Any propagates decoding and encoding services. Constrain both service types to never when required. +- `Model.AnyNoContext` -> `effect/schema/Model#Any`: The distinct no-context alias was removed; Model.Any propagates decoding and encoding services. Constrain both service types to never when required. - `Model.BooleanFromNumber` -> `effect/Schema#BooleanFromBit`: Use the core 0 | 1 to boolean schema; Model.BooleanSqlite is the ready-made model field. -- `Model.Class` -> `effect/unstable/schema/Model#Class`: Moved; model variants remain select, insert, update, json, jsonCreate, and jsonUpdate. +- `Model.Class` -> `effect/schema/Model#Class`: Moved; model variants remain select, insert, update, json, jsonCreate, and jsonUpdate. -- `Model.Date` -> `effect/unstable/schema/Model#Date`: Moved; still serializes DateTime.Utc as a YYYY-MM-DD string. +- `Model.Date` -> `effect/schema/Model#Date`: Moved; still serializes DateTime.Utc as a YYYY-MM-DD string. - `Model.DateTimeFromDate` -> `effect/Schema#DateTimeUtcFromDate`: Moved to core Schema and retains Date to DateTime.Utc conversion. -- `Model.Generated` -> `effect/unstable/schema/Model#GeneratedByDb`: Renamed and now read-only, with select and json variants only. Use Model.Field with select, update, and json to preserve writable v3 behavior. +- `Model.Generated` -> `effect/schema/Model#GeneratedByDb`: Renamed and now read-only, with select and json variants only. Use Model.Field with select, update, and json to preserve writable v3 behavior. -- `Model.extract` -> `effect/unstable/schema/Model#extract`: Retained after moving the model variant helpers into core Effect's unstable schema package. +- `Model.extract` -> `effect/schema/Model#extract`: Retained after moving the model variant helpers into core Effect's unstable schema package. - `Model.fieldFromKey` -> `effect/Schema#encodeKeys`: The field helper was removed; apply encodeKeys to each concrete struct or model-variant schema that crosses the naming boundary. -- `Model.fields` -> `effect/unstable/schema/Model#fields`: Moved with the variant-model helpers into core Effect's unstable schema package. +- `Model.fields` -> `effect/schema/Model#fields`: Moved with the variant-model helpers into core Effect's unstable schema package. -- `Model.makeDataLoaders` -> `effect/unstable/sql/SqlModel#makeResolvers`: Returns RequestResolvers instead of callable loaders; execute with SqlResolver.request and use RequestResolver delay/batch combinators for batching controls. The insert resolver requires model decoding services as well as insert-schema encoding services. +- `Model.makeDataLoaders` -> `effect/sql/SqlModel#makeResolvers`: Returns RequestResolvers instead of callable loaders; execute with SqlResolver.request and use RequestResolver delay/batch combinators for batching controls. The insert resolver requires model decoding services as well as insert-schema encoding services. ### `@effect/sql/SqlClient` -- `SqlClient.SafeIntegers` -> `effect/unstable/sql/SqlClient#SafeIntegers`: Moved and changed from a Reference subclass to a Context.Reference value; provide it as a service. +- `SqlClient.SafeIntegers` -> `effect/sql/SqlClient#SafeIntegers`: Moved and changed from a Reference subclass to a Context.Reference value; provide it as a service. -- `SqlClient.TransactionConnection` -> `effect/unstable/sql/SqlClient#TransactionConnection`: Now a factory keyed by client id, not a singleton tag. Prefer the client's transactionService; the payload type is TransactionConnection.Service. +- `SqlClient.TransactionConnection` -> `effect/sql/SqlClient#TransactionConnection`: Now a factory keyed by client id, not a singleton tag. Prefer the client's transactionService; the payload type is TransactionConnection.Service. - `SqlClient.TypeId` -> `none`: The brand is private in v4; do not inspect or attach it, and obtain clients through SqlClient or SqlClient.make. -- `SqlClient.make` -> `effect/unstable/sql/SqlClient#make`: Moved; custom clients rename MakeOptions.reactiveMailbox to reactiveQueue and may supply transactionService. +- `SqlClient.make` -> `effect/sql/SqlClient#make`: Moved; custom clients rename MakeOptions.reactiveMailbox to reactiveQueue and may supply transactionService. ### `@effect/sql/SqlConnection` -- `SqlConnection.Connection` -> `effect/unstable/sql/SqlConnection#Connection`: Moved; Connection.Acquirer is now top-level SqlConnection.Acquirer, and custom connections must implement executeValuesUnprepared. +- `SqlConnection.Connection` -> `effect/sql/SqlConnection#Connection`: Moved; Connection.Acquirer is now top-level SqlConnection.Acquirer, and custom connections must implement executeValuesUnprepared. ### `@effect/sql/SqlError` -- `SqlError.SqlErrorTypeId` -> `effect/unstable/sql/SqlError#isSqlError`: The type id is private; use isSqlError for runtime narrowing or isSqlErrorReason for structured reason values. +- `SqlError.SqlErrorTypeId` -> `effect/sql/SqlError#isSqlError`: The type id is private; use isSqlError for runtime narrowing or isSqlErrorReason for structured reason values. ### `@effect/sql/SqlEventJournal` -- `SqlEventJournal.layer` -> `effect/unstable/eventlog/SqlEventJournal#layer`: Moved; rename the eventLogTable layer option to entryTable. +- `SqlEventJournal.layer` -> `effect/eventlog/SqlEventJournal#layer`: Moved; rename the eventLogTable layer option to entryTable. -- `SqlEventJournal.make` -> `effect/unstable/eventlog/SqlEventJournal#make`: Moved with the same entryTable and remotesTable options. +- `SqlEventJournal.make` -> `effect/eventlog/SqlEventJournal#make`: Moved with the same entryTable and remotesTable options. ### `@effect/sql/SqlEventLogServer` -- `SqlEventLogServer.layerStorage` -> `effect/unstable/eventlog/SqlEventLogServerEncrypted#layerStorage`: Moved to the encrypted server module with the same options and EventLogEncryption requirement. +- `SqlEventLogServer.layerStorage` -> `effect/eventlog/SqlEventLogServerEncrypted#layerStorage`: Moved to the encrypted server module with the same options and EventLogEncryption requirement. -- `SqlEventLogServer.makeStorage` -> `effect/unstable/eventlog/SqlEventLogServerEncrypted#makeStorage`: Moved to the encrypted server module with the same SQL, encryption, and scope requirements. +- `SqlEventLogServer.makeStorage` -> `effect/eventlog/SqlEventLogServerEncrypted#makeStorage`: Moved to the encrypted server module with the same SQL, encryption, and scope requirements. ### `@effect/sql/SqlPersistedQueue` -- `SqlPersistedQueue.layerStore` -> `effect/unstable/persistence/PersistedQueue#layerStoreSql`: Moved into PersistedQueue and renamed with the Sql suffix; options are unchanged. +- `SqlPersistedQueue.layerStore` -> `effect/persistence/PersistedQueue#layerStoreSql`: Moved into PersistedQueue and renamed with the Sql suffix; options are unchanged. -- `SqlPersistedQueue.make` -> `effect/unstable/persistence/PersistedQueue#makeStoreSql`: Use the SQL store constructor; PersistedQueue.make creates a typed queue from a store factory and is not equivalent. +- `SqlPersistedQueue.make` -> `effect/persistence/PersistedQueue#makeStoreSql`: Use the SQL store constructor; PersistedQueue.make creates a typed queue from a store factory and is not equivalent. ### `@effect/sql/SqlResolver` -- `SqlResolver.SqlResolver` -> `RequestResolver.RequestResolver>`: The wrapper interface was removed; constructors return RequestResolvers. Execute them with effect/unstable/sql/SqlResolver#request. +- `SqlResolver.SqlResolver` -> `RequestResolver.RequestResolver>`: The wrapper interface was removed; constructors return RequestResolvers. Execute them with effect/sql/SqlResolver#request. -- `SqlResolver.void` -> `effect/unstable/sql/SqlResolver#void`: Moved, but remove the leading tag and withContext arguments; it now returns a RequestResolver synchronously and runs through SqlResolver.request. +- `SqlResolver.void` -> `effect/sql/SqlResolver#void`: Moved, but remove the leading tag and withContext arguments; it now returns a RequestResolver synchronously and runs through SqlResolver.request. ### `@effect/sql/SqlSchema` -- `SqlSchema.single` -> `effect/unstable/sql/SqlSchema#findOne`: Renamed with the same first-row-or-fail behavior; empty results use Cause.NoSuchElementError and schema failures use Schema.SchemaError. +- `SqlSchema.single` -> `effect/sql/SqlSchema#findOne`: Renamed with the same first-row-or-fail behavior; empty results use Cause.NoSuchElementError and schema failures use Schema.SchemaError. -- `SqlSchema.void` -> `effect/unstable/sql/SqlSchema#void`: Moved with the same encode, execute, and discard-result pattern; schema failures now use Schema.SchemaError. +- `SqlSchema.void` -> `effect/sql/SqlSchema#void`: Moved with the same encode, execute, and discard-result pattern; schema failures now use Schema.SchemaError. ### `@effect/sql/Statement` - `Statement.FragmentId` -> `none`: The v4 fragment brand is private; use Fragment, fragment, and isFragment instead of direct type-id access. -- `Statement.Statement` -> `effect/unstable/sql/Statement#Statement`: Moved; the nested Transformer type is now top-level and its callback receives Fiber.Fiber rather than FiberRefs.FiberRefs. +- `Statement.Statement` -> `effect/sql/Statement#Statement`: Moved; the nested Transformer type is now top-level and its callback receives Fiber.Fiber rather than FiberRefs.FiberRefs. -- `Statement.currentTransformer` -> `effect/unstable/sql/Statement#CurrentTransformer`: Capitalized and changed from FiberRef\\> to Context.Reference\. +- `Statement.currentTransformer` -> `effect/sql/Statement#CurrentTransformer`: Capitalized and changed from FiberRef\\> to Context.Reference\. -- `Statement.custom` -> `effect/unstable/sql/Statement#custom`: Retained, but returns a Custom segment and uses paramA/paramB/paramC; wrap it with Statement.fragment when a Fragment is required. +- `Statement.custom` -> `effect/sql/Statement#custom`: Retained, but returns a Custom segment and uses paramA/paramB/paramC; wrap it with Statement.fragment when a Fragment is required. -- `Statement.defaultEscape` -> `effect/unstable/sql/Statement#defaultEscape`: Moved with the same signature. +- `Statement.defaultEscape` -> `effect/sql/Statement#defaultEscape`: Moved with the same signature. -- `Statement.join` -> `effect/unstable/sql/Statement#join`: Moved with the same empty, single, and multiple-clause behavior. +- `Statement.join` -> `effect/sql/Statement#join`: Moved with the same empty, single, and multiple-clause behavior. -- `Statement.make` -> `effect/unstable/sql/Statement#make`: Moved with the same constructor inputs. +- `Statement.make` -> `effect/sql/Statement#make`: Moved with the same constructor inputs. -- `Statement.makeCompiler` -> `effect/unstable/sql/Statement#makeCompiler`: Moved to core Effect; the constructor options are exposed as Statement.CompilerOptions and retain the dialect-specific callbacks. +- `Statement.makeCompiler` -> `effect/sql/Statement#makeCompiler`: Moved to core Effect; the constructor options are exposed as Statement.CompilerOptions and retain the dialect-specific callbacks. - `Statement.setTransformer` -> `Layer.succeed(Statement.CurrentTransformer, transformer)`: The helper was removed; provide the CurrentTransformer reference as a layer. @@ -8622,75 +8650,73 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) ### `@effect/workflow/Activity` -- `Activity.Any` -> `effect/unstable/workflow/Activity#Any`: Moved into core Effect. V4 Any is minimal; use AnyWithProps when schemas or execution properties are required. +- `Activity.Any` -> `effect/workflow/Activity#Any`: Moved into core Effect. V4 Any is minimal; use AnyWithProps when schemas or execution properties are required. -- `Activity.CurrentAttempt` -> `effect/unstable/workflow/Activity#CurrentAttempt`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same default of 1. +- `Activity.CurrentAttempt` -> `effect/workflow/Activity#CurrentAttempt`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same default of 1. - `Activity.TypeId` -> `none`: The activity marker is private in v4. Use Activity, Activity.Any, or Activity.AnyWithProps constraints. -- `Activity.make` -> `effect/unstable/workflow/Activity#make`: Moved into core Effect with the same constructor shape, v4 Schema.Constraint service directions, and optional annotations. +- `Activity.make` -> `effect/workflow/Activity#make`: Moved into core Effect with the same constructor shape, v4 Schema.Constraint service directions, and optional annotations. -- `Activity.raceAll` -> `effect/unstable/workflow/Activity#raceAll`: Moved into core Effect with the same named durable race behavior. +- `Activity.raceAll` -> `effect/workflow/Activity#raceAll`: Moved into core Effect with the same named durable race behavior. ### `@effect/workflow/DurableClock` - `DurableClock.TypeId` -> `none`: The durable-clock marker is private in v4. Use DurableClock values structurally. -- `DurableClock.make` -> `effect/unstable/workflow/DurableClock#make`: Moved into core Effect; Duration.DurationInput is now Duration.Input. +- `DurableClock.make` -> `effect/workflow/DurableClock#make`: Moved into core Effect; Duration.DurationInput is now Duration.Input. ### `@effect/workflow/DurableDeferred` -- `DurableDeferred.Any` -> `effect/unstable/workflow/DurableDeferred#Any`: Moved into core Effect. V4 Any is minimal; use AnyWithProps when success, error, or exit schemas are required. +- `DurableDeferred.Any` -> `effect/workflow/DurableDeferred#Any`: Moved into core Effect. V4 Any is minimal; use AnyWithProps when success, error, or exit schemas are required. - `DurableDeferred.TypeId` -> `none`: The durable-deferred marker is private in v4. Use DurableDeferred, Any, or AnyWithProps constraints. -- `DurableDeferred.await` -> `effect/unstable/workflow/DurableDeferred#await`: Moved into core Effect with the same persisted-result and workflow-suspension behavior. +- `DurableDeferred.await` -> `effect/workflow/DurableDeferred#await`: Moved into core Effect with the same persisted-result and workflow-suspension behavior. -- `DurableDeferred.done` -> `effect/unstable/workflow/DurableDeferred#done`: Moved into core Effect; schema requirements now use explicit directional encoding services. +- `DurableDeferred.done` -> `effect/workflow/DurableDeferred#done`: Moved into core Effect; schema requirements now use explicit directional encoding services. -- `DurableDeferred.fail` -> `effect/unstable/workflow/DurableDeferred#fail`: Moved into core Effect and now requires the error schema encoding services. +- `DurableDeferred.fail` -> `effect/workflow/DurableDeferred#fail`: Moved into core Effect and now requires the error schema encoding services. -- `DurableDeferred.failCause` -> `effect/unstable/workflow/DurableDeferred#failCause`: Moved into core Effect and now requires the error schema encoding services. +- `DurableDeferred.failCause` -> `effect/workflow/DurableDeferred#failCause`: Moved into core Effect and now requires the error schema encoding services. -- `DurableDeferred.into` -> `effect/unstable/workflow/DurableDeferred#into`: Moved into core Effect with the same exit recording and suspension propagation behavior. Provide both decoding and encoding services for the success and error schemas; recording the exit requires encoding services. +- `DurableDeferred.into` -> `effect/workflow/DurableDeferred#into`: Moved into core Effect with the same exit recording and suspension propagation behavior. Provide both decoding and encoding services for the success and error schemas; recording the exit requires encoding services. -- `DurableDeferred.make` -> `effect/unstable/workflow/DurableDeferred#make`: Moved into core Effect with the same name and optional schemas, expressed through v4 Schema.Constraint. +- `DurableDeferred.make` -> `effect/workflow/DurableDeferred#make`: Moved into core Effect with the same name and optional schemas, expressed through v4 Schema.Constraint. -- `DurableDeferred.raceAll` -> `effect/unstable/workflow/DurableDeferred#raceAll`: Moved into core Effect with the same persisted-winner behavior. +- `DurableDeferred.raceAll` -> `effect/workflow/DurableDeferred#raceAll`: Moved into core Effect with the same persisted-winner behavior. -- `DurableDeferred.succeed` -> `effect/unstable/workflow/DurableDeferred#succeed`: Moved into core Effect and now requires the success schema encoding services. +- `DurableDeferred.succeed` -> `effect/workflow/DurableDeferred#succeed`: Moved into core Effect and now requires the success schema encoding services. ### `@effect/workflow/DurableQueue` -- `DurableQueue.TypeId` -> `effect/unstable/workflow/DurableQueue#TypeId`: Moved into core Effect; the marker literal changed to \~effect/workflow/DurableQueue. Use typeof DurableQueue.TypeId in type position. +- `DurableQueue.TypeId` -> `effect/workflow/DurableQueue#TypeId`: Moved into core Effect; the marker literal changed to \~effect/workflow/DurableQueue. Use typeof DurableQueue.TypeId in type position. -- `DurableQueue.make` -> `effect/unstable/workflow/DurableQueue#make`: Moved into core Effect; queue persistence now comes from effect/unstable/persistence. +- `DurableQueue.make` -> `effect/workflow/DurableQueue#make`: Moved into core Effect; queue persistence now comes from effect/persistence. ### `@effect/workflow/Workflow` -- `Workflow.Any` -> `effect/unstable/workflow/Workflow#Any`: Moved into core Effect. Workflow identity changed from name to \_tag and definitions are now class-compatible constructors. +- `Workflow.Any` -> `effect/workflow/Workflow#Any`: Moved into core Effect. Workflow identity changed from name to \_tag and definitions are now class-compatible constructors. - `Workflow.AnyTaggedRequestSchema` -> `none`: The TaggedRequest adapter constraint was removed. Define the workflow explicitly with Workflow.make and the request payload, success, error, and PrimaryKey schemas. -- `Workflow.CaptureDefects` -> `effect/unstable/workflow/Workflow#CaptureDefects`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same true default. - -- `Workflow.Complete`: TODO: needs guidance +- `Workflow.CaptureDefects` -> `effect/workflow/Workflow#CaptureDefects`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same true default. -- `Workflow.Execution` -> `effect/unstable/workflow/Workflow#Execution`: Moved into core Effect; its workflow discriminator changed from name to \_tag. +- `Workflow.Execution` -> `effect/workflow/Workflow#Execution`: Moved into core Effect; its workflow discriminator changed from name to \_tag. - `Workflow.Requirements` -> `Workflow.RequirementsClient / Workflow.RequirementsHandler`: The schema Context union split by direction: client payload encoding and result decoding versus handler payload decoding and result encoding. -- `Workflow.Result` -> `effect/unstable/workflow/Workflow#Result`: Moved into core Effect and remains the Complete or Suspended result type and schema constructor. +- `Workflow.Result` -> `effect/workflow/Workflow#Result`: Moved into core Effect and remains the Complete or Suspended result type and schema constructor. -- `Workflow.ResultEncoded` -> `effect/unstable/workflow/Workflow#ResultEncoded`: Moved into core Effect and remains both the encoded result type and generic encoded-result codec. +- `Workflow.ResultEncoded` -> `effect/workflow/Workflow#ResultEncoded`: Moved into core Effect and remains both the encoded result type and generic encoded-result codec. - `Workflow.ResultTypeId` -> `none`: The result marker is private in v4. Use Workflow.isResult for narrowing. -- `Workflow.SuspendOnFailure` -> `effect/unstable/workflow/Workflow#SuspendOnFailure`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default. +- `Workflow.SuspendOnFailure` -> `effect/workflow/Workflow#SuspendOnFailure`: Moved into core Effect and changed from a Context.Tag subclass to a Context.Reference value with the same false default. - `Workflow.TypeId` -> `none`: The workflow marker is private in v4. Use Workflow.Any or Workflow.Workflow constraints. -- `Workflow.Workflow` -> `effect/unstable/workflow/Workflow#Workflow`: Name and name became Tag and \_tag, schemas use directional services, definitions are constructable, and poll returns Option. +- `Workflow.Workflow` -> `effect/workflow/Workflow#Workflow`: Name and name became Tag and \_tag, schemas use directional services, definitions are constructable, and poll returns Option. - `Workflow.Workflow.Error` -> `W["errorSchema"]["Type"]`: The namespace alias was removed. Extract the decoded error type from the public errorSchema property. @@ -8700,23 +8726,23 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Workflow.fromTaggedRequest` -> `none`: Removed. Expand to Workflow.make(schema.\_tag, { payload: schema, success: schema.success, error: schema.failure, idempotencyKey: PrimaryKey.value }). -- `Workflow.make` -> `effect/unstable/workflow/Workflow#make`: The signature changed from make({ name, ... }) to make(tag, { ... }); definitions expose \_tag and are class-compatible constructors. +- `Workflow.make` -> `effect/workflow/Workflow#make`: The signature changed from make({ name, ... }) to make(tag, { ... }); definitions expose \_tag and are class-compatible constructors. ### `@effect/workflow/WorkflowEngine` -- `WorkflowEngine.makeUnsafe` -> `effect/unstable/workflow/WorkflowEngine#makeUnsafe`: Moved into core Effect. Context service projections now use Service instead of Type, and absent encoded results use Option. +- `WorkflowEngine.makeUnsafe` -> `effect/workflow/WorkflowEngine#makeUnsafe`: Moved into core Effect. Context service projections now use Service instead of Type, and absent encoded results use Option. ### `@effect/workflow/WorkflowProxy` -- `WorkflowProxy.ConvertHttpApi` -> `effect/unstable/workflow/WorkflowProxy#ConvertHttpApi`: Moved into core Effect and updated to v4 HttpApiEndpoint types and the consolidated HttpApi architecture. +- `WorkflowProxy.ConvertHttpApi` -> `effect/workflow/WorkflowProxy#ConvertHttpApi`: Moved into core Effect and updated to v4 HttpApiEndpoint types and the consolidated HttpApi architecture. -- `WorkflowProxy.ConvertRpcs` -> `effect/unstable/workflow/WorkflowProxy#ConvertRpcs`: Moved into core Effect; generated execute, discard, and resume RPCs are now keyed from workflow \_tag. +- `WorkflowProxy.ConvertRpcs` -> `effect/workflow/WorkflowProxy#ConvertRpcs`: Moved into core Effect; generated execute, discard, and resume RPCs are now keyed from workflow \_tag. ### `@effect/workflow/WorkflowProxyServer` -- `WorkflowProxyServer.layerHttpApi` -> `effect/unstable/workflow/WorkflowProxyServer#layerHttpApi`: Moved into core Effect. Use v4 HttpApi group identifiers and Workflow.RequirementsHandler schema services. +- `WorkflowProxyServer.layerHttpApi` -> `effect/workflow/WorkflowProxyServer#layerHttpApi`: Moved into core Effect. Use v4 HttpApi group identifiers and Workflow.RequirementsHandler schema services. -- `WorkflowProxyServer.layerRpcHandlers` -> `effect/unstable/workflow/WorkflowProxyServer#layerRpcHandlers`: Moved into core Effect; generated handlers require Workflow.RequirementsHandler rather than the undirected Requirements union. +- `WorkflowProxyServer.layerRpcHandlers` -> `effect/workflow/WorkflowProxyServer#layerRpcHandlers`: Moved into core Effect; generated handlers require Workflow.RequirementsHandler rather than the undirected Requirements union. ### `effect/Arbitrary` @@ -8724,11 +8750,11 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc) - `Arbitrary.ArbitraryGenerationContext` -> `Schema.Annotations.ToArbitrary.DeclarationInput`: Native arbitrary callbacks receive DeclarationInput with decoded type-parameter schemas and normalized constraints. -- `Arbitrary.LazyArbitrary` -> `effect/unstable/arbitrary/Arbitrary#Arbitrary`: The generated-value description is now the native Arbitrary interface from effect/unstable/arbitrary. +- `Arbitrary.LazyArbitrary` -> `effect/Arbitrary#Arbitrary`: The generated-value description is now the native Arbitrary interface from effect/Arbitrary. #### `Arbitrary.make` -**Replacement:** `effect/unstable/arbitrary/Arbitrary#schema` +**Replacement:** `effect/Arbitrary#schema` Derive a native Arbitrary from a Schema. Effect no longer bridges to fast-check. @@ -8740,7 +8766,7 @@ Arbitrary.schema(schema) #### `Arbitrary.makeLazy` -**Replacement:** `effect/unstable/arbitrary/Arbitrary#schema` +**Replacement:** `effect/Arbitrary#schema` Lazy and eager Schema derivation are the same native Arbitrary.schema constructor. @@ -8788,12 +8814,16 @@ Arbitrary.schema(schema) - `Array.modifyOption` -> `Array.modify`: The Option suffix was dropped; an out-of-bounds index still returns Option.none. -- `Array.partitionMap` -> `Array.partition`: Pass a Result-returning mapper instead of Either; the output remains [failures, successes], corresponding to v3 [lefts, rights]. +- `Array.partition` -> `Array.partition`: Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]. + +- `Array.partitionMap` -> `Array.partition`: Pass a Result-returning mapper instead of Either. The tuple is [passes, fails], the reverse of v3 [lefts, rights]. - `Array.removeOption` -> `Array.remove`: The closest API now returns an unchanged copy out of bounds; use Array.get before Array.remove to preserve the old Option result. - `Array.replaceOption` -> `Array.replace`: The Option suffix was dropped; an out-of-bounds index still returns Option.none. +- `Array.separate` -> `Array.separate`: The tuple is now [successes, failures], the reverse of v3 [lefts, rights]. + - `Array.setNonEmptyHead` -> `Array.setHeadNonEmpty`: Direct word-order rename with the same non-empty-preserving result. - `Array.setNonEmptyLast` -> `Array.setLastNonEmpty`: Direct word-order rename with the same non-empty-preserving result. @@ -9196,12 +9226,16 @@ Arbitrary.schema(schema) - `Chunk.modifyOption` -> `Chunk.modify`: The Option suffix was dropped; an out-of-bounds index still returns Option.none. -- `Chunk.partitionMap` -> `Chunk.partition`: Pass a Result-returning mapper instead of Either; the output remains [failures, successes]. +- `Chunk.partition` -> `Chunk.partition`: Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]. + +- `Chunk.partitionMap` -> `Chunk.partition`: Pass a Result-returning mapper instead of Either. The tuple is [passes, fails], the reverse of v3 [failures, successes]. - `Chunk.removeOption` -> `Chunk.remove`: The closest API now returns the unchanged Chunk out of bounds; use Chunk.get before Chunk.remove to preserve the old Option result. - `Chunk.replaceOption` -> `Chunk.replace`: The Option suffix was dropped; an out-of-bounds index still returns Option.none. +- `Chunk.separate` -> `Chunk.separate`: The tuple is now [successes, failures], the reverse of v3 [failures, successes]. + - `Chunk.unsafeFromArray` -> `Chunk.fromArrayUnsafe`: Direct word-order rename; it still wraps without copying and is unsafe if the source array is mutated. - `Chunk.unsafeFromNonEmptyArray` -> `Chunk.fromNonEmptyArrayUnsafe`: Direct word-order rename; it still wraps without copying and preserves NonEmptyChunk. @@ -9240,7 +9274,7 @@ Arbitrary.schema(schema) - `Config.LiteralValue` -> `SchemaAST.LiteralValue`: Use the literal value type shared by v4 Schema constructors. -- `Config.all` -> `Config.all`: Combine an iterable or record of Config values. A wholly absent product can use Config.withDefault or Config.option, while a partially supplied product fails. +- `Config.all` -> `Config.all`: Combine an iterable or record of Config values. If any child is absent and none fail, Config.withDefault replaces the whole group and Config.option returns None. Apply defaults to individual children to preserve other supplied values. - `Config.array` -> `Config.Array(valueSchema, path)`: Array parsing is schema-based in v4; rebuild the element Config as a Schema and pass it with the optional path to Config.Array. @@ -9274,6 +9308,10 @@ Arbitrary.schema(schema) - `Config.number` -> `Config.Number`: Direct constructor rename; use Config.Finite when NaN and infinities must be rejected. +- `Config.option` -> `Config.option`: Returns None for an absent config, including an all group with a missing child and no failed children. Successfully decoded undefined is wrapped in Some; validation and source errors still propagate. + +- `Config.orElse` -> `Config.orElse`: The fallback receives Config.ConfigError and its result replaces the original failure. Unlike v3, failed attempts are not combined: an absent fallback can be handled by a later withDefault or option even if the original input was invalid. + - `Config.orElseIf` -> `Config.orElse`: The fallback now receives Config.ConfigError; test it in the callback and re-fail with Config.fail(error.cause) when the predicate is false. - `Config.port` -> `Config.Port`: Direct constructor rename. @@ -9298,6 +9336,8 @@ Arbitrary.schema(schema) - `Config.validate` -> `Config.schema(schema.check(check), path)`: Validation moved to Schema checks; attach the predicate and message to the Schema used by Config.schema. +- `Config.withDefault` -> `Config.withDefault`: Defaults absent configs, including all groups with missing children and no failed children. A group default replaces the whole group; use defaults on children to preserve other supplied values. Validation and source errors still propagate. + - `Config.withDescription` -> `Config.schema(schema.annotate({ description }), path)`: Config descriptions moved to Schema annotations in v4. - `Config.zip` -> `Config.all([self, that])`: Use the tuple overload of Config.all. @@ -9324,7 +9364,7 @@ Arbitrary.schema(schema) - `ConfigError.Options` -> `none`: The shared constructor options type was removed; ConfigProvider.SourceError accepts message and optional cause, while Schema issues have issue-specific constructors. -- `ConfigError.Or` -> `SchemaIssue.AnyOf`: The ConfigError boolean ADT was removed; alternative schema failures are represented inside Config.ConfigError.cause as SchemaIssue.AnyOf. +- `ConfigError.Or` -> `SchemaIssue.AnyOf`: The ConfigError boolean ADT was removed. Schema union failures can use SchemaIssue.AnyOf inside Config.ConfigError.cause; Config.orElse adopts the fallback's result instead of combining failed attempts. - `ConfigError.SourceUnavailable` -> `new ConfigProvider.SourceError({ message, cause })`: Source failures moved to effect/ConfigProvider and are wrapped by Config.ConfigError when a Config is parsed. @@ -9336,11 +9376,11 @@ Arbitrary.schema(schema) - `ConfigError.isInvalidData` -> `Schema.isSchemaError(error.cause)`: Parsing and validation failures are SchemaError causes; inspect the contained SchemaIssue for finer classification. -- `ConfigError.isMissingData` -> `none`: Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they distinguish absent provider input from successful undefined, invalid input, and partial products. +- `ConfigError.isMissingData` -> `none`: Do not infer semantic absence from a SchemaIssue. Use Config.withDefault or Config.option; they recover missing data, including Config.all groups with missing children, while preserving successful undefined and propagating validation and source errors. -- `ConfigError.isMissingDataOnly` -> `Config.withDefault / Config.option`: The public classifier was removed. These combinators use provider lookup evidence rather than recursively classifying SchemaIssue values. +- `ConfigError.isMissingDataOnly` -> `Config.withDefault / Config.option`: The public classifier was removed. These combinators handle absence without recursively classifying SchemaIssue values; invalid input and source errors still propagate. -- `ConfigError.isOr` -> `error.cause.issue._tag === "AnyOf"`: After narrowing cause with Schema.isSchemaError, inspect the SchemaIssue tag; the old Or node no longer exists. +- `ConfigError.isOr` -> `error.cause.issue._tag === "AnyOf"`: After narrowing cause with Schema.isSchemaError, AnyOf identifies schema union failures. Config.orElse does not combine failed attempts; it propagates only the fallback's error. - `ConfigError.isSourceUnavailable` -> `error.cause instanceof ConfigProvider.SourceError`: Provider source failures now use the ConfigProvider.SourceError class. @@ -9984,6 +10024,8 @@ Arbitrary.schema(schema) - `Effect.parallelFinalizers` -> `none`: Parallel finalizer strategy mutation was removed; fork independent cleanup explicitly when ordering is irrelevant. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive. +- `Effect.partition` -> `Effect.partition`: The tuple is now [passes, fails], the reverse of v3 [failures, successes]. Every effect still runs, and the outer effect does not fail. + - `Effect.patchFiberRefs` -> `none`: Bulk FiberRefs patching was removed; update individual context references with `Effect.updateService`. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive. - `Effect.patchRuntimeFlags` -> `none`: RuntimeFlags patching was removed from the public API; use supported high-level runtime options. No direct public replacement exists in v4; rewrite the call site around the stated v4 primitive. @@ -10242,21 +10284,39 @@ Arbitrary.schema(schema) ### `effect/Encoding` -- `Encoding.DecodeException` -> `Encoding.EncodingError`: Use the unified error class with kind Decode. +- `Encoding.DecodeException` -> `effect/encoding/EncodingError#EncodingError`: Use the unified error class with kind Decode. + +- `Encoding.DecodeExceptionTypeId` -> `effect/encoding/EncodingError#EncodingErrorTypeId`: Decode and encode failures now share one marker. + +- `Encoding.EncodeException` -> `effect/encoding/EncodingError#EncodingError`: Use the unified error class with kind Encode. + +- `Encoding.EncodeExceptionTypeId` -> `effect/encoding/EncodingError#EncodingErrorTypeId`: Decode and encode failures now share one marker. + +- `Encoding.decodeBase64` -> `effect/encoding/Base64#decode`: Import the Base64 helpers from their direct subpath. + +- `Encoding.decodeBase64String` -> `effect/encoding/Base64#decodeString`: Import the Base64 helpers from their direct subpath. + +- `Encoding.decodeBase64Url` -> `effect/encoding/Base64Url#decode`: Import the Base64Url helpers from their direct subpath. -- `Encoding.DecodeExceptionTypeId` -> `Encoding.EncodingErrorTypeId`: Decode and encode failures now share one marker. +- `Encoding.decodeBase64UrlString` -> `effect/encoding/Base64Url#decodeString`: Import the Base64Url helpers from their direct subpath. -- `Encoding.EncodeException` -> `Encoding.EncodingError`: Use the unified error class with kind Encode. +- `Encoding.decodeHex` -> `effect/encoding/Hex#decode`: Import the Hex helpers from their direct subpath. -- `Encoding.EncodeExceptionTypeId` -> `Encoding.EncodingErrorTypeId`: Decode and encode failures now share one marker. +- `Encoding.decodeHexString` -> `effect/encoding/Hex#decodeString`: Import the Hex helpers from their direct subpath. - `Encoding.decodeUriComponent` -> `Result.try`: Wrap decodeURIComponent in Result.try and map failure to EncodingError, or decode Schema.StringFromUriComponent. +- `Encoding.encodeBase64` -> `effect/encoding/Base64#encode`: Import the Base64 helpers from their direct subpath. + +- `Encoding.encodeBase64Url` -> `effect/encoding/Base64Url#encode`: Import the Base64Url helpers from their direct subpath. + +- `Encoding.encodeHex` -> `effect/encoding/Hex#encode`: Import the Hex helpers from their direct subpath. + - `Encoding.encodeUriComponent` -> `Result.try`: Wrap encodeURIComponent in Result.try and map failure to EncodingError, or encode Schema.StringFromUriComponent. -- `Encoding.isDecodeException` -> `Encoding.isEncodingError`: Use the unified guard and test kind === Decode when decode-only narrowing is required. +- `Encoding.isDecodeException` -> `effect/encoding/EncodingError#isEncodingError`: Use the unified guard and test kind === Decode when decode-only narrowing is required. -- `Encoding.isEncodeException` -> `Encoding.isEncodingError`: Use the unified guard and test kind === Encode when encode-only narrowing is required. +- `Encoding.isEncodeException` -> `effect/encoding/EncodingError#isEncodingError`: Use the unified guard and test kind === Encode when encode-only narrowing is required. ### `effect/Equal` @@ -11689,9 +11749,9 @@ JsonSchema.toDocumentDraft07(Schema.toJsonSchemaDocument(schema)) - `List.of` -> `Array.of`: List was removed; use Array.of. It preserves ordering but returns arrays rather than persistent linked lists. -- `List.partition` -> `Array.partition`: Use a Result-returning callback: failure values form the first array and success values the second. +- `List.partition` -> `Array.partition`: Use a Result-returning callback: success values form the first array and failure values the second. -- `List.partitionMap` -> `Array.partition`: Migrate the Either-returning mapper to Result; failures form the first array and successes the second. +- `List.partitionMap` -> `Array.partition`: Migrate the Either-returning mapper to Result; successes form the first array and failures the second. - `List.prependAll` -> `Array.prependAll`: List was removed; use Array.prependAll. It preserves ordering but returns arrays rather than persistent linked lists. @@ -12755,6 +12815,8 @@ Effect.forEach(items, handle, { concurrency: 10 }) - `Option.orElseEither` -> `Option.orElseResult`: Either was replaced by Result; source tracking now uses Failure and Success. +- `Option.partitionMap` -> `Option.partitionMap`: The tuple is now [passes, fails], the reverse of v3 [left, right]. + ### `effect/Order` - `Order.Order` -> `Order.Order`: The callable type is retained; its return type remains the -1 | 0 | 1 Ordering union. @@ -12823,7 +12885,7 @@ SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues - `ParseResult.Composite` -> `SchemaIssue.Composite`: Composite parse failures moved to SchemaIssue. The v4 constructor takes the failing AST and an array of nested issues; input is retained only when reportInput is enabled. -- `ParseResult.DeclarationDecodeUnknown` -> `SchemaGetter.Getter`: Custom declaration decoding now uses SchemaGetter values and Schema.declare annotations. +- `ParseResult.DeclarationDecodeUnknown` -> `SchemaGetter.Getter`: Custom declaration decoding now uses SchemaGetter values and Schema.declare annotations. Getter is a tagged union, not a constructor; use transformOptionalEffect for an Option-to-Effect decoder. Use standalone SchemaGetter.run, map, and compose instead of instance methods. - `ParseResult.DecodeUnknown` -> `Schema.decodeUnknownEffect`: Use the function type returned by Schema.decodeUnknownEffect. @@ -13217,10 +13279,14 @@ Schema.toFormatter(schema) - `Record.modifyOption` -> `Record.modify`: The Option suffix was dropped; missing keys still return Option.none. -- `Record.partitionMap` -> `Record.partition`: Pass a mapper returning Result; failures and successes form the two output records. +- `Record.partition` -> `Record.partition`: Return Result.succeed(value) for a match and Result.fail(value) otherwise; Filter.fromPredicate adapts predicates that only inspect the value. The tuple is now [passes, fails], the reverse of v3 [excluded, satisfying]. + +- `Record.partitionMap` -> `Record.partition`: Pass a mapper returning Result. The tuple is [passes, fails], the reverse of v3 [failures, successes]. - `Record.replaceOption` -> `Record.replace`: The Option suffix was dropped; missing keys still return Option.none. +- `Record.separate` -> `Record.separate`: The tuple is now [successes, failures], the reverse of v3 [failures, successes]. + ### `effect/RedBlackTree` - `RedBlackTree.Direction` -> `none`: The tree direction type was removed; use normal array order or Array.reverse. @@ -13741,7 +13807,7 @@ Schema.toFormatter(schema) - `STM.orTry` -> `none`: V4 exposes no recoverable retry signal or public transactional savepoint; restructure branch selection before Effect.txRetry. -- `STM.partition` -> `Effect.partition`: The combinator keeps its name, but STM values are now ordinary Effects. Run the complete transaction with Effect.tx. V4 Tx operations return ordinary Effects; compose multiple operations under one outer Effect.tx to keep them atomic. +- `STM.partition` -> `Effect.partition`: STM values are now ordinary Effects. Run the complete transaction with Effect.tx. The tuple is [passes, fails], the reverse of v3 [failures, successes]. - `STM.provideServiceSTM` -> `Effect.provideServiceEffect`: The effectful service provider was renamed on Effect. @@ -13921,8 +13987,6 @@ Schema.toFormatter(schema) - `Schedule.onDecision` -> `Schedule.tap`: Use Schedule.tap for effects on recurrence metadata. To also observe final completion, wrap Schedule.toStep with Pull.matchEffect in Schedule.fromStep. -- `Schedule.once` -> `Schedule.duration(Duration.zero)`: A zero-duration schedule recurs once and then completes; map its Duration output to void if needed. - - `Schedule.provideContext` -> `Schedule.fromStep + Effect.provide`: Provide the Context to both Schedule.toStep acquisition and each Effect returned by the acquired step. - `Schedule.provideService` -> `Schedule.fromStep + Effect.provideService`: Provide the service to both Schedule.toStep acquisition and each Effect returned by the acquired step. @@ -14097,7 +14161,7 @@ Schema.toFormatter(schema) - `Schema.BooleanFromUnknown` -> `Schema.Boolean`: Use the boolean schema and perform any coercion explicitly before decoding. -- `Schema.BrandSchema` -> `Schema.brand`: Use the schema returned by the v4 `brand` combinator and infer its concrete type. +- `Schema.BrandSchema` -> `Schema.brand`: Use the schema returned by the v4 `brand` combinator and infer its concrete type. The brand exists only in the TypeScript type and cannot be inspected through the runtime AST. - `Schema.BrandSchemaId` -> `none`: The v3 schema-id symbol was removed. Use the corresponding public v4 constructor/check instead of inspecting schema ids. @@ -14533,13 +14597,13 @@ Schema.toFormatter(schema) - `Schema.TaggedErrorClass` -> `Schema.TaggedError`: The exported helper interface was removed; use the class returned by Schema.TaggedError and infer its types. -- `Schema.TaggedRequest` -> `effect/unstable/rpc/Rpc.make`: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. +- `Schema.TaggedRequest` -> `effect/rpc/Rpc.make`: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. - `Schema.TaggedRequest.All` -> `none`: The v3 helper/protocol type was removed by the v4 Schema model rewrite. Use the public v4 constructor and infer its result types instead. - `Schema.TaggedRequest.Any` -> `none`: The v3 helper/protocol type was removed by the v4 Schema model rewrite. Use the public v4 constructor and infer its result types instead. -- `Schema.TaggedRequestClass` -> `effect/unstable/rpc/Rpc.make`: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. +- `Schema.TaggedRequestClass` -> `effect/rpc/Rpc.make`: The Schema request/serialization protocol was removed; migrate RPC requests to the v4 Rpc APIs. - `Schema.TaggedStruct` -> `Schema.TaggedStruct`: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. @@ -14673,7 +14737,7 @@ Schema.toFormatter(schema) - `Schema.betweenDuration` -> `Schema.isBetween`: Rename the predicate to `isBetween` and apply it with `Schema.check` or a schema's `check` method. -- `Schema.brand` -> `Schema.brand`: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. +- `Schema.brand` -> `Schema.brand`: Pass one concrete string literal as the brand identifier. Widened strings, unions, open template literals, and symbol identifiers are rejected; apply brand repeatedly to compose brands. V4 branding is type-only and does not add runtime AST metadata. - `Schema.capitalized` -> `Schema.isCapitalized`: Rename the string predicate to `isCapitalized` and apply it with `Schema.check` or a schema's `check` method. @@ -14721,7 +14785,7 @@ Schema.toFormatter(schema) - `Schema.encodedSchema` -> `Schema.toEncoded`: Rename the encoded-side projection. -- `Schema.endsWith` -> `Schema.isEndsWith`: Rename the string predicate to `isEndsWith` and apply it with `Schema.check` or a schema's `check` method. +- `Schema.endsWith` -> `Schema.isEndingWith`: Rename the string predicate to `isEndingWith` and apply it with `Schema.check` or a schema's `check` method. - `Schema.equivalence` -> `Schema.toEquivalence`: Rename the equivalence derivation utility. @@ -14739,7 +14803,7 @@ Schema.toFormatter(schema) - `Schema.format` -> `SchemaRepresentation.toCodeDocument`: Build a representation with `SchemaRepresentation.toRepresentation`, `toMultiDocument`, then `toCodeDocument`. -- `Schema.fromBrand` -> `Schema.fromBrand`: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. +- `Schema.fromBrand` -> `Schema.fromBrand`: Pass the constructor's single concrete string brand key as the first argument, followed by the constructor and then the schema. Apply fromBrand repeatedly for multiple brands; use Schema.Union for alternatives. Constructor checks remain at runtime, while the brand is type-only and does not add AST metadata. - `Schema.fromKey` -> `Schema.encodeKeys`: Use `encodeKeys` to map decoded property names to encoded keys. @@ -14771,7 +14835,7 @@ Schema.toFormatter(schema) - `Schema.headOrElse` -> `none`: Compared the v3 declaration with v4 Schema and the schema migration guide; no direct public replacement remains. Rebuild the behavior from public v4 codecs/getters where still required. -- `Schema.includes` -> `Schema.isIncludes`: Rename the string predicate to `isIncludes` and apply it with `Schema.check` or a schema's `check` method. +- `Schema.includes` -> `Schema.isIncluding`: Rename the string predicate to `isIncluding` and apply it with `Schema.check` or a schema's `check` method. - `Schema.instanceOf` -> `Schema.instanceOf`: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. @@ -14781,11 +14845,11 @@ Schema.toFormatter(schema) - `Schema.isSchema` -> `Schema.isSchema`: The API remains public in v4, but its type/value declaration was consolidated; use the v4 declaration and update inferred types/signature as needed. -- `Schema.itemsCount` -> `Schema.isLengthBetween`: Use the v4 collection-size check and apply it with `Schema.check` or a schema's `check` method. +- `Schema.itemsCount` -> `Schema.isBetweenLength`: Use the v4 collection-size check and apply it with `Schema.check` or a schema's `check` method. - `Schema.keyof` -> `none`: Removed with the schema model rewrite; derive keys from struct fields or use `Schema.Literals` explicitly. -- `Schema.length` -> `Schema.isLengthBetween`: Use `isLengthBetween` with equal minimum and maximum values for an exact string length. +- `Schema.length` -> `Schema.isBetweenLength`: Use `isBetweenLength` with equal minimum and maximum values for an exact string length. - `Schema.lessThan` -> `Schema.isLessThan`: Rename the predicate to `isLessThan` and apply it with `Schema.check` or a schema's `check` method. @@ -14903,7 +14967,7 @@ Schema.toFormatter(schema) - `Schema.standardSchemaV1` -> `Schema.toStandardSchemaV1`: Rename the Standard Schema adapter. -- `Schema.startsWith` -> `Schema.isStartsWith`: Rename the string predicate to `isStartsWith` and apply it with `Schema.check` or a schema's `check` method. +- `Schema.startsWith` -> `Schema.isStartingWith`: Rename the string predicate to `isStartingWith` and apply it with `Schema.check` or a schema's `check` method. - `Schema.successSchema` -> `none`: Compared the v3 declaration with v4 Schema and the schema migration guide; no direct public replacement remains. Rebuild the behavior from public v4 codecs/getters where still required. @@ -14963,9 +15027,9 @@ Schema.toFormatter(schema) - `SchemaAST.BooleanKeyword` -> `SchemaAST.Boolean`: The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role. -- `SchemaAST.BrandAnnotation` -> `Schema.Annotations.Bottom["brands"]`: Brands are stored under the string-keyed brands annotation and normally added with Schema.brand. +- `SchemaAST.BrandAnnotation` -> `none`: Runtime brand annotations were removed. Schema.brand adds a TypeScript-only nominal distinction and does not store brand metadata in the AST. -- `SchemaAST.BrandAnnotationId` -> `Schema.brand`: Symbol annotation IDs were removed; add brands through Schema.brand. +- `SchemaAST.BrandAnnotationId` -> `Schema.brand`: Use Schema.brand for a TypeScript-only nominal distinction. V4 does not store brand metadata in the AST. - `SchemaAST.Compiler` -> `none`: The generic AST compiler abstraction was removed; traverse the discriminated SchemaAST.AST union directly or use a higher-level Schema derivation API. @@ -15129,7 +15193,7 @@ Schema.toFormatter(schema) - `SchemaAST.getBatchingAnnotation` -> `none`: Batching annotations were removed. ParseOptions.concurrency controls product child parsing only; configure request batching separately. -- `SchemaAST.getBrandAnnotation` -> `SchemaAST.resolveAt("brands")`: Resolve the string-keyed brands annotation. +- `SchemaAST.getBrandAnnotation` -> `none`: Runtime brand inspection was removed because Schema.brand is type-only and does not store brand metadata in the AST. - `SchemaAST.getCompiler` -> `none`: The Match-based compiler was removed; traverse SchemaAST.AST directly or use the relevant Schema derivation API. @@ -15291,6 +15355,8 @@ Schema.toFormatter(schema) - `Secret.fromIterable` -> `Redacted.make(Array.from(iterable).join(""))`: Secret was removed; join the character iterable and wrap the resulting string in Redacted. +- `Secret.fromString`: TODO: needs guidance + - `Secret.isSecret` -> `Redacted.isRedacted`: Secret was removed in favor of Redacted. - `Secret.make` -> `Redacted.make(bytes.map((byte) => String.fromCharCode(byte)).join(""))`: Secret was removed; preserve the v3 byte-to-code-unit conversion explicitly, then wrap the string in Redacted. @@ -15559,7 +15625,7 @@ Schema.toFormatter(schema) - `Stream.aggregateWithinEither` -> `Stream.aggregateWithin`: Either-emitting variant removed; v4 aggregateWithin(sink, schedule) emits only the sink outputs B (schedule outputs are no longer surfaced as Either.right). -- `Stream.as` -> `Stream.map(() => value)`: Stream.as was removed; replace each element with a constant via Stream.map. +- `Stream.as` -> `Stream.as`: Retained in v4 with the same signature; replaces every element with the provided constant value. - `Stream.async` -> `Stream.callback`: Stream.callback((queue) =\> Effect | void, { bufferSize?, strategy? }) replaces the Emit-based async; push with Queue.offer/offerAll, end with Queue.end, fail with Queue.fail. @@ -15569,6 +15635,10 @@ Schema.toFormatter(schema) - `Stream.asyncScoped` -> `Stream.callback`: Stream.callback's register effect may use Scope (Scope is excluded from the resulting R), replacing asyncScoped; the Option\ end signal becomes Queue.end. +- `Stream.bind` -> `Stream.bind`: The input must be a record. Start from Stream.Do or use Stream.bindTo(name) to wrap a non-record element. The field name may now re-bind an existing key; the resulting record type is Simplify\ & Record\\>. + +- `Stream.bindEffect` -> `Stream.bindEffect`: The input must be a record. Start from Stream.Do or use Stream.bindTo(name) to wrap a non-record element. Existing keys may be re-bound, with the new value replacing the old field type. The callback still returns an Effect. + #### `Stream.branchAfter` **Replacement:** `Stream.peel` @@ -15597,6 +15667,8 @@ Stream.unwrap(Effect.map(Stream.peel(self, Sink.take(n)), ([head, rest]) => f(he - `Stream.catchSomeCause` -> `Stream.catchCauseFilter`: Option-returning cause handler replaced by Stream.catchCauseFilter(filter, f, orElse?) using a Filter on the Cause (Stream.catchCauseIf for refinements). +- `Stream.catchTags` -> `Stream.catchTags`: Only keys present in the tagged error union are accepted; remove handlers for tags the stream cannot fail with. Untagged errors remain in the error channel. + - `Stream.chunksWith` -> `Stream.flattenArray(f(Stream.chunks(self)))`: No dedicated combinator; expose chunk structure with Stream.chunks (Stream\\>), transform, then re-flatten with Stream.flattenArray. - `Stream.combineChunks` -> `Stream.combineArray`: Chunk-\>Array rename of the pull-level combining primitive; pulls now yield NonEmptyReadonlyArray values and halt via Cause.Done-failing Pull effects instead of Option-typed errors. @@ -15671,6 +15743,10 @@ Stream.unwrap(Effect.map(Stream.peel(self, Sink.take(n)), ([head, rest]) => f(he - `Stream.interruptWhenDeferred` -> `Stream.interruptWhen(Deferred.await(deferred))`: Deferred-specialized variant removed; pass Deferred.await to Stream.interruptWhen (a Deferred failure surfaces as the stream's failure, as before). +- `Stream.let` -> `Stream.let`: Retained in v4 and aligned with Effect.let: the field name may re-bind an existing key (the computed value replaces it) and the record type is Simplify\ & Record\\>. + +- `Stream.mapBoth` -> `Stream.mapBoth`: Rename the options onSuccess to onElement and onFailure to onError. The callbacks still transform emitted elements and typed errors respectively. + - `Stream.mapChunks` -> `Stream.mapArray`: Chunk-\>Array rename; transforms each emitted chunk as a NonEmptyReadonlyArray. - `Stream.mapChunksEffect` -> `Stream.mapArrayEffect`: Chunk-\>Array rename of the effectful per-chunk transform. @@ -15723,6 +15799,8 @@ Stream.mergeAll(Object.entries(streams).map(([_tag, s]) => Stream.map(s, (value) - `Stream.paginateEffect` -> `Stream.paginate`: v4 Stream.paginate emits a batch per step; wrap the single value in an array: `(s) => Effect.map(step(s), ([a, next]) => [[a], next])`. +- `Stream.partition` -> `Stream.partition`: Pass a Result-returning Filter instead of a boolean predicate or refinement; Filter.fromPredicate(predicate) adapts existing predicates. The tuple order is now [passes, fails], the reverse of v3 [excluded, satisfying]. Rename bufferSize to capacity (default 16; also accepts "unbounded"). Acquisition is scoped and does not fail with E; the returned streams carry E. + - `Stream.partitionEither` -> `Stream.partitionEffect`: Either-based split replaced by Filter.FilterEffect: the function now returns Effect\\> (Result.succeed/Result.fail instead of Either.right/left). Returns Effect\<[passes, fails], never, R | Scope\> — note the tuple is [passes, fails], v3 was [left, right]; options are { capacity?, concurrency? }. - `Stream.provideLayer` -> `Stream.provide`: v4 Stream.provide accepts a Layer or a Context; behavior identical. @@ -15793,6 +15871,8 @@ Effect.suspend(() => { - `Stream.runForEachWhileScoped` -> `Stream.runForEachWhile`: Scoped run variants are gone; v4 runForEachWhile (callback returns Effect\) manages the stream scope internally. +- `Stream.runIntoPubSub` -> `Stream.runIntoPubSub`: Pass a PubSub of plain elements rather than Take wrappers. Both overloads return Effect\, so handle stream failures on the returned Effect instead of expecting them as PubSub messages. The optional shutdownOnEnd flag controls PubSub shutdown. + - `Stream.runIntoPubSubScoped` -> `Stream.runIntoPubSub`: Scoped variant removed; v4 runIntoPubSub(pubsub, { shutdownOnEnd? }) publishes plain values (the Take wrapper is gone) and does not require Scope — fork the returned effect (Effect.forkIn/Effect.forkScoped) to reproduce the background scoped behavior. - `Stream.runIntoQueueElementsScoped` -> `Stream.runIntoQueue`: The per-element Exit\\> encoding is gone; v4 runIntoQueue targets a Queue\ — elements are offered plainly and failure/end are signalled through the queue's error/done channel. Fork with Effect.forkIn for scoped background running. @@ -15801,6 +15881,10 @@ Effect.suspend(() => { - `Stream.runScoped` -> `Stream.run`: Scoped variant removed; v4 Stream.run(sink) manages the stream's scope internally. For consumption tied to an enclosing Scope, use Stream.toPull and drive the Pull manually. +- `Stream.scan` -> `Stream.scan`: Wrap the initial state in a thunk: Stream.scan(() =\> initial, step). The thunk is evaluated for each stream run; allocate mutable initial state inside it to avoid sharing state between runs. + +- `Stream.scanEffect` -> `Stream.scanEffect`: Wrap the initial state in a thunk: Stream.scanEffect(() =\> initial, step). It is evaluated for each stream run; the step still returns an Effect. + - `Stream.scanReduce` -> `Stream.mapAccum`: Removed; emulate first-element-as-seed with `Stream.mapAccum(self, () => undefined as A | undefined, (acc, a) => { const next = acc === undefined ? a : f(acc, a); return [next, [next]] })`. - `Stream.scanReduceEffect` -> `Stream.mapAccumEffect`: Removed; same first-element-as-seed emulation as scanReduce but with Stream.mapAccumEffect and an effectful step. @@ -16142,6 +16226,8 @@ switch (strategy) { - `TMap.removeAll` -> `TxHashMap.removeMany`: The bulk removal operation was renamed. +- `TMap.set`: TODO: needs guidance + - `TMap.setIfAbsent` -> `Effect.tx + TxHashMap.get/TxHashMap.set`: No direct helper remains; check and conditionally set under one outer transaction. - `TMap.size` -> `TxHashMap.size`: Import TxHashMap from "effect/TxHashMap"; the operation keeps its name. V4 Tx operations return ordinary Effects; compose multiple operations under one outer Effect.tx to keep them atomic. diff --git a/repos/effect/package.json b/repos/effect/package.json index 39b3dab4d5..9cd72218e9 100644 --- a/repos/effect/package.json +++ b/repos/effect/package.json @@ -34,19 +34,19 @@ "changeset-publish": "node scripts/set-strip-internal.mjs && pnpm codemod && pnpm build && changeset publish" }, "devDependencies": { - "@babel/cli": "^8.0.4", - "@babel/core": "^8.0.1", + "@babel/cli": "^8.0.6", + "@babel/core": "^8.0.6", "@babel/plugin-transform-export-namespace-from": "^8.0.1", "@babel/plugin-transform-modules-commonjs": "^8.0.1", "@changesets/changelog-github": "1.0.1", - "@changesets/cli": "3.0.2", + "@changesets/cli": "3.0.3", "@effect/ai-docgen": "workspace:^", "@effect/bundle": "workspace:^", "@effect/docgen": "workspace:^", "@effect/doctest": "workspace:^", "@effect/jsdocs": "workspace:^", "@effect/oxc": "workspace:^", - "@effect/tsgo": "^0.41.0", + "@effect/tsgo": "^0.46.1", "@effect/utils": "workspace:^", "@effect/vitest": "workspace:^", "@faker-js/faker": "^10.6.0", @@ -54,31 +54,31 @@ "@rollup/plugin-replace": "^6.0.3", "@rollup/plugin-terser": "^1.0.0", "@types/jscodeshift": "^17.3.0", - "@types/node": "^26.4.1", - "@vitest/coverage-v8": "^5.0.0", - "@vitest/web-worker": "^5.0.0", + "@types/node": "^26.6.3", + "@vitest/coverage-v8": "^5.0.2", + "@vitest/web-worker": "^5.0.2", "ast-types": "^0.16.3", "babel-plugin-annotate-pure-calls": "^0.5.0", - "dprint": "^0.57.3", - "fast-check": "^4.9.0", + "dprint": "^0.57.4", + "fast-check": "^4.10.2", "glob": "^13.0.6", - "happy-dom": "^20.14.0", + "happy-dom": "^20.14.5", "jscodeshift": "^17.4.0", "lalph": "^0.3.139", "madge": "^8.0.0", - "oxlint": "^1.81.0", + "oxlint": "^1.85.0", "pkg-pr-new": "0.0.88", "playwright": "^1.63.0", - "rollup": "^4.63.1", + "rollup": "^4.63.5", "rollup-plugin-bundle-stats": "^4.22.3", "rollup-plugin-esbuild": "^6.2.1", "rollup-plugin-visualizer": "^7.1.1", "terser": "^5.51.2", - "tstyche": "^7.2.4", + "tstyche": "^7.2.5", "typescript": "^7.0.2", - "vite": "^8.2.2", - "vitest": "^5.0.0", - "vitest-websocket-mock": "^0.7.0", - "zod": "^4.5.4" + "vite": "^8.3.1", + "vitest": "^5.0.2", + "vitest-websocket-mock": "^0.8.0", + "zod": "^4.6.5" } } diff --git a/repos/effect/packages/ai/anthropic/CHANGELOG.md b/repos/effect/packages/ai/anthropic/CHANGELOG.md index c72a03eab2..ddf48575a2 100644 --- a/repos/effect/packages/ai/anthropic/CHANGELOG.md +++ b/repos/effect/packages/ai/anthropic/CHANGELOG.md @@ -1,5 +1,100 @@ # @effect/ai-anthropic +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8479](https://github.com/Effect-TS/effect/pull/8479) [`0f42b56`](https://github.com/Effect-TS/effect/commit/0f42b56447002b9884acbfbe6fbf8acc9683d060) Thanks @tim-smart! - Map malformed Anthropic-compatible gateway 4xx errors by status instead of `InvalidOutputError`, including 429 retry delays. Direct `AnthropicClient.client.betaMessagesPost` calls now fail with `HttpClientError` instead of `SchemaError` for these responses. + +- [#8478](https://github.com/Effect-TS/effect/pull/8478) [`92e9151`](https://github.com/Effect-TS/effect/commit/92e91511e3b448fac74ce0a90cecc094731d0540) Thanks @tim-smart! - Accept message responses that omit `usage.inference_geo`, as some Anthropic-compatible gateways do. +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/ai/anthropic/README.md b/repos/effect/packages/ai/anthropic/README.md index da2336285d..eb76b44223 100644 --- a/repos/effect/packages/ai/anthropic/README.md +++ b/repos/effect/packages/ai/anthropic/README.md @@ -5,10 +5,21 @@ An [Anthropic](https://www.anthropic.com) provider for the Effect AI modules. In ## Installation ```sh -npm install effect@rc @effect/ai-anthropic@rc +npm install effect @effect/ai-anthropic ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/ai-anthropic) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/ai-anthropic): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/ai/anthropic/codegen.yml b/repos/effect/packages/ai/anthropic/codegen.yml index e112fc77af..81c875a766 100644 --- a/repos/effect/packages/ai/anthropic/codegen.yml +++ b/repos/effect/packages/ai/anthropic/codegen.yml @@ -9,6 +9,7 @@ patches: - '[{"op":"remove","path":"/components/schemas/BetaUsage/required/9"},{"op":"remove","path":"/components/schemas/BetaUsage/required/5"},{"op":"remove","path":"/components/schemas/BetaMessageDeltaUsage/required/3"}]' header: | /** + * @stability unstable * @since 1.0.0 */ excludeAnnotations: @@ -48,6 +49,11 @@ replacements: to: '"server_tool_use": Schema.optionalKey(Schema.Union([BetaServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })) })' - from: '"server_tool_use": Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null }) })' to: '"server_tool_use": Schema.optionalKey(Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })) })' + # Anthropic-compatible gateways may omit inference_geo in response usage (#8464) + - from: 'readonly "inference_geo": string | null' + to: 'readonly "inference_geo"?: string | null' + - from: '"inference_geo": Schema.Union([Schema.String, Schema.Null]).annotate({ "title": "Inference Geo", "description": "The geographic region where inference was performed for this request.", "default": null }),' + to: '"inference_geo": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "title": "Inference Geo", "description": "The geographic region where inference was performed for this request.", "default": null })),' # Make context_management and container optional in BetaMessage (API may omit these fields) - from: 'readonly "context_management": BetaResponseContextManagement | null' to: 'readonly "context_management"?: BetaResponseContextManagement | null' diff --git a/repos/effect/packages/ai/anthropic/package.json b/repos/effect/packages/ai/anthropic/package.json index 52d8fbf011..af34eba6c4 100644 --- a/repos/effect/packages/ai/anthropic/package.json +++ b/repos/effect/packages/ai/anthropic/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-anthropic", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "An Anthropic provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts b/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts index 93288554fc..077d5b3ce4 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts +++ b/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts @@ -4,26 +4,27 @@ * authentication headers, API version headers, response decoding, and error * mapping, then exposes helpers for regular and streaming message requests. * + * @stability unstable * @since 4.0.0 */ +import type * as AiError from "effect/ai/AiError" import * as Array from "effect/Array" import type * as Config from "effect/Config" import * as Context from "effect/Context" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" import { identity } from "effect/Function" +import * as Headers from "effect/http/Headers" +import * as HttpBody from "effect/http/HttpBody" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import type * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Predicate from "effect/Predicate" import * as Redacted from "effect/Redacted" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import type * as AiError from "effect/unstable/ai/AiError" -import * as Sse from "effect/unstable/encoding/Sse" -import * as Headers from "effect/unstable/http/Headers" -import * as HttpBody from "effect/unstable/http/HttpBody" -import * as HttpClient from "effect/unstable/http/HttpClient" -import type * as HttpClientError from "effect/unstable/http/HttpClientError" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import { AnthropicConfig } from "./AnthropicConfig.ts" import * as Generated from "./Generated.ts" import * as Errors from "./internal/errors.ts" @@ -36,6 +37,7 @@ import * as Errors from "./internal/errors.ts" * Represents the Anthropic client service with methods for the Messages API, including regular and streaming message * creation. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -98,6 +100,7 @@ export interface Service { * - `content_block_stop`: End of a content block * - `error`: Error events with type and message * + * @stability unstable * @category models * @since 4.0.0 */ @@ -126,6 +129,7 @@ export type MessageStreamEvent = * @see {@link layer} for providing a client from explicit options * @see {@link layerConfig} for providing a client from `Config` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -155,6 +159,7 @@ export class AnthropicClient extends Context.Service() * @see {@link layer} for providing an Anthropic client from explicit options * @see {@link layerConfig} for loading Anthropic client settings from `Config` * + * @stability unstable * @category options * @since 4.0.0 */ @@ -216,6 +221,7 @@ const withRedactedHeaders = Effect.updateService( * @see {@link layer} for providing the client as a `Layer` from explicit options * @see {@link layerConfig} for providing the client as a `Layer` with `Config`-based settings * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -243,7 +249,41 @@ export const make = Effect.fnUntraced( : identity ) - const client = Generated.make(httpClient, { + // Let status mapping handle gateway errors without an Anthropic envelope. + const generatedHttpClient = HttpClient.transformResponse( + httpClient, + Effect.flatMap((response) => { + if ( + response.status < 400 || response.status >= 500 || + !response.request.url.endsWith("/v1/messages?beta=true") + ) { + return Effect.succeed(response) + } + return Effect.flatMap(Effect.option(response.json), (body) => { + if ( + body._tag === "Some" && + Schema.decodeUnknownOption(Generated.BetaMessagesPost4XX)(body.value)._tag === "Some" + ) { + return Effect.succeed(response) + } + return Effect.flatMap( + Effect.orElseSucceed(response.text, () => "Unexpected status code"), + (description) => + Effect.fail( + new HttpClientError.HttpClientError({ + reason: new HttpClientError.StatusCodeError({ + request: response.request, + response, + description + }) + }) + ) + ) + }) + }) + ) + + const client = Generated.make(generatedHttpClient, { transformClient: Effect.fnUntraced(function*(client) { const config = yield* AnthropicConfig.getOrUndefined if (Predicate.isNotUndefined(config?.transformClient)) { @@ -366,6 +406,7 @@ export const make = Effect.fnUntraced( * @see {@link make} for constructing the client service effectfully * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -385,6 +426,7 @@ export const layer = (options: Options): Layer.Layer = { end_turn: "stop", diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts b/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts index 7eeb0ea366..e77c4d2f06 100644 --- a/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts +++ b/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts @@ -1,16 +1,54 @@ import { AnthropicClient } from "@effect/ai-anthropic" import * as Errors from "@effect/ai-anthropic/internal/errors" import { assert, describe, it } from "@effect/vitest" -import { Context, Effect, Layer, Redacted, type Schema } from "effect" -import { - Headers, - HttpClient, - type HttpClientError, - type HttpClientRequest, - HttpClientResponse -} from "effect/unstable/http" +import { Context, Duration, Effect, Layer, Redacted, type Schema } from "effect" +import { Headers, HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("AnthropicClient", () => { + it.effect("maps a gateway 400 without an Anthropic error envelope", () => + Effect.gen(function*() { + const client = yield* AnthropicClient.AnthropicClient + const error = yield* client.createMessage({ + payload: { + model: "claude-sonnet-4-20250514", + max_tokens: 1, + messages: [{ role: "user", content: "hello" }] + } + }).pipe(Effect.flip) + + assert.strictEqual(error.reason._tag, "InvalidRequestError") + if (error.reason._tag !== "InvalidRequestError") return + assert.include(error.reason.description ?? "", "HTTP 400") + assert.include(error.reason.description ?? "", "gateway rejected request") + assert.strictEqual(error.reason.http?.response?.status, 400) + }).pipe(Effect.provide(makeTestLayer({ + _tag: "Json", + status: 400, + body: { error: { message: "gateway rejected request" } } + })))) + + it.effect("maps a gateway 429 without an Anthropic error envelope and preserves retry-after", () => + Effect.gen(function*() { + const client = yield* AnthropicClient.AnthropicClient + const error = yield* client.createMessage({ + payload: { + model: "claude-sonnet-4-20250514", + max_tokens: 1, + messages: [{ role: "user", content: "hello" }] + } + }).pipe(Effect.flip) + + assert.strictEqual(error.reason._tag, "RateLimitError") + if (error.reason._tag !== "RateLimitError") return + assert.strictEqual(Duration.toMillis(error.reason.retryAfter!), 3000) + assert.strictEqual(error.reason.http?.response?.status, 429) + }).pipe(Effect.provide(makeTestLayer({ + _tag: "Json", + status: 429, + headers: { "retry-after": "3" }, + body: { error: { message: "gateway rate limit" } } + })))) + it.effect("redacts the API key in AI error context", () => Effect.gen(function*() { const client = yield* AnthropicClient.AnthropicClient @@ -63,6 +101,10 @@ describe("AnthropicClient", () => { return yield* Effect.die(new Error("Expected AuthenticationError")) } assert.strictEqual(result.reason.kind, "InvalidKey") + assert.deepStrictEqual(result.reason.metadata?.anthropic, { + errorType: "authentication_error", + requestId: "req_anthropic" + }) assert.strictEqual( result.reason.description, "invalid x-api-key (POST https://api.anthropic.com/v1/messages?beta=true) [type: authentication_error] [requestId: req_anthropic]" diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts b/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts index 56bab6b2dc..ef3a0c5fe2 100644 --- a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +++ b/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts @@ -9,11 +9,76 @@ import { Response as AiResponse, Tool, Toolkit -} from "effect/unstable/ai" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +} from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("AnthropicLanguageModel", () => { describe("streamText", () => { + for ( + const [label, geo] of [ + ["missing", {}], + ["null", { inference_geo: null }], + ["string", { inference_geo: "us" }] + ] as const + ) { + it.effect(`accepts ${label} usage.inference_geo in message_start`, () => + Effect.gen(function*() { + const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.succeed(sseResponse(request, [ + { + type: "message_start", + message: { + id: "msg_test_1", + type: "message", + role: "assistant", + model: "claude-sonnet-4-20250514", + content: [], + stop_reason: null, + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + ...geo, + input_tokens: 10, + output_tokens: 0, + service_tier: null + } + } + }, + { type: "content_block_start", index: 0, content_block: { type: "text", text: "Hello" } }, + { type: "content_block_delta", index: 0, delta: { type: "text_delta", text: "Hello" } }, + { type: "content_block_stop", index: 0 }, + { + type: "message_delta", + delta: { stop_reason: "end_turn", stop_sequence: null }, + usage: { + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + input_tokens: null, + output_tokens: 5 + } + }, + { type: "message_stop" } + ])) + ) + )) + ) + + const parts = yield* LanguageModel.streamText({ prompt: "Hello" }).pipe( + Stream.runCollect, + Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")), + Effect.provide(layer) + ) + assert.isTrue( + globalThis.Array.from(parts).some((part) => part.type === "text-delta" && part.delta === "Hello") + ) + })) + } + it.effect("decodes tool call params in content_block_stop", () => Effect.gen(function*() { const toolParams = { pattern: "*.ts" } @@ -437,6 +502,49 @@ describe("AnthropicLanguageModel", () => { }) describe("generateText", () => { + for ( + const [label, geo] of [ + ["missing", {}], + ["null", { inference_geo: null }], + ["string", { inference_geo: "us" }] + ] as const + ) { + it.effect(`accepts ${label} usage.inference_geo in a message response`, () => + Effect.gen(function*() { + const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.succeed(jsonResponse(request, { + id: "msg_test_1", + type: "message", + role: "assistant", + model: "claude-sonnet-4-20250514", + content: [{ type: "text", text: "Hello" }], + stop_reason: "end_turn", + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + ...geo, + input_tokens: 10, + output_tokens: 5, + service_tier: null + } + })) + ) + )) + ) + + const response = yield* LanguageModel.generateText({ prompt: "Hello" }).pipe( + Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")), + Effect.provide(layer) + ) + assert.isTrue(response.content.some((part) => part.type === "text" && part.text === "Hello")) + })) + } + it.effect("omits strictJsonSchema from the request while preserving tool strictness", () => Effect.gen(function*() { let capturedRequest: HttpClientRequest.HttpClientRequest | undefined = undefined diff --git a/repos/effect/packages/ai/openai-compat/CHANGELOG.md b/repos/effect/packages/ai/openai-compat/CHANGELOG.md index 5b012e6a8a..716a0ef6b2 100644 --- a/repos/effect/packages/ai/openai-compat/CHANGELOG.md +++ b/repos/effect/packages/ai/openai-compat/CHANGELOG.md @@ -1,5 +1,100 @@ # @effect/ai-openai-compat +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8363](https://github.com/Effect-TS/effect/pull/8363) [`1519fd7`](https://github.com/Effect-TS/effect/commit/1519fd7b21a890aa7e187bfd978520dbb66a2bd2) Thanks @dardanbujupaj! - Preserve tool call parameters for dynamic tools backed by raw JSON Schema. + +- [#8333](https://github.com/Effect-TS/effect/pull/8333) [`621a650`](https://github.com/Effect-TS/effect/commit/621a65093d0b687f8cbc4068f69ff0f9417e2da7) Thanks @skusez! - Preserve streamed text and tool arguments when providers send nullable delta fields. +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/ai/openai-compat/README.md b/repos/effect/packages/ai/openai-compat/README.md index 68bcbb7765..80e47391d4 100644 --- a/repos/effect/packages/ai/openai-compat/README.md +++ b/repos/effect/packages/ai/openai-compat/README.md @@ -5,10 +5,21 @@ Connects the Effect AI modules to any OpenAI-compatible API, with support for ch ## Installation ```sh -npm install effect@rc @effect/ai-openai-compat@rc +npm install effect @effect/ai-openai-compat ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/ai-openai-compat) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/ai-openai-compat): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/ai/openai-compat/package.json b/repos/effect/packages/ai/openai-compat/package.json index 0910f8d81f..2c3fac74af 100644 --- a/repos/effect/packages/ai/openai-compat/package.json +++ b/repos/effect/packages/ai/openai-compat/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai-compat", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "An OpenAI compat integration for Effect", diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts b/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts index c8ecad004a..8551f926ea 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts +++ b/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts @@ -5,23 +5,24 @@ * typed helpers for non-streaming chat completions, streaming chat completions, * and embedding requests. * + * @stability unstable * @since 4.0.0 */ +import type * as AiError from "effect/ai/AiError" import * as Array from "effect/Array" import type * as Config from "effect/Config" import * as Context from "effect/Context" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" import { identity } from "effect/Function" +import * as Headers from "effect/http/Headers" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Redacted from "effect/Redacted" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import type * as AiError from "effect/unstable/ai/AiError" -import * as Sse from "effect/unstable/encoding/Sse" -import * as Headers from "effect/unstable/http/Headers" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import * as Errors from "./internal/errors.ts" import { OpenAiConfig } from "./OpenAiConfig.ts" @@ -34,6 +35,7 @@ import { OpenAiConfig } from "./OpenAiConfig.ts" * completions, streaming chat completions, and embeddings. Transport and * schema decoding failures are mapped to `AiError`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -77,6 +79,7 @@ export interface Service { * @see {@link layer} for providing the service from explicit options * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -87,6 +90,7 @@ export class OpenAiClient extends Context.Service()( /** * Configuration options used to construct an OpenAI-compatible client. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -130,6 +134,7 @@ const withRedactedHeaders = Effect.updateService( * @see {@link layer} for providing this client from explicit options * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -288,6 +293,7 @@ export const make = Effect.fnUntraced( * @see {@link make} for constructing the client service effectfully * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -312,6 +318,7 @@ export const layer = (options: Options): Layer.Layer & { readonly [x: string]: unknow * * @see {@link withConfigOverride} for scoping embedding request overrides * + * @stability unstable * @category services * @since 4.0.0 */ @@ -65,6 +68,7 @@ export class Config extends Context.Service< * @see {@link layer} for providing only the embedding model service * @see {@link withConfigOverride} for scoped request configuration overrides * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -112,6 +116,7 @@ export const model = ( * @see {@link layer} for providing the service as a `Layer` * @see {@link withConfigOverride} for scoping embedding request overrides * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -146,6 +151,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig * @see {@link make} for constructing the embedding model service effectfully * @see {@link model} for creating an `AiModel` with configured dimensions * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -172,6 +178,7 @@ export const layer = (options: { * * @see {@link Config} for available OpenAI-compatible embedding request configuration fields * + * @stability unstable * @category configuration * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts b/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts index 88ba2dd9f5..e89e40c41a 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +++ b/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts @@ -66,7 +66,7 @@ export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & { readonly resetTokens: string | null } -declare module "effect/unstable/ai/AiError" { +declare module "effect/ai/AiError" { /** * Metadata attached to rate limit errors returned by OpenAI-compatible APIs. * diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts b/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts index 923f39397a..3feeed9c97 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +++ b/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts @@ -6,13 +6,23 @@ * or streaming chat completion results back into Effect AI response content and * metadata. * + * @stability unstable * @since 4.0.0 */ +import * as AiError from "effect/ai/AiError" +import * as LanguageModel from "effect/ai/LanguageModel" +import * as AiModel from "effect/ai/Model" +import { toCodecOpenAI } from "effect/ai/OpenAiStructuredOutput" +import type * as Prompt from "effect/ai/Prompt" +import type * as Response from "effect/ai/Response" +import * as Tool from "effect/ai/Tool" import * as Context from "effect/Context" import * as DateTime from "effect/DateTime" import * as Effect from "effect/Effect" -import * as Encoding from "effect/Encoding" +import * as Base64 from "effect/encoding/Base64" import { dual } from "effect/Function" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import type * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" @@ -23,15 +33,6 @@ import * as AST from "effect/SchemaAST" import * as Stream from "effect/Stream" import type { Span } from "effect/Tracer" import type { DeepMutable, Simplify } from "effect/Types" -import * as AiError from "effect/unstable/ai/AiError" -import * as LanguageModel from "effect/unstable/ai/LanguageModel" -import * as AiModel from "effect/unstable/ai/Model" -import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput" -import type * as Prompt from "effect/unstable/ai/Prompt" -import type * as Response from "effect/unstable/ai/Response" -import * as Tool from "effect/unstable/ai/Tool" -import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import * as InternalUtilities from "./internal/utilities.ts" import { type Annotation, @@ -111,6 +112,7 @@ type ModelConfig = Omit & { readonly [x: string]: unknow * * @see {@link withConfigOverride} for scoping language model request overrides * + * @stability unstable * @category services * @since 4.0.0 */ @@ -123,10 +125,11 @@ export class Config extends Context.Service< // Provider Options / Metadata // ============================================================================= -declare module "effect/unstable/ai/Prompt" { +declare module "effect/ai/Prompt" { /** * OpenAI-compatible options for file prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -145,6 +148,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-compatible options for reasoning prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -169,6 +173,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-compatible options for assistant tool-call prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -191,6 +196,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-compatible options for tool-result prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -213,6 +219,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-compatible options for text prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -237,10 +244,11 @@ declare module "effect/unstable/ai/Prompt" { } } -declare module "effect/unstable/ai/Response" { +declare module "effect/ai/Response" { /** * OpenAI-compatible metadata attached to a complete text response part. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -273,6 +281,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata emitted when a streamed text part starts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -291,6 +300,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata emitted when a streamed text part ends. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -313,6 +323,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata attached to a complete reasoning response part. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -335,6 +346,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata emitted when a streamed reasoning part starts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -357,6 +369,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata emitted for a streamed reasoning delta. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -375,6 +388,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata emitted when a streamed reasoning part ends. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -397,6 +411,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata attached to tool-call response parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -415,6 +430,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata attached to document source citations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -471,6 +487,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata attached to URL source citations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -497,6 +514,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI-compatible metadata attached to finish response parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -528,6 +546,7 @@ declare module "effect/unstable/ai/Response" { * @see {@link layer} for creating a `LanguageModel.LanguageModel` layer directly * @see {@link make} for constructing the language model service effectfully * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -566,6 +585,7 @@ export const model = ( * @see {@link layer} for providing the service as a `Layer` * @see {@link model} for creating a model descriptor for `AiModel.provide` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -675,6 +695,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig * @see {@link make} for constructing the language model service effectfully * @see {@link model} for creating an AI model descriptor * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -699,6 +720,7 @@ export const layer = (options: { * * @see {@link Config} for the configuration shape * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -791,7 +813,7 @@ const prepareMessages = Effect.fnUntraced( const imageUrl = part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array - ? `data:${mediaType};base64,${Encoding.encodeBase64(part.data)}` + ? `data:${mediaType};base64,${Base64.encode(part.data)}` : /^(data:|https?:\/\/)/i.test(part.data) ? part.data : `data:${mediaType};base64,${part.data}` @@ -807,7 +829,7 @@ const prepareMessages = Effect.fnUntraced( } if (part.data instanceof Uint8Array) { - const base64 = Encoding.encodeBase64(part.data) + const base64 = Base64.encode(part.data) const fileName = part.fileName ?? `part-${index}.pdf` const fileData = `data:application/pdf;base64,${base64}` content.push({ type: "input_file", filename: fileName, file_data: fileData }) @@ -1445,6 +1467,10 @@ const transformToolCallParams = Effect.fnUntraced(function* = { content_filter: "content-filter", diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts b/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts index 7cae883a95..ac3e031b83 100644 --- a/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts +++ b/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts @@ -2,13 +2,7 @@ import { OpenAiClient } from "@effect/ai-openai-compat" import * as Errors from "@effect/ai-openai-compat/internal/errors" import { assert, describe, it } from "@effect/vitest" import { Context, Effect, Layer, Redacted, type Schema, Stream } from "effect" -import { - Headers, - HttpClient, - type HttpClientError, - type HttpClientRequest, - HttpClientResponse -} from "effect/unstable/http" +import { Headers, HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenAiClient", () => { describe("request behavior", () => { diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts b/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts index b80e664f49..b7f6132c2d 100644 --- a/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts +++ b/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts @@ -1,8 +1,8 @@ import { OpenAiClient, OpenAiEmbeddingModel } from "@effect/ai-openai-compat" import { assert, describe, it } from "@effect/vitest" import { Effect, Layer, Redacted } from "effect" -import { EmbeddingModel } from "effect/unstable/ai" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { EmbeddingModel } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenAiEmbeddingModel", () => { it.effect("model provides dimensions service", () => diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts b/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts index dc90d1f4d1..43a2dc4ae7 100644 --- a/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts +++ b/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts @@ -1,8 +1,8 @@ import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai-compat" import { assert, describe, it } from "@effect/vitest" import { Effect, Layer, Redacted, Ref, Schema, Stream } from "effect" -import { type AiError, LanguageModel, Prompt, Tool, Toolkit } from "effect/unstable/ai" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { type AiError, LanguageModel, Prompt, Tool, Toolkit } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenAiLanguageModel", () => { describe("generateText", () => { @@ -456,6 +456,93 @@ describe("OpenAiLanguageModel", () => { assert.deepStrictEqual(toolCall.params, { env: { PATH: "/usr/bin" } }) })) + it.effect("preserves raw JSON Schema dynamic tool call params", () => + Effect.gen(function*() { + const params = { query: "effect" } + const client = makeHttpClient((request) => + Effect.succeed(jsonResponse( + request, + makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { + role: "assistant", + content: null, + tool_calls: [{ + id: "call_dynamic_1", + type: "function", + function: { name: "DynamicTool", arguments: JSON.stringify(params) } + }] + } + }] + }) + )) + ) + const DynamicTool = Tool.dynamic("DynamicTool", { + parameters: { + type: "object", + properties: { query: { type: "string" } }, + required: ["query"], + additionalProperties: false + } as const + }) + const result = yield* LanguageModel.generateText({ + prompt: "use the dynamic tool", + toolkit: Toolkit.make(DynamicTool), + disableToolCallResolution: true + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + assert.deepStrictEqual(result.toolCalls[0]?.params, params) + })) + + it.effect("decodes Effect Schema dynamic tool call params with the OpenAI codec", () => + Effect.gen(function*() { + const DynamicTool = Tool.dynamic("DynamicTool", { + parameters: Schema.Struct({ + env: Schema.Record(Schema.String, Schema.String) + }) + }) + const client = makeHttpClient((request) => + Effect.succeed(jsonResponse( + request, + makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { + role: "assistant", + content: null, + tool_calls: [{ + id: "call_dynamic_1", + type: "function", + function: { + name: "DynamicTool", + arguments: JSON.stringify({ env: [{ 0: "PATH", 1: "/usr/bin" }] }) + } + }] + } + }] + }) + )) + ) + const result = yield* LanguageModel.generateText({ + prompt: "use the dynamic tool", + toolkit: Toolkit.make(DynamicTool), + disableToolCallResolution: true + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + assert.deepStrictEqual(result.toolCalls[0]?.params, { env: { PATH: "/usr/bin" } }) + })) + it.effect("groups parallel tool calls into one assistant message", () => Effect.gen(function*() { let capturedRequest: HttpClientRequest.HttpClientRequest | undefined @@ -1021,6 +1108,79 @@ describe("OpenAiLanguageModel", () => { assert.strictEqual(text, "Hello") })) + it.effect("preserves streamed text and tool args with nullable delta fields", () => + Effect.gen(function*() { + const chunk = (delta: Record) => ({ + id: "chatcmpl_nullable_delta_fields", + object: "chat.completion.chunk", + model: "gpt-4o-mini", + created: 1, + choices: [{ index: 0, delta, finish_reason: null }] + }) + + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.succeed(sseResponse(request, [ + chunk({ content: "Hello", role: null }), + chunk({ + tool_calls: [{ + index: 0, + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: "" } + }] + }), + chunk({ + tool_calls: [{ index: 0, id: null, function: { name: null, arguments: "{\"in" } }] + }), + chunk({ + tool_calls: [{ index: 0, id: null, function: { name: null, arguments: "put\":\"hel" } }] + }), + chunk({ + tool_calls: [{ index: 0, id: null, function: { name: null, arguments: "lo\"}" } }] + }), + { + id: "chatcmpl_nullable_delta_fields", + object: "chat.completion.chunk", + model: "gpt-4o-mini", + created: 1, + choices: [{ index: 0, delta: {}, finish_reason: "tool_calls" }] + }, + "[DONE]" + ])) + ) + )) + ) + + const partsChunk = yield* LanguageModel.streamText({ + prompt: "use the tool", + toolkit: TestToolkit, + disableToolCallResolution: true + }).pipe( + Stream.runCollect, + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(TestToolkitLayer), + Effect.provide(layer) + ) + + const parts = globalThis.Array.from(partsChunk) + const text = parts + .filter((part) => part.type === "text-delta") + .map((part) => part.delta) + .join("") + const toolCall = parts.find((part) => part.type === "tool-call") + + assert.strictEqual(text, "Hello") + assert.isDefined(toolCall) + if (toolCall?.type !== "tool-call") { + return + } + assert.strictEqual(toolCall.id, "call_1") + assert.deepStrictEqual(toolCall.params, { input: "hello" }) + })) + it.effect("decodes streamed tool call params with the OpenAI codec", () => Effect.gen(function*() { const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( diff --git a/repos/effect/packages/ai/openai/CHANGELOG.md b/repos/effect/packages/ai/openai/CHANGELOG.md index 5bfd0f85f8..83d7e6618b 100644 --- a/repos/effect/packages/ai/openai/CHANGELOG.md +++ b/repos/effect/packages/ai/openai/CHANGELOG.md @@ -1,5 +1,104 @@ # @effect/ai-openai +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8400](https://github.com/Effect-TS/effect/pull/8400) [`cb85792`](https://github.com/Effect-TS/effect/commit/cb8579219677c49f5033e49ba72fbbab9db5de5c) Thanks @danieljvdm! - Accept web searches without an action, preserving incomplete calls as failed tool results. + The `action` field is now optional on generated web-search calls and tool parameters/results. + +- [#8363](https://github.com/Effect-TS/effect/pull/8363) [`1519fd7`](https://github.com/Effect-TS/effect/commit/1519fd7b21a890aa7e187bfd978520dbb66a2bd2) Thanks @dardanbujupaj! - Preserve tool call parameters for dynamic tools backed by raw JSON Schema. + +- [#8355](https://github.com/Effect-TS/effect/pull/8355) [`24fa0ab`](https://github.com/Effect-TS/effect/commit/24fa0ab30b34685ecea3cae387e43a1075ddc3ce) Thanks @tim-smart! - Retry an OpenAI websocket turn with the full prompt when the provider returns previous_response_not_found, and keep the socket when it is still open. +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- [#8236](https://github.com/Effect-TS/effect/pull/8236) [`90695b0`](https://github.com/Effect-TS/effect/commit/90695b0d5bee3aa33e67cbea1aa2e6298816a64f) Thanks @danieljvdm! - Support API sources in OpenAI web search. Narrow sources by `type` before reading `url` or `name`. +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/ai/openai/README.md b/repos/effect/packages/ai/openai/README.md index 2ef377e345..381f4e46f9 100644 --- a/repos/effect/packages/ai/openai/README.md +++ b/repos/effect/packages/ai/openai/README.md @@ -5,10 +5,21 @@ An [OpenAI](https://openai.com) provider for the Effect AI modules. Includes a t ## Installation ```sh -npm install effect@rc @effect/ai-openai@rc +npm install effect @effect/ai-openai ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/ai-openai) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/ai-openai): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/ai/openai/codegen.yaml b/repos/effect/packages/ai/openai/codegen.yaml index 05724087a2..4c9ed89bfc 100644 --- a/repos/effect/packages/ai/openai/codegen.yaml +++ b/repos/effect/packages/ai/openai/codegen.yaml @@ -4,6 +4,7 @@ output: src/Generated.ts name: OpenAiClient header: | /** + * @stability unstable * @since 1.0.0 */ patches: @@ -17,6 +18,10 @@ patches: - '[{"op":"add","path":"/components/schemas/PromptCacheRetentionEnum/enum/1","value":"in-memory"}]' - '[{"op":"replace","path":"/components/schemas/OpenAIFile/properties/expires_at","value":{"anyOf":[{"type":"integer","format":"unixtime","description":"The Unix timestamp (in seconds) for when the file will expire."},{"type":"null"}]}}]' - '[{"op":"add","path":"/components/schemas/OpenAIFile/properties/status_details/nullable","value":true}]' + # Remove when the upstream schema includes named API sources: https://github.com/openai/openai-openapi/issues/573 + - '[{"op":"replace","path":"/components/schemas/WebSearchActionSearch/properties/sources/items","value":{"anyOf":[{"type":"object","title":"Web search source","description":"A source used in the search.\n","properties":{"type":{"type":"string","enum":["url"],"description":"The type of source. Always `url`.\n","x-stainless-const":true},"url":{"type":"string","format":"uri","description":"The URL of the source.\n"}},"required":["type","url"]},{"type":"object","title":"Web search API source","description":"An API source used in the search.\n","properties":{"type":{"type":"string","enum":["api"],"description":"The type of source. Always `api`.\n","x-stainless-const":true},"name":{"type":"string","description":"The name of the API source, such as `oai-weather`, `oai-sports`, or `oai-finance`.\n"}},"required":["type","name"]}]}}]' + # OpenAI may omit action (https://github.com/openai/openai-openapi/issues/572) or report incomplete. + - '[{"op":"replace","path":"/components/schemas/WebSearchToolCall/required","value":["id","type","status"]},{"op":"replace","path":"/components/schemas/WebSearchToolCall/properties/status","value":{"type":"string","enum":["in_progress","searching","completed","failed","incomplete"],"description":"The status of the web search tool call.\n"}}]' # Add missing keepalive stream event support - '[{"op":"add","path":"/components/schemas/ResponseKeepAliveEvent","value":{"type":"object","title":"Keep alive","description":"A keepalive event emitted during long-running response streams.","properties":{"type":{"type":"string","enum":["keepalive"],"description":"The type of the keepalive event. Always `keepalive`.","default":"keepalive","x-stainless-const":true},"sequence_number":{"type":"integer","description":"The sequence number of this keepalive event."}},"required":["type","sequence_number"]}}]' - '[{"op":"add","path":"/components/schemas/ResponseStreamEvent/anyOf/-","value":{"$ref":"#/components/schemas/ResponseKeepAliveEvent"}}]' diff --git a/repos/effect/packages/ai/openai/package.json b/repos/effect/packages/ai/openai/package.json index 7c7554d3bc..63202ff94e 100644 --- a/repos/effect/packages/ai/openai/package.json +++ b/repos/effect/packages/ai/openai/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "An OpenAI provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/openai/src/Generated.ts b/repos/effect/packages/ai/openai/src/Generated.ts index d51b312440..4d5918bba1 100644 --- a/repos/effect/packages/ai/openai/src/Generated.ts +++ b/repos/effect/packages/ai/openai/src/Generated.ts @@ -1,17 +1,18 @@ /** + * @stability unstable * @since 1.0.0 */ import * as Data from "effect/Data" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" import type { SchemaError } from "effect/Schema" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import * as Sse from "effect/unstable/encoding/Sse" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientError from "effect/unstable/http/HttpClientError" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" // non-recursive definitions export type AddUploadPartRequest = { readonly "data": string } export const AddUploadPartRequest = Schema.Struct({ @@ -8405,13 +8406,15 @@ export const WebSearchLocation = Schema.Struct({ export type WebSearchToolCall = { readonly "id": string readonly "type": "web_search_call" - readonly "status": "in_progress" | "searching" | "completed" | "failed" - readonly "action": + readonly "status": "in_progress" | "searching" | "completed" | "failed" | "incomplete" + readonly "action"?: | { readonly "type": "search" readonly "query"?: string readonly "queries"?: ReadonlyArray - readonly "sources"?: ReadonlyArray<{ readonly "type": "url"; readonly "url": string }> + readonly "sources"?: ReadonlyArray< + { readonly "type": "url"; readonly "url": string } | { readonly "type": "api"; readonly "name": string } + > } | { readonly "type": "open_page"; readonly "url"?: string | null } | { readonly "type": "find_in_page"; readonly "url": string; readonly "pattern": string } @@ -8421,10 +8424,10 @@ export const WebSearchToolCall = Schema.Struct({ "type": Schema.Literal("web_search_call").annotate({ "description": "The type of the web search tool call. Always `web_search_call`.\n" }), - "status": Schema.Literals(["in_progress", "searching", "completed", "failed"]).annotate({ + "status": Schema.Literals(["in_progress", "searching", "completed", "failed", "incomplete"]).annotate({ "description": "The status of the web search tool call.\n" }), - "action": Schema.Union([ + "action": Schema.optionalKey(Schema.Union([ Schema.Struct({ "type": Schema.Literal("search").annotate({ "description": "The action type.\n" }), "query": Schema.optionalKey(Schema.String.annotate({ "description": "[DEPRECATED] The search query.\n" })), @@ -8436,10 +8439,18 @@ export const WebSearchToolCall = Schema.Struct({ ), "sources": Schema.optionalKey( Schema.Array( - Schema.Struct({ - "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), - "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) - }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }) + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), + "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) + }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }), + Schema.Struct({ + "type": Schema.Literal("api").annotate({ "description": "The type of source. Always `api`.\n" }), + "name": Schema.String.annotate({ + "description": "The name of the API source, such as `oai-weather`, `oai-sports`, or `oai-finance`.\n" + }) + }).annotate({ "title": "Web search API source", "description": "An API source used in the search.\n" }) + ]) ).annotate({ "title": "Web search sources", "description": "The sources used in the search.\n" }) ) }).annotate({ @@ -8471,7 +8482,7 @@ export const WebSearchToolCall = Schema.Struct({ "description": "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" }) - ], { mode: "oneOf" }) + ], { mode: "oneOf" })) }).annotate({ "title": "Web search tool call", "description": @@ -22767,13 +22778,15 @@ export type InputItem = | { readonly "id": string readonly "type": "web_search_call" - readonly "status": "in_progress" | "searching" | "completed" | "failed" - readonly "action": + readonly "status": "in_progress" | "searching" | "completed" | "failed" | "incomplete" + readonly "action"?: | { readonly "type": "search" readonly "query"?: string readonly "queries"?: ReadonlyArray - readonly "sources"?: ReadonlyArray<{ readonly "type": "url"; readonly "url": string }> + readonly "sources"?: ReadonlyArray< + { readonly "type": "url"; readonly "url": string } | { readonly "type": "api"; readonly "name": string } + > } | { readonly "type": "open_page"; readonly "url"?: string | null } | { readonly "type": "find_in_page"; readonly "url": string; readonly "pattern": string } @@ -23052,10 +23065,10 @@ export const InputItem = Schema.Union([ "type": Schema.Literal("web_search_call").annotate({ "description": "The type of the web search tool call. Always `web_search_call`.\n" }), - "status": Schema.Literals(["in_progress", "searching", "completed", "failed"]).annotate({ + "status": Schema.Literals(["in_progress", "searching", "completed", "failed", "incomplete"]).annotate({ "description": "The status of the web search tool call.\n" }), - "action": Schema.Union([ + "action": Schema.optionalKey(Schema.Union([ Schema.Struct({ "type": Schema.Literal("search").annotate({ "description": "The action type.\n" }), "query": Schema.optionalKey(Schema.String.annotate({ "description": "[DEPRECATED] The search query.\n" })), @@ -23067,10 +23080,19 @@ export const InputItem = Schema.Union([ ), "sources": Schema.optionalKey( Schema.Array( - Schema.Struct({ - "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), - "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) - }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }) + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), + "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) + }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }), + Schema.Struct({ + "type": Schema.Literal("api").annotate({ "description": "The type of source. Always `api`.\n" }), + "name": Schema.String.annotate({ + "description": + "The name of the API source, such as `oai-weather`, `oai-sports`, or `oai-finance`.\n" + }) + }).annotate({ "title": "Web search API source", "description": "An API source used in the search.\n" }) + ]) ).annotate({ "title": "Web search sources", "description": "The sources used in the search.\n" }) ) }).annotate({ @@ -23102,7 +23124,7 @@ export const InputItem = Schema.Union([ "description": "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" }) - ], { mode: "oneOf" }) + ], { mode: "oneOf" })) }).annotate({ "title": "Web search tool call", "description": "Content item used to generate a response.\n" }), Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" })), diff --git a/repos/effect/packages/ai/openai/src/OpenAiClient.ts b/repos/effect/packages/ai/openai/src/OpenAiClient.ts index 21b4f38e6b..0238e5152a 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiClient.ts +++ b/repos/effect/packages/ai/openai/src/OpenAiClient.ts @@ -6,14 +6,24 @@ * WebSocket response streams, and embeddings, and maps transport or decoding * failures into `AiError`. * + * @stability unstable * @since 4.0.0 */ +import * as AiError from "effect/ai/AiError" +import * as ResponseIdTracker from "effect/ai/ResponseIdTracker" import * as Array from "effect/Array" +import * as Cause from "effect/Cause" import type * as Config from "effect/Config" import * as Context from "effect/Context" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" import { identity } from "effect/Function" import * as Function from "effect/Function" +import * as Headers from "effect/http/Headers" +import * as HttpBody from "effect/http/HttpBody" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Predicate from "effect/Predicate" import * as Queue from "effect/Queue" @@ -22,16 +32,8 @@ import * as Redacted from "effect/Redacted" import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" import * as Semaphore from "effect/Semaphore" +import * as Socket from "effect/socket/Socket" import * as Stream from "effect/Stream" -import * as AiError from "effect/unstable/ai/AiError" -import * as ResponseIdTracker from "effect/unstable/ai/ResponseIdTracker" -import * as Sse from "effect/unstable/encoding/Sse" -import * as Headers from "effect/unstable/http/Headers" -import * as HttpBody from "effect/unstable/http/HttpBody" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" -import * as Socket from "effect/unstable/socket/Socket" import * as Errors from "./internal/errors.ts" import { OpenAiConfig } from "./OpenAiConfig.ts" import * as OpenAiSchema from "./OpenAiSchema.ts" @@ -47,6 +49,7 @@ import * as OpenAiSchema from "./OpenAiSchema.ts" * * Provides the configured HTTP client plus helpers for Responses API calls, streaming Responses events, and embeddings. Transport and schema decoding failures are mapped to `AiError`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -103,6 +106,7 @@ export interface Service { * @see {@link layer} for providing a client from explicit options * @see {@link layerConfig} for providing a client from `Config` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -117,6 +121,7 @@ export class OpenAiClient extends Context.Service()( /** * Options for configuring the OpenAI client. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -185,6 +190,7 @@ const withRedactedHeaders = Effect.updateService( * @see {@link layer} for providing this client from explicit options * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -350,6 +356,7 @@ export const make = Effect.fnUntraced( * @see {@link make} for constructing the client service effectfully * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -373,6 +380,7 @@ export const layer = (options: Options): Layer.Layer Effect.Effect - readonly incoming: Queue.Dequeue - } - > = yield* RcRef.make({ + // The response currently in flight, or undefined between turns. Each turn + // owns its queue; the reader ends or fails it, so a turn that is still + // current when its consumer leaves was abandoned mid-response. + type Turn = Queue.Queue + type SocketConnection = { + readonly send: (message: typeof OpenAiSchema.CreateResponse.Encoded) => Effect.Effect + current: Turn | undefined + } + + const queueRef: RcRef.RcRef = yield* RcRef.make({ idleTimeToLive: 60_000, acquire: Effect.gen(function*() { const scope = yield* Effect.scope @@ -519,68 +533,98 @@ const makeSocket = Effect.gen(function*() { return Effect.void }) - const incoming = yield* Queue.unbounded() - const send = (message: typeof OpenAiSchema.CreateResponse.Encoded) => - writer.write(JSON.stringify({ - type: "response.create", - ...message - })).pipe( - Effect.mapError((_error) => - AiError.make({ - module: "OpenAiClient", - method: "createResponseStream", - reason: new AiError.NetworkError({ - reason: "TransportError", - request: { - method: "POST", - url: request.url, - urlParams: [], - hash: undefined, - headers: request.headers - }, - description: "Failed to send message over WebSocket" - }) - }) - ) - ) - - const handleMessage = (msg: Uint8Array | string): Effect.Effect | undefined => { - const text = typeof msg === "string" ? msg : decoder.decode(msg) - try { - const event = decodeEvent(text) - if (event.type === "error" && "status" in event) { - const status = Number(event.status) - const error = "error" in event ? event.error as typeof ErrorEvent.Type.error : event - const json = JSON.stringify(error) - return Effect.fail( + const connection: SocketConnection = { + current: undefined, + send: (message) => + writer.write(JSON.stringify({ + type: "response.create", + ...message + })).pipe( + Effect.mapError((_error) => AiError.make({ module: "OpenAiClient", method: "createResponseStream", - reason: AiError.reasonFromHttpStatus({ - description: json, - status: isNaN(status) ? - Object.hasOwn(errorTypeToStatus, error.type) - ? errorTypeToStatus[error.type] - : 500 : - status, - metadata: error as any, - http: { - body: json, - request: { - method: "POST", - url: request.url, - urlParams: [], - hash: undefined, - headers: request.headers - } - } + reason: new AiError.NetworkError({ + reason: "TransportError", + request: { + method: "POST", + url: request.url, + urlParams: [], + hash: undefined, + headers: request.headers + }, + description: "Failed to send message over WebSocket" }) }) ) + ) + } + + const handleMessage = (msg: Uint8Array | string): void => { + const turn = connection.current + if (turn === undefined) return + let event: typeof AllEvents.Type + try { + event = decodeEvent(typeof msg === "string" ? msg : decoder.decode(msg)) + } catch { + return + } + if (event.type === "error") { + const status = "status" in event ? Number(event.status) : NaN + const error = ("error" in event ? event.error : event) as { + readonly type?: string + readonly code?: string + readonly message: string + } + const errorType = error.code ?? + (error.type === "api_error" && /^gRPC error: Response with id=\S+ not found$/.test(error.message) + ? "previous_response_not_found" + : error.type ?? "unknown") + const json = JSON.stringify(error) + // LanguageModel retries `previous_response_not_found` with the full + // prompt on this socket. Other errors leave the turn current so its + // finalizer invalidates the connection, preserving the existing policy. + if (errorType === "previous_response_not_found") { + connection.current = undefined } - Queue.offerUnsafe(incoming, event) - } catch {} - return undefined + Queue.failCauseUnsafe( + turn, + Cause.fail(AiError.make({ + module: "OpenAiClient", + method: "createResponseStream", + reason: AiError.reasonFromHttpStatus({ + description: json, + status: errorType === "previous_response_not_found" + ? 400 + : isNaN(status) + ? Object.hasOwn(errorTypeToStatus, errorType) + ? errorTypeToStatus[errorType] + : 500 + : status, + metadata: error as any, + http: { + body: json, + request: { + method: "POST", + url: request.url, + urlParams: [], + hash: undefined, + headers: request.headers + } + } + }) + })) + ) + return + } + Queue.offerUnsafe(turn, event) + if ( + event.type === "response.completed" || event.type === "response.incomplete" || + event.type === "response.failed" + ) { + connection.current = undefined + Queue.endUnsafe(turn) + } } yield* Effect.gen(function*() { @@ -588,10 +632,7 @@ const makeSocket = Effect.gen(function*() { while (true) { const messages = yield* pull for (let i = 0; i < messages.length; i++) { - const result = handleMessage(messages[i]) - if (result !== undefined) { - yield* result - } + handleMessage(messages[i]) } } }).pipe( @@ -612,14 +653,16 @@ const makeSocket = Effect.gen(function*() { description: error.message }) })), - Effect.catchCause((cause) => Queue.failCause(incoming, cause)), + Effect.catchCause((cause) => + connection.current === undefined ? Effect.void : Queue.failCause(connection.current, cause) + ), Effect.ensuring(Effect.forkIn(RcRef.invalidate(queueRef), socketScope, { startImmediately: true })), Effect.forkScoped({ startImmediately: true }) ) - return { send, incoming } as const + return connection }) }) @@ -639,24 +682,22 @@ const makeSocket = Effect.gen(function*() { () => semaphore.release(1), { interruptible: true } ) - const { send, incoming } = yield* RcRef.get(queueRef) - let done = false + const connection = yield* RcRef.get(queueRef) + const turn: Turn = yield* Queue.unbounded() + connection.current = turn + // Leaving while the response is still streaming makes the socket + // unusable for the next turn. yield* Scope.addFinalizerExit( scope, - () => done ? Effect.void : RcRef.invalidate(queueRef) + () => connection.current === turn ? RcRef.invalidate(queueRef) : Effect.void ) - yield* send(options).pipe( + yield* connection.send(options).pipe( Effect.forkScoped({ startImmediately: true }) ) - return Stream.fromQueue(incoming).pipe( - Stream.takeUntil((e) => { - done = e.type === "response.completed" || e.type === "response.incomplete" || e.type === "response.failed" - return done - }) - ) + return Stream.fromQueue(turn) })) return Effect.succeed([ @@ -670,12 +711,14 @@ const makeSocket = Effect.gen(function*() { }) const ErrorEvent = Schema.Struct({ - type: Schema.Literal("error"), - status: Schema.Int.pipe( - Schema.withDecodingDefault(Effect.succeed(500)) + type: Schema.Literal("error").pipe( + Schema.withDecodingDefault(Effect.succeed("error" as const)) ), + status: Schema.optionalKey(Schema.Int), + // xAI sends `code` in place of `type`, e.g. `previous_response_not_found` error: Schema.Struct({ - type: Schema.String, + type: Schema.optional(Schema.String), + code: Schema.optional(Schema.String), message: Schema.String }) }) @@ -712,6 +755,7 @@ const decodeEvent = Schema.decodeUnknownSync(Schema.fromJsonString(AllEvents)) * @see {@link layerWebSocketMode} for providing WebSocket mode through a layer * @see {@link OpenAiSocket} for direct access to the WebSocket-backed streaming service * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -749,6 +793,7 @@ export const withWebSocketMode = ( * * @see {@link withWebSocketMode} for enabling WebSocket mode around a single effect * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts b/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts index 6df83c43a4..1dbf87adfc 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts +++ b/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts @@ -1,4 +1,5 @@ /** + * @stability unstable * @since 4.0.0 */ import * as Array from "effect/Array" @@ -7,12 +8,12 @@ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { identity } from "effect/Function" import * as Function from "effect/Function" +import * as Headers from "effect/http/Headers" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientRequest from "effect/http/HttpClientRequest" import * as Layer from "effect/Layer" import * as Predicate from "effect/Predicate" import * as Redacted from "effect/Redacted" -import * as Headers from "effect/unstable/http/Headers" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" import * as Generated from "./Generated.ts" import { OpenAiConfig } from "./OpenAiConfig.ts" @@ -24,6 +25,7 @@ import { OpenAiConfig } from "./OpenAiConfig.ts" * Service identifier for the generated OpenAI client. * * @since 4.0.0 + * @stability unstable * @category service */ export class OpenAiClientGenerated extends Context.Service()( @@ -38,6 +40,7 @@ export class OpenAiClientGenerated extends Context.Service => @@ -150,6 +155,7 @@ export const layer = (options: Options): Layer.Layer >()("@effect/ai-openai/OpenAiLanguageModel/Config") {} @@ -125,10 +138,11 @@ export class Config extends Context.Service< // Provider Options / Metadata // ============================================================================= -declare module "effect/unstable/ai/Prompt" { +declare module "effect/ai/Prompt" { /** * OpenAI-specific options for system messages. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -150,6 +164,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-specific options for file prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -168,6 +183,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-specific options for reasoning prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -192,6 +208,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-specific options for assistant tool-call prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -218,6 +235,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-specific options for tool-result prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -244,6 +262,7 @@ declare module "effect/unstable/ai/Prompt" { /** * OpenAI-specific options for text prompt parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -275,10 +294,11 @@ declare module "effect/unstable/ai/Prompt" { } } -declare module "effect/unstable/ai/Response" { +declare module "effect/ai/Response" { /** * OpenAI metadata attached to a complete text response part. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -311,6 +331,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata emitted when a streamed text part starts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -329,6 +350,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata emitted when a streamed text part ends. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -351,6 +373,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata attached to a complete reasoning response part. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -373,6 +396,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata emitted when a streamed reasoning part starts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -395,6 +419,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata emitted for a streamed reasoning delta. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -413,6 +438,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata emitted when a streamed reasoning part ends. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -435,6 +461,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata attached to tool-call response parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -453,6 +480,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata attached to document source citations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -509,6 +537,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata attached to URL source citations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -535,6 +564,7 @@ declare module "effect/unstable/ai/Response" { /** * OpenAI metadata attached to finish response parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -567,6 +597,7 @@ declare module "effect/unstable/ai/Response" { * @see {@link layer} for creating a `LanguageModel.LanguageModel` layer directly * @see {@link make} for constructing the language model service effectfully * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -606,6 +637,7 @@ export const model = ( * @see {@link layer} for providing the service as a `Layer` * @see {@link model} for creating a model descriptor for `Effect.provide` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -644,7 +676,12 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig config, options }) - const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } = config + const { + fileIdPrefixes: _fip, + strictJsonSchema: _sjs, + useItemReferences: _uir, + ...apiConfig + } = config const request: Mutable = { ...apiConfig, input: messages, @@ -726,6 +763,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig * @see {@link model} for creating a model descriptor for `Effect.provide` * @see {@link withConfigOverride} for scoped request configuration overrides * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -753,6 +791,7 @@ export const layer = (options: { * * @see {@link Config} for the scoped configuration service consumed by this function * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -801,8 +840,12 @@ const prepareMessages = Effect.fnUntraced( readonly toolNameMapper: Tool.NameMapper }): Effect.fn.Return, AiError.AiError> { const processedApprovalIds = new Set() + const websocketMode = Option.isSome(yield* Effect.serviceOption(OpenAiSocket)) const hasConversation = Predicate.isNotNullish(config.conversation) + const useItemReferences = config.store === true && + config.useItemReferences !== false && + options.incrementalFallback !== true // Provider-Defined Tools const applyPatchTool = options.tools.find((tool): tool is ReturnType => @@ -828,7 +871,7 @@ const prepareMessages = Effect.fnUntraced( if (Predicate.isNotUndefined(config.top_logprobs)) { include.add("message.output_text.logprobs") } - if (config.store === false && capabilities.isReasoningModel) { + if ((websocketMode || !useItemReferences) && capabilities.isReasoningModel) { include.add("reasoning.encrypted_content") } if (codeInterpreterTool) { @@ -882,7 +925,7 @@ const prepareMessages = Effect.fnUntraced( const imageUrl = part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array - ? `data:${mediaType};base64,${Encoding.encodeBase64(part.data)}` + ? `data:${mediaType};base64,${Base64.encode(part.data)}` : /^(data:|https?:\/\/)/i.test(part.data) ? part.data : `data:${mediaType};base64,${part.data}` @@ -898,7 +941,7 @@ const prepareMessages = Effect.fnUntraced( } if (part.data instanceof Uint8Array) { - const base64 = Encoding.encodeBase64(part.data) + const base64 = Base64.encode(part.data) const fileName = part.fileName ?? `part-${index}.pdf` const fileData = `data:application/pdf;base64,${base64}` content.push({ type: "input_file", filename: fileName, file_data: fileData }) @@ -925,6 +968,18 @@ const prepareMessages = Effect.fnUntraced( const reasoningMessages: Record> = Object .create(null) + const providerExecutedParts = message.content.filter((part) => + (part.type === "tool-call" || part.type === "tool-result") && part.providerExecuted + ) + const unreplayableProviderItem = (name: string, id: string) => + AiError.make({ + module: "OpenAiLanguageModel", + method: "prepareMessages", + reason: new AiError.InvalidRequestError({ + description: `Cannot replay provider-executed tool item '${name}' ('${id}') losslessly` + }) + }) + for (const part of message.content) { switch (part.type) { case "text": { @@ -936,7 +991,7 @@ const prepareMessages = Effect.fnUntraced( break } - if (config.store === true && Predicate.isNotNull(id)) { + if (useItemReferences && Predicate.isNotNull(id)) { messages.push({ type: "item_reference", id }) break } @@ -968,7 +1023,7 @@ const prepareMessages = Effect.fnUntraced( if (Predicate.isNotNull(id)) { const message = reasoningMessages[id] - if (config.store === true) { + if (useItemReferences) { // Use item references to refer to reasoning (single reference) // when the first part is encountered if (Predicate.isUndefined(message)) { @@ -1022,12 +1077,101 @@ const prepareMessages = Effect.fnUntraced( break } - if (config.store && Predicate.isNotNull(id)) { + if (useItemReferences && Predicate.isNotNull(id)) { messages.push({ type: "item_reference", id }) break } if (part.providerExecuted) { + if ( + config.store !== true && options.incrementalFallback !== true + ) { + break + } + const result = providerExecutedParts.find((candidate) => + candidate.type === "tool-result" && candidate.id === part.id && candidate.name === part.name + ) + if (Predicate.isUndefined(result) || result.type !== "tool-result") { + return yield* unreplayableProviderItem(part.name, part.id) + } + if ( + Predicate.hasProperty(result.result, "type") && + result.result.type === "execution-denied" + ) { + break + } + + const itemId = getItemId(part) ?? getItemId(result) ?? part.id + switch (part.name) { + case "OpenAiWebSearch": + case "OpenAiWebSearchPreview": { + if (!Predicate.hasProperty(result.result, "status")) { + return yield* unreplayableProviderItem(part.name, part.id) + } + messages.push({ + type: "web_search_call", + id: itemId, + ...(Predicate.hasProperty(result.result, "action") + ? { action: result.result.action } + : undefined), + status: result.result.status as string + }) + break + } + case "OpenAiCodeInterpreter": { + if ( + !Predicate.hasProperty(part.params, "code") || + !Predicate.hasProperty(part.params, "container_id") || + !Predicate.hasProperty(result.result, "status") || + !Predicate.hasProperty(result.result, "outputs") + ) { + return yield* unreplayableProviderItem(part.name, part.id) + } + messages.push({ + type: "code_interpreter_call", + id: itemId, + code: part.params.code as string | null, + container_id: part.params.container_id as string, + status: result.result.status as any, + outputs: result.result.outputs as any + }) + break + } + case "OpenAiFileSearch": { + if ( + !Predicate.hasProperty(result.result, "status") || + !Predicate.hasProperty(result.result, "queries") || + !Predicate.hasProperty(result.result, "results") + ) { + return yield* unreplayableProviderItem(part.name, part.id) + } + messages.push({ + type: "file_search_call", + id: itemId, + status: result.result.status as string, + queries: result.result.queries as Array, + results: result.result.results + }) + break + } + case "OpenAiImageGeneration": { + if (!Predicate.hasProperty(result.result, "result")) { + return yield* unreplayableProviderItem(part.name, part.id) + } + messages.push({ + type: "image_generation_call", + id: itemId, + result: result.result.result as string | null, + status: (Predicate.hasProperty(result.result, "status") + ? result.result.status + : "completed") as any + }) + break + } + default: { + return yield* unreplayableProviderItem(part.name, part.id) + } + } break } @@ -1110,9 +1254,19 @@ const prepareMessages = Effect.fnUntraced( break } - if (config.store === true) { + if (useItemReferences) { const id = getItemId(part) ?? part.id messages.push({ type: "item_reference", id }) + } else if ( + part.providerExecuted && + (config.store === true || options.incrementalFallback === true) + ) { + const call = providerExecutedParts.find((candidate) => + candidate.type === "tool-call" && candidate.id === part.id && candidate.name === part.name + ) + if (Predicate.isUndefined(call)) { + return yield* unreplayableProviderItem(part.name, part.id) + } } } } @@ -1130,7 +1284,7 @@ const prepareMessages = Effect.fnUntraced( processedApprovalIds.add(part.approvalId) - if (config.store === true) { + if (useItemReferences) { messages.push({ type: "item_reference", id: part.approvalId }) } @@ -1656,7 +1810,7 @@ const makeResponse = Effect.fnUntraced( type: "tool-call", id: part.id, name: toolName, - params: webSearchTool?.name === "OpenAiWebSearchPreview" + params: webSearchTool?.name === "OpenAiWebSearchPreview" || Predicate.isUndefined(part.action) ? {} : { action: part.action }, providerExecuted: true @@ -1666,7 +1820,10 @@ const makeResponse = Effect.fnUntraced( id: part.id, name: toolName, isFailure: part.status !== "completed", - result: { action: part.action, status: part.status }, + result: { + ...(Predicate.isUndefined(part.action) ? undefined : { action: part.action }), + status: part.status + }, providerExecuted: true }) break @@ -1710,6 +1867,7 @@ const makeStreamResponse = Effect.fnUntraced( IdGenerator.IdGenerator > { const idGenerator = yield* IdGenerator.IdGenerator + const websocketMode = Option.isSome(yield* Effect.serviceOption(OpenAiSocket)) const approvalRequests = getApprovalRequestIdMapping(options.prompt) const streamApprovalRequests = new Map() @@ -2354,7 +2512,7 @@ const makeStreamResponse = Effect.fnUntraced( type: "tool-call", id: event.item.id, name: toolName, - params: { action: event.item.action }, + params: Predicate.isUndefined(event.item.action) ? {} : { action: event.item.action }, providerExecuted: true }) } @@ -2363,7 +2521,10 @@ const makeStreamResponse = Effect.fnUntraced( id: event.item.id, name: toolName, isFailure: event.item.status !== "completed", - result: { action: event.item.action, status: event.item.status }, + result: { + ...(Predicate.isUndefined(event.item.action) ? undefined : { action: event.item.action }), + status: event.item.status + }, providerExecuted: true }) break @@ -2627,9 +2788,9 @@ const makeStreamResponse = Effect.fnUntraced( case "response.reasoning_summary_part.done": { const reasoningPart = getOrCreateReasoningPart(event.item_id) - // When OpenAI stores message data, we can immediately conclude the - // reasoning part given that we do not need the encrypted content - if (config.store === true) { + // HTTP requests using stored item references do not need encrypted + // content, so the reasoning part can be concluded immediately. + if (!websocketMode && config.store === true && config.useItemReferences !== false) { parts.push({ type: "reasoning-end", id: `${event.item_id}:${event.summary_index}`, @@ -3206,6 +3367,10 @@ const transformToolCallParams = Effect.fnUntraced(function*( * @see {@link Response} for complete response objects carried by lifecycle events * @see {@link UnknownResponseStreamEvent} for the fallback shape for future event types * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1178,6 +1244,7 @@ export const ResponseStreamEvent = Schema.Union([ * Includes known response stream events plus a fallback shape for unknown future * event types. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1201,6 +1268,7 @@ export type ResponseStreamEvent = typeof ResponseStreamEvent.Type * Callers that need numeric vectors must account for string embeddings, such as * base64-encoded embeddings returned for string encoding formats. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1221,6 +1289,7 @@ export const Embedding = Schema.Struct({ * Contains the item index and embedding payload. The embedding payload may be a * numeric vector or a string. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1246,6 +1315,7 @@ export type Embedding = typeof Embedding.Type * provider-side constraints such as non-empty input, integer token ids, input * size limits, positive dimensions, and model-specific dimension support. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1265,6 +1335,7 @@ export const CreateEmbeddingRequest = Schema.Struct({ /** * Request payload sent to the OpenAI embeddings endpoint. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1293,6 +1364,7 @@ export type CreateEmbeddingRequest = typeof CreateEmbeddingRequest.Type * @see {@link CreateEmbeddingRequest} for the request schema sent to the embeddings endpoint * @see {@link Embedding} for individual embedding items in the response * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1320,6 +1392,7 @@ export const CreateEmbeddingResponse = Schema.Struct({ * Contains embedding items, the model name, optional list marker, and optional * token usage counts. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts b/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts index 6af5c9b4fb..f5c1e74005 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts +++ b/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts @@ -7,11 +7,11 @@ * * @since 4.0.0 */ +import * as Telemetry from "effect/ai/Telemetry" import { dual } from "effect/Function" import * as String from "effect/String" import type { Span } from "effect/Tracer" import type { Simplify } from "effect/Types" -import * as Telemetry from "effect/unstable/ai/Telemetry" /** * The attributes used to describe telemetry in the context of Generative diff --git a/repos/effect/packages/ai/openai/src/OpenAiTool.ts b/repos/effect/packages/ai/openai/src/OpenAiTool.ts index 2cf230c603..b74dec8177 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiTool.ts +++ b/repos/effect/packages/ai/openai/src/OpenAiTool.ts @@ -5,15 +5,17 @@ * shell-like local tools, including their provider names, configuration * arguments, call parameters, success schemas, and handler requirements. * + * @stability unstable * @since 4.0.0 */ +import * as Tool from "effect/ai/Tool" import * as Schema from "effect/Schema" -import * as Tool from "effect/unstable/ai/Tool" import * as Generated from "./Generated.ts" /** * Union of all OpenAI provider-defined tools. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -39,6 +41,7 @@ export type OpenAiTool = * delete, or update operations that your application executes through a local * handler. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -72,6 +75,7 @@ export const ApplyPatch = Tool.providerDefined({ * status indicates success; omitted statuses default to it. Other statuses * produce failure results that preserve any outputs. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -115,6 +119,7 @@ export const CodeInterpreter = Tool.providerDefined({ * successful; all other statuses produce failure results, preserving partial * matches. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -160,6 +165,7 @@ export const FileSearch = Tool.providerDefined({ * with a status other than `completed` are failures that include `status`. * Omitted statuses default to `completed`. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -206,6 +212,7 @@ export const ImageGeneration = Tool.providerDefined({ * handler-required, so applications must provide the command execution policy * and implementation. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -251,6 +258,7 @@ const McpResultFields = { * This schema leaves both `server_url` and `connector_id` optional, but OpenAI * may require a server URL or connector id for a usable MCP tool configuration. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -289,6 +297,7 @@ export const Mcp = Tool.providerDefined({ * handler-required, so applications must provide the command execution policy * and implementation. * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -326,11 +335,14 @@ const WebSearchFailure = Schema.Struct({ * **Details** * * The tool accepts optional filters, user location, and search context size. - * Results preserve the action and status. Only `completed` is successful; - * all other statuses produce failure results. + * Results include status and, when available, action. Only `completed` succeeds; + * other statuses produce failure results. + * Narrow search sources by `type` to read `url` for URL sources or `name` + * for API sources. * * @see {@link WebSearchPreview} for the preview web search provider tool * + * @stability unstable * @category tools * @since 4.0.0 */ @@ -360,11 +372,14 @@ export const WebSearch = Tool.providerDefined({ * **Details** * * The preview tool accepts optional user location and search context size. - * Results preserve the action and status. Only `completed` is successful; - * all other statuses produce failure results. + * Results include status and, when available, action. Only `completed` succeeds; + * other statuses produce failure results. + * Narrow search sources by `type` to read `url` for URL sources or `name` + * for API sources. * * @see {@link WebSearch} for the stable web search provider tool * + * @stability unstable * @category tools * @since 4.0.0 */ diff --git a/repos/effect/packages/ai/openai/src/index.ts b/repos/effect/packages/ai/openai/src/index.ts index b1758b6171..77389e360e 100644 --- a/repos/effect/packages/ai/openai/src/index.ts +++ b/repos/effect/packages/ai/openai/src/index.ts @@ -5,16 +5,19 @@ // @barrel: Auto-generated exports. Do not edit manually. /** + * @stability unstable * @since 1.0.0 */ export * as Generated from "./Generated.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiClient from "./OpenAiClient.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiClientGenerated from "./OpenAiClientGenerated.ts" @@ -25,6 +28,7 @@ export * as OpenAiClientGenerated from "./OpenAiClientGenerated.ts" export * as OpenAiConfig from "./OpenAiConfig.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts" @@ -35,11 +39,13 @@ export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts" export * as OpenAiError from "./OpenAiError.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiLanguageModel from "./OpenAiLanguageModel.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiSchema from "./OpenAiSchema.ts" @@ -50,6 +56,7 @@ export * as OpenAiSchema from "./OpenAiSchema.ts" export * as OpenAiTelemetry from "./OpenAiTelemetry.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenAiTool from "./OpenAiTool.ts" diff --git a/repos/effect/packages/ai/openai/src/internal/errors.ts b/repos/effect/packages/ai/openai/src/internal/errors.ts index fc93181b21..4e2ec36aa3 100644 --- a/repos/effect/packages/ai/openai/src/internal/errors.ts +++ b/repos/effect/packages/ai/openai/src/internal/errors.ts @@ -1,17 +1,17 @@ +import * as AiError from "effect/ai/AiError" +import type * as Response from "effect/ai/Response" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" +import type * as Sse from "effect/encoding/Sse" import { dual } from "effect/Function" +import type * as HttpClientError from "effect/http/HttpClientError" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import type * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Number from "effect/Number" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" import * as Redactable from "effect/Redactable" import * as Schema from "effect/Schema" -import * as AiError from "effect/unstable/ai/AiError" -import type * as Response from "effect/unstable/ai/Response" -import type * as Sse from "effect/unstable/encoding/Sse" -import type * as HttpClientError from "effect/unstable/http/HttpClientError" -import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import type { OpenAiErrorMetadata } from "../OpenAiError.ts" // ============================================================================= diff --git a/repos/effect/packages/ai/openai/src/internal/utilities.ts b/repos/effect/packages/ai/openai/src/internal/utilities.ts index 26cf670c31..7651401057 100644 --- a/repos/effect/packages/ai/openai/src/internal/utilities.ts +++ b/repos/effect/packages/ai/openai/src/internal/utilities.ts @@ -1,4 +1,4 @@ -import type * as Response from "effect/unstable/ai/Response" +import type * as Response from "effect/ai/Response" const finishReasonMap: Record = { content_filter: "content-filter", diff --git a/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts b/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts index c20107c5c1..8440e4893b 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts +++ b/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts @@ -4,13 +4,15 @@ import * as Errors from "@effect/ai-openai/internal/errors" import * as OpenAiClient from "@effect/ai-openai/OpenAiClient" import * as OpenAiClientGenerated from "@effect/ai-openai/OpenAiClientGenerated" import * as OpenAiConfig from "@effect/ai-openai/OpenAiConfig" +import * as OpenAiLanguageModel from "@effect/ai-openai/OpenAiLanguageModel" import { assert, describe, it } from "@effect/vitest" import { Config, ConfigProvider, Context, Effect, Layer, Redacted, Schema, Stream } from "effect" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientError from "effect/unstable/http/HttpClientError" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" -import * as Socket from "effect/unstable/socket/Socket" +import { Chat } from "effect/ai" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import * as Socket from "effect/socket/Socket" import { WS } from "vitest-websocket-mock" describe("OpenAiClient", () => { @@ -527,11 +529,7 @@ describe("OpenAiClient", () => { Effect.gen(function*() { const server = yield* Effect.acquireRelease( Effect.sync(() => new WS("wss://api.openai.com/v1/responses", { jsonProtocol: true })), - (server) => - Effect.sync(() => { - server.close() - WS.clean() - }) + (server) => Effect.sync(() => server.close()) ) const event = { type: "response.failed", @@ -559,6 +557,209 @@ describe("OpenAiClient", () => { assert.strictEqual(result._tag, "Some") })) + it.live("replaces the WebSocket after non-retryable error events", () => + Effect.gen(function*() { + const server = yield* Effect.acquireRelease( + Effect.sync(() => new WS("wss://socket-errors.test/v1/responses", { jsonProtocol: true })), + (server) => Effect.sync(() => server.close()) + ) + let connections = 0 + + yield* OpenAiClient.withWebSocketMode( + Effect.gen(function*() { + const client = yield* OpenAiClient.OpenAiClient + const [, failedStream] = yield* client.createResponseStream({ model: "gpt-4o", input: "first" }) + yield* Effect.all([ + Stream.runDrain(failedStream).pipe(Effect.flip), + nextWebSocketCreate(server).pipe( + Effect.tap(() => + Effect.sync(() => + server.send({ + type: "error", + code: "invalid_request_error", + message: "bad", + param: null + }) + ) + ) + ) + ], { concurrency: "unbounded" }) + + const [, secondFailedStream] = yield* client.createResponseStream({ model: "gpt-4o", input: "second" }) + yield* Effect.all([ + Stream.runDrain(secondFailedStream).pipe(Effect.flip), + nextWebSocketCreate(server).pipe( + Effect.tap(() => + Effect.sync(() => + server.send({ + error: { + message: "gRPC error: permission denied", + type: "api_error" + } + }) + ) + ) + ) + ], { concurrency: "unbounded" }) + + const [, thirdFailedStream] = yield* client.createResponseStream({ model: "gpt-4o", input: "third" }) + const [error] = yield* Effect.all([ + Stream.runDrain(thirdFailedStream).pipe(Effect.flip), + nextWebSocketCreate(server).pipe( + Effect.tap(() => + Effect.sync(() => + server.send({ + error: { + message: "bad request", + type: "invalid_request_error" + } + }) + ) + ) + ) + ], { concurrency: "unbounded" }) + assert.strictEqual(error.reason._tag, "InvalidRequestError") + + const [, nextStream] = yield* client.createResponseStream({ model: "gpt-4o", input: "fourth" }) + yield* Effect.all([ + Stream.runDrain(nextStream), + nextWebSocketCreate(server).pipe( + Effect.tap(() => Effect.sync(() => sendWebSocketCompleted(server, "resp_4", "msg_4", "ok"))) + ) + ], { concurrency: "unbounded" }) + }) + ).pipe( + Effect.provide(OpenAiClient.layer({ + apiKey: Redacted.make("sk-test"), + apiUrl: "https://socket-errors.test/v1" + })), + Effect.provideService(Socket.WebSocketConstructor, (url) => { + connections++ + return new globalThis.WebSocket(url) + }), + Effect.provideService(HttpClient.HttpClient, HttpClient.make(() => Effect.die("unexpected http"))), + Effect.timeout("5 seconds") + ) + + assert.strictEqual(connections, 4) + })) + + it.live("retries an untagged xAI response-not-found error with the full prompt", () => + Effect.gen(function*() { + const server = yield* Effect.acquireRelease( + Effect.sync(() => new WS("wss://previous-response.test/v1/responses", { jsonProtocol: true })), + (server) => Effect.sync(() => server.close()) + ) + + const drive = Effect.gen(function*() { + const chat = yield* Chat.empty + yield* chat.streamText({ prompt: "hello" }).pipe(Stream.runDrain) + yield* chat.streamText({ prompt: "again" }).pipe(Stream.runDrain) + yield* chat.streamText({ prompt: "later" }).pipe(Stream.runDrain) + }).pipe( + OpenAiClient.withWebSocketMode, + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { store: true })), + Effect.provide(OpenAiClient.layer({ + apiKey: Redacted.make("sk-test"), + apiUrl: "https://previous-response.test/v1" + })), + Effect.provideService(Socket.WebSocketConstructor, (url) => new globalThis.WebSocket(url)), + Effect.provideService(HttpClient.HttpClient, HttpClient.make(() => Effect.die("unexpected http"))) + ) + + const respond = Effect.gen(function*() { + const first = yield* nextWebSocketCreate(server) + assert.isUndefined(first.previous_response_id) + assert.deepStrictEqual(first.input, [webSocketUserInput("hello")]) + sendWebSocketCompleted(server, "resp_1", "msg_1", "ok") + + const incremental = yield* nextWebSocketCreate(server) + assert.strictEqual(incremental.previous_response_id, "resp_1") + assert.deepStrictEqual(incremental.input, [webSocketUserInput("again")]) + server.send({ + error: { + message: "gRPC error: Response with id=resp_1 not found", + type: "api_error" + } + }) + + const retried = yield* nextWebSocketCreate(server) + assert.isUndefined(retried.previous_response_id) + assert.deepStrictEqual(retried.input, [ + webSocketUserInput("hello"), + webSocketAssistantInput("msg_1", "ok"), + webSocketUserInput("again") + ]) + sendWebSocketCompleted(server, "resp_2", "msg_2", "retried") + + const later = yield* nextWebSocketCreate(server) + assert.strictEqual(later.previous_response_id, "resp_2") + assert.deepStrictEqual(later.input, [webSocketUserInput("later")]) + sendWebSocketCompleted(server, "resp_3", "msg_3", "later") + }) + + yield* Effect.all([drive, respond], { concurrency: "unbounded" }).pipe( + Effect.timeout("5 seconds") + ) + })) + + it.live("round-trips streamed encrypted reasoning in a full-prompt retry", () => + Effect.gen(function*() { + const server = yield* Effect.acquireRelease( + Effect.sync(() => new WS("wss://reasoning-recovery.test/v1/responses", { jsonProtocol: true })), + (server) => Effect.sync(() => server.close()) + ) + const requests: Array = [] + + const drive = Effect.gen(function*() { + const chat = yield* Chat.empty + yield* chat.streamText({ prompt: "think" }).pipe(Stream.runDrain) + yield* chat.streamText({ prompt: "continue" }).pipe(Stream.runDrain) + }).pipe( + OpenAiClient.withWebSocketMode, + Effect.provide(OpenAiLanguageModel.model("o3-mini", { store: true })), + Effect.provide(OpenAiClient.layer({ + apiKey: Redacted.make("sk-test"), + apiUrl: "https://reasoning-recovery.test/v1" + })), + Effect.provideService(Socket.WebSocketConstructor, (url) => new globalThis.WebSocket(url)), + Effect.provideService(HttpClient.HttpClient, HttpClient.make(() => Effect.die("unexpected http"))) + ) + + const respond = Effect.gen(function*() { + requests.push(yield* nextWebSocketCreate(server)) + sendWebSocketReasoningCompleted(server, "resp_1", "rs_1", "thinking", "encrypted-reasoning") + + requests.push(yield* nextWebSocketCreate(server)) + server.send({ + error: { + message: "gRPC error: Response with id=resp_1 not found", + type: "api_error" + } + }) + + requests.push(yield* nextWebSocketCreate(server)) + sendWebSocketCompleted(server, "resp_2", "msg_2", "done") + }) + + yield* Effect.all([drive, respond], { concurrency: "unbounded" }).pipe( + Effect.timeout("5 seconds") + ) + + assert.deepStrictEqual({ + include: requests[0]?.include, + reasoning: requests[2]?.input.find((item) => item.type === "reasoning") + }, { + include: ["reasoning.encrypted_content"], + reasoning: { + type: "reasoning", + id: "rs_1", + summary: [{ type: "summary_text", text: "thinking" }], + encrypted_content: "encrypted-reasoning" + } + }) + })) + it.effect("accepts keepalive stream events", () => Effect.gen(function*() { const client = yield* OpenAiClient.OpenAiClient @@ -764,6 +965,150 @@ describe("OpenAiClient", () => { }) }) +type WebSocketResponseCreate = { + readonly type: "response.create" + readonly previous_response_id?: string | undefined + readonly include?: ReadonlyArray | undefined + readonly input: ReadonlyArray> +} + +const nextWebSocketCreate = (server: WS) => + Effect.promise(() => server.nextMessage).pipe( + Effect.map((message) => message as WebSocketResponseCreate) + ) + +const webSocketUserInput = (text: string) => ({ + role: "user", + content: [{ type: "input_text", text }] +}) + +const webSocketAssistantInput = (id: string, text: string) => ({ + id, + type: "message", + role: "assistant", + status: "completed", + content: [{ + type: "output_text", + text, + annotations: [], + logprobs: [] + }] +}) + +const sendWebSocketCompleted = (server: WS, id: string, itemId: string, text: string) => { + const response = { + id, + object: "response", + model: "gpt-4o-mini", + created_at: 1, + output: [], + error: null, + incomplete_details: null + } + const events = [ + { type: "response.created", response }, + { + type: "response.output_item.added", + output_index: 0, + item: { + id: itemId, + type: "message", + role: "assistant", + status: "in_progress", + content: [] + } + }, + { + type: "response.output_text.delta", + item_id: itemId, + output_index: 0, + content_index: 0, + delta: text + }, + { + type: "response.output_item.done", + output_index: 0, + item: { + id: itemId, + type: "message", + role: "assistant", + status: "completed", + content: [{ type: "output_text", text, annotations: [] }] + } + }, + { type: "response.completed", response } + ] + for (const event of events) { + server.send(event) + } +} + +const sendWebSocketReasoningCompleted = ( + server: WS, + id: string, + itemId: string, + summary: string, + encryptedContent: string +) => { + const response = { + id, + object: "response", + model: "o3-mini", + created_at: 1, + output: [], + error: null, + incomplete_details: null + } + const events = [ + { type: "response.created", sequence_number: 1, response }, + { + type: "response.output_item.added", + sequence_number: 2, + output_index: 0, + item: { type: "reasoning", id: itemId, summary: [], encrypted_content: null } + }, + { + type: "response.reasoning_summary_part.added", + sequence_number: 3, + output_index: 0, + item_id: itemId, + summary_index: 0, + part: { type: "summary_text", text: summary } + }, + { + type: "response.reasoning_summary_text.delta", + sequence_number: 4, + output_index: 0, + item_id: itemId, + summary_index: 0, + delta: summary + }, + { + type: "response.reasoning_summary_part.done", + sequence_number: 5, + output_index: 0, + item_id: itemId, + summary_index: 0, + part: { type: "summary_text", text: summary } + }, + { + type: "response.output_item.done", + sequence_number: 6, + output_index: 0, + item: { + type: "reasoning", + id: itemId, + summary: [{ type: "summary_text", text: summary }], + encrypted_content: encryptedContent + } + }, + { type: "response.completed", sequence_number: 7, response } + ] + for (const event of events) { + server.send(event) + } +} + type MockResponse = | { readonly _tag: "Json" diff --git a/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts b/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts index 5bf08890ff..65ab739b31 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts +++ b/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts @@ -1,8 +1,8 @@ import { OpenAiClient, OpenAiEmbeddingModel } from "@effect/ai-openai" import { assert, describe, it } from "@effect/vitest" import { Effect, Layer, Redacted } from "effect" -import { EmbeddingModel } from "effect/unstable/ai" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { EmbeddingModel } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenAiEmbeddingModel", () => { it.effect("model provides dimensions service", () => diff --git a/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts b/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts index eb02c7516d..53844a15a7 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts +++ b/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts @@ -2,11 +2,12 @@ import { type Generated, OpenAiClient, OpenAiLanguageModel, OpenAiSchema, OpenAi import { assert, describe, it } from "@effect/vitest" import { assertTrue, deepStrictEqual, strictEqual } from "@effect/vitest/utils" import { Array, Context, Effect, Layer, Redacted, Ref, Schema, SchemaGetter, Stream } from "effect" -import { type AiError, LanguageModel, Prompt, Response as AiResponse, Tool, Toolkit } from "effect/unstable/ai" -import { HttpClient, type HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { AiError, LanguageModel, Prompt, Response as AiResponse, Tool, Toolkit } from "effect/ai" +import { HttpClient, type HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http" const webSearchFailures = [ { tool: OpenAiTool.WebSearch({}), status: "failed" }, + { tool: OpenAiTool.WebSearch({}), status: "incomplete" }, { tool: OpenAiTool.WebSearchPreview({}), status: "searching" } ] as const @@ -40,6 +41,113 @@ describe("OpenAiLanguageModel", () => { }).pipe(Effect.provide(makeTestLayer({ body: { model: "ft:gpt-4o-mini:custom" as any } })))) }) + describe("web search sources", () => { + it.effect.each(["generateText", "streamText"] as const)( + "preserves URL and API sources with %s", + (method) => + Effect.gen(function*() { + const sources = [ + { type: "url", url: "https://example.com/weather" }, + { type: "api", name: "oai-weather" } + ] as const + const action = { type: "search", query: "weather in London", sources } as const + const item = makeWebSearchCall({ action }) + const event = { type: "response.output_item.done", sequence_number: 1, output_index: 0, item } + const body = method === "generateText" + ? JSON.stringify(makeDefaultResponse({ output: [item] })) + : `data: ${JSON.stringify(event)}\n\n` + const client = makeRawResponseClient( + body, + method === "generateText" ? "application/json" : "text/event-stream" + ) + const options = { prompt: "Weather in London", toolkit: Toolkit.make(OpenAiTool.WebSearch({})) } + const parts = yield* Effect.gen(function*() { + if (method === "generateText") { + const response = yield* LanguageModel.generateText(options) + return response.content + } + return yield* LanguageModel.streamText(options).pipe(Stream.runCollect) + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-5.6")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + const calls = parts.filter((part) => part.type === "tool-call") + deepStrictEqual(calls.map((part) => part.params), [{ action }]) + + const results = parts.filter((part) => part.type === "tool-result") + deepStrictEqual(results.map((part) => part.result), [{ action, status: "completed" }]) + }) + ) + }) + + describe("web search without an action", () => { + it.effect.each( + [ + { method: "streamText", status: "incomplete", isFailure: true }, + { method: "generateText", status: "incomplete", isFailure: true }, + { method: "generateText", status: "completed", isFailure: false } + ] as const + )("preserves omitted actions for $status with $method", ({ method, status, isFailure }) => + Effect.gen(function*() { + const item: Generated.WebSearchToolCall = { type: "web_search_call", id: "ws_123", status } + const event = { type: "response.output_item.done", sequence_number: 1, output_index: 0, item } + const body = method === "generateText" + ? JSON.stringify(makeDefaultResponse({ output: [item] })) + : `data: ${JSON.stringify(event)}\n\n` + const client = makeRawResponseClient(body, method === "generateText" ? "application/json" : "text/event-stream") + const options = { prompt: "Search the web", toolkit: Toolkit.make(OpenAiTool.WebSearch({})) } + const parts = yield* Effect.gen(function*() { + if (method === "generateText") return (yield* LanguageModel.generateText(options)).content + return yield* LanguageModel.streamText(options).pipe(Stream.runCollect) + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-6-luna")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + deepStrictEqual( + parts.filter((part) => part.type === "tool-call" || part.type === "tool-result") + .map((part) => ({ + type: part.type, + id: part.id, + providerExecuted: part.providerExecuted, + ...(part.type === "tool-call" + ? { params: part.params } + : { result: part.result, isFailure: part.isFailure }) + })), + [ + { type: "tool-call", id: "ws_123", providerExecuted: true, params: {} }, + { type: "tool-result", id: "ws_123", providerExecuted: true, result: { status }, isFailure } + ] + ) + })) + + it.effect("rejects null and malformed actions when present", () => + Effect.forEach([null, { type: "find_in_page", url: "https://example.com" }], (action) => { + const event = { + type: "response.output_item.done", + sequence_number: 1, + output_index: 0, + item: { type: "web_search_call", id: "ws_123", status: "completed", action } + } + const client = makeRawResponseClient(`data: ${JSON.stringify(event)}\n\n`, "text/event-stream") + return LanguageModel.streamText({ prompt: "Search", toolkit: Toolkit.make(OpenAiTool.WebSearch({})) }).pipe( + Stream.runCollect, + Effect.flip, + Effect.tap((error) => { + assert(AiError.isAiError(error)) + strictEqual(error.reason._tag, "InvalidOutputError") + return Effect.void + }), + Effect.provide(OpenAiLanguageModel.model("gpt-6-luna")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + })) + }) + describe("generateText", () => { describe("message preparation", () => { it.effect("forwards prompt cache configuration and text breakpoints", () => @@ -461,6 +569,164 @@ describe("OpenAiLanguageModel", () => { }) }).pipe(Effect.provide(makeTestLayer({ body: { model: "o1" } })))) + it.effect("serializes stored assistant history according to item reference config", () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ + prompt: storedHistoryPrompt, + toolkit: TestToolkit, + disableToolCallResolution: true + }).pipe(Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { store: true }))) + + yield* LanguageModel.generateText({ + prompt: storedHistoryPrompt, + toolkit: TestToolkit, + disableToolCallResolution: true + }).pipe(Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { + store: true, + useItemReferences: false + }))) + + const requests = yield* MockHttpClient.requests + const referencedBody = yield* getRequestBody(requests[0]) + const inlineBody = yield* getRequestBody(requests[1]) + + assert.deepStrictEqual(referencedBody.input, [ + { role: "user", content: [{ type: "input_text", text: "Question" }] }, + { type: "item_reference", id: "msg_1" }, + { type: "item_reference", id: "rs_1" }, + { type: "item_reference", id: "fc_1" }, + { type: "function_call_output", call_id: "call_1", output: "{\"output\":\"result\"}" }, + { role: "user", content: [{ type: "input_text", text: "Continue" }] } + ]) + + assert.deepStrictEqual(inlineBody.input, [ + { role: "user", content: [{ type: "input_text", text: "Question" }] }, + { + id: "msg_1", + type: "message", + role: "assistant", + status: "completed", + content: [{ + type: "output_text", + text: "Answer", + annotations: [], + logprobs: [] + }] + }, + { + type: "reasoning", + id: "rs_1", + summary: [{ type: "summary_text", text: "Thinking" }], + encrypted_content: "encrypted-reasoning" + }, + { + type: "function_call", + name: "TestTool", + call_id: "call_1", + arguments: "{\"input\":\"value\"}", + id: "fc_1" + }, + { type: "function_call_output", call_id: "call_1", output: "{\"output\":\"result\"}" }, + { role: "user", content: [{ type: "input_text", text: "Continue" }] } + ]) + strictEqual(inlineBody.useItemReferences, undefined) + }).pipe(Effect.provide(makeTestLayer()))) + + it.effect("replays matched provider-executed history inline", () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ + prompt: providerExecutedHistoryPrompt, + toolkit: Toolkit.make(OpenAiTool.WebSearch({})), + disableToolCallResolution: true + }).pipe(Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { + store: true, + useItemReferences: false + }))) + + const requests = yield* MockHttpClient.requests + const body = yield* getRequestBody(requests[0]) + + assert.deepStrictEqual(body.input, [ + { role: "user", content: [{ type: "input_text", text: "Search" }] }, + { + type: "web_search_call", + id: "ws_1", + status: "completed", + action: { type: "search", query: "Effect TypeScript" } + }, + { role: "user", content: [{ type: "input_text", text: "Continue" }] } + ]) + }).pipe(Effect.provide(makeTestLayer()))) + + it.effect("replays web search history with an omitted action", () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ + prompt: Prompt.make([ + { role: "user", content: "Search" }, + { + role: "assistant", + content: [ + Prompt.toolCallPart({ id: "ws_1", name: "OpenAiWebSearch", params: {}, providerExecuted: true }), + Prompt.toolResultPart({ + id: "ws_1", + name: "OpenAiWebSearch", + providerExecuted: true, + isFailure: true, + result: { status: "incomplete" } + }) + ] + }, + { role: "user", content: "Continue" } + ]), + toolkit: Toolkit.make(OpenAiTool.WebSearch({})), + disableToolCallResolution: true + }).pipe(Effect.provide(OpenAiLanguageModel.model("gpt-6-luna", { + store: true, + useItemReferences: false + }))) + + const body = yield* getRequestBody((yield* MockHttpClient.requests)[0]) + deepStrictEqual(body.input?.[1], { type: "web_search_call", id: "ws_1", status: "incomplete" }) + }).pipe(Effect.provide(makeTestLayer()))) + + it.effect("fails locally when provider-executed history cannot be replayed inline", () => + Effect.gen(function*() { + const cases = [ + providerExecutedCall, + providerExecutedResult + ] as const + yield* Effect.forEach(cases, (part) => + LanguageModel.generateText({ + prompt: Prompt.make([ + { role: "user", content: "Search" }, + { + role: "assistant", + content: [part] + }, + { role: "user", content: "Continue" } + ]), + toolkit: Toolkit.make(OpenAiTool.WebSearch({})), + disableToolCallResolution: true + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { + store: true, + useItemReferences: false + })), + Effect.flip, + Effect.tap((error) => + Effect.sync(() => { + strictEqual(error.reason._tag, "InvalidRequestError") + if (error.reason._tag === "InvalidRequestError") { + assert.include(error.reason.description, "OpenAiWebSearch") + assert.include(error.reason.description, "ws_1") + } + }) + ) + )) + + strictEqual((yield* MockHttpClient.requests).length, 0) + }).pipe(Effect.provide(makeTestLayer()))) + it.effect("converts tool call parts to function_call", () => Effect.gen(function*() { yield* LanguageModel.generateText({ @@ -1048,6 +1314,54 @@ describe("OpenAiLanguageModel", () => { ]) )) + it.effect("preserves raw JSON Schema dynamic tool call params", () => + Effect.gen(function*() { + const DynamicTool = Tool.dynamic("DynamicTool", { + parameters: { + type: "object", + properties: { query: { type: "string" } }, + required: ["query"], + additionalProperties: false + } as const + }) + const params = { query: "effect" } + const result = yield* LanguageModel.generateText({ + prompt: "Use the dynamic tool", + toolkit: Toolkit.make(DynamicTool), + disableToolCallResolution: true + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(makeTestLayer({ + body: { output: [makeFunctionCall("DynamicTool", params)] } + })) + ) + + deepStrictEqual(result.toolCalls[0]?.params, params) + })) + + it.effect("decodes Effect Schema dynamic tool call params with the OpenAI codec", () => + Effect.gen(function*() { + const DynamicTool = Tool.dynamic("DynamicTool", { + parameters: Schema.Struct({ + env: Schema.Record(Schema.String, Schema.String) + }) + }) + const result = yield* LanguageModel.generateText({ + prompt: "Use the dynamic tool", + toolkit: Toolkit.make(DynamicTool), + disableToolCallResolution: true + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(makeTestLayer({ + body: { + output: [makeFunctionCall("DynamicTool", { env: [{ 0: "PATH", 1: "/usr/bin" }] })] + } + })) + ) + + deepStrictEqual(result.toolCalls[0]?.params, { env: { PATH: "/usr/bin" } }) + })) + it.effect("routes invalid tool call params through failureMode: return without failing the effect", () => Effect.gen(function*() { const toolkit = Toolkit.make(ReturnModeTool) @@ -2028,6 +2342,7 @@ describe("OpenAiLanguageModel", () => { Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini", { fileIdPrefixes: ["file-"], strictJsonSchema: false, + useItemReferences: false, temperature: 0.5 })) ) @@ -2037,6 +2352,7 @@ describe("OpenAiLanguageModel", () => { strictEqual(body.fileIdPrefixes, undefined) strictEqual(body.strictJsonSchema, undefined) + strictEqual(body.useItemReferences, undefined) strictEqual(body.temperature, 0.5) }).pipe(Effect.provide(makeTestLayer()))) }) @@ -2089,6 +2405,14 @@ const makeHttpClient = Effect.gen(function*() { const HttpClientLayer = Layer.effectContext(makeHttpClient) +const makeRawResponseClient = (body: string, contentType: string) => + HttpClient.make((request) => + Effect.succeed(HttpClientResponse.fromWeb( + request, + new Response(body, { headers: { "content-type": contentType } }) + )) + ) + const makeStreamTestLayer = (events: ReadonlyArray) => { const response = HttpClientResponse.fromWeb( HttpClientRequest.get("https://api.openai.com/v1/responses"), @@ -2318,6 +2642,74 @@ const AsymmetricParamsTool = Tool.make("AsymmetricParamsTool", { const TestToolkit = Toolkit.make(TestTool) +const storedHistoryPrompt = Prompt.make([ + { role: "user", content: "Question" }, + { + role: "assistant", + content: [ + Prompt.textPart({ + text: "Answer", + options: { openai: { itemId: "msg_1" } } + }), + Prompt.reasoningPart({ + text: "Thinking", + options: { + openai: { + itemId: "rs_1", + encryptedContent: "encrypted-reasoning" + } + } + }), + Prompt.toolCallPart({ + id: "call_1", + name: "TestTool", + params: { input: "value" }, + providerExecuted: false, + options: { openai: { itemId: "fc_1" } } + }) + ] + }, + { + role: "tool", + content: [Prompt.toolResultPart({ + id: "call_1", + name: "TestTool", + isFailure: false, + result: { output: "result" }, + providerExecuted: false + })] + }, + { role: "user", content: "Continue" } +]) + +const providerExecutedCall = Prompt.toolCallPart({ + id: "ws_1", + name: "OpenAiWebSearch", + params: { + action: { type: "search", query: "Effect TypeScript" } + }, + providerExecuted: true, + options: { openai: { itemId: "ws_1", status: "completed" } } +}) + +const providerExecutedResult = Prompt.toolResultPart({ + id: "ws_1", + name: "OpenAiWebSearch", + isFailure: false, + result: { + action: { type: "search", query: "Effect TypeScript" }, + status: "completed" + }, + providerExecuted: true, + options: { openai: { itemId: "ws_1", status: "completed" } } +}) + +const providerExecutedHistoryPrompt = Prompt.make([ + { role: "user", content: "Search" }, + { role: "assistant", content: [providerExecutedCall, providerExecutedResult] }, + { role: "user", content: "Continue" } +]) + const McpToolkit = Toolkit.make(OpenAiTool.Mcp({ server_label: "npm", server_url: "https://example.com/mcp", diff --git a/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts b/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts index 8cdd06a1bb..7de0dc6aee 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts +++ b/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts @@ -1,10 +1,10 @@ -import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" +import { Generated, OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai" import * as OpenAiSchema from "@effect/ai-openai/OpenAiSchema" import { assert, describe, it } from "@effect/vitest" import { Effect, Layer, Schema, Stream } from "effect" -import { LanguageModel } from "effect/unstable/ai" -import * as Sse from "effect/unstable/encoding/Sse" -import { HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { LanguageModel } from "effect/ai" +import * as Sse from "effect/encoding/Sse" +import { HttpClientRequest, HttpClientResponse } from "effect/http" const makeResponse = (overrides: Record = {}) => ({ id: "resp_123", @@ -17,6 +17,36 @@ const makeResponse = (overrides: Record = {}) => ({ }) describe("OpenAiSchema", () => { + describe("web search sources", () => { + it("preserves API sources in response history", () => { + const item = { + type: "web_search_call", + id: "ws_123", + status: "completed", + action: { type: "search", sources: [{ type: "api", name: "oai-weather" }] } + } as const + + assert.deepStrictEqual(Schema.decodeUnknownSync(Generated.InputItem)(item), item) + }) + + it.each([ + { type: "url" }, + { type: "api" }, + { type: "api", name: null }, + { type: "unknown", name: "oai-weather" } + ])("rejects malformed search sources: %j", (source) => { + const item = { + type: "web_search_call", + id: "ws_123", + status: "completed", + action: { type: "search", sources: [source] } + } + + assert.throws(() => Schema.decodeUnknownSync(Generated.WebSearchToolCall)(item)) + assert.throws(() => Schema.decodeUnknownSync(Generated.InputItem)(item)) + }) + }) + it("accepts max reasoning effort", () => { const decoded = Schema.decodeUnknownSync(OpenAiSchema.CreateResponse)({ reasoning: { effort: "max" } diff --git a/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts b/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts index 34addf2ff5..b22e49d08e 100644 --- a/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts +++ b/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts @@ -1,5 +1,5 @@ import { type Generated, OpenAiTool } from "@effect/ai-openai" -import type { Tool } from "effect/unstable/ai" +import type { Tool } from "effect/ai" import { describe, expect, it } from "tstyche" describe("OpenAiTool", () => { @@ -20,11 +20,32 @@ describe("OpenAiTool", () => { const webSearch = OpenAiTool.WebSearch({}) type Failure = { - readonly action: Generated.WebSearchToolCall["action"] - readonly status: "in_progress" | "searching" | "failed" + readonly action?: Exclude + readonly status: "in_progress" | "searching" | "failed" | "incomplete" } expect>().type.toBe() - expect["status"]>().type.toBe<"completed">() + expect>().type.toBe>() + expect>().type.toBe & { readonly status: "completed" }>() + }) + + it("keeps search sources optional and distinguishes URL sources from named API sources", () => { + const webSearch = OpenAiTool.WebSearch({}) + const preview = OpenAiTool.WebSearchPreview({}) + type Sources = Pick< + Extract, + "sources" + > + type Expected = { + readonly sources?: ReadonlyArray< + | { readonly type: "url"; readonly url: string } + | { readonly type: "api"; readonly name: string } + > + } + + expect["action"]>>().type.toBe() + expect["action"]>>().type.toBe() + expect["action"]>>().type.toBe() + expect["action"]>>().type.toBe() }) }) diff --git a/repos/effect/packages/ai/openrouter/CHANGELOG.md b/repos/effect/packages/ai/openrouter/CHANGELOG.md index f7e22cb87f..e512e15003 100644 --- a/repos/effect/packages/ai/openrouter/CHANGELOG.md +++ b/repos/effect/packages/ai/openrouter/CHANGELOG.md @@ -1,5 +1,101 @@ # @effect/ai-openrouter +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8405](https://github.com/Effect-TS/effect/pull/8405) [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d) Thanks @tim-smart! - Add a `probabilityPrecision` option to `DecisionModel.make` that accepts and rescales the sum drift caused by a provider rounding each probability. OpenRouter and TypeSafe decision models set it to two decimals, so rounded distributions such as `0.02 / 0.93 / 0.04` no longer fail with `InvalidOutputError`. Providers that do not opt in keep strict sum validation. +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- [#8287](https://github.com/Effect-TS/effect/pull/8287) [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2) Thanks @tim-smart! - Add `OpenRouterDecisionModel` for OpenRouter's alpha Decisions API. + + Custom `OpenRouterClient.Service` implementations and mocks must now provide `createDecisions`. +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/ai/openrouter/README.md b/repos/effect/packages/ai/openrouter/README.md index cc120b6a40..be78338487 100644 --- a/repos/effect/packages/ai/openrouter/README.md +++ b/repos/effect/packages/ai/openrouter/README.md @@ -5,10 +5,21 @@ An [OpenRouter](https://openrouter.ai) provider for the Effect AI modules. Inclu ## Installation ```sh -npm install effect@rc @effect/ai-openrouter@rc +npm install effect @effect/ai-openrouter ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/ai-openrouter) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/ai-openrouter): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/ai/openrouter/codegen.yml b/repos/effect/packages/ai/openrouter/codegen.yml index 7fea7aae9d..982fd08729 100644 --- a/repos/effect/packages/ai/openrouter/codegen.yml +++ b/repos/effect/packages/ai/openrouter/codegen.yml @@ -4,6 +4,7 @@ output: src/Generated.ts name: OpenRouterClient header: | /** + * @stability unstable * @since 1.0.0 */ excludeAnnotations: diff --git a/repos/effect/packages/ai/openrouter/package.json b/repos/effect/packages/ai/openrouter/package.json index 9f05c59d48..6b407bb5a3 100644 --- a/repos/effect/packages/ai/openrouter/package.json +++ b/repos/effect/packages/ai/openrouter/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openrouter", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "An OpenRouter provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/openrouter/src/Generated.ts b/repos/effect/packages/ai/openrouter/src/Generated.ts index 4c3859a375..1edfa865b0 100644 --- a/repos/effect/packages/ai/openrouter/src/Generated.ts +++ b/repos/effect/packages/ai/openrouter/src/Generated.ts @@ -1,17 +1,18 @@ /** + * @stability unstable * @since 1.0.0 */ import * as Data from "effect/Data" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" import type { SchemaError } from "effect/Schema" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import * as Sse from "effect/unstable/encoding/Sse" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientError from "effect/unstable/http/HttpClientError" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" // non-recursive definitions export type AABenchmarkEntry = { readonly "agentic_index": number | null diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts index 8a2e664292..67b4a92625 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts +++ b/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts @@ -1,29 +1,29 @@ /** - * The `OpenRouterClient` module provides an Effect service for calling - * OpenRouter's chat completions API. It wraps the generated OpenRouter HTTP - * client with Effect-native constructors, layers, authentication and optional - * site ranking headers, typed errors, and streaming support. + * HTTP client for OpenRouter's chat completions and alpha Decisions APIs, + * with authentication, site ranking headers, typed errors, and chat streaming. * + * @stability unstable * @since 4.0.0 */ +import * as AiError from "effect/ai/AiError" import type * as Config from "effect/Config" import * as Context from "effect/Context" import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" import { identity } from "effect/Function" +import * as HttpBody from "effect/http/HttpBody" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Predicate from "effect/Predicate" import type * as Redacted from "effect/Redacted" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import type * as AiError from "effect/unstable/ai/AiError" -import * as Sse from "effect/unstable/encoding/Sse" -import * as HttpBody from "effect/unstable/http/HttpBody" -import * as HttpClient from "effect/unstable/http/HttpClient" -import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import * as Generated from "./Generated.ts" import * as Errors from "./internal/errors.ts" import { OpenRouterConfig } from "./OpenRouterConfig.ts" +import * as OpenRouterSchema from "./OpenRouterSchema.ts" // ============================================================================= // Service Interface @@ -37,12 +37,20 @@ import { OpenRouterConfig } from "./OpenRouterConfig.ts" * Provides methods for interacting with OpenRouter's Chat Completions API, * including both synchronous and streaming message creation. * + * @stability unstable * @category services * @since 4.0.0 */ export interface Service { readonly client: Generated.OpenRouterClient + readonly createDecisions: ( + options: typeof OpenRouterSchema.DecisionsRequest.Encoded + ) => Effect.Effect< + [body: typeof OpenRouterSchema.DecisionsResponse.Type, response: HttpClientResponse.HttpClientResponse], + AiError.AiError + > + readonly createChatCompletion: ( options: typeof Generated.ChatRequest.Encoded ) => Effect.Effect< @@ -69,6 +77,7 @@ export interface Service { * The payload contains streamed choices, model metadata, optional usage, and may * include an OpenRouter error object for a streamed response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -90,6 +99,7 @@ export type ChatStreamingResponseChunkData = typeof Generated.ChatStreamingRespo * @see {@link layer} for providing a client from explicit options * @see {@link layerConfig} for providing a client from `Config` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -105,12 +115,17 @@ export class OpenRouterClient extends Context.Service< /** * Configuration for creating an OpenRouter client. * + * @stability unstable * @category options * @since 4.0.0 */ export type Options = { readonly apiKey?: Redacted.Redacted | undefined + /** + * Base URL for the versioned API. Decisions replace a trailing "/v1" with + * "/alpha/decisions"; otherwise they append "/alpha/decisions" to this URL. + */ readonly apiUrl?: string | undefined /** @@ -140,10 +155,6 @@ export type Options = { /** * Creates an OpenRouter client service from explicit options. * - * **When to use** - * - * Use when you need the OpenRouter client service value inside an effect. - * * **Details** * * The returned service uses the current `HttpClient`, prepends `apiUrl` or @@ -151,15 +162,13 @@ export type Options = { * `HTTP-Referer` and `X-Title` headers, accepts JSON responses, and applies * `transformClient` when provided. * - * **Gotchas** - * * Scoped `OpenRouterConfig.withClientTransform` applies to generated client - * request methods. Streaming chat completion requests are sent directly by this - * module and do not read that scoped transform. + * methods and alpha Decisions requests, but not streaming chat completions. * * @see {@link layer} for providing this client from explicit options * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -167,21 +176,55 @@ export const make = Effect.fnUntraced( function*(options: Options): Effect.fn.Return { const baseClient = yield* HttpClient.HttpClient - const httpClient = baseClient.pipe( - HttpClient.mapRequest((request) => - request.pipe( - HttpClientRequest.prependUrl(options.apiUrl ?? "https://openrouter.ai/api/v1"), - options.apiKey ? HttpClientRequest.bearerToken(options.apiKey) : identity, - options.siteReferrer ? HttpClientRequest.setHeader("HTTP-Referer", options.siteReferrer) : identity, - options.siteTitle ? HttpClientRequest.setHeader("X-Title", options.siteTitle) : identity, - HttpClientRequest.acceptJson - ) - ), - options.transformClient ?? identity - ) + const makeHttpClient = (baseUrl: string) => + baseClient.pipe( + HttpClient.mapRequest((request) => + request.pipe( + HttpClientRequest.prependUrl(baseUrl), + options.apiKey ? HttpClientRequest.bearerToken(options.apiKey) : identity, + options.siteReferrer ? HttpClientRequest.setHeader("HTTP-Referer", options.siteReferrer) : identity, + options.siteTitle ? HttpClientRequest.setHeader("X-Title", options.siteTitle) : identity, + HttpClientRequest.acceptJson + ) + ), + options.transformClient ?? identity + ) + const httpClient = makeHttpClient(options.apiUrl ?? "https://openrouter.ai/api/v1") const httpClientOk = HttpClient.filterStatusOk(httpClient) + // Remove the version suffix for alpha requests, preserving proxy prefixes. + const decisionsClient = makeHttpClient( + (options.apiUrl ?? "https://openrouter.ai/api/v1").replace(/\/v1\/?$/, "") + ) + const createDecisions: Service["createDecisions"] = Effect.fnUntraced( + function*(payload) { + const config = yield* OpenRouterConfig.getOrUndefined + const client = HttpClient.filterStatusOk( + config?.transformClient ? config.transformClient(decisionsClient) : decisionsClient + ) + const request = yield* HttpClientRequest.bodyJson(HttpClientRequest.post("/alpha/decisions"), payload).pipe( + Effect.mapError((error) => + AiError.make({ + module: "OpenRouterClient", + method: "createDecisions", + reason: new AiError.InvalidRequestError({ description: String(error) }) + }) + ) + ) + const response = yield* client.execute(request) + const body = yield* HttpClientResponse.schemaBodyJson(OpenRouterSchema.DecisionsResponse)(response) + return [body, response] as [ + typeof OpenRouterSchema.DecisionsResponse.Type, + HttpClientResponse.HttpClientResponse + ] + }, + Effect.catchTags({ + HttpClientError: (error) => Errors.mapHttpClientError(error, "createDecisions"), + SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createDecisions")) + }) + ) + const client = Generated.make(httpClient, { transformClient: Effect.fnUntraced(function*(client) { const config = yield* OpenRouterConfig.getOrUndefined @@ -255,6 +298,7 @@ export const make = Effect.fnUntraced( return OpenRouterClient.of({ client, + createDecisions, createChatCompletion, createChatCompletionStream }) @@ -276,6 +320,7 @@ export const make = Effect.fnUntraced( * @see {@link make} for constructing the client service effectfully * @see {@link layerConfig} for loading client settings from `Config` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -299,6 +344,7 @@ export const layer = (options: Options): Layer.Layer +>()("@effect/ai-openrouter/OpenRouterDecisionModel/Config") {} + +/** + * Creates an OpenRouter model descriptor with decision support. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const model = ( + model: string, + config?: typeof Config.Service +): Model.Model<"openrouter", DecisionModel.DecisionModel, OpenRouterClient> => + Model.make("openrouter", model, layer({ model, config })) + +/** + * Creates a decision service that requires full choice and score distributions. + * Score indices map to criteria labels; cost, id, and provider metadata are omitted. + * Probabilities arrive rounded to two decimals, so small sum drift is rescaled. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = Effect.fnUntraced(function*(options: { + readonly model: string + readonly config?: typeof Config.Service | undefined +}): Effect.fn.Return { + const client = yield* OpenRouterClient + const config = Effect.contextWith((services: Context.Context) => + Effect.succeed({ ...options.config, ...Context.getOrUndefined(services, Config) }) + ) + return yield* DecisionModel.make({ + probabilityPrecision: 2, + decide: Effect.fnUntraced(function*({ state, decisions }) { + if (state === null || typeof state === "number" || typeof state === "boolean") { + return yield* AiError.make({ + module: "OpenRouterDecisionModel", + method: "decide", + reason: new AiError.InvalidUserInputError({ + description: "OpenRouter decision state must be a string, object, or array" + }) + }) + } + const questions: Record = Object.create(null) + for (const [key, decision] of Object.entries(decisions)) { + switch (decision._tag) { + case "Classify": + questions[key] = { type: "choice", instructions: decision.instructions, criteria: decision.criteria } + break + case "Rate": + questions[key] = { type: "score", instructions: decision.instructions, criteria: decision.criteria } + break + case "Probability": + questions[key] = { type: "noul", instructions: decision.instructions, criteria: decision.criteria } + break + } + } + const [response] = yield* client.createDecisions({ ...yield* config, model: options.model, state, questions }) + const answers: Record = Object.create(null) + for (const [key, answer] of Object.entries(response.answers)) { + if (answer.type === "noul") { + answers[key] = { _tag: "Probability", probability: answer.noul } + continue + } + if (answer.probabilities === undefined) { + return yield* AiError.make({ + module: "OpenRouterDecisionModel", + method: "decide", + reason: new AiError.InvalidOutputError({ + description: `Provider returned no probabilities for decision "${key}"` + }) + }) + } + if (answer.type === "choice") { + answers[key] = { + _tag: "Classify", + label: answer.choice, + probabilities: answer.probabilities, + confidence: answer.confidence + } + } else { + const decision = decisions[key] + const probabilities: Record = Object.create(null) + if (decision?._tag === "Rate") { + for (const [index, label] of decision.criteria.entries()) { + const probability = answer.probabilities[String(index)] + if (probability !== undefined) { + probabilities[label] = probability + } + } + } + answers[key] = { _tag: "Rate", rating: answer.score, probabilities, confidence: answer.confidence } + } + } + return { + answers, + usage: { inputTokens: response.usage.input_tokens, outputTokens: response.usage.output_tokens } + } + }) + }) +}) + +/** + * Provides a decision model using the OpenRouter client service. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer = (options: { + readonly model: string + readonly config?: typeof Config.Service | undefined +}): Layer.Layer => + Layer.effect(DecisionModel.DecisionModel, make(options)) diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts index 63d0555e04..e9ee93af49 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts +++ b/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts @@ -41,7 +41,7 @@ export type OpenRouterRateLimitMetadata = OpenRouterErrorMetadata & { readonly resetTokens: string | null } -declare module "effect/unstable/ai/AiError" { +declare module "effect/ai/AiError" { /** * OpenRouter metadata attached to `RateLimitError` values. * diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts index 0a8d769187..bf96042f66 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts +++ b/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts @@ -6,15 +6,28 @@ * requests, records GenAI telemetry around those calls, and converts normal or * streaming results back into Effect AI response content and metadata. * + * @stability unstable * @since 4.0.0 */ /** @effect-diagnostics preferSchemaOverJson:skip-file */ +import * as AiError from "effect/ai/AiError" +import { toCodecAnthropic } from "effect/ai/AnthropicStructuredOutput" +import * as IdGenerator from "effect/ai/IdGenerator" +import * as LanguageModel from "effect/ai/LanguageModel" +import * as AiModel from "effect/ai/Model" +import { toCodecOpenAI } from "effect/ai/OpenAiStructuredOutput" +import type * as Prompt from "effect/ai/Prompt" +import type * as Response from "effect/ai/Response" +import { addGenAIAnnotations } from "effect/ai/Telemetry" +import * as Tool from "effect/ai/Tool" import * as Arr from "effect/Array" import * as Context from "effect/Context" import * as DateTime from "effect/DateTime" import * as Effect from "effect/Effect" -import * as Encoding from "effect/Encoding" +import * as Base64 from "effect/encoding/Base64" import { dual } from "effect/Function" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import type * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" @@ -24,18 +37,6 @@ import * as SchemaAST from "effect/SchemaAST" import * as Stream from "effect/Stream" import type { Span } from "effect/Tracer" import type { DeepMutable, Mutable, Simplify } from "effect/Types" -import * as AiError from "effect/unstable/ai/AiError" -import { toCodecAnthropic } from "effect/unstable/ai/AnthropicStructuredOutput" -import * as IdGenerator from "effect/unstable/ai/IdGenerator" -import * as LanguageModel from "effect/unstable/ai/LanguageModel" -import * as AiModel from "effect/unstable/ai/Model" -import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput" -import type * as Prompt from "effect/unstable/ai/Prompt" -import type * as Response from "effect/unstable/ai/Response" -import { addGenAIAnnotations } from "effect/unstable/ai/Telemetry" -import * as Tool from "effect/unstable/ai/Tool" -import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import type * as Generated from "./Generated.ts" import { ReasoningDetailsDuplicateTracker, resolveFinishReason } from "./internal/utilities.ts" import { type ChatStreamingResponseChunkData, OpenRouterClient } from "./OpenRouterClient.ts" @@ -54,6 +55,7 @@ import { type ChatStreamingResponseChunkData, OpenRouterClient } from "./OpenRou * * @see {@link withConfigOverride} for scoping language model request overrides * + * @stability unstable * @category services * @since 4.0.0 */ @@ -86,6 +88,7 @@ export class Config extends Context.Service< * OpenRouter assistant reasoning detail blocks preserved for multi-turn * conversations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -95,6 +98,7 @@ export type ReasoningDetails = Exclude -declare module "effect/unstable/ai/Prompt" { +declare module "effect/ai/Prompt" { /** * OpenRouter-specific options for system messages. * @@ -112,6 +116,7 @@ declare module "effect/unstable/ai/Prompt" { * These options are used when translating system instructions into * OpenRouter chat messages. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -135,6 +140,7 @@ declare module "effect/unstable/ai/Prompt" { * These options are used when translating user content into OpenRouter chat * messages. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -158,6 +164,7 @@ declare module "effect/unstable/ai/Prompt" { * Preserves reasoning metadata when assistant messages are replayed in later * OpenRouter requests. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -185,6 +192,7 @@ declare module "effect/unstable/ai/Prompt" { * These options are used when converting tool results into OpenRouter chat * messages. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -207,6 +215,7 @@ declare module "effect/unstable/ai/Prompt" { * * Use when you use these options to control how text content is sent to OpenRouter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -230,6 +239,7 @@ declare module "effect/unstable/ai/Prompt" { * Preserves provider reasoning blocks so reasoning-aware conversations can * continue across OpenRouter requests. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -256,6 +266,7 @@ declare module "effect/unstable/ai/Prompt" { * * Controls file naming and prompt caching for files sent to OpenRouter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -284,6 +295,7 @@ declare module "effect/unstable/ai/Prompt" { * Preserves reasoning details associated with tool calls when a conversation * is sent back to OpenRouter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -306,6 +318,7 @@ declare module "effect/unstable/ai/Prompt" { * * Controls prompt caching for tool results sent to OpenRouter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -322,7 +335,7 @@ declare module "effect/unstable/ai/Prompt" { } } -declare module "effect/unstable/ai/Response" { +declare module "effect/ai/Response" { /** * OpenRouter metadata attached to completed reasoning response parts. * @@ -330,6 +343,7 @@ declare module "effect/unstable/ai/Response" { * * Preserves provider reasoning details that can be sent back in later turns. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -352,6 +366,7 @@ declare module "effect/unstable/ai/Response" { * * Carries the first reasoning detail chunk when OpenRouter exposes one. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -374,6 +389,7 @@ declare module "effect/unstable/ai/Response" { * * Carries provider reasoning detail chunks as they arrive from OpenRouter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -397,6 +413,7 @@ declare module "effect/unstable/ai/Response" { * Associates tool calls with provider reasoning details when the model emits * reasoning and tool calls together. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -420,6 +437,7 @@ declare module "effect/unstable/ai/Response" { * Includes citation text and offsets returned by providers that support URL * annotations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -451,6 +469,7 @@ declare module "effect/unstable/ai/Response" { * Exposes provider response details that are not represented by the common * Effect AI finish part fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -501,6 +520,7 @@ declare module "effect/unstable/ai/Response" { * @see {@link make} for constructing the language model service effectfully * @see {@link withConfigOverride} for scoping OpenRouter request overrides * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -535,6 +555,7 @@ export const model = ( * @see {@link model} for creating a model descriptor for `Effect.provide` * @see {@link withConfigOverride} for scoping request defaults around operations * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -613,6 +634,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig * @see {@link make} for constructing the language model service effectfully * @see {@link model} for creating a model descriptor for `Effect.provide` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -639,6 +661,7 @@ export const layer = (options: { * * @see {@link Config} for available OpenRouter request configuration fields * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -747,7 +770,7 @@ const prepareMessages = Effect.fnUntraced( url: part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array - ? `data:${mediaType};base64,${Encoding.encodeBase64(part.data)}` + ? `data:${mediaType};base64,${Base64.encode(part.data)}` : part.data }, ...(Predicate.isNotNull(partCacheControl) ? { cache_control: partCacheControl } : undefined) @@ -785,7 +808,7 @@ const prepareMessages = Effect.fnUntraced( type: "input_audio", input_audio: { data: part.data instanceof Uint8Array - ? Encoding.encodeBase64(part.data) + ? Base64.encode(part.data) : getBase64FromDataUrl(part.data), format }, @@ -805,7 +828,7 @@ const prepareMessages = Effect.fnUntraced( file_data: part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array - ? `data:${part.mediaType};base64,${Encoding.encodeBase64(part.data)}` + ? `data:${part.mediaType};base64,${Base64.encode(part.data)}` : part.data }, ...(Predicate.isNotNull(partCacheControl) ? { cache_control: partCacheControl } : undefined) diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts b/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts new file mode 100644 index 0000000000..c222a73ace --- /dev/null +++ b/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts @@ -0,0 +1,120 @@ +/** + * Schemas for OpenRouter's alpha Decisions API. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Schema from "effect/Schema" +import * as Generated from "./Generated.ts" + +/** + * A choice question with descriptions keyed by choice label. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ChoiceQuestion = Schema.Struct({ + type: Schema.Literal("choice"), + instructions: Schema.String, + criteria: Schema.Record(Schema.String, Schema.String) +}) + +/** + * A score question with ordered criteria. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ScoreQuestion = Schema.Struct({ + type: Schema.Literal("score"), + instructions: Schema.String, + criteria: Schema.Array(Schema.String) +}) + +/** + * A probability question with optional true and false descriptions. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const NoulQuestion = Schema.Struct({ + type: Schema.Literal("noul"), + instructions: Schema.String, + criteria: Schema.optional(Schema.Struct({ true: Schema.String, false: Schema.String })) +}) + +/** + * A choice, ordered score, or probability question. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const DecisionsQuestion = Schema.Union([ + ChoiceQuestion, + ScoreQuestion, + NoulQuestion +]) + +/** + * A provider answer with optional probabilities and confidence. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Answer = Schema.Union([ + Schema.Struct({ + type: Schema.Literal("choice"), + choice: Schema.String, + confidence: Schema.optional(Schema.Finite), + probabilities: Schema.optional(Schema.Record(Schema.String, Schema.Finite)) + }), + Schema.Struct({ + type: Schema.Literal("score"), + score: Schema.Finite, + confidence: Schema.optional(Schema.Finite), + probabilities: Schema.optional(Schema.Record(Schema.String, Schema.Finite)), + legend: Schema.optional(Schema.Record(Schema.String, Schema.String)) + }), + Schema.Struct({ type: Schema.Literal("noul"), noul: Schema.Finite }) +]) + +/** + * Request body for the alpha Decisions endpoint. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const DecisionsRequest = Schema.Struct({ + model: Schema.String, + state: Schema.Unknown, + questions: Schema.Record(Schema.String, DecisionsQuestion), + provider: Schema.optional(Generated.ProviderPreferences), + session_id: Schema.optional(Schema.String), + user: Schema.optional(Schema.String), + trace: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)) +}) + +/** + * Response body for the alpha Decisions endpoint. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const DecisionsResponse = Schema.Struct({ + model: Schema.String, + answers: Schema.Record(Schema.String, Answer), + usage: Schema.Struct({ + input_tokens: Schema.Int, + output_tokens: Schema.Int, + cost: Schema.optional(Schema.Finite) + }), + id: Schema.optional(Schema.String), + provider: Schema.optional(Schema.String) +}) diff --git a/repos/effect/packages/ai/openrouter/src/index.ts b/repos/effect/packages/ai/openrouter/src/index.ts index 2ea005eb19..73678854ca 100644 --- a/repos/effect/packages/ai/openrouter/src/index.ts +++ b/repos/effect/packages/ai/openrouter/src/index.ts @@ -5,11 +5,13 @@ // @barrel: Auto-generated exports. Do not edit manually. /** - * @since 4.0.0 + * @stability unstable + * @since 1.0.0 */ export * as Generated from "./Generated.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenRouterClient from "./OpenRouterClient.ts" @@ -19,12 +21,25 @@ export * as OpenRouterClient from "./OpenRouterClient.ts" */ export * as OpenRouterConfig from "./OpenRouterConfig.ts" +/** + * @stability unstable + * @since 4.0.0 + */ +export * as OpenRouterDecisionModel from "./OpenRouterDecisionModel.ts" + /** * @since 4.0.0 */ export * as OpenRouterError from "./OpenRouterError.ts" /** + * @stability unstable * @since 4.0.0 */ export * as OpenRouterLanguageModel from "./OpenRouterLanguageModel.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as OpenRouterSchema from "./OpenRouterSchema.ts" diff --git a/repos/effect/packages/ai/openrouter/src/internal/errors.ts b/repos/effect/packages/ai/openrouter/src/internal/errors.ts index 27caabb28e..618c2c800d 100644 --- a/repos/effect/packages/ai/openrouter/src/internal/errors.ts +++ b/repos/effect/packages/ai/openrouter/src/internal/errors.ts @@ -1,17 +1,17 @@ +import * as AiError from "effect/ai/AiError" +import type * as Response from "effect/ai/Response" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" +import type * as Sse from "effect/encoding/Sse" import { dual } from "effect/Function" +import type * as HttpClientError from "effect/http/HttpClientError" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import type * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Number from "effect/Number" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" import * as Redactable from "effect/Redactable" import * as Schema from "effect/Schema" -import * as AiError from "effect/unstable/ai/AiError" -import type * as Response from "effect/unstable/ai/Response" -import type * as Sse from "effect/unstable/encoding/Sse" -import type * as HttpClientError from "effect/unstable/http/HttpClientError" -import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest" -import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse" import type * as Generated from "../Generated.ts" import type { OpenRouterErrorMetadata } from "../OpenRouterError.ts" diff --git a/repos/effect/packages/ai/openrouter/src/internal/utilities.ts b/repos/effect/packages/ai/openrouter/src/internal/utilities.ts index 1d5112e943..7bfc1dc43a 100644 --- a/repos/effect/packages/ai/openrouter/src/internal/utilities.ts +++ b/repos/effect/packages/ai/openrouter/src/internal/utilities.ts @@ -1,5 +1,5 @@ +import type * as Response from "effect/ai/Response" import * as Predicate from "effect/Predicate" -import type * as Response from "effect/unstable/ai/Response" import type { ReasoningDetails } from "../OpenRouterLanguageModel.ts" const finishReasonMap: Record = { diff --git a/repos/effect/packages/ai/openrouter/test/Generated.test.ts b/repos/effect/packages/ai/openrouter/test/Generated.test.ts index 0db46d615b..4539750ea9 100644 --- a/repos/effect/packages/ai/openrouter/test/Generated.test.ts +++ b/repos/effect/packages/ai/openrouter/test/Generated.test.ts @@ -2,8 +2,8 @@ import { Generated, OpenRouterClient, OpenRouterLanguageModel } from "@effect/ai import { describe, it } from "@effect/vitest" import { deepStrictEqual } from "@effect/vitest/utils" import { Effect, Schema, Stream } from "effect" -import { type AiError, LanguageModel, type Response, Tool, Toolkit } from "effect/unstable/ai" -import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { type AiError, LanguageModel, type Response, Tool, Toolkit } from "effect/ai" +import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http" describe("Generated", () => { it("decodes nullable generation statistics", () => { @@ -132,6 +132,7 @@ describe("Generated", () => { })) const client = OpenRouterClient.OpenRouterClient.of({ client: Generated.make(HttpClient.make(() => Effect.die("Unexpected HTTP request"))), + createDecisions: () => Effect.die("Unexpected decisions request"), createChatCompletion: () => Effect.succeed([body, response]), createChatCompletionStream: () => Effect.succeed([response, Stream.fromIterable(chunks)]) }) diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts b/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts index 3e5eac5a4b..c2fba232dc 100644 --- a/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts +++ b/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts @@ -1,10 +1,41 @@ import { OpenRouterClient } from "@effect/ai-openrouter" import * as Errors from "@effect/ai-openrouter/internal/errors" import { assert, describe, it } from "@effect/vitest" -import { Context, Effect, Layer, Redacted, type Schema } from "effect" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { Cause, Context, Effect, Exit, Layer, Option, Redacted, type Schema } from "effect" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenRouterClient", () => { + it.effect("rejects unserializable bigint decision state with a typed error before making an HTTP call", () => + Effect.gen(function*() { + const client = yield* OpenRouterClient.OpenRouterClient + const exit = yield* client.createDecisions({ + model: "test-model", + state: { n: 1n }, + questions: { + urgent: { + type: "noul", + instructions: "Is this urgent?", + criteria: { false: "Not urgent", true: "Urgent" } + } + } + }).pipe(Effect.exit) + + assert.deepStrictEqual(yield* MockHttpClient.requests, []) + assert.isTrue(Exit.isFailure(exit)) + if (!Exit.isFailure(exit)) { + return yield* Effect.die(new Error("Expected serialization to fail")) + } + assert.isFalse(Cause.hasDies(exit.cause)) + const error = Option.getOrThrow(Cause.findErrorOption(exit.cause)) + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.module, "OpenRouterClient") + assert.strictEqual(error.method, "createDecisions") + assert.strictEqual(error.reason._tag, "InvalidRequestError") + }).pipe(Effect.provide(makeTestLayer({ + _tag: "Json", + body: {} + })))) + it.effect("redacts the API key in AI error context", () => Effect.gen(function*() { const client = yield* OpenRouterClient.OpenRouterClient diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts b/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts new file mode 100644 index 0000000000..bd1d21c332 --- /dev/null +++ b/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts @@ -0,0 +1,874 @@ +import { OpenRouterClient, OpenRouterConfig, OpenRouterDecisionModel } from "@effect/ai-openrouter" +import { assert, describe, it } from "@effect/vitest" +import { Effect, Layer, Redacted, Result, Schema } from "effect" +import { Decision, DecisionModel, Model } from "effect/ai" +import { HttpClient, type HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http" + +const Ticket = Schema.Struct({ + message: Schema.String, + orderId: Schema.String +}) + +const TicketTriage = Decision.make({ + input: Ticket, + decisions: { + department: Decision.classify({ + instructions: "Which team should handle this", + criteria: { + billing: "Payment or subscription issues", + technical: "Bugs or integration problems", + sales: "Pricing or account questions" + } + }), + frustration: Decision.rate({ + instructions: "How frustrated the customer appears", + criteria: ["Calm, just stating facts", "Frustrated but civil", "Very angry, strong language"] + }), + urgent: Decision.probability({ + instructions: "The message conveys urgency or time-sensitivity", + criteria: { + false: "No time pressure", + true: "Needs action now" + } + }) + } +}) + +const ticket = { + message: "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. Please help ASAP.", + orderId: "A-104" +} + +const decisionsResponse = { + model: "test/decision-model", + answers: { + department: { + type: "choice", + choice: "billing", + probabilities: { billing: 0.84, technical: 0.159, sales: 0.001 }, + confidence: 0.596 + }, + frustration: { + type: "score", + score: 1.6, + legend: { + "0": "Calm, just stating facts", + "1": "Frustrated but civil", + "2": "Very angry, strong language" + }, + probabilities: { "0": 0.05, "1": 0.3, "2": 0.65 }, + confidence: 0.78 + }, + urgent: { + type: "noul", + noul: 0.999 + } + }, + usage: { input_tokens: 312, output_tokens: 48 } +} + +describe("OpenRouterDecisionModel", () => { + it.effect("accepts rounded choice probabilities that total 0.99", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + neutral3: Decision.classify({ + instructions: "Choose a sentiment", + criteria: { negative: "Negative", neutral: "Neutral", positive: "Positive" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "It is fine" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "test/decision-model", + answers: { + neutral3: { + type: "choice", + choice: "neutral", + probabilities: { negative: 0.02, neutral: 0.93, positive: 0.04 }, + confidence: 0.93 + } + }, + usage: { input_tokens: 10, output_tokens: 5 } + })) + )) + ) + + assert.strictEqual(answers.neutral3.label, "neutral") + assert.closeTo(Object.values(answers.neutral3.probabilities).reduce((sum, value) => sum + value, 0), 1, 1e-6) + })) + + for ( + const [name, probabilities, succeeds] of [ + ["extra provider key", { a: 0.5, b: 0.5, extra: 0.01 }, true], + ["drift outside two-decimal rounding", { a: 0.49, b: 0.49 }, false], + ["out-of-range probability", { a: 1.01, b: -0.02 }, false], + ["all-zero distribution", { a: 0, b: 0 }, false], + ["eight rounded labels", { a: 0.12, b: 0.12, c: 0.12, d: 0.12, e: 0.12, f: 0.12, g: 0.12, h: 0.13 }, true] + ] as const + ) { + it.effect(name, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + choice: Decision.classify({ + instructions: "Pick a label", + criteria: Object.fromEntries( + Object.keys(probabilities).filter((key) => key !== "extra").map((key) => [key, key]) + ) + }) + } + }) + const result = yield* Effect.result( + DecisionModel.decide(definition, { input: "test" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "test/decision-model", + answers: { choice: { type: "choice", choice: "a", probabilities, confidence: 0.5 } }, + usage: { input_tokens: 1, output_tokens: 1 } + })) + )) + ) + ) + if (succeeds) { + assert.isTrue(Result.isSuccess(result)) + if (Result.isSuccess(result)) { + assert.closeTo( + Object.values(result.success.answers.choice.probabilities).reduce((sum, value) => sum + value, 0), + 1, + 1e-6 + ) + if (name === "extra provider key") { + assert.deepStrictEqual(result.success.answers.choice.probabilities, { a: 0.5, b: 0.5 }) + } + } + } else { + assert.isTrue(Result.isFailure(result)) + if (Result.isFailure(result)) assert.strictEqual(result.failure.reason._tag, "InvalidOutputError") + } + })) + } + + it.effect("normalizes rounded score probabilities", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { score: Decision.rate({ instructions: "Rate", criteria: ["low", "mid", "high"] }) } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "test" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "test/decision-model", + answers: { + score: { type: "score", score: 1, probabilities: { "0": 0.1, "1": 0.7, "2": 0.19 }, confidence: 0.7 } + }, + usage: { input_tokens: 1, output_tokens: 1 } + })) + )) + ) + assert.closeTo(Object.values(answers.score.probabilities).reduce((sum, value) => sum + value, 0), 1, 1e-6) + })) + + it.effect("omits absent probability criteria from the encoded question", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ instructions: "Needs action now" }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help ASAP" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + assert.deepStrictEqual(questions.urgent, { + type: "noul", + instructions: "Needs action now" + }) + + return jsonResponse(request, { + model: "test/decision-model", + answers: { urgent: { type: "noul", noul: 0.75 } }, + usage: { input_tokens: 20, output_tokens: 4 } + }) + }) + )) + ) + + assert.deepStrictEqual(answers.urgent, { probability: 0.75 }) + })) + + for (const key of ["__proto__", "constructor", "toString"]) { + it.effect("round trips a classification label named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + category: Decision.classify({ + instructions: "Choose a category", + criteria: { [key]: "Special category", ordinary: "Ordinary category" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + const criteria = questions.category.criteria + + assert.isTrue(Object.hasOwn(criteria, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(criteria, key)?.enumerable) + assert.deepStrictEqual(Object.keys(criteria), [key, "ordinary"]) + assert.strictEqual(criteria[key], "Special category") + assert.strictEqual(Object.getPrototypeOf(criteria), Object.prototype) + + return jsonResponse(request, { + model: "test/decision-model", + answers: { + category: { + type: "choice", + choice: key, + probabilities: { [key]: 0.75, ordinary: 0.25 }, + confidence: 0.5 + } + }, + usage: { input_tokens: 10, output_tokens: 5 } + }) + }) + )) + ) + const { label, probabilities } = answers.category + + assert.isTrue(Object.hasOwn(probabilities, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(probabilities, key)?.enumerable) + assert.deepStrictEqual(Object.keys(probabilities), [key, "ordinary"]) + assert.deepStrictEqual(probabilities, { [key]: 0.75, ordinary: 0.25 }) + assert.strictEqual(label, key) + assert.strictEqual(Object.getPrototypeOf(probabilities), null) + })) + + it.effect("maps a score distribution to a rate level named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + intensity: Decision.rate({ instructions: "How intense", criteria: ["low", key, "high"] }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + assert.deepStrictEqual(questions.intensity.criteria, ["low", key, "high"]) + + return jsonResponse(request, { + model: "test/decision-model", + answers: { + intensity: { + type: "score", + score: 1.1, + legend: { "0": "low", "1": key, "2": "high" }, + probabilities: { "0": 0.1, "1": 0.7, "2": 0.2 }, + confidence: 0.5 + } + }, + usage: { input_tokens: 10, output_tokens: 5 } + }) + }) + )) + ) + const { label, probabilities, rating } = answers.intensity + + assert.isTrue(Object.hasOwn(probabilities, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(probabilities, key)?.enumerable) + assert.deepStrictEqual(Object.keys(probabilities), ["low", key, "high"]) + assert.deepStrictEqual(probabilities, { low: 0.1, [key]: 0.7, high: 0.2 }) + assert.strictEqual(label, key) + assert.strictEqual(rating, 1.1) + assert.strictEqual(Object.getPrototypeOf(probabilities), null) + })) + + it.effect("sends an own enumerable question named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + [key]: Decision.probability({ + instructions: "Needs action now", + criteria: { false: "No", true: "Yes" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + + assert.strictEqual(Object.getPrototypeOf(questions), Object.prototype) + assert.isTrue(Object.hasOwn(questions, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(questions, key)?.enumerable) + assert.deepStrictEqual(Object.keys(questions), [key]) + assert.deepStrictEqual(questions[key], { + type: "noul", + instructions: "Needs action now", + criteria: { false: "No", true: "Yes" } + }) + + return jsonResponse(request, { + model: "test/decision-model", + answers: { [key]: { type: "noul", noul: 0.75 } }, + usage: { input_tokens: 20, output_tokens: 4 } + }) + }) + )) + ) + + assert.strictEqual(Object.getPrototypeOf(answers), null) + assert.isTrue(Object.hasOwn(answers, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(answers, key)?.enumerable) + assert.deepStrictEqual(Object.keys(answers), [key]) + assert.deepStrictEqual(answers[key], { probability: 0.75 }) + })) + } + + for ( + const apiUrl of [ + "https://proxy.test/openrouter/v1", + "https://proxy.test/openrouter/v1/", + "https://proxy.test/openrouter", + "https://proxy.test/openrouter/" + ] + ) { + it.effect("resolves the alpha endpoint for " + apiUrl, () => + Effect.gen(function*() { + const requests: Array = [] + yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, decisionsResponse)) + }, apiUrl)) + ) + assert.strictEqual(requests.length, 1) + assert.strictEqual(requests[0].url, "https://proxy.test/openrouter/alpha/decisions") + })) + } + + it.effect("applies a scoped client transform only to the enclosed decisions request", () => + Effect.gen(function*() { + const requests: Array = [] + yield* Effect.gen(function*() { + yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + OpenRouterConfig.withClientTransform( + HttpClient.mapRequest(HttpClientRequest.setHeader("x-decision-scope", "scoped")) + ) + ) + yield* DecisionModel.decide(TicketTriage, { input: ticket }) + }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, decisionsResponse)) + })) + ) + assert.strictEqual(requests.length, 2) + assert.strictEqual(requests[0].headers["x-decision-scope"], "scoped") + assert.isUndefined(requests[1].headers["x-decision-scope"]) + })) + + it.effect("rejects numeric state before making an HTTP request", () => + Effect.gen(function*() { + const definition = Decision.make({ input: Schema.Number, decisions: TicketTriage.decisions }) + const error = yield* DecisionModel.decide(definition, { input: 42 }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer(() => Effect.die(new Error("Unexpected HTTP request")))), + Effect.flip + ) + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidUserInputError") + })) + + it.effect("maps a malformed successful response to a client InvalidOutputError", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + usage: { input_tokens: "not a number", output_tokens: 48 } + })) + )), + Effect.flip + ) + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.module, "OpenRouterClient") + assert.strictEqual(error.method, "createDecisions") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("make constructs a DecisionModel service", () => + Effect.gen(function*() { + const service = yield* OpenRouterDecisionModel.make({ model: "test/decision-model" }) + assert.isFunction(service.decide) + }).pipe(Effect.provide(makeClientLayer(() => Effect.die(new Error("Unexpected HTTP request")))))) + + it.effect("forwards request config and scoped overrides", () => + Effect.gen(function*() { + const requests: Array = [] + yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provideService(OpenRouterDecisionModel.Config, { user: "scoped-user", trace: { trace_id: "scoped" } }), + Effect.provide(OpenRouterDecisionModel.layer({ + model: "test/decision-model", + config: { + provider: { order: ["test-provider"] }, + session_id: "session-1", + user: "default-user", + trace: { trace_id: "default" } + } + })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, decisionsResponse)) + })) + ) + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body.provider, { order: ["test-provider"] }) + assert.strictEqual(body.session_id, "session-1") + assert.strictEqual(body.user, "scoped-user") + assert.deepStrictEqual(body.trace, { trace_id: "scoped" }) + })) + + it.effect("accepts absent confidence, cost, id and provider without inventing values", () => + Effect.gen(function*() { + const { confidence: _choiceConfidence, ...department } = decisionsResponse.answers.department + const { confidence: _scoreConfidence, legend: _legend, ...frustration } = decisionsResponse.answers.frustration + const { answers, usage } = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + answers: { ...decisionsResponse.answers, department, frustration } + })) + )) + ) + assert.isUndefined(answers.department.confidence) + assert.isUndefined(answers.frustration.confidence) + assert.strictEqual(answers.frustration.label, "Very angry, strong language") + assert.strictEqual(answers.urgent.probability, 0.999) + assert.deepStrictEqual({ ...usage }, { inputTokens: 312, outputTokens: 48 }) + })) + + it.effect("drops provider-specific cost, id and provider metadata", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + id: "decision-1", + provider: "test-provider", + usage: { ...decisionsResponse.usage, cost: 0.001 } + })) + )) + ) + assert.deepStrictEqual({ ...result.usage }, { inputTokens: 312, outputTokens: 48 }) + assert.isFalse("id" in result) + assert.isFalse("provider" in result) + assert.isFalse("cost" in result.answers.department) + })) + + it.effect("fails with InvalidOutputError when a choice answer omits probabilities", () => + Effect.gen(function*() { + const { probabilities: _probabilities, ...department } = decisionsResponse.answers.department + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + answers: { ...decisionsResponse.answers, department } + })) + )), + Effect.flip + ) + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("model provides DecisionModel with openrouter provider metadata", () => + Effect.gen(function*() { + const provider = yield* Model.ProviderName + const modelName = yield* Model.ModelName + const model = yield* DecisionModel.DecisionModel + + assert.strictEqual(provider, "openrouter") + assert.strictEqual(modelName, "test/decision-model") + assert.isDefined(model.decide) + }).pipe( + Effect.provide(OpenRouterDecisionModel.model("test/decision-model")), + Effect.provide(makeClientLayer(() => Effect.die(new Error("Unexpected HTTP request")))) + )) + + it.effect("sends the encoded input as state and every decision as a question in one request", () => + Effect.gen(function*() { + const requests: Array = [] + const clientLayer = makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, decisionsResponse)) + }) + + yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(clientLayer) + ) + + assert.strictEqual(requests.length, 1) + assert.strictEqual(requests[0].method, "POST") + assert.strictEqual(requests[0].url, "https://openrouter.ai/api/alpha/decisions") + assert.strictEqual(requests[0].headers["authorization"], "Bearer or-test-key") + + assert.strictEqual(requests[0].headers["http-referer"], "https://example.com") + assert.strictEqual(requests[0].headers["x-title"], "Decision contract tests") + + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body, { + state: { + message: ticket.message, + orderId: "A-104" + }, + model: "test/decision-model", + questions: { + department: { + type: "choice", + instructions: "Which team should handle this", + criteria: { + billing: "Payment or subscription issues", + technical: "Bugs or integration problems", + sales: "Pricing or account questions" + } + }, + frustration: { + type: "score", + instructions: "How frustrated the customer appears", + criteria: ["Calm, just stating facts", "Frustrated but civil", "Very angry, strong language"] + }, + urgent: { + type: "noul", + instructions: "The message conveys urgency or time-sensitivity", + criteria: { + false: "No time pressure", + true: "Needs action now" + } + } + } + }) + })) + + it.effect("maps choice, score, and noul answers onto classify, rate, and probability answers", () => + Effect.gen(function*() { + const { answers, usage } = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => Effect.succeed(jsonResponse(request, decisionsResponse)))) + ) + + assert.strictEqual(answers.department.label, "billing") + assert.deepStrictEqual(answers.department.probabilities, { billing: 0.84, technical: 0.159, sales: 0.001 }) + assert.strictEqual(answers.department.confidence, 0.596) + + assert.strictEqual(answers.frustration.rating, 1.6) + assert.strictEqual(answers.frustration.label, "Very angry, strong language") + assert.deepStrictEqual(answers.frustration.probabilities, { + "Calm, just stating facts": 0.05, + "Frustrated but civil": 0.3, + "Very angry, strong language": 0.65 + }) + assert.strictEqual(answers.frustration.confidence, 0.78) + + assert.strictEqual(answers.urgent.probability, 0.999) + assert.isFalse("confidence" in answers.urgent) + + assert.strictEqual(usage.inputTokens, 312) + assert.strictEqual(usage.outputTokens, 48) + })) + + it.effect("encodes an explicitly undefined optional input field as JSON null", () => + Effect.gen(function*() { + const requests: Array = [] + const definition = Decision.make({ + input: Schema.Struct({ message: Schema.String, orderId: Schema.optional(Schema.String) }), + decisions: TicketTriage.decisions + }) + const { answers } = yield* DecisionModel.decide(definition, { + input: { message: ticket.message, orderId: undefined } + }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, decisionsResponse)) + })) + ) + + assert.strictEqual(requests.length, 1) + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body.state, { message: ticket.message, orderId: null }) + assert.strictEqual(answers.urgent.probability, 0.999) + })) + + it.effect("sends a pinned model id through unchanged", () => + Effect.gen(function*() { + const requests: Array = [] + + const modelName = yield* Effect.gen(function*() { + yield* DecisionModel.decide(TicketTriage, { input: ticket }) + return yield* Model.ModelName + }).pipe( + Effect.provide(OpenRouterDecisionModel.model("test/decision-model-pinned")), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { ...decisionsResponse, model: "test/decision-model-pinned" })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.strictEqual(modelName, "test/decision-model-pinned") + assert.strictEqual(body.model, "test/decision-model-pinned") + })) + + it.effect("passes a string input through as a string state", () => + Effect.gen(function*() { + const requests: Array = [] + const Sentiment = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } + }) + + yield* DecisionModel.decide(Sentiment, { input: "My card was charged twice." }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { + model: "test/decision-model", + answers: { urgent: { type: "noul", noul: 0.93 } }, + usage: { input_tokens: 20, output_tokens: 4 } + })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.strictEqual(body.state, "My card was charged twice.") + })) + + it.effect("passes an array input through as an array state", () => + Effect.gen(function*() { + const requests: Array = [] + const Conversation = Decision.make({ + input: Schema.Array(Schema.String), + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } + }) + + yield* DecisionModel.decide(Conversation, { + input: ["Hi", "My customer number is TS1337.", "My card was charged twice."] + }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { + model: "test/decision-model", + answers: { urgent: { type: "noul", noul: 0.93 } }, + usage: { input_tokens: 20, output_tokens: 4 } + })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body.state, ["Hi", "My customer number is TS1337.", "My card was charged twice."]) + })) + + it.effect("fails with InvalidOutputError when an answer type does not match the decision", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + answers: { + ...decisionsResponse.answers, + urgent: { + type: "choice", + choice: "true", + probabilities: { true: 0.9, false: 0.1 }, + confidence: 0.8 + } + } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when an answer is missing", () => + Effect.gen(function*() { + const { urgent: _urgent, ...answers } = decisionsResponse.answers + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide( + makeClientLayer((request) => Effect.succeed(jsonResponse(request, { ...decisionsResponse, answers }))) + ), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when a score answer omits probabilities", () => + Effect.gen(function*() { + const { probabilities: _probabilities, ...frustration } = decisionsResponse.answers.frustration + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + answers: { ...decisionsResponse.answers, frustration } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when a score distribution skips a level index", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...decisionsResponse, + answers: { + ...decisionsResponse.answers, + frustration: { + ...decisionsResponse.answers.frustration, + probabilities: { "0": 0.35, "2": 0.65 } + } + } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + for ( + const [status, reason] of [ + [400, "InvalidRequestError"], + [401, "AuthenticationError"], + [402, "UnknownError"], + [403, "AuthenticationError"], + [404, "InvalidRequestError"], + [413, "UnknownError"], + [429, "RateLimitError"], + [500, "InternalProviderError"], + [502, "InternalProviderError"], + [503, "InternalProviderError"], + [524, "InternalProviderError"], + [529, "InternalProviderError"] + ] as const + ) { + it.effect("maps HTTP " + status + " using the existing OpenRouter error mapping", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(HttpClientResponse.fromWeb( + request, + new Response( + JSON.stringify({ error: { message: "Decision request failed", code: status } }), + { status, headers: { "content-type": "application/json", "retry-after": "3" } } + ) + )) + )), + Effect.flip + ) + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, reason) + })) + } +}) + +const makeClientLayer = ( + handler: ( + request: HttpClientRequest.HttpClientRequest + ) => Effect.Effect, + apiUrl?: string +) => + OpenRouterClient.layer({ + apiUrl, + apiKey: Redacted.make("or-test-key"), + siteReferrer: "https://example.com", + siteTitle: "Decision contract tests" + }).pipe( + Layer.provide(Layer.succeed(HttpClient.HttpClient, makeHttpClient(handler))) + ) + +const makeHttpClient = ( + handler: ( + request: HttpClientRequest.HttpClientRequest + ) => Effect.Effect +) => + HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + return yield* handler(request) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + +const jsonResponse = ( + request: HttpClientRequest.HttpClientRequest, + body: unknown +): HttpClientResponse.HttpClientResponse => + HttpClientResponse.fromWeb( + request, + new Response(JSON.stringify(body), { + status: 200, + headers: { + "content-type": "application/json" + } + }) + ) + +const getRequestBody = (request: HttpClientRequest.HttpClientRequest) => + Effect.gen(function*() { + const body = request.body + if (body._tag === "Uint8Array") { + const text = new TextDecoder().decode(body.body) + return JSON.parse(text) + } + return yield* Effect.die(new Error("Expected Uint8Array body")) + }) diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts b/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts index 1fa775be21..4d0723c5fb 100644 --- a/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts +++ b/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts @@ -2,8 +2,8 @@ import { Generated, OpenRouterClient, OpenRouterLanguageModel } from "@effect/ai import { assert, describe, it } from "@effect/vitest" import { deepStrictEqual, strictEqual } from "@effect/vitest/utils" import { Array, Context, Effect, Layer, Redacted, Ref, Schema, Stream } from "effect" -import { LanguageModel, Prompt, Tool, Toolkit } from "effect/unstable/ai" -import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http" +import { LanguageModel, Prompt, Tool, Toolkit } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenRouterLanguageModel", () => { describe("strictJsonSchema", () => { diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts b/repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts new file mode 100644 index 0000000000..6e05c34c12 --- /dev/null +++ b/repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts @@ -0,0 +1,144 @@ +import * as OpenRouterSchema from "@effect/ai-openrouter/OpenRouterSchema" +import { assert, describe, it } from "@effect/vitest" +import { Schema } from "effect" + +const choiceAnswer = { + type: "choice", + choice: "billing", + probabilities: { billing: 0.84, technical: 0.159, sales: 0.001 }, + confidence: 0.596 +} + +const scoreAnswer = { + type: "score", + score: 1.6, + legend: { "0": "Calm", "1": "Frustrated", "2": "Very angry" }, + probabilities: { "0": 0.05, "1": 0.3, "2": 0.65 }, + confidence: 0.78 +} + +const noulAnswer = { type: "noul", noul: 0.999 } + +describe("OpenRouterSchema", () => { + it("encodes a decisions request with every question type", () => { + const request = { + model: "openai/gpt-4o-mini", + state: { message: "My card was charged twice.", orderId: "A-104" }, + questions: { + department: { + type: "choice", + instructions: "Which team should handle this", + criteria: { billing: "Payment issues", technical: "Bugs" } + }, + frustration: { + type: "score", + instructions: "How frustrated the customer appears", + criteria: ["Calm", "Frustrated", "Very angry"] + }, + urgent: { + type: "noul", + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + } + } + } as const + + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.DecisionsRequest)(request) + const encoded = Schema.encodeSync(OpenRouterSchema.DecisionsRequest)(decoded) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(encoded)), request) + }) + + it("accepts a request carrying routing and tracing options", () => { + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.DecisionsRequest)({ + model: "openai/gpt-4o-mini", + state: "hello", + questions: { urgent: { type: "noul", instructions: "Urgent" } }, + provider: { order: ["openai"] }, + session_id: "session-1", + user: "user-1" + }) + + assert.strictEqual(decoded.session_id, "session-1") + assert.strictEqual(decoded.user, "user-1") + }) + + it("accepts a noul question without criteria", () => { + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.NoulQuestion)({ + type: "noul", + instructions: "The message conveys urgency" + }) + + assert.strictEqual(decoded.type, "noul") + assert.isUndefined(decoded.criteria) + }) + + it.each([ + { name: "string", state: "a plain string" }, + { name: "object", state: { message: "hi", orderId: "A-1" } }, + { name: "array", state: ["first", "second"] } + ])("accepts a $name state", ({ state }) => { + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.DecisionsRequest)({ + model: "openai/gpt-4o-mini", + state, + questions: { urgent: { type: "noul", instructions: "Urgent" } } + }) + + assert.deepStrictEqual(decoded.state, state) + }) + + it("decodes a response with choice, score and noul answers", () => { + const response = { + id: "dec_123", + model: "openai/gpt-4o-mini", + provider: "OpenAI", + answers: { department: choiceAnswer, frustration: scoreAnswer, urgent: noulAnswer }, + usage: { input_tokens: 312, output_tokens: 48, cost: 0.00042 } + } + + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.DecisionsResponse)(response) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(decoded)), response) + }) + + it("decodes a response without id, provider or cost", () => { + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.DecisionsResponse)({ + model: "openai/gpt-4o-mini", + answers: { urgent: noulAnswer }, + usage: { input_tokens: 10, output_tokens: 2 } + }) + + assert.isUndefined(decoded.id) + assert.isUndefined(decoded.provider) + assert.isUndefined(decoded.usage.cost) + }) + + it.each([ + { name: "choice answer without probabilities", answer: { type: "choice", choice: "billing" } }, + { name: "choice answer without confidence", answer: { type: "choice", choice: "billing", probabilities: {} } }, + { name: "score answer without probabilities", answer: { type: "score", score: 1.6 } }, + { name: "score answer without legend", answer: { type: "score", score: 1.6, probabilities: { "0": 1 } } } + ])("decodes a $name, since the field is optional upstream", ({ answer }) => { + const decoded = Schema.decodeUnknownSync(OpenRouterSchema.Answer)(answer) + + assert.strictEqual(decoded.type, (answer as { type: string }).type) + }) + + it.each([ + { name: "choice answer without choice", answer: { type: "choice", probabilities: { billing: 1 } } }, + { name: "score answer without score", answer: { type: "score", probabilities: { "0": 1 } } }, + { name: "noul answer without noul", answer: { type: "noul" } }, + { name: "answer with an unknown type", answer: { type: "rank", rank: [] } } + ])("rejects a $name", ({ answer }) => { + assert.throws(() => Schema.decodeUnknownSync(OpenRouterSchema.Answer)(answer)) + }) + + it("rejects a response without usage", () => { + assert.throws(() => + Schema.decodeUnknownSync(OpenRouterSchema.DecisionsResponse)({ + model: "openai/gpt-4o-mini", + answers: { urgent: noulAnswer } + }) + ) + }) +}) diff --git a/repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts b/repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts new file mode 100644 index 0000000000..263589b0d6 --- /dev/null +++ b/repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts @@ -0,0 +1,69 @@ +import { type OpenRouterClient, OpenRouterDecisionModel } from "@effect/ai-openrouter" +import { Effect, type Layer, Schema } from "effect" +import { Decision, DecisionModel, type Model } from "effect/ai" +import { describe, expect, it } from "tstyche" + +const Triage = Decision.make({ + input: Schema.String, + decisions: { + department: Decision.classify({ + instructions: "Which team should handle this", + criteria: { billing: "Payments", technical: "Bugs" } + }), + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } +}) + +describe("OpenRouterDecisionModel", () => { + it("model provides DecisionModel and requires OpenRouterClient", () => { + expect(OpenRouterDecisionModel.model("openai/gpt-4o-mini")).type.toBe< + Model.Model<"openrouter", DecisionModel.DecisionModel, OpenRouterClient.OpenRouterClient> + >() + }) + + it("layer provides DecisionModel and requires OpenRouterClient", () => { + expect(OpenRouterDecisionModel.layer({ model: "openai/gpt-4o-mini" })).type.toBe< + Layer.Layer + >() + }) + + it("make builds a DecisionModel from the client service", () => { + expect(OpenRouterDecisionModel.make({ model: "openai/gpt-4o-mini" })).type.toBe< + Effect.Effect + >() + }) + + it("accepts any model id, since OpenRouter ids are namespaced strings", () => { + expect(OpenRouterDecisionModel.model).type.toBeCallableWith("anthropic/claude-sonnet-4") + expect(OpenRouterDecisionModel.model).type.toBeCallableWith("some-new-provider/some-new-model") + }) + + it("satisfies a decide program's DecisionModel requirement", () => { + const program = DecisionModel.decide(Triage, { input: "hello" }).pipe( + Effect.provide(OpenRouterDecisionModel.model("openai/gpt-4o-mini")) + ) + + type Requirements = typeof program extends Effect.Effect ? R : never + + expect().type.toBe() + }) + + it("accepts routing and tracing options in the layer config", () => { + expect(OpenRouterDecisionModel.layer).type.toBeCallableWith({ + model: "openai/gpt-4o-mini", + config: { session_id: "session-1", user: "user-1" } + }) + }) + + it("exposes Config as a context service whose fields are all optional", () => { + expect<{ readonly user: string }>().type.toBeAssignableTo< + typeof OpenRouterDecisionModel.Config.Service + >() + expect<{ readonly session_id: string }>().type.toBeAssignableTo< + typeof OpenRouterDecisionModel.Config.Service + >() + }) +}) diff --git a/repos/effect/packages/ai/typesafe/CHANGELOG.md b/repos/effect/packages/ai/typesafe/CHANGELOG.md new file mode 100644 index 0000000000..bbfea7c980 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/CHANGELOG.md @@ -0,0 +1,95 @@ +# @effect/ai-typesafe + +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8405](https://github.com/Effect-TS/effect/pull/8405) [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d) Thanks @tim-smart! - Add a `probabilityPrecision` option to `DecisionModel.make` that accepts and rescales the sum drift caused by a provider rounding each probability. OpenRouter and TypeSafe decision models set it to two decimals, so rounded distributions such as `0.02 / 0.93 / 0.04` no longer fail with `InvalidOutputError`. Providers that do not opt in keep strict sum validation. +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- [#8287](https://github.com/Effect-TS/effect/pull/8287) [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2) Thanks @tim-smart! - Add `@effect/ai-typesafe`, a `DecisionModel` provider for TypeSafe's System One API. +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 diff --git a/repos/effect/packages/ai/typesafe/README.md b/repos/effect/packages/ai/typesafe/README.md new file mode 100644 index 0000000000..1df4b28e97 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/README.md @@ -0,0 +1,31 @@ +# @effect/ai-typesafe + +TypeSafe System One provider for Effect's `DecisionModel`. Supports classification, ordered ratings, and probabilities through Effect `HttpClient`. + +## Installation + +```sh +npm install effect @effect/ai-typesafe +``` + +## Links + +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/ai-typesafe): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. + +## Usage + +Use `TypeSafeClient.layerConfig()` to read `TYPESAFE_API_KEY`, then provide `TypeSafeDecisionModel.model("jev-latest")` to a decision workflow. Versioned model identifiers such as `jev-1.13.0` are also supported. + +The client does not retry automatically. Rate-limit errors include the provider's retry delay when available. Provider distributions are preserved, not normalized; they must satisfy the core `DecisionModel` validation, including its current `1e-6` sum tolerance. TypeSafe's rounding behavior has not been verified against the live API. diff --git a/repos/effect/packages/ai/typesafe/package.json b/repos/effect/packages/ai/typesafe/package.json new file mode 100644 index 0000000000..566f585b8f --- /dev/null +++ b/repos/effect/packages/ai/typesafe/package.json @@ -0,0 +1,69 @@ +{ + "name": "@effect/ai-typesafe", + "version": "4.0.0", + "type": "module", + "license": "MIT", + "description": "A TypeSafe DecisionModel provider for Effect", + "homepage": "https://effect.website", + "repository": { + "type": "git", + "url": "https://github.com/Effect-TS/effect.git", + "directory": "packages/ai/typesafe" + }, + "bugs": { + "url": "https://github.com/Effect-TS/effect/issues" + }, + "tags": [ + "typescript", + "ai", + "typesafe" + ], + "keywords": [ + "typescript", + "ai", + "typesafe" + ], + "sideEffects": [], + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts", + "./*": "./src/*.ts", + "./internal/*": null, + "./index": null, + "./*/index": null + }, + "files": [ + "src/**/*.ts", + "dist/**/*.js", + "dist/**/*.js.map", + "dist/**/*.d.ts", + "dist/**/*.d.ts.map", + "AGENTS.md", + "CLAUDE.md", + "ai-docs/**/*" + ], + "publishConfig": { + "access": "public", + "provenance": true, + "exports": { + "./package.json": "./package.json", + ".": "./dist/index.js", + "./*": "./dist/*.js", + "./internal/*": null, + "./index": null, + "./*/index": null + } + }, + "scripts": { + "codegen": "effect-utils codegen", + "build": "tsc -b tsconfig.json && pnpm babel", + "babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps", + "check": "tsc -b tsconfig.json" + }, + "devDependencies": { + "effect": "workspace:^" + }, + "peerDependencies": { + "effect": "workspace:^" + } +} diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts b/repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts new file mode 100644 index 0000000000..db42a29686 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts @@ -0,0 +1,150 @@ +/** + * HTTP client for TypeSafe System One and model discovery. + * + * @stability unstable + * @since 4.0.0 + */ +import * as AiError from "effect/ai/AiError" +import * as Config from "effect/Config" +import * as Context from "effect/Context" +import * as Effect from "effect/Effect" +import { flow, identity } from "effect/Function" +import * as HttpClient from "effect/http/HttpClient" +import type * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import * as Layer from "effect/Layer" +import * as Redacted from "effect/Redacted" +import type * as Schema from "effect/Schema" +import * as Errors from "./internal/errors.ts" +import { TypeSafeConfig } from "./TypeSafeConfig.ts" +import * as TypeSafeSchema from "./TypeSafeSchema.ts" + +/** + * Low-level TypeSafe operations. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export interface Service { + readonly client: HttpClient.HttpClient + readonly systemOne: ( + request: typeof TypeSafeSchema.SystemOneRequest.Encoded + ) => Effect.Effect + readonly listModels: () => Effect.Effect +} + +/** + * TypeSafe client service. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class TypeSafeClient extends Context.Service()("@effect/ai-typesafe/TypeSafeClient") {} + +/** + * Authentication, endpoint and HTTP customization options. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface Options { + readonly apiKey?: Redacted.Redacted | undefined + readonly apiUrl?: string | undefined + readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined +} + +const decodeSystemOne = HttpClientResponse.schemaBodyJson(TypeSafeSchema.SystemOneResponse) +const decodeListModels = HttpClientResponse.schemaBodyJson(TypeSafeSchema.ListModelsResponse) + +/** + * Builds a client without automatic retries. Scoped transforms run after the constructor transform. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = Effect.fnUntraced( + function*(options: Options): Effect.fn.Return { + const base = yield* HttpClient.HttpClient + const client = base.pipe( + HttpClient.mapRequest(flow( + HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.typesafe.ai/v1"), + options.apiKey ? HttpClientRequest.bearerToken(Redacted.value(options.apiKey)) : identity, + HttpClientRequest.acceptJson + )), + HttpClient.filterStatusOk, + options.transformClient ?? identity + ) + const resolveClient = Effect.map( + TypeSafeConfig.getOrUndefined, + (config) => config?.transformClient?.(client) ?? client + ) + const execute = ( + request: HttpClientRequest.HttpClientRequest, + decode: ( + response: HttpClientResponse.HttpClientResponse + ) => Effect.Effect, + method: string + ) => + resolveClient.pipe( + Effect.flatMap((client) => client.execute(request)), + Effect.flatMap(decode), + Effect.catchTags({ + HttpClientError: (error) => Errors.mapHttpClientError(error, method), + SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, method)) + }) + ) + return TypeSafeClient.of({ + client, + systemOne: (payload) => + HttpClientRequest.bodyJson(HttpClientRequest.post("/systemone"), payload).pipe( + Effect.mapError((error) => + AiError.make({ + module: "TypeSafeClient", + method: "systemOne", + reason: new AiError.InvalidRequestError({ description: String(error) }) + }) + ), + Effect.flatMap((request) => execute(request, decodeSystemOne, "systemOne")) + ), + listModels: () => execute(HttpClientRequest.get("/models"), decodeListModels, "listModels") + }) + } +) + +/** + * Provides a client from explicit options. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer = (options: Options): Layer.Layer => + Layer.effect(TypeSafeClient, make(options)) + +/** + * Provides a client from configuration, defaulting to TYPESAFE_API_KEY. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layerConfig = (options?: { + readonly apiKey?: Config.Config | undefined> | undefined + readonly apiUrl?: Config.Config | undefined + readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined +}): Layer.Layer => + Layer.effect( + TypeSafeClient, + Effect.gen(function*() { + return yield* make({ + apiKey: yield* (options?.apiKey ?? Config.Redacted("TYPESAFE_API_KEY")), + apiUrl: options?.apiUrl ? yield* options.apiUrl : undefined, + transformClient: options?.transformClient + }) + }) + ) diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts b/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts new file mode 100644 index 0000000000..851216a17c --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts @@ -0,0 +1,68 @@ +/** + * Scoped HTTP client customization for TypeSafe requests. + * + * @since 4.0.0 + */ +import * as Context from "effect/Context" +import * as Effect from "effect/Effect" +import { dual } from "effect/Function" +import type { HttpClient } from "effect/http/HttpClient" + +/** + * Scoped configuration read when executing TypeSafe requests. + * + * @see {@link withClientTransform} for scoping an HTTP client transformation + * + * @category services + * @since 4.0.0 + */ +export class TypeSafeConfig extends Context.Service< + TypeSafeConfig, + TypeSafeConfig.Service +>()("@effect/ai-typesafe/TypeSafeConfig") { + /** + * Reads the current configuration, if present. + * + * @since 4.0.0 + */ + static readonly getOrUndefined: Effect.Effect = Effect.map( + Effect.context(), + Context.getOrUndefined(TypeSafeConfig) + ) +} + +/** + * Types used by the `TypeSafeConfig` context service. + * + * @since 4.0.0 + */ +export declare namespace TypeSafeConfig { + /** + * Per-request HTTP client configuration. + * + * @category services + * @since 4.0.0 + */ + export interface Service { + readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined + } +} + +/** + * Transforms the TypeSafe HTTP client for requests made by the supplied effect. + * Replaces any existing scoped transform; compose them manually to apply both. + * + * @category configuration + * @since 4.0.0 + */ +export const withClientTransform: { + (transform: (client: HttpClient) => HttpClient): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, transform: (client: HttpClient) => HttpClient): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + transformClient: (client: HttpClient) => HttpClient +) => + Effect.flatMap( + TypeSafeConfig.getOrUndefined, + (config) => Effect.provideService(self, TypeSafeConfig, { ...config, transformClient }) + )) diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts b/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts new file mode 100644 index 0000000000..91fead032b --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts @@ -0,0 +1,113 @@ +/** + * TypeSafe's System One implementation of DecisionModel. + * + * @stability unstable + * @since 4.0.0 + */ +import * as DecisionModel from "effect/ai/DecisionModel" +import * as AiModel from "effect/ai/Model" +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" +import { TypeSafeClient } from "./TypeSafeClient.ts" +import type * as TypeSafeSchema from "./TypeSafeSchema.ts" + +/** + * Known TypeSafe model identifiers. Constructors also accept custom identifiers. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Model = "jev-latest" | "jev-preview" | "jev-1.13.0" + +/** + * Creates a decision model with TypeSafe provider metadata. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const model = ( + model: Model | (string & {}) +): AiModel.Model<"typesafe", DecisionModel.DecisionModel, TypeSafeClient> => + AiModel.make("typesafe", model, layer({ model })) + +/** + * Builds a decision service. Probabilities arrive rounded to two decimals, so + * DecisionModel rescales small sum drift when it validates distributions and + * derives rating labels. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = Effect.fnUntraced( + function*( + options: { readonly model: Model | (string & {}) } + ): Effect.fn.Return { + const client = yield* TypeSafeClient + return yield* DecisionModel.make({ + probabilityPrecision: 2, + decide: Effect.fnUntraced(function*({ state, decisions }) { + const questions: Record = Object.create(null) + for (const [key, decision] of Object.entries(decisions)) { + switch (decision._tag) { + case "Classify": + questions[key] = { type: "choice", instructions: decision.instructions, criteria: decision.criteria } + break + case "Rate": + questions[key] = { type: "score", instructions: decision.instructions, criteria: decision.criteria } + break + case "Probability": + questions[key] = { type: "noul", instructions: decision.instructions, criteria: decision.criteria } + break + } + } + const response = yield* client.systemOne({ model: options.model, state, questions }) + const answers: Record = Object.create(null) + for (const [key, decision] of Object.entries(decisions)) { + const answer = response.answers[key] + switch (answer?.type) { + case "choice": + answers[key] = { + _tag: "Classify", + label: answer.choice, + probabilities: answer.probabilities, + confidence: answer.confidence + } + break + case "score": { + const levels = decision._tag === "Rate" ? decision.criteria : [] + const probabilities: Record = Object.create(null) + for (let index = 0; index < levels.length; index++) { + const probability = answer.probabilities[String(index)] + if (probability !== undefined) probabilities[levels[index]] = probability + } + answers[key] = { _tag: "Rate", rating: answer.score, probabilities, confidence: answer.confidence } + break + } + case "noul": + answers[key] = { _tag: "Probability", probability: answer.noul } + break + } + } + return { + answers, + usage: { inputTokens: response.usage?.input_tokens, outputTokens: response.usage?.output_tokens } + } + }) + }) + } +) + +/** + * Provides DecisionModel using an existing TypeSafeClient. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer = ( + options: { readonly model: Model | (string & {}) } +): Layer.Layer => + Layer.effect(DecisionModel.DecisionModel, make(options)) diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts b/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts new file mode 100644 index 0000000000..5f034aca51 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts @@ -0,0 +1,146 @@ +/** + * Codecs for the TypeSafe System One API. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Schema from "effect/Schema" + +/** + * A classification question. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ChoiceQuestion = Schema.Struct({ + type: Schema.Literal("choice"), + instructions: Schema.String, + criteria: Schema.Record(Schema.String, Schema.String) +}) + +/** + * An ordered rating question. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ScoreQuestion = Schema.Struct({ + type: Schema.Literal("score"), + instructions: Schema.String, + criteria: Schema.Array(Schema.String) +}) + +/** + * A probability question. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const NoulQuestion = Schema.Struct({ + type: Schema.Literal("noul"), + instructions: Schema.String, + criteria: Schema.optional(Schema.Struct({ false: Schema.String, true: Schema.String })) +}) + +/** + * A System One question. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Question = Schema.Union([ChoiceQuestion, ScoreQuestion, NoulQuestion]) + +/** + * A batch of questions against encoded state. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const SystemOneRequest = Schema.Struct({ + model: Schema.String, + state: Schema.Json, + questions: Schema.Record(Schema.String, Question) +}) + +/** + * A classification distribution. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ChoiceAnswer = Schema.Struct({ + type: Schema.Literal("choice"), + choice: Schema.String, + probabilities: Schema.Record(Schema.String, Schema.Number), + confidence: Schema.Number +}) + +/** + * A zero-based rating and index-keyed distribution. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ScoreAnswer = Schema.Struct({ + type: Schema.Literal("score"), + score: Schema.Number, + probabilities: Schema.Record(Schema.String, Schema.Number), + confidence: Schema.Number, + legend: Schema.optional(Schema.Record(Schema.String, Schema.String)) +}) + +/** + * A Bernoulli probability. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const NoulAnswer = Schema.Struct({ type: Schema.Literal("noul"), noul: Schema.Number }) + +/** + * A System One answer. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Answer = Schema.Union([ChoiceAnswer, ScoreAnswer, NoulAnswer]) + +/** + * Answers and token usage for a batch. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const SystemOneResponse = Schema.Struct({ + model: Schema.String, + answers: Schema.Record(Schema.String, Answer), + usage: Schema.optional(Schema.Struct({ + input_tokens: Schema.optional(Schema.Number), + output_tokens: Schema.optional(Schema.Number) + })) +}) + +/** + * Available TypeSafe models. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ListModelsResponse = Schema.Struct({ + models: Schema.Array(Schema.Struct({ + name: Schema.String, + description: Schema.optional(Schema.String), + release_date: Schema.optional(Schema.String) + })) +}) diff --git a/repos/effect/packages/ai/typesafe/src/index.ts b/repos/effect/packages/ai/typesafe/src/index.ts new file mode 100644 index 0000000000..872b30078b --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/index.ts @@ -0,0 +1,28 @@ +/** + * @since 4.0.0 + */ + +// @barrel + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as TypeSafeClient from "./TypeSafeClient.ts" + +/** + * @since 4.0.0 + */ +export * as TypeSafeConfig from "./TypeSafeConfig.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as TypeSafeDecisionModel from "./TypeSafeDecisionModel.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as TypeSafeSchema from "./TypeSafeSchema.ts" diff --git a/repos/effect/packages/ai/typesafe/src/internal/errors.ts b/repos/effect/packages/ai/typesafe/src/internal/errors.ts new file mode 100644 index 0000000000..388bcdb186 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/src/internal/errors.ts @@ -0,0 +1,113 @@ +import * as AiError from "effect/ai/AiError" +import * as Duration from "effect/Duration" +import * as Effect from "effect/Effect" +import type * as HttpClientError from "effect/http/HttpClientError" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as Num from "effect/Number" +import * as Option from "effect/Option" +import * as Redactable from "effect/Redactable" +import * as Schema from "effect/Schema" + +const requestDetails = (request: HttpClientRequest.HttpClientRequest) => ({ + method: request.method, + url: request.url, + urlParams: Array.from(request.urlParams), + hash: Option.getOrUndefined(request.hash), + headers: Redactable.redact(request.headers) as Record +}) + +const ErrorBody = Schema.Struct({ + message: Schema.optional(Schema.String), + type: Schema.optional(Schema.String), + code: Schema.optional(Schema.String) +}) + +const decodeErrorBody = Schema.decodeUnknownOption(ErrorBody) + +const nonNegative = (value: string | undefined): number | undefined => { + if (value === undefined) return undefined + const parsed = Option.getOrUndefined(Num.parse(value)) + return parsed !== undefined && Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined +} + +const retryAfter = (headers: Readonly>) => { + const ms = nonNegative(headers["retry-after-ms"]) + if (ms !== undefined) return Duration.millis(ms) + const raw = headers["retry-after"] + if (raw === undefined || raw.trim() === "") return undefined + const seconds = nonNegative(raw) + if (seconds !== undefined) return Duration.seconds(seconds) + const date = Date.parse(raw) + return Number.isNaN(date) ? undefined : Duration.millis(Math.max(0, date - Date.now())) +} + +export const mapSchemaError = (error: Schema.SchemaError, method: string): AiError.AiError => + AiError.make({ + module: "TypeSafeClient", + method, + reason: AiError.InvalidOutputError.fromSchemaError(error) + }) + +export const mapHttpClientError = Effect.fnUntraced( + function*(error: HttpClientError.HttpClientError, method: string): Effect.fn.Return { + const source = error.reason + let reason: AiError.AiErrorReason + switch (source._tag) { + case "TransportError": + case "EncodeError": + case "InvalidUrlError": + reason = new AiError.NetworkError({ + reason: source._tag, + description: source.description, + request: requestDetails(source.request) + }) + break + case "DecodeError": + case "EmptyBodyError": + reason = new AiError.InvalidOutputError({ + description: source.description ?? "Failed to decode TypeSafe response" + }) + break + case "StatusCodeError": { + const { request, response } = source + const body = yield* response.text.pipe(Effect.catch(() => Effect.succeed(source.description))) + const parsed = yield* Effect.try(() => JSON.parse(body ?? "")).pipe(Effect.orElseSucceed(() => undefined)) + const details = Option.getOrUndefined(decodeErrorBody(parsed)) + const http = { + request: requestDetails(request), + response: { status: response.status, headers: Redactable.redact(response.headers) as Record }, + body + } + const description = AiError.buildErrorDescription({ + status: response.status, + method: request.method, + url: request.url, + body, + message: details?.message, + errorCode: details?.code, + errorType: details?.type, + requestId: response.headers["x-typesafe-request-id"] + }) + if (response.status === 429) { + reason = new AiError.RateLimitError({ + retryAfter: retryAfter(response.headers), + http, + metadata: { + typesafe: { + requestId: response.headers["x-typesafe-request-id"] ?? null, + errorCode: details?.code ?? null, + errorType: details?.type ?? null + } + } + }) + } else if (response.status === 404 || response.status === 422) { + reason = new AiError.InvalidRequestError({ description, http }) + } else { + reason = AiError.reasonFromHttpStatus({ status: response.status, description, http }) + } + break + } + } + return yield* AiError.make({ module: "TypeSafeClient", method, reason }) + } +) diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts b/repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts new file mode 100644 index 0000000000..c3340a5a97 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts @@ -0,0 +1,568 @@ +import * as TypeSafeClient from "@effect/ai-typesafe/TypeSafeClient" +import * as TypeSafeConfig from "@effect/ai-typesafe/TypeSafeConfig" +import { assert, describe, it } from "@effect/vitest" +import { Config, ConfigProvider, Context, Duration, Effect, Layer, Redacted } from "effect" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import { vi } from "vitest" +const systemOneRequest = { + state: "My card was charged twice.", + model: "jev-latest", + questions: { + urgent: { + type: "noul", + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + } + } +} as const + +const systemOneBody = { + model: "jev-latest", + answers: { + urgent: { type: "noul", noul: 0.99 } + }, + usage: { input_tokens: 360, output_tokens: 39 } +} + +describe("TypeSafeClient", () => { + describe("make", () => { + it.effect("sets Bearer token from apiKey", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.headers["authorization"], "Bearer ts-test-12345") + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("ts-test-12345") + })))) + + it.effect("posts systemone requests to the default URL as JSON", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const response = yield* client.systemOne(systemOneRequest) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.method, "POST") + assert.strictEqual(requests[0]?.url, "https://api.typesafe.ai/v1/systemone") + assert.strictEqual(requests[0]?.headers["content-type"], "application/json") + assert.deepStrictEqual(yield* getRequestBody(requests[0]), systemOneRequest) + assert.strictEqual(response.model, "jev-latest") + assert.deepStrictEqual(response.usage, { input_tokens: 360, output_tokens: 39 }) + }).pipe(Effect.provide(makeTestLayer()))) + + it.effect("uses custom apiUrl when provided", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.url, "https://custom.api.com/v2/systemone") + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("test-key"), + apiUrl: "https://custom.api.com/v2" + })))) + + it.effect("lists models from the models endpoint", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const response = yield* client.listModels() + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.method, "GET") + assert.strictEqual(requests[0]?.url, "https://api.typesafe.ai/v1/models") + assert.deepStrictEqual(response.models, [ + { name: "jev-1.13.0", description: "Jev 1.13", release_date: "2026-08-01" } + ]) + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 200, + body: { + models: [{ name: "jev-1.13.0", description: "Jev 1.13", release_date: "2026-08-01" }] + } + })))) + + it.effect("applies transformClient option", () => { + let transformApplied = false + return Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest) + assert.isTrue(transformApplied) + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("test-key"), + transformClient: (client) => { + transformApplied = true + return client + } + }))) + }) + + it.effect("exposes transformed HttpClient via client field", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.client.execute(HttpClientRequest.get("/models")) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.url, "https://api.typesafe.ai/v1/models") + assert.strictEqual(requests[0]?.headers["authorization"], "Bearer test-key") + assert.strictEqual(requests[0]?.headers["x-client-field"], "enabled") + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("test-key"), + transformClient: (client) => + client.pipe(HttpClient.mapRequest(HttpClientRequest.setHeader("x-client-field", "enabled"))) + })))) + + it.effect("applies TypeSafeConfig transformClient after options transformClient", () => { + let optionsTransformApplied = false + let configTransformApplied = false + + return Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest).pipe( + TypeSafeConfig.withClientTransform((client) => { + configTransformApplied = true + return client.pipe( + HttpClient.mapRequest(HttpClientRequest.setHeader("x-typesafe-transform", "config")) + ) + }) + ) + + const requests = yield* MockHttpClient.requests + assert.isTrue(optionsTransformApplied) + assert.isTrue(configTransformApplied) + assert.strictEqual(requests[0]?.headers["x-typesafe-transform"], "config") + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("test-key"), + transformClient: (client) => { + optionsTransformApplied = true + return client.pipe( + HttpClient.mapRequest(HttpClientRequest.setHeader("x-typesafe-transform", "options")) + ) + } + }))) + }) + }) + + describe("layer", () => { + it.effect("creates working service", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + assert.isNotNull(client.client) + }).pipe(Effect.provide(makeTestLayer()))) + + it.effect("layerConfig loads from Config", () => { + const configProvider = ConfigProvider.fromEnv({ + env: { + MY_API_KEY: "ts-config-key", + MY_API_URL: "https://config.api.com/v1" + } + }) + + return Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.headers["authorization"], "Bearer ts-config-key") + assert.strictEqual(requests[0]?.url, "https://config.api.com/v1/systemone") + }).pipe(Effect.provide(makeConfigTestLayer(configProvider))) + }) + + it.effect("layerConfig defaults to TYPESAFE_API_KEY", () => { + const configProvider = ConfigProvider.fromEnv({ + env: { + TYPESAFE_API_KEY: "ts-env-key" + } + }) + + return Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + yield* client.systemOne(systemOneRequest) + + const requests = yield* MockHttpClient.requests + assert.strictEqual(requests[0]?.headers["authorization"], "Bearer ts-env-key") + assert.strictEqual(requests[0]?.url, "https://api.typesafe.ai/v1/systemone") + }).pipe(Effect.provide( + TypeSafeClient.layerConfig().pipe( + Layer.provideMerge(HttpClientLayer), + Layer.provide(Layer.succeed(MockTypeSafeResponse, { response: defaultResponse })), + Layer.provide(ConfigProvider.layer(configProvider)) + ) + )) + }) + }) + + describe("request behavior", () => { + it.effect("redacts the authorization header in AI error context", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "InvalidRequestError") + if (result.reason._tag !== "InvalidRequestError" || result.reason.http === undefined) { + return yield* Effect.die(new Error("Expected InvalidRequestError with HTTP context")) + } + assert.strictEqual(String(result.reason.http.request.headers["authorization"]), "") + assert.strictEqual(result.reason.http.response?.status, 422) + }).pipe(Effect.provide(makeTestLayer({ + apiKey: Redacted.make("secret-key") + }, { + status: 422, + body: { message: "questions must not be empty" } + })))) + }) + + describe("error mapping", () => { + it.effect("maps undocumented 409 status to the generic UnknownError fallback", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "UnknownError") + if (result.reason._tag === "UnknownError") { + assert.strictEqual(result.reason.http?.response?.status, 409) + assert.include(result.reason.description ?? "", "Conflict") + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 409, + body: { message: "Conflict" } + })))) + + it.effect("retains 429 diagnostics in provider metadata", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "RateLimitError") + if (result.reason._tag === "RateLimitError") { + assert.deepStrictEqual(result.reason.metadata.typesafe, { + requestId: "req_typesafe_429", + errorCode: "rate_limit_exceeded", + errorType: "requests" + }) + assert.strictEqual(result.reason.http?.response?.status, 429) + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 429, + body: { message: "Slow down", code: "rate_limit_exceeded", type: "requests" }, + headers: { "x-typesafe-request-id": "req_typesafe_429" } + })))) + + describe("HTTP-date Retry-After", { concurrent: false }, () => { + for ( + const { date, expected, name } of [ + { name: "future", date: "Fri, 18 Sep 2026 12:00:30 GMT", expected: 30000 }, + { name: "past", date: "Fri, 18 Sep 2026 11:59:30 GMT", expected: 0 } + ] + ) { + it.effect("honors a " + name + " HTTP-date Retry-After", () => + Effect.gen(function*() { + const now = vi.spyOn(Date, "now").mockReturnValue(Date.parse("2026-09-18T12:00:00Z")) + try { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "RateLimitError") + if (result.reason._tag === "RateLimitError") { + assert.isDefined(result.reason.retryAfter) + assert.strictEqual(Duration.toMillis(result.reason.retryAfter!), expected) + } + } finally { + now.mockRestore() + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 429, + body: { message: "Rate limit exceeded" }, + headers: { "retry-after": date } + })))) + } + }) + + it.effect("maps TransportError to NetworkError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.make({ + apiKey: Redacted.make("test-key") + }) + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result._tag, "AiError") + assert.strictEqual(result.module, "TypeSafeClient") + assert.strictEqual(result.method, "systemOne") + assert.strictEqual(result.reason._tag, "NetworkError") + }).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, makeTransportErrorHttpClient())))) + + it.effect("maps 400 status to InvalidRequestError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result._tag, "AiError") + assert.strictEqual(result.module, "TypeSafeClient") + assert.strictEqual(result.method, "systemOne") + assert.strictEqual(result.reason._tag, "InvalidRequestError") + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 400, + body: { message: "Bad request" } + })))) + + it.effect("maps 401 status to AuthenticationError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result._tag, "AiError") + assert.strictEqual(result.reason._tag, "AuthenticationError") + if (result.reason._tag === "AuthenticationError") { + assert.strictEqual(result.reason.kind, "InvalidKey") + assert.include(result.reason.description ?? "", "Invalid API key") + assert.include(result.reason.description ?? "", "req_typesafe") + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 401, + body: { message: "Invalid API key" }, + headers: { "x-typesafe-request-id": "req_typesafe" } + })))) + + it.effect("maps 403 status to AuthenticationError with InsufficientPermissions", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "AuthenticationError") + if (result.reason._tag === "AuthenticationError") { + assert.strictEqual(result.reason.kind, "InsufficientPermissions") + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 403, + body: { message: "Access denied" } + })))) + + it.effect("maps 404 status to InvalidRequestError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "InvalidRequestError") + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 404, + body: { message: "Not found" } + })))) + + it.effect("maps 422 status to InvalidRequestError reason with the message", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "InvalidRequestError") + if (result.reason._tag === "InvalidRequestError") { + assert.include(result.reason.description, "criteria must contain at least two levels") + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 422, + body: { message: "criteria must contain at least two levels" } + })))) + + it.effect("maps 429 status to RateLimitError honoring Retry-After seconds", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "RateLimitError") + if (result.reason._tag === "RateLimitError") { + assert.isDefined(result.reason.retryAfter) + assert.strictEqual(Duration.toMillis(result.reason.retryAfter!), 2000) + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 429, + body: { message: "Rate limit exceeded" }, + headers: { "retry-after": "2" } + })))) + + it.effect("maps 429 status to RateLimitError honoring retry-after-ms", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "RateLimitError") + if (result.reason._tag === "RateLimitError") { + assert.isDefined(result.reason.retryAfter) + assert.strictEqual(Duration.toMillis(result.reason.retryAfter!), 1500) + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 429, + body: { message: "Rate limit exceeded" }, + headers: { "retry-after-ms": "1500" } + })))) + + it.effect("maps 429 status without retry headers to RateLimitError without retryAfter", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "RateLimitError") + if (result.reason._tag === "RateLimitError") { + assert.isUndefined(result.reason.retryAfter) + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 429, + body: { message: "Rate limit exceeded" } + })))) + + it.effect("maps 529 status to InternalProviderError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "InternalProviderError") + if (result.reason._tag === "InternalProviderError") { + assert.strictEqual(result.reason.http?.response?.status, 529) + } + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 529, + body: { message: "Overloaded" } + })))) + + it.effect("maps 500 status to InternalProviderError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result.reason._tag, "InternalProviderError") + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 500, + body: { message: "Internal error" } + })))) + + it.effect("maps a malformed success body to InvalidOutputError reason", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.systemOne(systemOneRequest).pipe(Effect.flip) + + assert.strictEqual(result._tag, "AiError") + assert.strictEqual(result.method, "systemOne") + assert.strictEqual(result.reason._tag, "InvalidOutputError") + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 200, + body: { model: "jev-latest", answers: { urgent: { type: "noul" } } } + })))) + + it.effect("maps listModels failures with the listModels method", () => + Effect.gen(function*() { + const client = yield* TypeSafeClient.TypeSafeClient + const result = yield* client.listModels().pipe(Effect.flip) + + assert.strictEqual(result.module, "TypeSafeClient") + assert.strictEqual(result.method, "listModels") + assert.strictEqual(result.reason._tag, "AuthenticationError") + }).pipe(Effect.provide(makeTestLayer(undefined, { + status: 401, + body: { message: "Invalid API key" } + })))) + }) +}) + +interface MockResponse { + readonly status?: number + readonly body: unknown + readonly headers?: Record +} + +class MockTypeSafeResponse extends Context.Service()("MockTypeSafeResponse") {} + +class MockHttpClient extends Context.Service> +}>()("MockHttpClient") { + static requests = MockHttpClient.use((client) => client.requests) +} + +const makeHttpClientContext = Effect.gen(function*() { + const capturedRequests: Array = [] + const mock = yield* MockTypeSafeResponse + + const httpClient = HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + capturedRequests.push(request) + return makeResponse(request, mock.response) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + + const mockHttpClient: MockHttpClient["Service"] = { + requests: Effect.sync(() => capturedRequests) + } + + return Context.make(HttpClient.HttpClient, httpClient).pipe( + Context.add(MockHttpClient, mockHttpClient) + ) +}) + +const HttpClientLayer = Layer.effectContext(makeHttpClientContext) + +const defaultResponse: MockResponse = { + status: 200, + body: systemOneBody +} + +const makeTestLayer = ( + options: TypeSafeClient.Options = { apiKey: Redacted.make("test-key") }, + response: MockResponse = defaultResponse +) => + TypeSafeClient.layer(options).pipe( + Layer.provideMerge(HttpClientLayer), + Layer.provide(Layer.succeed(MockTypeSafeResponse, { response })) + ) + +const makeConfigTestLayer = (configProvider: ConfigProvider.ConfigProvider) => + TypeSafeClient.layerConfig({ + apiKey: Config.Redacted("MY_API_KEY"), + apiUrl: Config.String("MY_API_URL") + }).pipe( + Layer.provideMerge(HttpClientLayer), + Layer.provide(Layer.succeed(MockTypeSafeResponse, { response: defaultResponse })), + Layer.provide(ConfigProvider.layer(configProvider)) + ) + +const makeTransportErrorHttpClient = (): HttpClient.HttpClient => + HttpClient.makeWith( + (requestEffect) => + Effect.flatMap(requestEffect, (request) => + Effect.fail( + new HttpClientError.HttpClientError({ + reason: new HttpClientError.TransportError({ + request, + cause: new Error("Connection refused") + }) + }) + )), + Effect.succeed + ) + +const makeResponse = ( + request: HttpClientRequest.HttpClientRequest, + response: MockResponse +): HttpClientResponse.HttpClientResponse => + HttpClientResponse.fromWeb( + request, + new Response(JSON.stringify(response.body), { + status: response.status ?? 200, + headers: { + "content-type": "application/json", + ...response.headers + } + }) + ) + +const getRequestBody = (request: HttpClientRequest.HttpClientRequest) => + Effect.gen(function*() { + const body = request.body + if (body._tag === "Uint8Array") { + const text = new TextDecoder().decode(body.body) + return JSON.parse(text) + } + return yield* Effect.die(new Error("Expected Uint8Array body")) + }) diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts b/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts new file mode 100644 index 0000000000..12909ed0e4 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts @@ -0,0 +1,710 @@ +import { TypeSafeClient, TypeSafeDecisionModel } from "@effect/ai-typesafe" +import { assert, describe, it } from "@effect/vitest" +import { Effect, Layer, Redacted, Schema } from "effect" +import { Decision, DecisionModel, Model } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" + +const Ticket = Schema.Struct({ + message: Schema.String, + orderId: Schema.String +}) + +const TicketTriage = Decision.make({ + input: Ticket, + decisions: { + department: Decision.classify({ + instructions: "Which team should handle this", + criteria: { + billing: "Payment or subscription issues", + technical: "Bugs or integration problems", + sales: "Pricing or account questions" + } + }), + frustration: Decision.rate({ + instructions: "How frustrated the customer appears", + criteria: ["Calm, just stating facts", "Frustrated but civil", "Very angry, strong language"] + }), + urgent: Decision.probability({ + instructions: "The message conveys urgency or time-sensitivity", + criteria: { + false: "No time pressure", + true: "Needs action now" + } + }) + } +}) + +const ticket = { + message: "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. Please help ASAP.", + orderId: "A-104" +} + +const systemOneResponse = { + model: "jev-latest", + answers: { + department: { + type: "choice", + choice: "billing", + probabilities: { billing: 0.84, technical: 0.159, sales: 0.001 }, + confidence: 0.596 + }, + frustration: { + type: "score", + score: 1.6, + legend: { + "0": "Calm, just stating facts", + "1": "Frustrated but civil", + "2": "Very angry, strong language" + }, + probabilities: { "0": 0.05, "1": 0.3, "2": 0.65 }, + confidence: 0.78 + }, + urgent: { + type: "noul", + noul: 0.999 + } + }, + usage: { input_tokens: 312, output_tokens: 48 } +} + +describe("TypeSafeDecisionModel", () => { + it.effect("accepts rounded choice probabilities that total 0.99", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + neutral3: Decision.classify({ + instructions: "Choose a sentiment", + criteria: { negative: "Negative", neutral: "Neutral", positive: "Positive" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "It is fine" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { + neutral3: { + type: "choice", + choice: "neutral", + probabilities: { negative: 0.02, neutral: 0.93, positive: 0.04 }, + confidence: 0.93 + } + }, + usage: { input_tokens: 10, output_tokens: 5 } + })) + )) + ) + + assert.strictEqual(answers.neutral3.label, "neutral") + assert.closeTo(Object.values(answers.neutral3.probabilities).reduce((sum, value) => sum + value, 0), 1, 1e-6) + })) + + it.effect("normalizes rounded score probabilities", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { score: Decision.rate({ instructions: "Rate", criteria: ["low", "mid", "high"] }) } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "test" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { + score: { type: "score", score: 1, probabilities: { "0": 0.1, "1": 0.7, "2": 0.19 }, confidence: 0.7 } + }, + usage: { input_tokens: 1, output_tokens: 1 } + })) + )) + ) + assert.closeTo(Object.values(answers.score.probabilities).reduce((sum, value) => sum + value, 0), 1, 1e-6) + })) + + it.effect("rejects a distribution outside two-decimal rounding", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { choice: Decision.classify({ instructions: "Pick", criteria: { a: "A", b: "B" } }) } + }) + const error = yield* DecisionModel.decide(definition, { input: "test" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { choice: { type: "choice", choice: "a", probabilities: { a: 0.49, b: 0.49 }, confidence: 0.5 } } + })) + )), + Effect.flip + ) + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + for ( + const [name, probabilities] of [ + ["out-of-range", { a: 1.01, b: -0.02 }], + ["all-zero", { a: 0, b: 0 }], + ["missing label", { a: 0.5 }], + ["non-finite", { a: Infinity, b: 0.5 }] + ] as const + ) { + it.effect("rejects " + name + " choice probabilities", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { choice: Decision.classify({ instructions: "Pick", criteria: { a: "A", b: "B" } }) } + }) + const error = yield* DecisionModel.decide(definition, { input: "test" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { choice: { type: "choice", choice: "a", probabilities, confidence: 0.5 } } + })) + )), + Effect.flip + ) + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + } + + it.effect("omits absent probability criteria from the encoded question", () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ instructions: "Needs action now" }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help ASAP" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + assert.deepStrictEqual(questions.urgent, { + type: "noul", + instructions: "Needs action now" + }) + + return jsonResponse(request, { + model: "jev-latest", + answers: { urgent: { type: "noul", noul: 0.75 } }, + usage: { input_tokens: 20, output_tokens: 4 } + }) + }) + )) + ) + + assert.deepStrictEqual(answers.urgent, { probability: 0.75 }) + })) + + for (const key of ["__proto__", "constructor", "toString"]) { + it.effect("round trips a classification label named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + category: Decision.classify({ + instructions: "Choose a category", + criteria: { [key]: "Special category", ordinary: "Ordinary category" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + const criteria = questions.category.criteria + + assert.isTrue(Object.hasOwn(criteria, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(criteria, key)?.enumerable) + assert.deepStrictEqual(Object.keys(criteria), [key, "ordinary"]) + assert.strictEqual(criteria[key], "Special category") + assert.strictEqual(Object.getPrototypeOf(criteria), Object.prototype) + + return jsonResponse(request, { + model: "jev-latest", + answers: { + category: { + type: "choice", + choice: key, + probabilities: { [key]: 0.75, ordinary: 0.25 }, + confidence: 0.5 + } + } + }) + }) + )) + ) + const { label, probabilities } = answers.category + + assert.isTrue(Object.hasOwn(probabilities, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(probabilities, key)?.enumerable) + assert.deepStrictEqual(Object.keys(probabilities), [key, "ordinary"]) + assert.deepStrictEqual(probabilities, { [key]: 0.75, ordinary: 0.25 }) + assert.strictEqual(label, key) + assert.strictEqual(Object.getPrototypeOf(probabilities), null) + })) + + it.effect("maps a score distribution to a rate level named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + intensity: Decision.rate({ instructions: "How intense", criteria: ["low", key, "high"] }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + assert.deepStrictEqual(questions.intensity.criteria, ["low", key, "high"]) + + return jsonResponse(request, { + model: "jev-latest", + answers: { + intensity: { + type: "score", + score: 1.1, + legend: { "0": "low", "1": key, "2": "high" }, + probabilities: { "0": 0.1, "1": 0.7, "2": 0.2 }, + confidence: 0.5 + } + } + }) + }) + )) + ) + const { label, probabilities, rating } = answers.intensity + + assert.isTrue(Object.hasOwn(probabilities, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(probabilities, key)?.enumerable) + assert.deepStrictEqual(Object.keys(probabilities), ["low", key, "high"]) + assert.deepStrictEqual(probabilities, { low: 0.1, [key]: 0.7, high: 0.2 }) + assert.strictEqual(label, key) + assert.strictEqual(rating, 1.1) + assert.strictEqual(Object.getPrototypeOf(probabilities), null) + })) + + it.effect("sends an own enumerable question named " + key, () => + Effect.gen(function*() { + const definition = Decision.make({ + input: Schema.String, + decisions: { + [key]: Decision.probability({ + instructions: "Needs action now", + criteria: { false: "No", true: "Yes" } + }) + } + }) + const { answers } = yield* DecisionModel.decide(definition, { input: "Help" }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const { questions } = yield* getRequestBody(request) + + assert.strictEqual(Object.getPrototypeOf(questions), Object.prototype) + assert.isTrue(Object.hasOwn(questions, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(questions, key)?.enumerable) + assert.deepStrictEqual(Object.keys(questions), [key]) + assert.deepStrictEqual(questions[key], { + type: "noul", + instructions: "Needs action now", + criteria: { false: "No", true: "Yes" } + }) + + return jsonResponse(request, { + model: "jev-latest", + answers: { [key]: { type: "noul", noul: 0.75 } }, + usage: { input_tokens: 20, output_tokens: 4 } + }) + }) + )) + ) + + assert.strictEqual(Object.getPrototypeOf(answers), null) + assert.isTrue(Object.hasOwn(answers, key)) + assert.isTrue(Object.getOwnPropertyDescriptor(answers, key)?.enumerable) + assert.deepStrictEqual(Object.keys(answers), [key]) + assert.deepStrictEqual(answers[key], { probability: 0.75 }) + })) + } + + it.effect("model provides DecisionModel with typesafe provider metadata", () => + Effect.gen(function*() { + const provider = yield* Model.ProviderName + const modelName = yield* Model.ModelName + const model = yield* DecisionModel.DecisionModel + + assert.strictEqual(provider, "typesafe") + assert.strictEqual(modelName, "jev-latest") + assert.isDefined(model.decide) + }).pipe( + Effect.provide(TypeSafeDecisionModel.model("jev-latest")), + Effect.provideService(TypeSafeClient.TypeSafeClient, noopTypeSafeClient) + )) + + it.effect("sends the encoded input as state and every decision as a question in one request", () => + Effect.gen(function*() { + const requests: Array = [] + const clientLayer = makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, systemOneResponse)) + }) + + yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(clientLayer) + ) + + assert.strictEqual(requests.length, 1) + assert.strictEqual(requests[0].method, "POST") + assert.strictEqual(requests[0].url, "https://api.typesafe.ai/v1/systemone") + assert.strictEqual(requests[0].headers["authorization"], "Bearer ts-test-key") + + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body, { + state: { + message: ticket.message, + orderId: "A-104" + }, + model: "jev-latest", + questions: { + department: { + type: "choice", + instructions: "Which team should handle this", + criteria: { + billing: "Payment or subscription issues", + technical: "Bugs or integration problems", + sales: "Pricing or account questions" + } + }, + frustration: { + type: "score", + instructions: "How frustrated the customer appears", + criteria: ["Calm, just stating facts", "Frustrated but civil", "Very angry, strong language"] + }, + urgent: { + type: "noul", + instructions: "The message conveys urgency or time-sensitivity", + criteria: { + false: "No time pressure", + true: "Needs action now" + } + } + } + }) + })) + + it.effect("maps choice, score, and noul answers onto classify, rate, and probability answers", () => + Effect.gen(function*() { + const { answers, usage } = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => Effect.succeed(jsonResponse(request, systemOneResponse)))) + ) + + assert.strictEqual(answers.department.label, "billing") + assert.deepStrictEqual(answers.department.probabilities, { billing: 0.84, technical: 0.159, sales: 0.001 }) + assert.strictEqual(answers.department.confidence, 0.596) + + assert.strictEqual(answers.frustration.rating, 1.6) + assert.strictEqual(answers.frustration.label, "Very angry, strong language") + assert.deepStrictEqual(answers.frustration.probabilities, { + "Calm, just stating facts": 0.05, + "Frustrated but civil": 0.3, + "Very angry, strong language": 0.65 + }) + assert.strictEqual(answers.frustration.confidence, 0.78) + + assert.strictEqual(answers.urgent.probability, 0.999) + assert.isFalse("confidence" in answers.urgent) + + assert.strictEqual(usage.inputTokens, 312) + assert.strictEqual(usage.outputTokens, 48) + })) + + it.effect("encodes an explicitly undefined optional input field as JSON null", () => + Effect.gen(function*() { + const requests: Array = [] + const definition = Decision.make({ + input: Schema.Struct({ message: Schema.String, orderId: Schema.optional(Schema.String) }), + decisions: TicketTriage.decisions + }) + const { answers } = yield* DecisionModel.decide(definition, { + input: { message: ticket.message, orderId: undefined } + }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, systemOneResponse)) + })) + ) + + assert.strictEqual(requests.length, 1) + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body.state, { message: ticket.message, orderId: null }) + assert.strictEqual(answers.urgent.probability, 0.999) + })) + + for ( + const { extra, inputTokens, name, outputTokens } of [ + { name: "absent usage", extra: {}, inputTokens: undefined, outputTokens: undefined }, + { name: "empty usage", extra: { usage: {} }, inputTokens: undefined, outputTokens: undefined }, + { name: "input tokens only", extra: { usage: { input_tokens: 312 } }, inputTokens: 312, outputTokens: undefined }, + { name: "output tokens only", extra: { usage: { output_tokens: 48 } }, inputTokens: undefined, outputTokens: 48 } + ] + ) { + it.effect("preserves answers with " + name, () => + Effect.gen(function*() { + const { usage: _usage, ...response } = systemOneResponse + const { answers, usage } = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => Effect.succeed(jsonResponse(request, { ...response, ...extra })))) + ) + + assert.strictEqual(answers.department.label, "billing") + assert.strictEqual(answers.frustration.rating, 1.6) + assert.strictEqual(answers.urgent.probability, 0.999) + assert.strictEqual(usage.inputTokens, inputTokens) + assert.strictEqual(usage.outputTokens, outputTokens) + })) + } + + it.effect("sends a pinned model id through unchanged", () => + Effect.gen(function*() { + const requests: Array = [] + + const modelName = yield* Effect.gen(function*() { + yield* DecisionModel.decide(TicketTriage, { input: ticket }) + return yield* Model.ModelName + }).pipe( + Effect.provide(TypeSafeDecisionModel.model("jev-1.13.0")), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { ...systemOneResponse, model: "jev-1.13.0" })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.strictEqual(modelName, "jev-1.13.0") + assert.strictEqual(body.model, "jev-1.13.0") + })) + + it.effect("passes a string input through as a string state", () => + Effect.gen(function*() { + const requests: Array = [] + const Sentiment = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } + }) + + yield* DecisionModel.decide(Sentiment, { input: "My card was charged twice." }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { urgent: { type: "noul", noul: 0.93 } }, + usage: { input_tokens: 20, output_tokens: 4 } + })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.strictEqual(body.state, "My card was charged twice.") + })) + + it.effect("passes an array input through as an array state", () => + Effect.gen(function*() { + const requests: Array = [] + const Conversation = Decision.make({ + input: Schema.Array(Schema.String), + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } + }) + + yield* DecisionModel.decide(Conversation, { + input: ["Hi", "My customer number is TS1337.", "My card was charged twice."] + }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => { + requests.push(request) + return Effect.succeed(jsonResponse(request, { + model: "jev-latest", + answers: { urgent: { type: "noul", noul: 0.93 } }, + usage: { input_tokens: 20, output_tokens: 4 } + })) + })) + ) + + const body = yield* getRequestBody(requests[0]) + assert.deepStrictEqual(body.state, ["Hi", "My customer number is TS1337.", "My card was charged twice."]) + })) + + it.effect("fails with InvalidOutputError when an answer type does not match the decision", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...systemOneResponse, + answers: { + ...systemOneResponse.answers, + urgent: { + type: "choice", + choice: "true", + probabilities: { true: 0.9, false: 0.1 }, + confidence: 0.8 + } + } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when an answer is missing", () => + Effect.gen(function*() { + const { urgent: _urgent, ...answers } = systemOneResponse.answers + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide( + makeClientLayer((request) => Effect.succeed(jsonResponse(request, { ...systemOneResponse, answers }))) + ), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when a score answer omits probabilities", () => + Effect.gen(function*() { + const { probabilities: _probabilities, ...frustration } = systemOneResponse.answers.frustration + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...systemOneResponse, + answers: { ...systemOneResponse.answers, frustration } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("fails with InvalidOutputError when a score distribution skips a level index", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed(jsonResponse(request, { + ...systemOneResponse, + answers: { + ...systemOneResponse.answers, + frustration: { + ...systemOneResponse.answers.frustration, + probabilities: { "0": 0.35, "2": 0.65 } + } + } + })) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("surfaces client errors through decide", () => + Effect.gen(function*() { + const error = yield* DecisionModel.decide(TicketTriage, { input: ticket }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.succeed( + HttpClientResponse.fromWeb( + request, + new Response(JSON.stringify({ message: "Rate limit exceeded" }), { + status: 429, + headers: { "content-type": "application/json", "retry-after": "3" } + }) + ) + ) + )), + Effect.flip + ) + + assert.strictEqual(error._tag, "AiError") + assert.strictEqual(error.reason._tag, "RateLimitError") + })) +}) + +const makeClientLayer = ( + handler: ( + request: HttpClientRequest.HttpClientRequest + ) => Effect.Effect +) => + TypeSafeClient.layer({ apiKey: Redacted.make("ts-test-key") }).pipe( + Layer.provide(Layer.succeed(HttpClient.HttpClient, makeHttpClient(handler))) + ) + +const makeHttpClient = ( + handler: ( + request: HttpClientRequest.HttpClientRequest + ) => Effect.Effect +) => + HttpClient.makeWith( + Effect.fnUntraced(function*(requestEffect) { + const request = yield* requestEffect + return yield* handler(request) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + +const jsonResponse = ( + request: HttpClientRequest.HttpClientRequest, + body: unknown +): HttpClientResponse.HttpClientResponse => + HttpClientResponse.fromWeb( + request, + new Response(JSON.stringify(body), { + status: 200, + headers: { + "content-type": "application/json" + } + }) + ) + +const getRequestBody = (request: HttpClientRequest.HttpClientRequest) => + Effect.gen(function*() { + const body = request.body + if (body._tag === "Uint8Array") { + const text = new TextDecoder().decode(body.body) + return JSON.parse(text) + } + return yield* Effect.die(new Error("Expected Uint8Array body")) + }) + +const noopTypeSafeClient: TypeSafeClient.Service = { + client: undefined as unknown as TypeSafeClient.Service["client"], + systemOne: () => Effect.die(new Error("noop")), + listModels: () => Effect.die(new Error("noop")) +} diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts b/repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts new file mode 100644 index 0000000000..4de6ac02e6 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts @@ -0,0 +1,134 @@ +import * as TypeSafeSchema from "@effect/ai-typesafe/TypeSafeSchema" +import { assert, describe, it } from "@effect/vitest" +import { Schema } from "effect" + +const choiceAnswer = { + type: "choice", + choice: "billing", + probabilities: { billing: 0.84, technical: 0.159, sales: 0.001 }, + confidence: 0.596 +} + +const scoreAnswer = { + type: "score", + score: 1.6, + legend: { "0": "Calm", "1": "Frustrated", "2": "Very angry" }, + probabilities: { "0": 0.05, "1": 0.3, "2": 0.65 }, + confidence: 0.78 +} + +const noulAnswer = { + type: "noul", + noul: 0.999 +} + +describe("TypeSafeSchema", () => { + it("encodes a systemone request with every question type", () => { + const request = { + state: { message: "My card was charged twice.", order_id: "A-104" }, + model: "jev-latest", + questions: { + department: { + type: "choice", + instructions: "Which team should handle this", + criteria: { billing: "Payment or subscription issues", technical: "Bugs or integration problems" } + }, + frustration: { + type: "score", + instructions: "How frustrated the customer appears", + criteria: ["Calm", "Frustrated", "Very angry"] + }, + urgent: { + type: "noul", + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + } + } + } as const + + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.SystemOneRequest)(request) + const encoded = Schema.encodeSync(TypeSafeSchema.SystemOneRequest)(decoded) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(encoded)), request) + }) + + it("accepts a noul question without criteria", () => { + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.NoulQuestion)({ + type: "noul", + instructions: "The message conveys urgency" + }) + + assert.strictEqual(decoded.type, "noul") + assert.isUndefined(decoded.criteria) + }) + + it("decodes a systemone response with choice, score, and noul answers", () => { + const response = { + model: "jev-latest", + answers: { + department: choiceAnswer, + frustration: scoreAnswer, + urgent: noulAnswer + }, + usage: { input_tokens: 312, output_tokens: 48 } + } + + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.SystemOneResponse)(response) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(decoded)), response) + }) + + it("decodes a score answer without a legend", () => { + const { legend: _legend, ...withoutLegend } = scoreAnswer + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.ScoreAnswer)(withoutLegend) + + assert.strictEqual(decoded.score, 1.6) + assert.deepStrictEqual(decoded.probabilities, { "0": 0.05, "1": 0.3, "2": 0.65 }) + }) + + it.each([ + { name: "score answer without probabilities", answer: { type: "score", score: 1.6, confidence: 0.78 } }, + { name: "score answer without score", answer: { type: "score", probabilities: { "0": 1 }, confidence: 1 } }, + { name: "choice answer without probabilities", answer: { type: "choice", choice: "billing", confidence: 1 } }, + { name: "choice answer without choice", answer: { type: "choice", probabilities: { billing: 1 }, confidence: 1 } }, + { name: "noul answer without noul", answer: { type: "noul" } }, + { name: "answer with an unknown type", answer: { type: "rank", rank: [] } } + ])("rejects a $name", ({ answer }) => { + assert.throws(() => Schema.decodeUnknownSync(TypeSafeSchema.Answer)(answer)) + }) + + it.each([ + { name: "absent usage", extra: {} }, + { name: "empty usage", extra: { usage: {} } }, + { name: "input tokens only", extra: { usage: { input_tokens: 312 } } }, + { name: "output tokens only", extra: { usage: { output_tokens: 48 } } } + ])("decodes a response with $name", ({ extra }) => { + const response = { model: "jev-latest", answers: { urgent: noulAnswer }, ...extra } + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.SystemOneResponse)(response) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(decoded)), response) + }) + + it.each([ + { name: "neither optional field", model: { name: "jev-latest" } }, + { name: "description only", model: { name: "jev-latest", description: "Latest Jev" } }, + { name: "release date only", model: { name: "jev-latest", release_date: "2026-08-01" } } + ])("decodes a model with $name", ({ model }) => { + const response = { models: [model] } + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.ListModelsResponse)(response) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(decoded)), response) + }) + + it("decodes a models list response", () => { + const response = { + models: [ + { name: "jev-1.13.0", description: "Jev 1.13", release_date: "2026-08-01" } + ] + } + + const decoded = Schema.decodeUnknownSync(TypeSafeSchema.ListModelsResponse)(response) + + assert.deepStrictEqual(JSON.parse(JSON.stringify(decoded)), response) + }) +}) diff --git a/repos/effect/packages/ai/typesafe/tsconfig.json b/repos/effect/packages/ai/typesafe/tsconfig.json new file mode 100644 index 0000000000..e2a8ca19a0 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/tsconfig.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../../tsconfig.base.json", + "include": ["src"], + "references": [ + { "path": "../../effect" } + ] +} diff --git a/repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts b/repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts new file mode 100644 index 0000000000..9d366ec3d0 --- /dev/null +++ b/repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts @@ -0,0 +1,78 @@ +import { TypeSafeClient, TypeSafeDecisionModel } from "@effect/ai-typesafe" +import { type Config, Effect, type Layer, Redacted, Schema } from "effect" +import { Decision, DecisionModel, type Model } from "effect/ai" +import type { HttpClient } from "effect/http" +import { describe, expect, it } from "tstyche" + +declare const acceptsKnownModel: (model: TypeSafeDecisionModel.Model) => void + +const Triage = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } +}) + +describe("TypeSafeDecisionModel", () => { + describe("Model", () => { + it("keeps the known model ids as literals, while the constructors still accept custom ids", () => { + expect(acceptsKnownModel).type.toBeCallableWith("jev-latest") + expect(acceptsKnownModel).type.toBeCallableWith("jev-preview") + expect(acceptsKnownModel).type.toBeCallableWith("jev-1.13.0") + expect(acceptsKnownModel).type.not.toBeCallableWith("not-a-real-model") + expect(TypeSafeDecisionModel.model).type.toBeCallableWith("not-a-real-model") + expect(TypeSafeDecisionModel.layer).type.toBeCallableWith({ model: "not-a-real-model" }) + }) + }) + + describe("model", () => { + it("provides DecisionModel and requires TypeSafeClient", () => { + expect(TypeSafeDecisionModel.model("jev-latest")).type.toBe< + Model.Model<"typesafe", DecisionModel.DecisionModel, TypeSafeClient.TypeSafeClient> + >() + }) + + it("satisfies a decide program's DecisionModel requirement", () => { + const program = DecisionModel.decide(Triage, { input: "My card was charged twice." }).pipe( + Effect.provide(TypeSafeDecisionModel.model("jev-latest")) + ) + + type Requirements = typeof program extends Effect.Effect ? R : never + + expect().type.toBe() + }) + }) + + describe("layer", () => { + it("provides DecisionModel and requires TypeSafeClient", () => { + expect(TypeSafeDecisionModel.layer({ model: "jev-latest" })).type.toBe< + Layer.Layer + >() + }) + }) + + describe("make", () => { + it("builds a DecisionModel from the TypeSafeClient service", () => { + expect(TypeSafeDecisionModel.make({ model: "jev-latest" })).type.toBe< + Effect.Effect + >() + }) + }) +}) + +describe("TypeSafeClient", () => { + it("layer requires only HttpClient", () => { + expect(TypeSafeClient.layer({ apiKey: Redacted.make("ts-test-key") })).type.toBe< + Layer.Layer + >() + }) + + it("layerConfig fails with ConfigError", () => { + expect(TypeSafeClient.layerConfig()).type.toBe< + Layer.Layer + >() + }) +}) diff --git a/repos/effect/packages/atom/react/CHANGELOG.md b/repos/effect/packages/atom/react/CHANGELOG.md index b5e964c2d0..8db542a470 100644 --- a/repos/effect/packages/atom/react/CHANGELOG.md +++ b/repos/effect/packages/atom/react/CHANGELOG.md @@ -1,5 +1,97 @@ # @effect/atom-react +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/atom/react/README.md b/repos/effect/packages/atom/react/README.md index f3b2b6c49b..bc437cf9cd 100644 --- a/repos/effect/packages/atom/react/README.md +++ b/repos/effect/packages/atom/react/README.md @@ -5,10 +5,21 @@ ## Installation ```sh -npm install effect@rc @effect/atom-react@rc +npm install effect @effect/atom-react ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/atom-react) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/atom-react): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/atom/react/package.json b/repos/effect/packages/atom/react/package.json index 94adc90b08..33ccd8d035 100644 --- a/repos/effect/packages/atom/react/package.json +++ b/repos/effect/packages/atom/react/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-react", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "React bindings for the Effect Atom modules", @@ -65,17 +65,17 @@ "scheduler": ">=0.25.0 <0.28.0" }, "devDependencies": { - "@testing-library/dom": "^10.4.1", + "@testing-library/dom": "^10.4.2", "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.7", - "@types/scheduler": "^0.26.0", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/scheduler": "^0.28.0", "effect": "workspace:^", - "jsdom": "^30.0.1", - "react": "^19.2.8", - "react-dom": "^19.2.8", - "react-error-boundary": "^6.1.5", - "scheduler": "^0.27.0" + "jsdom": "~30.0.1", + "react": "^19.3.0", + "react-dom": "^19.3.0", + "react-error-boundary": "^6.1.6", + "scheduler": "^0.28.0" } } diff --git a/repos/effect/packages/atom/react/src/Hooks.ts b/repos/effect/packages/atom/react/src/Hooks.ts index 5fe03b33bc..dc10f753e9 100644 --- a/repos/effect/packages/atom/react/src/Hooks.ts +++ b/repos/effect/packages/atom/react/src/Hooks.ts @@ -11,10 +11,10 @@ import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" -import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" -import type * as AtomRef from "effect/unstable/reactivity/AtomRef" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +import type * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" +import type * as AtomRef from "effect/reactivity/AtomRef" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import * as React from "react" import { RegistryContext } from "./RegistryContext.ts" diff --git a/repos/effect/packages/atom/react/src/ReactHydration.ts b/repos/effect/packages/atom/react/src/ReactHydration.ts index e160d67960..62625d715f 100644 --- a/repos/effect/packages/atom/react/src/ReactHydration.ts +++ b/repos/effect/packages/atom/react/src/ReactHydration.ts @@ -8,7 +8,7 @@ * @since 4.0.0 */ "use client" -import * as Hydration from "effect/unstable/reactivity/Hydration" +import * as Hydration from "effect/reactivity/Hydration" import * as React from "react" import { RegistryContext } from "./RegistryContext.ts" diff --git a/repos/effect/packages/atom/react/src/RegistryContext.ts b/repos/effect/packages/atom/react/src/RegistryContext.ts index 0a179bbead..7ea69b4559 100644 --- a/repos/effect/packages/atom/react/src/RegistryContext.ts +++ b/repos/effect/packages/atom/react/src/RegistryContext.ts @@ -8,8 +8,8 @@ */ "use client" -import type * as Atom from "effect/unstable/reactivity/Atom" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +import type * as Atom from "effect/reactivity/Atom" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import * as React from "react" import * as Scheduler from "scheduler" @@ -17,6 +17,7 @@ import * as Scheduler from "scheduler" * Schedules Atom registry work with React's scheduler at low priority and * returns a cancellation function for the scheduled task. * + * @stability unstable * @category context * @since 4.0.0 */ diff --git a/repos/effect/packages/atom/react/src/ScopedAtom.ts b/repos/effect/packages/atom/react/src/ScopedAtom.ts index 9e7e3e7a06..23ab2319fc 100644 --- a/repos/effect/packages/atom/react/src/ScopedAtom.ts +++ b/repos/effect/packages/atom/react/src/ScopedAtom.ts @@ -8,7 +8,7 @@ */ "use client" -import type * as Atom from "effect/unstable/reactivity/Atom" +import type * as Atom from "effect/reactivity/Atom" import * as React from "react" /** @@ -44,7 +44,7 @@ export const TypeId: TypeId = "~@effect/atom-react/ScopedAtom" * * ```ts import.meta.vitest * import { make, useAtomValue } from "@effect/atom-react" - * import { Atom } from "effect/unstable/reactivity" + * import { Atom } from "effect/reactivity" * import * as React from "react" * import { renderToStaticMarkup } from "react-dom/server" * @@ -97,7 +97,7 @@ export interface ScopedAtom, Input = never> { * * ```ts import.meta.vitest * import { make, useAtomValue } from "@effect/atom-react" - * import { Atom } from "effect/unstable/reactivity" + * import { Atom } from "effect/reactivity" * import * as React from "react" * import { renderToStaticMarkup } from "react-dom/server" * diff --git a/repos/effect/packages/atom/react/test/index.test.tsx b/repos/effect/packages/atom/react/test/index.test.tsx index f00e71a6f7..6a9e7ecf71 100644 --- a/repos/effect/packages/atom/react/test/index.test.tsx +++ b/repos/effect/packages/atom/react/test/index.test.tsx @@ -1,12 +1,12 @@ // import { act, render, screen, waitFor } from "@testing-library/react" import { Cause, Context, Effect, Latch, Layer } from "effect" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" +import * as AtomRef from "effect/reactivity/AtomRef" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" +import * as Hydration from "effect/reactivity/Hydration" import * as Schema from "effect/Schema" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" -import * as AtomRef from "effect/unstable/reactivity/AtomRef" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" -import * as Hydration from "effect/unstable/reactivity/Hydration" import * as React from "react" import { Suspense } from "react" import { renderToString } from "react-dom/server" diff --git a/repos/effect/packages/atom/solid/CHANGELOG.md b/repos/effect/packages/atom/solid/CHANGELOG.md index b8d4af9bf8..dbe714db3b 100644 --- a/repos/effect/packages/atom/solid/CHANGELOG.md +++ b/repos/effect/packages/atom/solid/CHANGELOG.md @@ -1,5 +1,97 @@ # @effect/atom-solid +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/atom/solid/README.md b/repos/effect/packages/atom/solid/README.md index 4d984b0de1..00419ba104 100644 --- a/repos/effect/packages/atom/solid/README.md +++ b/repos/effect/packages/atom/solid/README.md @@ -5,10 +5,21 @@ ## Installation ```sh -npm install effect@rc @effect/atom-solid@rc +npm install effect @effect/atom-solid ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/atom-solid) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/atom-solid): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/atom/solid/package.json b/repos/effect/packages/atom/solid/package.json index ca437fb063..0bc5b19e1a 100644 --- a/repos/effect/packages/atom/solid/package.json +++ b/repos/effect/packages/atom/solid/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-solid", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "SolidJS bindings for the Effect Atom modules", @@ -65,10 +65,10 @@ }, "devDependencies": { "@solidjs/testing-library": "^0.8.10", - "@testing-library/dom": "^10.4.1", + "@testing-library/dom": "^10.4.2", "@testing-library/jest-dom": "^7.0.1", "effect": "workspace:^", - "jsdom": "^30.0.1", + "jsdom": "~30.0.1", "solid-js": "^1.9.15" } } diff --git a/repos/effect/packages/atom/solid/src/Hooks.ts b/repos/effect/packages/atom/solid/src/Hooks.ts index d4d98de171..a8763e874e 100644 --- a/repos/effect/packages/atom/solid/src/Hooks.ts +++ b/repos/effect/packages/atom/solid/src/Hooks.ts @@ -9,10 +9,10 @@ import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" -import * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import * as Atom from "effect/unstable/reactivity/Atom" -import type * as AtomRef from "effect/unstable/reactivity/AtomRef" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +import * as AsyncResult from "effect/reactivity/AsyncResult" +import * as Atom from "effect/reactivity/Atom" +import type * as AtomRef from "effect/reactivity/AtomRef" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import type { Accessor, ResourceOptions, ResourceReturn } from "solid-js" import { createComputed, createEffect, createMemo, createResource, createSignal, onCleanup, useContext } from "solid-js" import { RegistryContext } from "./RegistryContext.ts" diff --git a/repos/effect/packages/atom/solid/src/RegistryContext.ts b/repos/effect/packages/atom/solid/src/RegistryContext.ts index 111cfab28e..b534f26c32 100644 --- a/repos/effect/packages/atom/solid/src/RegistryContext.ts +++ b/repos/effect/packages/atom/solid/src/RegistryContext.ts @@ -6,8 +6,8 @@ * * @since 4.0.0 */ -import type * as Atom from "effect/unstable/reactivity/Atom" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +import type * as Atom from "effect/reactivity/Atom" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import type { JSX } from "solid-js" import { createComponent, createContext, onCleanup } from "solid-js" diff --git a/repos/effect/packages/atom/solid/test/index.test.tsx b/repos/effect/packages/atom/solid/test/index.test.tsx index cd90407be8..da605eba9e 100644 --- a/repos/effect/packages/atom/solid/test/index.test.tsx +++ b/repos/effect/packages/atom/solid/test/index.test.tsx @@ -9,7 +9,7 @@ import { useAtomValue } from "@effect/atom-solid" import { assert, describe, it } from "@effect/vitest" -import { AsyncResult, Atom, AtomRef, AtomRegistry } from "effect/unstable/reactivity" +import { AsyncResult, Atom, AtomRef, AtomRegistry } from "effect/reactivity" import { type Accessor, createComponent, createEffect, createRoot, type Resource } from "solid-js" describe("atom-solid", () => { diff --git a/repos/effect/packages/atom/vue/CHANGELOG.md b/repos/effect/packages/atom/vue/CHANGELOG.md index 56e3cb6eea..100ce4646c 100644 --- a/repos/effect/packages/atom/vue/CHANGELOG.md +++ b/repos/effect/packages/atom/vue/CHANGELOG.md @@ -1,5 +1,97 @@ # @effect/atom-vue +## 4.0.0 + +### Patch Changes + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. +- Updated dependencies [[`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d), [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af), [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3), [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e), [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd), [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34), [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade), [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac), [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1), [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178), [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25), [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e), [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d), [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1), [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36), [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7), [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56), [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c), [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac), [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a), [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd), [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4)]: + - effect@4.0.0 + +## 4.0.0-rc.118 + +### Patch Changes + +- Updated dependencies [[`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1), [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49), [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0), [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9), [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563), [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1), [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a), [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d), [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889), [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03), [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955), [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78), [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a), [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462), [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1), [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0), [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e), [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f), [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b), [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683), [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6), [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a), [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069), [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899), [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c), [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636), [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b), [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0), [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742), [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1), [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2), [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc), [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5), [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a), [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af), [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277), [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772), [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1), [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999), [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8), [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d), [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015), [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2), [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83), [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1), [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9), [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167), [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5), [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075), [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6), [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe), [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347), [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd), [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9), [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3), [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48), [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5), [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768), [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3), [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54), [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69), [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a), [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89), [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114), [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b), [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5), [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd), [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508), [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9), [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd), [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee), [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792), [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c), [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32), [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1), [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468), [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231), [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74), [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6), [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c), [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503), [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024), [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7), [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78), [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e), [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db), [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd), [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe), [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542), [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f), [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63), [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4), [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258), [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2), [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba), [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8), [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7), [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb), [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d), [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682), [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d), [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20), [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95), [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272), [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b), [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7), [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c), [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d), [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea), [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87)]: + - effect@4.0.0-rc.118 + +## 4.0.0-rc.117 + +### Patch Changes + +- Updated dependencies [[`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9), [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724), [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0), [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b), [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9), [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a), [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca), [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1), [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5), [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741)]: + - effect@4.0.0-rc.117 + +## 4.0.0-rc.116 + +### Patch Changes + +- Updated dependencies [[`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f), [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2), [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d), [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356), [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf), [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b), [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f), [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a), [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac), [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06), [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8), [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153), [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537), [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746), [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9), [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c), [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3), [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b), [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b), [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72), [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f), [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5), [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55), [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b)]: + - effect@4.0.0-rc.116 + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/atom/vue/README.md b/repos/effect/packages/atom/vue/README.md index c6e27a4852..1b1cb1ac10 100644 --- a/repos/effect/packages/atom/vue/README.md +++ b/repos/effect/packages/atom/vue/README.md @@ -5,10 +5,21 @@ ## Installation ```sh -npm install effect@rc @effect/atom-vue@rc +npm install effect @effect/atom-vue ``` -## Documentation +## Links -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/atom-vue) +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/atom-vue): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. diff --git a/repos/effect/packages/atom/vue/package.json b/repos/effect/packages/atom/vue/package.json index 9fa2e67653..3a68907c8f 100644 --- a/repos/effect/packages/atom/vue/package.json +++ b/repos/effect/packages/atom/vue/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-vue", - "version": "4.0.0-rc.115", + "version": "4.0.0", "type": "module", "license": "MIT", "description": "Vue bindings for the Effect Atom modules", @@ -61,7 +61,7 @@ }, "devDependencies": { "effect": "workspace:^", - "vue": "^3.5.42" + "vue": "^3.5.43" }, "peerDependencies": { "effect": "workspace:^", diff --git a/repos/effect/packages/atom/vue/src/index.ts b/repos/effect/packages/atom/vue/src/index.ts index b4fff5a91a..ecd8a09c72 100644 --- a/repos/effect/packages/atom/vue/src/index.ts +++ b/repos/effect/packages/atom/vue/src/index.ts @@ -4,47 +4,53 @@ import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" -import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult" -import type * as Atom from "effect/unstable/reactivity/Atom" -import type * as AtomRef from "effect/unstable/reactivity/AtomRef" -import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +import type * as AsyncResult from "effect/reactivity/AsyncResult" +import type * as Atom from "effect/reactivity/Atom" +import type * as AtomRef from "effect/reactivity/AtomRef" +import * as AtomRegistry from "effect/reactivity/AtomRegistry" import { computed, type ComputedRef, inject, type InjectionKey, type Ref, shallowRef, watchEffect } from "vue" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry" +export * as AtomRegistry from "effect/reactivity/AtomRegistry" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as AsyncResult from "effect/unstable/reactivity/AsyncResult" +export * as AsyncResult from "effect/reactivity/AsyncResult" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as Atom from "effect/unstable/reactivity/Atom" +export * as Atom from "effect/reactivity/Atom" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as AtomRef from "effect/unstable/reactivity/AtomRef" +export * as AtomRef from "effect/reactivity/AtomRef" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as AtomHttpApi from "effect/unstable/reactivity/AtomHttpApi" +export * as AtomHttpApi from "effect/reactivity/AtomHttpApi" /** + * @stability unstable * @since 4.0.0 * @category re-exports */ -export * as AtomRpc from "effect/unstable/reactivity/AtomRpc" +export * as AtomRpc from "effect/reactivity/AtomRpc" /** * @since 4.0.0 diff --git a/repos/effect/packages/atom/vue/test/index.test.ts b/repos/effect/packages/atom/vue/test/index.test.ts index ae0b2f8856..f7e6fadb13 100644 --- a/repos/effect/packages/atom/vue/test/index.test.ts +++ b/repos/effect/packages/atom/vue/test/index.test.ts @@ -1,6 +1,6 @@ import { useAtomRef } from "@effect/atom-vue" import { assert, describe, it } from "@effect/vitest" -import * as AtomRef from "effect/unstable/reactivity/AtomRef" +import * as AtomRef from "effect/reactivity/AtomRef" import { effectScope, nextTick, shallowRef } from "vue" describe("atom-vue", () => { diff --git a/repos/effect/packages/effect/ARBITRARY-FOLLOW-UPS.md b/repos/effect/packages/effect/ARBITRARY-FOLLOW-UPS.md deleted file mode 100644 index 862a47e3a0..0000000000 --- a/repos/effect/packages/effect/ARBITRARY-FOLLOW-UPS.md +++ /dev/null @@ -1,62 +0,0 @@ -# Native Arbitrary Follow-ups - -This file tracks unfinished work for the native Schema-first Arbitrary implementation. Settled behavior and technical -decisions belong in [ARBITRARY.md](ARBITRARY.md); migration guidance belongs in -[ARBITRARY-MIGRATION.md](ARBITRARY-MIGRATION.md). Remove an item from this file when it is resolved rather than keeping -completed implementation history here. - -Every production change must preserve the existing guarantees: - -- Schema remains the only catalog of primitive and structural generator constructors; -- discarded roots are bounded by `maxDiscards`; -- inspected shrink candidates, including rejected nodes, are bounded by `maxShrinks`; -- shrinking and replay remain deterministic for supported pure callbacks; -- recursive and mutually recursive Schemas retain their productivity guarantees; -- `Sample`, the shrink carrier, the PRNG, generation budgets, and compiler metadata remain private; -- runtime performance and bundle cost are measured before and after the change. - -## Conditional research - -These items are intentionally dormant until their trigger is observed. - -### Finite-domain metadata - -Evaluate private finite-domain metadata only if constructive unique generation demonstrates a real exhaustion or -productivity problem. Do not add public cardinality vocabulary preemptively. - -### Decoded collection and Declaration profiling - -Profile collection generation Links and Declaration decoding only when a new runtime baseline identifies a regression. -ReadonlyMap and ReadonlySet are compiler-owned; Effect-specific HashMap, HashSet, and Chunk keep declaration-local -generation Links. - -### Schema-scoped distribution customization - -Revisit application-owned distribution overrides, including deterministic Faker integration, only after a concrete -use case establishes the required scope and bundle boundary. A future design must work for checked and nested Schemas, -must not overload filter or declaration annotations with a second contract, and must not require synthetic Schemas in -test integrations. Prefer a derivation-time override mechanism over executable metadata captured by production Schema -modules. - -### Trace-informed shrinking - -Compare the current `Sample` tree with private structural spans or trace-informed shrinking only when a reproducible -case shows poor shrunk output or excessive candidate traversal. - -### Concrete failing-input persistence - -Evaluate persistence and reuse of concrete failing inputs last. After `map` or `flatMap`, an Arbitrary may no longer -have a Schema or codec capable of serializing its output, while the existing opaque replay token remains persistable. - -## Verification policy - -For every activated item, use the narrowest representative validation and record exact commands and artifacts: - -- focused Arbitrary runtime tests and typetests when the public types change; -- package type checking and linting; -- seeded generation, shrinking, and replay characterization relevant to the slice; -- focused warm and cold runtime scenarios; -- focused Arbitrary bundle fixtures plus production Schema bundle sentinels. - -Do not update public JSDoc, [ARBITRARY.md](ARBITRARY.md), the migration guide, or the changeset until the corresponding -behavior has passed its semantic, runtime, and bundle gates. diff --git a/repos/effect/packages/effect/ARBITRARY-MIGRATION.md b/repos/effect/packages/effect/ARBITRARY-MIGRATION.md deleted file mode 100644 index c40815c2db..0000000000 --- a/repos/effect/packages/effect/ARBITRARY-MIGRATION.md +++ /dev/null @@ -1,335 +0,0 @@ -# Migrating to Native Arbitrary - -This guide covers migration from the fast-check bridge published in `effect@4.0.0-rc.109` to the native, -Schema-first module at `effect/unstable/arbitrary`. - -The new module removes fast-check from the `effect` package. Applications may still install and use fast-check -directly, but Effect Schema generation and `@effect/vitest` property tests no longer depend on it. - -For the new API and its semantics, see [Arbitrary in Effect](ARBITRARY.md). - -## Import Changes - -The following APIs have been removed: - -- `effect/testing/FastCheck`; -- `Schema.toArbitrary`; -- `Schema.Arbitrary`; -- the legacy `Schema.Annotations.ToArbitrary` contract and declaration-level `toArbitrary` annotation; -- the legacy `arbitrary` filter annotation; -- raw fast-check arbitrary inputs and `fastCheck` options in `@effect/vitest`. - -Import the native module explicitly: - -```ts -import { Arbitrary } from "effect/unstable/arbitrary" -``` - -If other tests still use fast-check-specific APIs, add fast-check as a direct development dependency and import it -from `"fast-check"`. Do not import it through Effect. - -## Generating Samples - -Previously, `Schema.toArbitrary` returned a factory that needed the fast-check module: - -```ts -import { Schema } from "effect" -import { FastCheck } from "effect/testing" - -const Person = Schema.Struct({ - name: Schema.String, - age: Schema.Int -}) - -const personArbitrary = Schema.toArbitrary(Person)(FastCheck) -const samples = FastCheck.sample(personArbitrary, { numRuns: 20, seed: 42 }) -``` - -Now derive and sample through the Effect-native module: - -```ts -import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" - -const Person = Schema.Struct({ - name: Schema.String, - age: Schema.Int -}) - -const personArbitrary = Arbitrary.schema(Person) -const samples = await Effect.runPromise( - Arbitrary.sampleEffect(personArbitrary, { count: 20, seed: 42 }) -) -``` - -`Arbitrary.sampleEffect` returns an `Effect` because sampling is interruptible, uses Effect `Random` when no seed is -provided, and reports bounded generation exhaustion as a typed `SampleError`. - -The generated values still use the decoded Schema `Type`. The sequence and distribution are not compatible with -fast-check, even when the same numeric seed is used. - -## Checking Properties - -Previously, fast-check owned both the property and the runner: - -```ts -import { Schema } from "effect" -import { FastCheck } from "effect/testing" - -const integer = Schema.toArbitrary(Schema.Int)(FastCheck) - -FastCheck.assert( - FastCheck.property(integer, (value) => Number.isInteger(value)), - { numRuns: 100, seed: 42 } -) -``` - -Now `Arbitrary.checkEffect` runs a pure or Effectful property and returns a structured result: - -```ts -import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" - -const result = await Effect.runPromise( - Arbitrary.checkEffect( - Arbitrary.schema(Schema.Int), - (value) => Number.isInteger(value), - { runs: 100, seed: 42 } - ) -) -``` - -Unlike `FastCheck.assert`, `Arbitrary.checkEffect` does not throw for an ordinary falsification. Handle `Passed`, -`Falsified`, `Exhausted`, and `ReplayMismatch` explicitly, or use `@effect/vitest`, which converts non-passing results -into test failures. - -Typed failures from Effectful properties are preserved in `Falsified.failure`. Defects and interruption continue -through the returned Effect. - -## Option Mapping - -The most common options map as follows: - -| Previous fast-check option | Native option | Migration note | -| -------------------------- | -------------------- | ------------------------------------------------------------------ | -| `numRuns` | `count` or `runs` | Use `count` for `sampleEffect` and `runs` for `checkEffect`. | -| `seed` | `seed` | The type is compatible, but generated sequences are not. | -| `path` | `replay` | Existing fast-check paths cannot be converted. | -| `maxSkipsPerRun` | `maxDiscards` | Native uses one absolute discard budget, not a multiplier per run. | -| `examples` | No direct equivalent | Keep explicit regression cases as ordinary tests. | -| `endOnFailure` | `maxShrinks` | Use `maxShrinks: 0` to stop at the initial failure. | -| `interruptAfterTimeLimit` | Effect interruption | Apply an Effect or test timeout around the check. | -| `skipAllAfterTimeLimit` | No direct equivalent | Prefer explicit run and discard bounds. | -| `verbose` | No direct equivalent | Inspect `CheckResult` or use `@effect/vitest` failure output. | - -Review any less common fast-check runner option manually. The native API deliberately does not reproduce the complete -`fc.Parameters` surface. - -## Replay Migration - -Fast-check replay used a seed plus a shrink `path`. Native replay uses one opaque token returned by a `Falsified` -result: - -```ts -const replayed = Arbitrary.checkEffect(arbitrary, property, { - replay: previousFailure.replay -}) -``` - -There is no conversion from a fast-check seed and path to a native replay token. Re-run the property with the native -engine, then record the new token from its `Falsified` result. - -Replay tokens are intended for reproducing and diagnosing a current failure. Because the module is unstable, they are -not guaranteed to survive upgrades. Preserve important failing inputs as explicit regression tests. - -## Migrating Declaration Annotations - -The old `toArbitrary` annotation directly constructed a fast-check arbitrary and exposed fast-check recursion and -constraint details: - -```ts -import { Schema } from "effect" - -class UserId { - readonly value: number - constructor(value: number) { - this.value = value - } -} - -const UserIdSchema = Schema.instanceOf(UserId, { - toArbitrary: () => (fc) => fc.integer({ min: 1, max: 1_000_000 }).map((value) => new UserId(value)) -}) -``` - -The native `toCodecArbitrary` annotation describes a generatable representation as a Schema `Link`: - -```ts -import { Schema, SchemaTransformation } from "effect" - -class UserId { - readonly value: number - constructor(value: number) { - this.value = value - } -} - -const UserIdSchema = Schema.instanceOf(UserId, { - toCodecArbitrary: () => - Schema.link()( - Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_000_000 })), - SchemaTransformation.transform({ - decode: (value) => new UserId(value), - encode: (id) => id.value - }) - ) -}) -``` - -Before adding `toCodecArbitrary`, check whether the declaration already has a useful `toCodecJson` or `toCodec`. -Native derivation falls back to those canonical codecs automatically. Add an arbitrary-specific Link only when the -canonical representation is opaque or generates valid values too rarely. - -The migration changes where generation logic lives: - -| Legacy contract | Native contract | -| ---------------------------------------------- | -------------------------------------------------------------- | -| Returns a `fast-check.Arbitrary` | Returns a `SchemaAST.Link` through `Schema.link`. | -| Receives generated arbitrary type parameters | Receives decoded Schema type parameters. | -| Receives fast-check constraints and recursion | Receives normalized constraints. | -| Manages terminal recursive branches explicitly | Leaves recursion analysis and budgets to the native compiler. | -| Uses arbitrary combinators | Uses Schema constructors, checks, and a Schema transformation. | - -The original declaration remains authoritative. Values decoded by the Link are checked against it. Failed decodes and -rejected values become bounded discards. - -### Custom Filter Metadata - -The old `arbitrary` filter annotation has been replaced by `arbitraryConstraint`. Ordinary custom filters continue to -work as residual filters without generation metadata: - -```ts -import { Schema } from "effect" - -const Even = Schema.Int.check( - Schema.makeFilter((value) => value % 2 === 0) -) -``` - -Residual filtering is bounded, so a very selective or impossible predicate may produce `SampleError` or `Exhausted`. - -If the previous annotation supplied a recognized constructive constraint, move it to `arbitraryConstraint` and adapt -its shape. The predicate remains authoritative: - -```ts -import { Order, Schema } from "effect" - -const Positive = Schema.Number.check( - Schema.makeFilter( - (value) => value > 0, - { - arbitraryConstraint: { - order: Order.Number, - minimum: 0, - exclusiveMinimum: true - } - } - ) -) -``` - -The main constraint-shape changes are: - -| Previous field | Native field | -| ------------------------------------ | ------------------------------------------------------------------- | -| `ordered.order` | `order` | -| `ordered.minimum` / `maximum` | `minimum` / `maximum` | -| `ordered.exclusiveMinimum` / maximum | `exclusiveMinimum: true` / `exclusiveMaximum: true` | -| `integer: true` | `number: "integer"` | -| `noNaN` and `noInfinity` | `number: "finite"` when both restrictions apply | -| collection `minLength` / `maxLength` | `minLength`, `minSize`, or `minProperties` and its matching maximum | -| string pattern | `{ source, flags }` in `patterns` | -| `unique: true` | `uniqueBy: identity` | -| `candidate` | No direct equivalent | - -Choose the cardinality field that matches the Schema domain: `minLength` and `maxLength` for strings and arrays, -`minSize` and `maxSize` for sized collections, and `minProperties` and `maxProperties` for object properties. - -For an opaque declaration that needs a reusable statistically better source domain, express that source as a Schema -Link with `toCodecArbitrary`. - -## Migrating `@effect/vitest` - -Property inputs may be Schemas, native Arbitraries, or mixtures of both. - -Schema-only properties need only an option rename: - -```ts -// Before -it.prop( - "commutative", - [Schema.Int, Schema.Int], - ([a, b]) => a + b === b + a, - { fastCheck: { numRuns: 200, seed: 42 } } -) - -// After -it.prop( - "commutative", - [Schema.Int, Schema.Int], - ([a, b]) => a + b === b + a, - { arbitrary: { runs: 200, seed: 42 } } -) -``` - -Raw or mixed fast-check inputs are no longer accepted: - -```ts -// No longer supported -it.prop("raw arbitrary", [fc.integer()], ([value]) => Number.isInteger(value)) -it.prop("mixed", [Schema.String, fc.integer()], ([text, value]) => true) -``` - -Replace those inputs with Schemas when they describe a domain supported by Schema, or compose a native Arbitrary: - -```ts -import { Arbitrary } from "effect/unstable/arbitrary" - -const integer = Arbitrary.schema(Schema.Int) - -it.prop("native arbitrary", [integer], ([value]) => Number.isInteger(value)) -it.prop("mixed", [Schema.String, integer], ([text, value]) => typeof text === "string" && Number.isInteger(value)) -``` - -If a test genuinely needs a fast-check-specific arbitrary or runner feature, use fast-check directly with Vitest -rather than passing it through `@effect/vitest`. - -`it.prop`, `it.effect.prop`, and `it.live.prop` all accept native check options under `arbitrary`. - -## Behavioral Differences to Review - -Migration is not only an import rename. Review the following differences: - -- native generation and shrinking have different distributions and may find different shrunk inputs; -- native checking returns structured results instead of using fast-check's assertion exceptions; -- generation that cannot find enough valid samples is bounded and reports `SampleError` or `Exhausted`; -- pure and Effectful properties share one interruptible runner; -- recursive and mutually recursive Schemas are analyzed as a graph and must have a finite generation path; -- replay tokens, seeds, and shrink paths are not compatible with fast-check; -- generated values are the decoded Schema `Type`; -- properties must not mutate generated values. - -## Migration Checklist - -1. Replace `effect/testing/FastCheck` imports. Use the native Arbitrary module for Schema generation and import - `"fast-check"` directly only where it is still independently required. -2. Replace `Schema.toArbitrary(schema)(FastCheck)` with `Arbitrary.schema(schema)`. -3. Replace `FastCheck.sample` with `Arbitrary.sampleEffect` and run the returned Effect. -4. Replace `FastCheck.check` or `FastCheck.assert` for Schema-derived inputs with `Arbitrary.checkEffect`, then handle its - structured result. -5. Rename `@effect/vitest` options from `fastCheck` to `arbitrary` and convert `numRuns` to `runs`. -6. Replace raw fast-check inputs in `@effect/vitest` with Schemas or native Arbitraries. -7. Migrate declaration-level `toArbitrary` callbacks to the `toCodecArbitrary` Link-returning contract and replace old - filter-level `arbitrary` annotations with `arbitraryConstraint`. -8. Re-run properties with the native engine and record new replay tokens or explicit regression examples. -9. Review discard limits for selective custom filters. diff --git a/repos/effect/packages/effect/ARBITRARY.md b/repos/effect/packages/effect/ARBITRARY.md index 9c77780243..50d277305a 100644 --- a/repos/effect/packages/effect/ARBITRARY.md +++ b/repos/effect/packages/effect/ARBITRARY.md @@ -14,12 +14,8 @@ Effect divides the work into three parts: it as an input generator with built-in shrinking. - `it.prop`, `it.effect.prop`, or `Arbitrary.checkEffect` runs the rule against generated inputs. -The API used here is currently available from `effect/unstable/arbitrary`. The `unstable` segment matters: -the ideas are stable, but names, result types, generation policies, and replay format may still change before this -module is promoted. - -If you are upgrading from the earlier Schema arbitrary integration available in `effect@4.0.0-rc.109`, see the -[migration guide](ARBITRARY-MIGRATION.md). +The API is available from `effect/Arbitrary` or as the `Arbitrary` export from `effect`. It is marked `@stability unstable`: +names, result types, generation policies, and replay format may still change before this module is promoted. ## Writing a First Property @@ -28,7 +24,7 @@ Consider the rule “adding zero does not change an integer.” With `@effect/vi ```ts import { it } from "@effect/vitest" import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const integer = Arbitrary.schema(Schema.Int) @@ -62,7 +58,7 @@ without overflow or loss of precision, we should restrict the inputs accordingly ```ts import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const SmallInt = Schema.Int.check( Schema.isBetween({ minimum: -100, maximum: 100 }) @@ -118,7 +114,7 @@ Use: ```ts import { Result, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const integers = Arbitrary.schema(Schema.Int) @@ -165,12 +161,28 @@ match the input. After a failure, it simplifies one member at a time. Empty tupl Effect occasionally creates generated records without inherited `Object` methods. This can reveal code that assumes methods such as `hasOwnProperty` always exist. Prefer `Object.hasOwn(value, key)` when checking generated objects. +Use `array` for variable-length collections of an existing Arbitrary: + +```ts +const command = integers.pipe(Arbitrary.map((value) => ({ _tag: "Add" as const, value }))) +const commands = Arbitrary.array(command, { maxLength: 50 }) +``` + +`minLength` defaults to zero. Generation grows with `size`, honors explicit minima even at size zero, and respects +`maxLength`. Invalid bounds throw immediately. After a failure, `array` tries removing blocks, including prefixes and +interior blocks, while retaining the other elements in their original order. Retained objects keep their identity. +It also simplifies individual elements. The bounded search may stop before finding the smallest possible failure. + +This differs from generating a length and using `flatMap` with `all`: shrinking that length regenerates the dependent +contents. Use `array` when shrinking should remove commands from the failing sequence. `Arbitrary.schema` applied to +`Schema.Array` uses the same block-removal strategy; prefer it when the complete collection is already a Schema. + Use `flatMap` when one generated value decides what can be generated next. Create the possible generators before the callback when you can, because creating a Schema inside the callback repeats that work each time it runs: ```ts import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const Length = Arbitrary.schema( Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 4 })) @@ -208,7 +220,7 @@ important transition: ```ts import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const Key = Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 20 })) @@ -272,7 +284,7 @@ the result instead of immediately failing a Vitest test. It accepts a function t ```ts import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const values = Arbitrary.schema(Schema.Array(Schema.Int)) @@ -290,7 +302,7 @@ A property may also return an `Effect`, so it can use Effect services or fail th ```ts import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const program = Arbitrary.checkEffect( Arbitrary.schema(Schema.String), @@ -332,7 +344,7 @@ rule is often called a **law**. ```ts import { it } from "@effect/vitest" import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const smallInt = Arbitrary.schema( Schema.Int.check(Schema.isBetween({ minimum: -100, maximum: 100 })) @@ -467,7 +479,7 @@ and update operations below are correct, but `front` deliberately reads the last ```ts import { assert, describe, it } from "@effect/vitest" import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" interface Queue { readonly front: ReadonlyArray @@ -607,7 +619,7 @@ sets directly, it generates command sequences and runs the same history against ```ts import { it } from "@effect/vitest" import { HashSet, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const Key = Schema.Int.check( Schema.isBetween({ minimum: 0, maximum: 20 }) @@ -671,7 +683,7 @@ reported counterexample: ```ts import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const arbitrary = Arbitrary.schema(Schema.Int) @@ -781,7 +793,7 @@ meaning from the object shape: ```ts import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" interface Literal { readonly _tag: "Literal" @@ -852,7 +864,7 @@ Recursive Schemas are supported as long as there is a way for generation to stop ```ts import { Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" interface Node { readonly value: string @@ -935,7 +947,7 @@ works needs no Arbitrary-specific annotation. ```ts import { assert, it } from "@effect/vitest" import { Effect, Schema } from "effect" -import { Arbitrary } from "effect/unstable/arbitrary" +import * as Arbitrary from "effect/Arbitrary" const Name = Arbitrary.schema(Schema.Literals(["Ada", "Grace"])) @@ -977,7 +989,7 @@ Effect finalizers still run. A timeout cannot stop a synchronous JavaScript call The module starts from Schema and currently does not provide: -- separate Arbitrary constructors for strings, numbers, arrays, and objects; describe those inputs with Schema; +- a second catalog of Schema-like constructors for strings, numbers, and objects; describe those inputs with Schema; - a public low-level generator constructor or direct access to simplification steps; - weighted choice; use [targeted scenarios](#targeting-rare-scenarios) to guarantee that a test reaches an important situation; @@ -1045,6 +1057,11 @@ The module therefore exposes `schema` and a small set of operations for combinin whose structure is hidden can provide a simpler Schema through `toCodecArbitrary`; it does not need a second language for generators. +`Arbitrary.array(item, options)` composes an existing element Arbitrary, including one built with `map`, `filter`, +`flatMap`, or `all`. It supports deleting commands while retaining the failing sequence's remaining values, which a +dependent length generator cannot preserve. Schema remains the place to describe primitive values and checked data +structures. + The implementation stored inside `Arbitrary` remains private. Users can combine and run an Arbitrary without depending on how Effect currently generates or simplifies values. Reconsider this decision only if an important input cannot be described by Schema or built clearly from existing Arbitraries. @@ -1142,8 +1159,7 @@ generated sequences, probabilities, and intermediate simplifications may change. The unstable interface does not currently expose: -- separate Arbitrary constructors for primitive values and structures, or direct access to internal samples and - simplification steps; +- a second catalog of Schema-like constructors, or direct access to internal samples and simplification steps; - weighted distribution controls; - custom simplification that is not attached to a Schema; - support for generators from other libraries or a broad set of runner settings; @@ -1228,17 +1244,27 @@ can alter generated values, simplification, replay, performance, or bundle size to `Schema.Struct`, `Schema.Record`, `Schema.Json`, and record-shaped `all`, but not to arrays, tuples, declarations, or collection classes. - Integer and BigInt generation avoids favoring some values accidentally. It tries boundary values more often on some - runs. Number generation includes signed zero, very small values, infinities, and `NaN` when the Schema permits them. - Finite and integer checks exclude the values they promise to exclude. -- The magnitude of unbounded integers grows with `size`. Ordinary strings combine printable ASCII with a fixed set of - JavaScript edge cases. Regular-expression length is measured in UTF-16 code units, matching JavaScript strings. + runs. Unbounded BigInts select their bit length separately from their value, mixing ordinary magnitudes with much + wider values even at a small `size`. Number generation includes signed zero, very small values, infinities, and `NaN` + when the Schema permits them. Finite and integer checks exclude the values they promise to exclude. +- BigDecimal generation selects precision and decimal exponent independently, while ordered constraints are projected + to the selected scale. Its simplifications operate on the represented numeric value, so a fixed generated scale does + not dictate the final counterexample. Local simplification continues between passing and failing values with a finite + limit on added precision. Ordinary strings combine printable ASCII with a fixed set of JavaScript edge cases. + Regular-expression length is measured in UTF-16 code units, matching JavaScript strings. - Exact probabilities, the value produced by a particular seed, and the order of simplifications may change. Source code comments credit algorithms adapted from other property-testing and random-number implementations. #### Generating and Simplifying Data Structures -- Arrays first remove optional or repeated items and then simplify remaining items. Objects choose optional properties - without favoring earlier declarations. If the first choice is too large for the available recursion space, Effect +- Arrays try removing progressively smaller blocks at successive positions and simplifying individual items. Deletions + retain the other elements and their shrink history, so an earlier element candidate can be tried again after deletion + changes the property context. This history is lazy and local to a collection generation. Products and objects also + retain shared child histories, so exploring a filtered descendant cannot exhaust a sibling's shrink candidates. + Ordinary field shrinking continues from the field being simplified; structural deletions restart field traversal. + Tuple prefixes and fixed tails cannot be deleted; optional positions can only be removed from the end when the tuple + permits it. Whole-array minima apply to every deletion. Objects choose optional properties without favoring earlier + declarations. If the first choice is too large for the available recursion space, Effect uses the smallest choice that still satisfies the Schema instead of rejecting a Schema that can produce a value. Simplification removes optional properties, simplifies values, and then simplifies generated keys while keeping keys unique. diff --git a/repos/effect/packages/effect/CHANGELOG.md b/repos/effect/packages/effect/CHANGELOG.md index 9e7c1dcbdb..dd7faa1f6e 100644 --- a/repos/effect/packages/effect/CHANGELOG.md +++ b/repos/effect/packages/effect/CHANGELOG.md @@ -1,5 +1,580 @@ # effect +## 4.0.0 + +### Patch Changes + +- [#8580](https://github.com/Effect-TS/effect/pull/8580) [`cbfc7b4`](https://github.com/Effect-TS/effect/commit/cbfc7b422046111c439a69ecbce7fc4f4899789d) Thanks @gcanti! - Restrict `Schema.brand` to a single concrete identifier and require `Schema.fromBrand` to use the constructor's sole brand key. Apply `brand` or `fromBrand` repeatedly when composing distinct brands. For enum brand keys, pass the enum member instead of its string value. + +- [#8601](https://github.com/Effect-TS/effect/pull/8601) [`5a81a6f`](https://github.com/Effect-TS/effect/commit/5a81a6f47521123a4075c6c221fb888d109778af) Thanks @RizgarOzan! - Prevent a stale `Atom.family` finalizer from evicting a newer cached atom. + +- [#8576](https://github.com/Effect-TS/effect/pull/8576) [`68b6394`](https://github.com/Effect-TS/effect/commit/68b639473ff211b654b49b7b82183cafd225f4a3) Thanks @tim-smart! - Prevent stale concurrent workflow resumes from clearing a newer completed reply. Custom message storage implementations must honor the optional expected reply ID on `clearReplies` to provide this safety guarantee. + +- [#8589](https://github.com/Effect-TS/effect/pull/8589) [`5250ca6`](https://github.com/Effect-TS/effect/commit/5250ca63c6f15152b4cdd36ef78b32ab06a0075e) Thanks @marek-hanzal! - Fix lost synchronous successes and failures in concurrent `Atom.fn` calls. + +- [#8625](https://github.com/Effect-TS/effect/pull/8625) [`5f9c9d1`](https://github.com/Effect-TS/effect/commit/5f9c9d17c1a2d18e3217b251d28de0015bba02fd) Thanks @tim-smart! - Fix `MutableHashMap` retaining previously used equal object keys after they are replaced or removed, including when `Cache` and `ScopedCache` refresh entries on hits. + +- [#8619](https://github.com/Effect-TS/effect/pull/8619) [`3278821`](https://github.com/Effect-TS/effect/commit/3278821a6490f7f76b30217115e921e1d3dc7f34) Thanks @tim-smart! - Keep `Queue.takeN` suspended when only part of its requested batch is available. Previously a single offer woke the batch taker in a synchronous retry loop that starved the host. `Queue.State.takers` now holds `Queue.Taker` entries: call `entry.resume(...)` instead of calling the entry as a function. + +- [#8607](https://github.com/Effect-TS/effect/pull/8607) [`83429b4`](https://github.com/Effect-TS/effect/commit/83429b42023e79c29137381dc210a57bc0137ade) Thanks @mannyc2! - Interrupt losers in `Effect.race`, `Effect.raceFirst`, `Effect.raceAll` and `Effect.raceAllFirst` when the race settles or is interrupted while other effects are still starting. + +- [#8626](https://github.com/Effect-TS/effect/pull/8626) [`0af6d0c`](https://github.com/Effect-TS/effect/commit/0af6d0c8ceaf08656f76bba41e5965bbf9a76bac) Thanks @tim-smart! - The `RpcMessage.ExitEncoded` interrupt `fiberId` type now admits the `null` emitted by JSON encoding. Consumers reading encoded interrupts should handle `null` alongside `undefined`. + +- [#8614](https://github.com/Effect-TS/effect/pull/8614) [`927e01e`](https://github.com/Effect-TS/effect/commit/927e01e00579c14d446e15e077775e973d75f9a1) Thanks @gcanti! - Fix `SchemaRepresentation.toCodeDocument` to preserve the grouping of branded union types in generated TypeScript code. + +- [#8617](https://github.com/Effect-TS/effect/pull/8617) [`10d8524`](https://github.com/Effect-TS/effect/commit/10d8524dc40fd06075f6a70f88d3a0cbd3477178) Thanks @gcanti! - Fix `Schema.toType` and `Schema.toEncoded` losing parent checks when projecting checked children without transformations. Suspended children remain opaque, and structural checks continue to be preserved. + +- [#8583](https://github.com/Effect-TS/effect/pull/8583) [`35ac25a`](https://github.com/Effect-TS/effect/commit/35ac25a80bec4977a082af57a618d6a02bb41e25) Thanks @KhraksMamtsov! - `Array`, `Chunk`, `Effect`, and `Record` `partition`, their `separate` helpers, and `Option.partitionMap` now return successes before failures, matching `Stream.partition`. Swap the tuple when moving from the previous `[failures, successes]` order. + +- [#8585](https://github.com/Effect-TS/effect/pull/8585) [`29e0aa7`](https://github.com/Effect-TS/effect/commit/29e0aa7dcc7735e207a687ba0f6a7ddebfe0996e) Thanks @roninjin10! - Preserve sibling and callback cleanup failures alongside interruption in concurrent `Effect.forEach` / `Effect.all`, including shared `Layer.build`. + +- [#8593](https://github.com/Effect-TS/effect/pull/8593) [`8783536`](https://github.com/Effect-TS/effect/commit/878353689f69b555ad64f9dc14d4c66fd547360d) Thanks @tim-smart! - Fail in-flight RPC calls when a socket misses a pong, even with transient connection retries enabled. Missed pongs no longer invoke `onTransientError`; connection-open failures still do. + +- [#8579](https://github.com/Effect-TS/effect/pull/8579) [`df77fff`](https://github.com/Effect-TS/effect/commit/df77fff9396fe31de72d1947ecb5b74f8cee89e1) Thanks @jasonkuhrt! - Fix union-of-tag inference for flat RPC clients and `AtomRpc.query` and `AtomRpc.mutation`. Payloads and results now reflect the selected RPCs instead of resolving to `never`. + +- [#8628](https://github.com/Effect-TS/effect/pull/8628) [`ef7d77f`](https://github.com/Effect-TS/effect/commit/ef7d77f382dcb2a4f7648211ed6d674984a47c36) Thanks @gcanti! - Make `Schema.brand` type-only: brand identifiers are no longer stored in AST annotations or preserved by `SchemaRepresentation`. Reapply `Schema.brand` after rebuilding a representation when a branded TypeScript type is required; checks added by `Schema.fromBrand` remain preserved. Preserve the order of equal-priority union members that share an AST when deriving JSON and string-tree codecs. + +- [#8597](https://github.com/Effect-TS/effect/pull/8597) [`035e369`](https://github.com/Effect-TS/effect/commit/035e369b3a30bcd840a7a78ccfb94d251bf931b7) Thanks @Ceereals! - Speed up JIT and AOT Schema decoding of transformations whose source has checks, such as a pattern-checked string decoded to a number. Fix compiled TemplateLiteral validation to enforce oneOf parts and preserve interpreter diagnostics when templates appear in transformation chains. + +- [#8630](https://github.com/Effect-TS/effect/pull/8630) [`f72790c`](https://github.com/Effect-TS/effect/commit/f72790c618260bdfbf7a4e14d10abc2341d21e56) Thanks @gcanti! - Fix duplicate references in decoded and encoded schema representations when reusing schemas through optional or mutable keys. + +- [#8555](https://github.com/Effect-TS/effect/pull/8555) [`aa19472`](https://github.com/Effect-TS/effect/commit/aa1947284cee5cec68d08df9c5b41fae8fe1aa8c) Thanks @front-depiction! - Resolve a Schema union's candidate parsers once per union instead of on every decode, so union decoding and encoding allocate less and run faster. + +- [#8598](https://github.com/Effect-TS/effect/pull/8598) [`988c467`](https://github.com/Effect-TS/effect/commit/988c467afa90449b1d441c2db645eaec236763ac) Thanks @tim-smart! - Close server WebSockets with 1000 on success, 1001 on interruption, or 1011 on failure, while preserving explicit close codes. HTTP request scopes retain the handler's failure exit through response handling and middleware. + +- [#8632](https://github.com/Effect-TS/effect/pull/8632) [`735b77b`](https://github.com/Effect-TS/effect/commit/735b77b65ca909ca6e41915f0d1366531c32142a) Thanks @gcanti! - Add `succeedEffect`, `failEffect`, and `verifyRoundTripEffect` to `TestSchema` so assertions can use the calling Effect's services, test clock, and interruption, and mark the module and its public APIs as unstable. Preserve defects and interruption when they occur alongside schema validation failures. Rename `verifyLosslessTransformation` to `verifyRoundTrip`; existing callers must update to the new name. + +- [#8575](https://github.com/Effect-TS/effect/pull/8575) [`e5f7d12`](https://github.com/Effect-TS/effect/commit/e5f7d12af9abef188f7dc39b0207af1801b03ffd) Thanks @tim-smart! - Retry failed workflow run resets instead of acknowledging deferred completion before the run resumes. + +- [#8633](https://github.com/Effect-TS/effect/pull/8633) [`2c552f2`](https://github.com/Effect-TS/effect/commit/2c552f220e09dd63d4a777db6cd20b9a13e732f4) Thanks @IMax153! - Effect 4.0 is the first stable release of Effect v4. It replaces the 4.0.0 beta and release-candidate series, whose per-release notes remain below under the `4.0.0-beta.*` and `4.0.0-rc.*` headings. To upgrade from Effect 3, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + + **Packaging and versioning** + + - All Effect packages share one version number and are released together. Use the same version of `effect` and every `@effect/*` package. + - `@effect/platform`, `@effect/rpc`, `@effect/cluster`, `@effect/cli`, `@effect/ai`, `@effect/sql`, `@effect/workflow`, `@effect/experimental` and similar packages are merged into `effect`. Separate packages remain only for platforms (`@effect/platform-*`), SQL drivers (`@effect/sql-*`), AI providers (`@effect/ai-*`), framework bindings (`@effect/atom-*`), OpenTelemetry, Vitest and tooling. + - Modules such as `effect/http`, `effect/http-api`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/ai`, `effect/cli`, `effect/sql` and `effect/schema` import from `effect/`. APIs tagged `@stability unstable` may have breaking changes in minor releases. APIs without a stability tag follow semver. + - APIs that expose a third-party dependency are tagged `@stability unstable`, because that dependency's releases can change them. This covers `NodeRedis`, `DenoRedis`, `BunRedis`, `@effect/platform-node/Undici` and the undici dispatcher APIs, the `ws` options and re-exports in the platform packages, driver-specific options and clients in `@effect/sql-clickhouse`, `@effect/sql-d1`, `@effect/sql-libsql`, `@effect/sql-mssql`, `@effect/sql-mysql2`, `@effect/sql-pglite` and `@effect/sql-sqlite-do`, the provider clients, models and generated schemas in the `@effect/ai-*` packages, `@effect/opentelemetry`, and the `vitest` re-export in `@effect/vitest`. + - `effect` has no runtime dependencies. + - New packages in v4: `@effect/platform-deno`, `@effect/sql-pglite`, `@effect/ai-openai-compat`, `@effect/ai-typesafe`, `@effect/atom-react`, `@effect/atom-solid`, `@effect/atom-vue`, `@effect/openapi-generator` and `@effect/doctest`. `@effect/docgen` now lives in this repository. + + **Core** + + - The fiber runtime was rewritten for lower memory use and faster execution, with smaller bundles and better tree-shaking. + - Services are defined with `Context.Service`, `FiberRef` is replaced by `Context.Reference`, `Cause` has a flat structure, and many combinators were renamed. See the migration guide for the full list. + - Software transactional memory is built into `Effect.tx`, with `TxRef`, `TxQueue`, `TxHashMap`, `TxPubSub`, `TxSemaphore` and related `Tx*` data types. + - `Clock` separates wall-clock time from monotonic time. + - New modules include `Semaphore`, `Latch`, `LayerRef`, `ErrorReporter`, `ByteSize`, `Newtype` and `Crypto`. `Arbitrary` is now a native property-based testing module and no longer depends on fast-check. + + **Schema** + + - Schema v4 is a new implementation with faster parsing, class-based schemas, `make` constructors on every schema, and effectful decoding with services. + - `SchemaRepresentation` powers JSON Schema (Draft-04, Draft-07, 2020-12 and OpenAPI 3.0/3.1) import and export, TypeScript code generation, and AI structured output. + - `SchemaBinary` provides a compact binary codec and is the default wire format for cluster transports. + - Optional JIT and AOT schema compilers are available as experimental modules. + + **Platform, HTTP and RPC** + + - `HttpApi` supports typed response headers, streaming and SSE responses, the HTTP `QUERY` method, `HttpApiTest` for in-memory testing, and much faster type checking for large APIs. + - The HTTP modules add static file serving, response compression, rate-limited clients and graceful server shutdown. + - `Socket` has a pull-based reader with backpressure and STARTTLS support. Network addresses are modeled by `effect/net`. + - RPC serialization is schema-aware and supports server-originated requests and notifications. + - `@effect/platform-deno` adds full Deno support. The Node, Bun, Deno and browser packages all provide the platform `Crypto` service, and the browser package adds IndexedDB support. + + **Cluster and Workflow** + + - Workflows can be declared with class syntax, and `DurableQueue` and an in-memory `WorkflowEngine` are available. + - Runner memory can be bounded with `ShardingConfig` limits. Shutdown, entity movement and persisted replies are more reliable. + + **SQL** + + - `@effect/sql-pg` uses a built-in PostgreSQL client with pipelining, prepared statements and binary codecs, and no longer depends on `pg`. + - `@effect/sql-sqlite-node` uses Node's built-in `node:sqlite` and requires Node 22.16 or newer. + - `SqlError` exposes structured reasons such as `UniqueViolation`. + + **AI** + + - `effect/ai` adds `EmbeddingModel`, the `DecisionModel` classification and rating API, dynamic tools and tool approvals. + - The MCP server supports protocol versions 2024-11-05 through 2026-07-28, including elicitation, sampling with tools, resource subscriptions and typed tool output. + - Provider packages cover Anthropic, OpenAI, OpenAI-compatible APIs and OpenRouter. + + **CLI, Atom, observability and testing** + + - `effect/cli` adds global flags, command aliases and examples, an interactive wizard mode, and shell completions for Bash, Zsh and Fish. + - `effect/reactivity` and the `@effect/atom-*` bindings add SSR hydration, stale-while-revalidate atoms and serializable RPC and HttpApi queries. + - The OTLP exporters can be configured with standard OpenTelemetry environment variables, and `@effect/opentelemetry` aligns logs and spans with the OpenTelemetry specification. + - `@effect/vitest` runs property tests with the native `Arbitrary` module and supports Vitest fixtures. + + **Requirements** + + - TypeScript 5.9 or newer. TypeScript 7 is recommended. + - `@effect/vitest` and `@effect/doctest` require Vitest 5. + - `@effect/atom-react` requires React 19. + - `@effect/platform-deno` requires Deno 2.8.3 or newer. + +## 4.0.0-rc.118 + +### Patch Changes + +- [#8367](https://github.com/Effect-TS/effect/pull/8367) [`f75468a`](https://github.com/Effect-TS/effect/commit/f75468a1358bdea7203344eecc818cc3370d45a1) Thanks @Marve10s! - Add `Arbitrary.configureGlobal` to set default options for property checking and sampling, including the run count used by `@effect/vitest`. Explicit per-call options take precedence; passing an empty object restores the built-in defaults. + +- [#8543](https://github.com/Effect-TS/effect/pull/8543) [`34cc788`](https://github.com/Effect-TS/effect/commit/34cc78833df55bcc35b1f92603897aad36976b49) Thanks @front-depiction! - Fix missed updates through stale `AtomRegistry` nodes, premature dependency release, leaked superseded builds, and failed observed builds that never recover. + +- [#8421](https://github.com/Effect-TS/effect/pull/8421) [`eb02cbb`](https://github.com/Effect-TS/effect/commit/eb02cbb3f4d1f4bf20e597fe8fc707892aa930e0) Thanks @danieljvdm! - Defer `Atom.swr` stale refreshes until after reads, skipping them if the source becomes fresh or the atom is disposed. One-shot unmounted reads no longer refresh stale sources after disposal. + +- [#8534](https://github.com/Effect-TS/effect/pull/8534) [`4bb949e`](https://github.com/Effect-TS/effect/commit/4bb949e64f513b136f47e7e8ca6790fa0fe9aca9) Thanks @tim-smart! - Check identity authorization before resolving unencrypted event-log session bindings + +- [#8353](https://github.com/Effect-TS/effect/pull/8353) [`b409e3f`](https://github.com/Effect-TS/effect/commit/b409e3f8f48a108620c2c9ba52512942dfcf7563) Thanks @tim-smart! - Make `Effect.awaitAllChildren` interruptible while it waits for child fibers. + +- [#8510](https://github.com/Effect-TS/effect/pull/8510) [`4093507`](https://github.com/Effect-TS/effect/commit/4093507c667c8888d44920a0d742573b39c66723) Thanks @tim-smart! - Fix `Cache.get` retaining zero-TTL lookup results, which could evict live entries. + +- [#8339](https://github.com/Effect-TS/effect/pull/8339) [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462) Thanks @tim-smart! - Add data-last forms for selected safe Queue operations and selected Option, TxSemaphore, and AsyncResult APIs + +- [#8503](https://github.com/Effect-TS/effect/pull/8503) [`e54211a`](https://github.com/Effect-TS/effect/commit/e54211a57d067d941d499e24deb0cae8fb381ca1) Thanks @marbemac! - `Cause.pretty` and `Cause.prettyErrors` now restore `Error.stackTraceLimit` when formatting an error throws, instead of leaving it at `1` for the rest of the process. + +- [#8413](https://github.com/Effect-TS/effect/pull/8413) [`0c6c2de`](https://github.com/Effect-TS/effect/commit/0c6c2dec861becef8ff6c240a60bf0a61254040a) Thanks @front-depiction! - `Cause.squash` no longer allocates intermediate arrays to pick the first failure or defect. + +- [#8371](https://github.com/Effect-TS/effect/pull/8371) [`cdeccc7`](https://github.com/Effect-TS/effect/commit/cdeccc7e526ac386cfeb04107881642bcc9b320d) Thanks @front-depiction! - Fix asymmetric `Chunk.makeEquivalence` comparisons on sparse arrays. Avoid redundant copies in `Chunk` and `Array.dedupeWith`, and balance `Chunk.flatMap` concatenations. + +- [#8504](https://github.com/Effect-TS/effect/pull/8504) [`254fcc1`](https://github.com/Effect-TS/effect/commit/254fcc1944574118ac32c57ca0298a715b558889) Thanks @IMax153! - Fix the CLI lexer to treat negative numbers such as `-3.70` as values instead of short options, so `--lon -3.70` parses correctly + +- [#8474](https://github.com/Effect-TS/effect/pull/8474) [`bd14b64`](https://github.com/Effect-TS/effect/commit/bd14b6431a1eaf3619508beaaffd87e542c7ab03) Thanks @tim-smart! - `Scope.closeUnsafe` now requires a `Scope.Closeable`, matching `Scope.close`. Use a scope created by `Scope.make` or `Scope.fork`; a plain `Scope.Scope` cannot be closed directly. + +- [#8473](https://github.com/Effect-TS/effect/pull/8473) [`37064fd`](https://github.com/Effect-TS/effect/commit/37064fdfc8fb7b6a765045e66b95f6106aef1955) Thanks @tim-smart! - `Scope.close` now requires a `Scope.Closeable`. Use a scope created by `Scope.make` or `Scope.fork`; a plain `Scope.Scope` cannot be closed directly. + +- [#8542](https://github.com/Effect-TS/effect/pull/8542) [`702c34e`](https://github.com/Effect-TS/effect/commit/702c34e2af388d411d666fe3eb102fe9c7c11c78) Thanks @Marve10s! - Avoid retaining completed request ids when `MessageStorage` is disabled. Without storage, redelivering a completed request id now runs the request again instead of failing with `AlreadyProcessingMessage`. + +- [#8454](https://github.com/Effect-TS/effect/pull/8454) [`7ef0ce6`](https://github.com/Effect-TS/effect/commit/7ef0ce6f4295ea6fab131ec8f4af9efd67d9cf1a) Thanks @front-depiction! - Delay copying a derived Context's base until it has been read at least as many times as its size (with a minimum of eight), avoiding full copies for short-lived contexts. + +- [#8339](https://github.com/Effect-TS/effect/pull/8339) [`325e0fb`](https://github.com/Effect-TS/effect/commit/325e0fb66a69705decafacb781e6f9fb45f4f462) Thanks @tim-smart! - `TxPriorityQueue.fromIterable(order)(iterable)` passed its arguments to the implementation in the wrong order and produced a queue of `undefined` values; the data-first form was unaffected. + +- [#8414](https://github.com/Effect-TS/effect/pull/8414) [`cdec5ef`](https://github.com/Effect-TS/effect/commit/cdec5efd02f6487f71bc1bdfc0ed8cf0ff2b49a1) Thanks @front-depiction! - Cache named time zones by the requested id as well as the Intl-resolved id, avoiding repeated formatter construction for aliases. Zones still report the Intl-resolved id. + +- [#8386](https://github.com/Effect-TS/effect/pull/8386) [`f9179cb`](https://github.com/Effect-TS/effect/commit/f9179cba72cf2549574553e1e126669c71be52c0) Thanks @front-depiction! - Clean up rendered error stacks for common Effect callbacks and avoid per-step closure allocations in several combinators on V8. + + `Effect.flatMap` callbacks now receive only the value, without internal fiber/exit arguments or an internal `this` receiver. + +- [#8442](https://github.com/Effect-TS/effect/pull/8442) [`c760e1e`](https://github.com/Effect-TS/effect/commit/c760e1e07497a658d971326152198b571fa5780e) Thanks @front-depiction! - Avoid allocating a closure context for each struct property and array element during sequential Schema traversal. + +- [#8354](https://github.com/Effect-TS/effect/pull/8354) [`1b4461e`](https://github.com/Effect-TS/effect/commit/1b4461ec3a17e527dd39576f1783f73354187f1f) Thanks @tim-smart! - Move unstable modules from `effect/unstable/*` to `effect/*` and remove the old export paths. Drop the `unstable` segment from imports. These APIs remain `@stability unstable`. + + Move `Arbitrary` to the top level and update `@effect/vitest` to use the new entrypoint. Replace imports from `effect/arbitrary` or `effect/arbitrary/Arbitrary` with `import { Arbitrary } from "effect"` or `import * as Arbitrary from "effect/Arbitrary"`. + +- [#8362](https://github.com/Effect-TS/effect/pull/8362) [`5709ef6`](https://github.com/Effect-TS/effect/commit/5709ef6c0375a4b0e028bfa288d6eb3f13d70d7b) Thanks @xia-chao! - Add type guards for `HttpBodyError`, `CookiesError`, `MultipartError`, and `NdjsonError`. + +- [#8475](https://github.com/Effect-TS/effect/pull/8475) [`6b84149`](https://github.com/Effect-TS/effect/commit/6b84149622de85ba147e578da37ae4d996c1a683) Thanks @mugnivenko! - Annotate `Config.fail` return type as `Config`, so it composes with `Config.orElse` without widening the result to `Config` + +- [#8469](https://github.com/Effect-TS/effect/pull/8469) [`508c70b`](https://github.com/Effect-TS/effect/commit/508c70be1379bbcf854d318604a992e0662122d6) Thanks @Marve10s! - Honor `startImmediately: false` in `FiberHandle.run`, `FiberMap.run`, and `FiberSet.run`. + +- [#8443](https://github.com/Effect-TS/effect/pull/8443) [`382992f`](https://github.com/Effect-TS/effect/commit/382992f168d8187686010d6aa781f0d1fd48093a) Thanks @front-depiction! - Reduce the memory each fiber retains: the type brand now lives on the prototype, `Effect.callback` no longer binds its registration, and `Fiber.joinAll` no longer keeps a cancel closure per joined fiber. + +- [#8427](https://github.com/Effect-TS/effect/pull/8427) [`83d7c95`](https://github.com/Effect-TS/effect/commit/83d7c954651fe6291c009f1d4198e796cad3b069) Thanks @front-depiction! - Preserve the original failure or interruption when an `onExit` finalizer throws synchronously. + +- [#8535](https://github.com/Effect-TS/effect/pull/8535) [`6bf2eb7`](https://github.com/Effect-TS/effect/commit/6bf2eb76f0800d2e8ca402322023db6a32b1a899) Thanks @BleedingDev! - Build `FindMyWay` route params without `new Function`, avoiding string code generation in environments with CSP restrictions. + +- [#8556](https://github.com/Effect-TS/effect/pull/8556) [`ccade14`](https://github.com/Effect-TS/effect/commit/ccade1492cdc6beb71f943d7605c2ef5208b155c) Thanks @Marve10s! - Process atom writes queued by batch commit listeners instead of dropping them. + +- [#8150](https://github.com/Effect-TS/effect/pull/8150) [`b8d14d3`](https://github.com/Effect-TS/effect/commit/b8d14d3eed3cc15e939baa660e9800b96144a636) Thanks @Tyagiquamar! - Fix `Formatter.formatJson` to include `name` and `message` and preserve enumerable properties when stringifying `Error` instances without `toJSON`. + + ```ts + import { Formatter } from "effect" + + Formatter.formatJson(new Error("boom")) // now `{"name":"Error","message":"boom"}`, previously `{}` + ``` + +- [#8359](https://github.com/Effect-TS/effect/pull/8359) [`8fca194`](https://github.com/Effect-TS/effect/commit/8fca1947498e50dfa10480221290115a99cee05b) Thanks @gcanti! - Fix `Number.remainder` for decimal operands whose scaled coefficients exceed the safe integer range, preserving the exact value of integer operands. This also prevents `Schema.isMultipleOf` from accepting or rejecting large values incorrectly. + +- [#8459](https://github.com/Effect-TS/effect/pull/8459) [`34e09be`](https://github.com/Effect-TS/effect/commit/34e09becb6b93b885bbdbc1da4780b7f848bd2d0) Thanks @gcanti! - Align JSON Schema imports with Unicode string semantics by counting code points for string length keywords and applying regular expression patterns in ECMAScript Unicode mode. + +- [#8415](https://github.com/Effect-TS/effect/pull/8415) [`97afa37`](https://github.com/Effect-TS/effect/commit/97afa37ed03caba82fe3aec8e376ca8388450742) Thanks @front-depiction! - Reduce allocations in `Graph` traversals: `neighbors` / `successors` / `predecessors` deduplicate without an intermediate `Set` for typical degrees, and `unweightedDistances`, `astar` and `allShortestPaths` no longer allocate a closure or predecessor record per visited node or relaxation. + +- [#8360](https://github.com/Effect-TS/effect/pull/8360) [`321bae6`](https://github.com/Effect-TS/effect/commit/321bae616dfafc17f89a5406919f52f8d133c4d1) Thanks @tim-smart! - Improve hash composition, number hashing, cyclic equality, native collection comparison, and `HashMap` hashing. Fix invalid caches and referential `MutableHashMap` lookups. Hash values may change. + +- [#8572](https://github.com/Effect-TS/effect/pull/8572) [`fb718d3`](https://github.com/Effect-TS/effect/commit/fb718d3192acdcc6123daff69d150d040d911bf2) Thanks @tim-smart! - Add per-slot `HttpApi` parse options for params, query, headers, payload, success and error codecs. Each slot falls back to `ParseOptions` when unset. Set `HeadersParseOptions` to `{}` to keep strict body parsing without rejecting transport headers. + +- [#8477](https://github.com/Effect-TS/effect/pull/8477) [`ccd3440`](https://github.com/Effect-TS/effect/commit/ccd344096340fd1cdb28a315a6eb3eaced82ccbc) Thanks @tim-smart! - Build each `HttpRouter` entrypoint with a fresh router in a forked layer memo map. Routes on separate servers and handlers no longer leak into each other. + + Breaking changes: + + - Routes registered on a router provided by the app itself (for example, via `Layer.provide(HttpRouter.layer)`) are not served; the entrypoint serves its own router. For RPC, put the protocol and its handlers in the app passed to `serve`, for example `HttpRouter.serve(RpcLayer.pipe(Layer.provideMerge(RpcServer.layerProtocolHttp({ path: "/rpc" }))))`. + - Layers first built inside an entrypoint are private to it. Providing the same stateful service to both the app and a sibling layer can build it twice depending on build order. Provide services that must be shared outside the entrypoint; services already built in the parent memo map are reused. + +- [#8385](https://github.com/Effect-TS/effect/pull/8385) [`107dbec`](https://github.com/Effect-TS/effect/commit/107dbecab1debe45baae97e50913c0b5d6d84bb5) Thanks @front-depiction! - Fix interrupted `Layer` memoization so waiting and later requesters receive the interrupted build's exit instead of hanging, and shared layers are released when scopes close. Correct `MemoMap.get` observer accounting for effects run twice or never run. + +- [#8511](https://github.com/Effect-TS/effect/pull/8511) [`af510e8`](https://github.com/Effect-TS/effect/commit/af510e8197a486d53a0164a49ca4bf9276f6a19a) Thanks @tim-smart! - Skip `LayerMap` preloading for keys with a zero idle TTL. With the default TTL, preload failures now surface on first use; set a non-zero `idleTimeToLive` to retain eager validation. + +- [#8453](https://github.com/Effect-TS/effect/pull/8453) [`537cd82`](https://github.com/Effect-TS/effect/commit/537cd821740036410494e7f420bbae5b84b7c0af) Thanks @front-depiction! - Reduce allocations per `yield*` in `Effect.gen`, `Option.gen` and `Result.gen`, and per `Effect.fn` call. + + `Utils.SingleShotGen` now serves as its own completion result and is no longer an `IterableIterator`: it only implements `next`, and its `self` field has been removed. + +- [#8512](https://github.com/Effect-TS/effect/pull/8512) [`ff554ae`](https://github.com/Effect-TS/effect/commit/ff554aeb9b000882accc1c7027507e753aabf277) Thanks @tim-smart! - Run `Scope.close` finalizers uninterruptibly so interruption cannot abandon remaining cleanup, including when closing a `ManagedRuntime`. + +- [#8528](https://github.com/Effect-TS/effect/pull/8528) [`9a5a383`](https://github.com/Effect-TS/effect/commit/9a5a383cc6526843fef645309f822400e053e772) Thanks @tim-smart! - Wait for managed fibers and their cleanup to finish before releasing layer resources when disposing a `ManagedRuntime`. + +- [#8439](https://github.com/Effect-TS/effect/pull/8439) [`d2f3547`](https://github.com/Effect-TS/effect/commit/d2f35475610c59596eec2cd47fb58c65b85ef0c1) Thanks @front-depiction! - Stop allocating closures on every `Effect.match` and `Effect.matchCause` call. + +- [#8507](https://github.com/Effect-TS/effect/pull/8507) [`25d9e47`](https://github.com/Effect-TS/effect/commit/25d9e47f0132e485af466a75e6008600b12da999) Thanks @IMax153! - Clarify the `McpServer.toolkit` error when a tool's parameters are not an object schema. + +- [#8505](https://github.com/Effect-TS/effect/pull/8505) [`372a45d`](https://github.com/Effect-TS/effect/commit/372a45de59170e2ac982aaac0b5d173d3f84b0f8) Thanks @IMax153! - Fix stdio MCP servers rejecting pings before initialization when a stateful protocol is configured, including when a stateless protocol is listed first. These pings now return an empty result without creating a session. + +- [#8508](https://github.com/Effect-TS/effect/pull/8508) [`5e9fd25`](https://github.com/Effect-TS/effect/commit/5e9fd25cbda46b0e035644fb94aa4a9c8851263d) Thanks @IMax153! - Report all parameter validation errors for MCP tools in a single response, including missing or invalid fields and, for strict tools, unknown keys. + +- [#8465](https://github.com/Effect-TS/effect/pull/8465) [`c85c8c3`](https://github.com/Effect-TS/effect/commit/c85c8c31783afab69ac2aff791549bd438ec6015) Thanks @izakfilmalter! - Preserve tool annotation titles in MCP `tools/list` responses for the 2025-06-18 and 2025-11-25 protocol revisions, alongside the top-level title and behavioral hints. + +- [#8552](https://github.com/Effect-TS/effect/pull/8552) [`b501557`](https://github.com/Effect-TS/effect/commit/b501557939d74c5fa6228c40011e9546c2af0fb2) Thanks @Floriferous! - Report declared MCP tool failures with empty messages using their encoded value. Handler execution errors now return `isError` results on protocol version 2025-11-25 and `-32603 Internal error` on older versions, instead of `-32602 Invalid params`. + +- [#8380](https://github.com/Effect-TS/effect/pull/8380) [`35ef06c`](https://github.com/Effect-TS/effect/commit/35ef06c56dcf2debedbf72043d660980eeb73e83) Thanks @front-depiction! - Fix `Metric` series key collisions, duplicate series for empty attributes, and updates lost after a registry is cleared. Reuse series keys when contextual attributes are unchanged. + +- [#8420](https://github.com/Effect-TS/effect/pull/8420) [`d6cc5d9`](https://github.com/Effect-TS/effect/commit/d6cc5d987011c10e42985dc692130511949838d1) Thanks @danieljvdm! - Emit parts buffered while reading a multipart file before pulling more input. + +- [#8450](https://github.com/Effect-TS/effect/pull/8450) [`83b7b1f`](https://github.com/Effect-TS/effect/commit/83b7b1fc9124d23519cc5b8b74a42129c2ce59d9) Thanks @front-depiction! - Fix `MutableList.prependAll` with empty input corrupting the list and breaking subsequent takes or appends. + +- [#8447](https://github.com/Effect-TS/effect/pull/8447) [`f05fe36`](https://github.com/Effect-TS/effect/commit/f05fe36f877b751d8602aa3fc5cb0c9501619167) Thanks @front-depiction! - Release consumed slots in a growing `MutableList` bucket, and pass list indices rather than bucket offsets to `MutableList.filter` predicates. + +- [#8448](https://github.com/Effect-TS/effect/pull/8448) [`1fd49d4`](https://github.com/Effect-TS/effect/commit/1fd49d4e379effa86cbfa81650afa39ca28293f5) Thanks @front-depiction! - Fix `MutableList.takeN` leaving a drained bucket at the head when it stops at a bucket boundary. This also prevents `Queue.takeN` from making subsequent messages inaccessible after an `offerAll` boundary. + +- [#8340](https://github.com/Effect-TS/effect/pull/8340) [`c79088d`](https://github.com/Effect-TS/effect/commit/c79088d69372ba533ecd6cacb846a43322c2d075) Thanks @fubhy! - Add family-aware internet address and multicast interface types to `NetAddress` + +- [#8407](https://github.com/Effect-TS/effect/pull/8407) [`0cbb457`](https://github.com/Effect-TS/effect/commit/0cbb45792b59e9ea00e19001a019e790d53407e6) Thanks @tim-smart! - Store `NetAddress` IPv4 and IPv6 addresses as numbers instead of a `Uint8Array`, making construction, `Equal` and `Hash` cheaper, especially on Deno. + +- [#8545](https://github.com/Effect-TS/effect/pull/8545) [`8202a6e`](https://github.com/Effect-TS/effect/commit/8202a6ea410aa9a5a92988ad54f83b831a9737fe) Thanks @hsyntax! - Mark OpenAPI parameters and response headers as optional when their encoded key is optional, such as keys using `Schema.withDecodingDefaultKey` or `Schema.withDecodingDefault`. Generated documents previously marked these keys as required, even though they can be absent on the wire. + +- [#8547](https://github.com/Effect-TS/effect/pull/8547) [`8d070c3`](https://github.com/Effect-TS/effect/commit/8d070c3eadc89bda58fec23977ba8ceebc244347) Thanks @gcanti! - Generate OpenAPI parameters and response headers from their encoded object representation while preserving property annotations. + +- [#8551](https://github.com/Effect-TS/effect/pull/8551) [`0cf7655`](https://github.com/Effect-TS/effect/commit/0cf7655d1fe0ca959f34d261e345cc222fd8e5fd) Thanks @hsyntax! - Preserve encoded string literals and exportable checks such as `pattern` in OpenAPI schemas for `HttpApiSchema.asText()` bodies. Text bodies keep their string type even when their schemas are opaque or referenced components. + +- [#8522](https://github.com/Effect-TS/effect/pull/8522) [`d470d54`](https://github.com/Effect-TS/effect/commit/d470d54495b9b7ac66375abd95e294417ebc64b9) Thanks @FNDEVVE! - Drain OTLP export response bodies before completing or retrying each request. + +- [#8395](https://github.com/Effect-TS/effect/pull/8395) [`2121472`](https://github.com/Effect-TS/effect/commit/21214728d20b17b4b0497dac85bc9be5c8ed0bd3) Thanks @tim-smart! - Prevent lost permits when a partitioned semaphore acquisition is interrupted, and keep competing immediate takes from acquiring the same permit. + +- [#8558](https://github.com/Effect-TS/effect/pull/8558) [`4113bc7`](https://github.com/Effect-TS/effect/commit/4113bc78eeed050b5833c9460d398cf8d8887e48) Thanks @fe-franco! - Fail `@effect/sql-pg` transactions when PostgreSQL returns `ROLLBACK` for `COMMIT` after a caught statement error. `SqlClient.make` accepts a commit effect so drivers can inspect the result. + +- [#8468](https://github.com/Effect-TS/effect/pull/8468) [`a41a441`](https://github.com/Effect-TS/effect/commit/a41a4414d30a7f96aa8f17ec25039dd194718ef5) Thanks @lloydrichards! - In MCP protocols that omit string `structuredContent`, return plain text for toolkit and hand-written tool results whose single text block is an exact JSON mirror of that string. + +- [#8337](https://github.com/Effect-TS/effect/pull/8337) [`3af6bd0`](https://github.com/Effect-TS/effect/commit/3af6bd0803ebdbc44b75ef82b16daa9a82ffa768) Thanks @mugnivenko! - Add PlatformError.isPlatformError guard + +- [#8502](https://github.com/Effect-TS/effect/pull/8502) [`67ee3b8`](https://github.com/Effect-TS/effect/commit/67ee3b87f025089cc01631985ad7860b391d6ef3) Thanks @elizabethcheng! - Use a consistent row-lock order for PostgreSQL shard lease acquisition, refresh, and bulk release when `shardLockDisableAdvisory` is enabled. + +- [#8540](https://github.com/Effect-TS/effect/pull/8540) [`e8df3d8`](https://github.com/Effect-TS/effect/commit/e8df3d8e7650ce272256559081ad91156826bf54) Thanks @Marve10s! - Treat an `Infinity` capacity passed to `PubSub` as unbounded. + +- [#8345](https://github.com/Effect-TS/effect/pull/8345) [`1dbc4c3`](https://github.com/Effect-TS/effect/commit/1dbc4c382c783099c74c93dae20d72a12db92e69) Thanks @xia-chao! - Add `PubSub.isPubSub`, matching `Queue.isQueue` and `TxPubSub.isTxPubSub`. + +- [#8449](https://github.com/Effect-TS/effect/pull/8449) [`80d5adb`](https://github.com/Effect-TS/effect/commit/80d5adb16787b11507a67809fbe6978da719d76a) Thanks @front-depiction! - Fix a race where interrupting a waiting `PubSub` subscriber could swallow a later message, or interrupting a backpressured publisher could still publish when space became available. + +- [#8569](https://github.com/Effect-TS/effect/pull/8569) [`aa430d8`](https://github.com/Effect-TS/effect/commit/aa430d89c134520d9e77babbf759a73650244c89) Thanks @tim-smart! - Batch takes drain remaining messages when a queue closes, even below the requested minimum. Later takes receive the terminal error. + +- [#8568](https://github.com/Effect-TS/effect/pull/8568) [`24f7d32`](https://github.com/Effect-TS/effect/commit/24f7d32b389026f0395b9ec30d49f9091eb2b114) Thanks @tim-smart! - Withdraw pending `Queue.offer` and `Queue.offerAll` values when their producer is interrupted after `Queue.end` or `Queue.fail`. The queue can finish closing once those offers are withdrawn. + +- [#8412](https://github.com/Effect-TS/effect/pull/8412) [`f4dd1ae`](https://github.com/Effect-TS/effect/commit/f4dd1aec1ab5aaad01d831958c8ee709a9f4661b) Thanks @front-depiction! - Fix lost wake-ups in `Queue` when a fiber yields between checking for a message or capacity and registering its waiter. + + `Queue.offer` on a zero-capacity `dropping` queue now hands the message to a waiting taker, matching `offerUnsafe` and `offerAll`. + +- [#8472](https://github.com/Effect-TS/effect/pull/8472) [`0981d01`](https://github.com/Effect-TS/effect/commit/0981d01df8e20390ca407e3b64d9e6b56f59d4b5) Thanks @tim-smart! - Fix Queue takers missing offers that arrive while they are registering. + +- [#8514](https://github.com/Effect-TS/effect/pull/8514) [`0a29ca6`](https://github.com/Effect-TS/effect/commit/0a29ca6840a362cbc9d32fb263cddef804cbc6fd) Thanks @tim-smart! - Retry failed background pool acquisitions when borrowed, rather than passing stale errors to later queries. Callers awaiting their own failed connection still receive its error. + +- [#8397](https://github.com/Effect-TS/effect/pull/8397) [`17810b4`](https://github.com/Effect-TS/effect/commit/17810b478d3d01445ab8da9062df48f7f841b508) Thanks @tim-smart! - Close the interruption gap before scope and cache cleanup registration. + +- [#8365](https://github.com/Effect-TS/effect/pull/8365) [`62b2ea9`](https://github.com/Effect-TS/effect/commit/62b2ea97fe8fd85f44b508cf2633fee5d1e285e9) Thanks @tim-smart! - Rename the HTTP API entry point from `effect/httpapi` to `effect/http-api` and remove the old export path. Runtime TypeIds under `~effect/httpapi/*`, service keys under `effect/httpapi/*`, and the reserved `effect/httpapi/stream/failure` SSE event name now use `http-api` as well. + +- [#8378](https://github.com/Effect-TS/effect/pull/8378) [`4e4fa8b`](https://github.com/Effect-TS/effect/commit/4e4fa8b18455dc05c5a601ae7613b95c27aadabd) Thanks @gcanti! - Rename range checks so their subject comes last: `Schema.isBetweenLength`, `Schema.isBetweenCodePoints`, `Schema.isBetweenSize`, and `Schema.isBetweenProperties`. Also rename the string checks to the grammatical `Schema.isStartingWith`, `Schema.isEndingWith`, and `Schema.isIncluding`. Update the corresponding `SchemaRepresentation.*Reviver` exports and persisted `effect/schema/...` check IDs to use the new names. + +- [#8533](https://github.com/Effect-TS/effect/pull/8533) [`3495bd8`](https://github.com/Effect-TS/effect/commit/3495bd8858de3a7b7e75090587f813e096e851ee) Thanks @tim-smart! - Keep the source result type for Effect.repeat with times or schedule, even when while or until is a refinement. Bounded repetition may finish before the predicate stops it, so callers relying on the previous narrowed type must check the result. + +- [#8390](https://github.com/Effect-TS/effect/pull/8390) [`b3032f0`](https://github.com/Effect-TS/effect/commit/b3032f01f288942e7ef7398efc654b58d6525792) Thanks @front-depiction! - Fix interrupt-time resource leaks: + + - `Pool.get`, `Pool.use` and `RcRef.get` register cleanup before they can be interrupted. + - `RcRef` and `RcMap` finish closing expired resources even if their idle fibers are interrupted. + - `ScopedRef` closes in-flight replacements with their owner. `make`, `fromAcquire` and `set` now interrupt if the owning scope has closed. + +- [#8541](https://github.com/Effect-TS/effect/pull/8541) [`d26a913`](https://github.com/Effect-TS/effect/commit/d26a913221566bd6156be860dfb9cd5c55796d1c) Thanks @Marve10s! - Skip malformed ndjson lines and non-object JSON-RPC messages so later requests still decode. Handle non-string notification methods without throwing. + +- [#8437](https://github.com/Effect-TS/effect/pull/8437) [`93f826a`](https://github.com/Effect-TS/effect/commit/93f826af79734296b9659400cb5f3c29f45dba32) Thanks @front-depiction! - Stop `Effect.map`, `Effect.tap`, `Effect.exit`, `Effect.match` and `Effect.matchCause` allocating a success `Exit` on every step. + +- [#8519](https://github.com/Effect-TS/effect/pull/8519) [`330b747`](https://github.com/Effect-TS/effect/commit/330b7475e2135bb9bc6aad1df5d513d32299ebc1) Thanks @tim-smart! - Add `Schedule.once`, a schedule that recurs immediately once and outputs `void`. + +- [#8441](https://github.com/Effect-TS/effect/pull/8441) [`b808b5d`](https://github.com/Effect-TS/effect/commit/b808b5d0c0132e7456f81125ff989b83106c1468) Thanks @front-depiction! - Skip generator allocation for synchronous array and tuple parsing. + +- [#8440](https://github.com/Effect-TS/effect/pull/8440) [`2575fcc`](https://github.com/Effect-TS/effect/commit/2575fcc12bb4d1aed5603cfab579d9030e78e231) Thanks @front-depiction! - Avoid rebuilding the parser wrappers on every `SchemaParser.asserts` call. + +- [#8364](https://github.com/Effect-TS/effect/pull/8364) [`287273c`](https://github.com/Effect-TS/effect/commit/287273cf55971f87dd62cb229d934dcf52242d74) Thanks @gcanti! - Add `Schema.isMinCodePoints`, `Schema.isMaxCodePoints`, and `Schema.isBetweenCodePoints` to validate string lengths by Unicode code point count, with JSON Schema export, representation revivers, and arbitrary generation support. + +- [#8430](https://github.com/Effect-TS/effect/pull/8430) [`0860c0c`](https://github.com/Effect-TS/effect/commit/0860c0c125322a99454b30aafa2963f7289d70d6) Thanks @front-depiction! - Speed up schema decoder generation for deeply nested schemas. + +- [#8451](https://github.com/Effect-TS/effect/pull/8451) [`8d27554`](https://github.com/Effect-TS/effect/commit/8d275546500c91a752da26ad37756e887fb4595c) Thanks @front-depiction! - Speed up Schema code generation for nested schemas, especially when a child cannot be generated. + +- [#8429](https://github.com/Effect-TS/effect/pull/8429) [`c2e810c`](https://github.com/Effect-TS/effect/commit/c2e810cc7ae3f49f3e888ce83700ebdf1c34c503) Thanks @front-depiction! - Memoize recursive `Schema.toEquivalence` compilation and remove unused path tracking, reducing work and retained memory for deeply nested values. + +- [#8423](https://github.com/Effect-TS/effect/pull/8423) [`35ecbb9`](https://github.com/Effect-TS/effect/commit/35ecbb986497d6593ff88faa44b8b1eb8e252024) Thanks @fml09! - Ignore non-enumerable own properties when checking schema excess properties, allowing `Schema.TaggedError` responses to encode under strict `HttpApi.ParseOptions`. Symbol index signatures now skip non-enumerable entries during decoding and encoding, matching string index signatures; make those properties enumerable or declare them explicitly in a `Struct` to retain them. + +- [#8436](https://github.com/Effect-TS/effect/pull/8436) [`b442528`](https://github.com/Effect-TS/effect/commit/b442528ddf9608cf9b559b94b77198cc51643dd7) Thanks @front-depiction! - Fix `Schema.toFormatter` compiling a recursive schema again at every nesting level of the formatted value. + +- [#8482](https://github.com/Effect-TS/effect/pull/8482) [`16623c7`](https://github.com/Effect-TS/effect/commit/16623c7d30c8ff654e33fce1122aae7f5e5d8d78) Thanks @gcanti! - Improve `Schema.toJsonSchemaDocument` export of built-in checks. String code-unit length checks now use safe code-point bounds, regex and literal checks avoid patterns that would reject accepted values under Unicode matching, and cardinality checks reject non-finite bounds instead of emitting invalid schemas. Custom check exporters can mark a fragment as approximate by returning `[schema, true]`; `oneOf` then falls back to `anyOf`, and approximate record-key patterns are not used as `patternProperties` selectors. + + Built-in checks whose exported constraints can be looser than their runtime checks, including approximations that predate this change: + + | Check | Approximation | Change in this PR | + | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | + | `isPattern` | The default applies to direct `isPattern` checks: the pattern is omitted unless the RegExp uses the Unicode flag and its other flags are `d`, `g`, or `y`. Sticky patterns are anchored at the start. Built-in checks may override the default when their Unicode semantics are known to be safe or intentionally loose. | Updated export and approximation metadata. | + | `isStartingWith` | A trailing high surrogate is removed from the exported prefix; if nothing remains, the pattern is omitted. Other prefixes retain an exact pattern. | Updated export. | + | `isEndingWith` | A leading low surrogate is removed from the exported suffix; if nothing remains, the pattern is omitted. Other suffixes retain an exact pattern. | Updated export. | + | `isIncluding` | A leading low surrogate or trailing high surrogate is removed from the exported substring; if nothing remains, the pattern is omitted. Other substrings retain an exact pattern. | Updated export. | + | `isMinLength` | On strings, the exported minimum is `Math.ceil(minLength / 2)` code points. Bounds of 0 and 1 are exact; array bounds are unchanged. | Updated export; rejects non-finite bounds. | + | `isMaxLength` | On strings, the exported maximum counts code points rather than UTF-16 code units. A bound of 0 is exact; array bounds are unchanged. | Updated type and union handling; rejects non-finite bounds. | + | `isBetweenLength` | On strings, combines the lower- and upper-bound approximations above. Array bounds are unchanged. | Updated export; rejects non-finite bounds. | + | `isUppercased`, `isLowercased` | Patterns exclude only ASCII letters of the opposite case. Non-ASCII characters that change under JavaScript casing can still pass JSON Schema validation. | Marked as approximate. | + | `isCapitalized`, `isUncapitalized` | Patterns exclude only ASCII letters of the opposite case at the start. Non-ASCII characters that change under JavaScript casing can still pass JSON Schema validation. | Marked as approximate. | + | `isInt` | Exports `type: "integer"` without the safe-integer bounds required by the runtime check. | Marked as approximate. | + | `isMinProperties`, `isMaxProperties`, `isBetweenProperties` | Counts match for parsed JSON objects, but JSON Schema property-count keywords do not constrain arrays, which these object-typed checks can also receive. | Rejects non-finite bounds and tracks non-object use. | + | `isPropertyNames` | Inherits any approximation in the key schema. JSON Schema's `propertyNames` also does not constrain array keys. | Propagates approximation from the key schema. | + | `isMinSize`, `isMaxSize`, `isBetweenSize` | The size constraint is omitted. | Rejects non-finite bounds and marks omission approximate. | + | `isUniqueKey` | Uniqueness of tuple keys is omitted. | Omission is now tracked as approximate. | + | Comparison and range checks for `Date`, `BigInt`, and `BigDecimal` | Their bounds are omitted from JSON Schema export. | Omission is now tracked as approximate. | + + Length checks on objects with a `length` property also omit that constraint. Code-point checks map directly to JSON Schema length bounds and are not approximate on strings; this PR adds rejection of non-finite bounds to those checks as well. + +- [#8435](https://github.com/Effect-TS/effect/pull/8435) [`5521209`](https://github.com/Effect-TS/effect/commit/5521209a7697498dc26fe658a52d137c6c82c47e) Thanks @front-depiction! - Build the detailed traversal for structs without index signatures only when needed, avoiding unused parser allocations under default parse options. + +- [#8434](https://github.com/Effect-TS/effect/pull/8434) [`477394d`](https://github.com/Effect-TS/effect/commit/477394deb644711551d4830e591b538d7267d0db) Thanks @front-depiction! - Split compiled tuple rest elements once per schema instead of on every equivalence or formatter call. + +- [#8428](https://github.com/Effect-TS/effect/pull/8428) [`3036241`](https://github.com/Effect-TS/effect/commit/3036241e1e4366dd92047f4126f62e3f241abefd) Thanks @front-depiction! - Stop Schema unions rebuilding their runtime-type candidate list on every decode. The list is now built once per union and reused. + +- [#8418](https://github.com/Effect-TS/effect/pull/8418) [`ca422fd`](https://github.com/Effect-TS/effect/commit/ca422fdf49416343328ac1d978bc1769877a5ebe) Thanks @front-depiction! - Run all scope finalizers even when one throws synchronously, and preserve the original failure when closing the scope. + +- [#8457](https://github.com/Effect-TS/effect/pull/8457) [`4f5f866`](https://github.com/Effect-TS/effect/commit/4f5f86689657e935e881652fd107fa184dc6a542) Thanks @front-depiction! - Forked scopes detach from their parent on close, even if cleanup throws or is interrupted, without a bookkeeping finalizer. The readonly `Scope.parent` field exposes the parent scope. + +- [#8396](https://github.com/Effect-TS/effect/pull/8396) [`54ff4f2`](https://github.com/Effect-TS/effect/commit/54ff4f2121d371f7c8c3b47df821c6e5ac8c923f) Thanks @tim-smart! - Shared `ScopedCache` lookups survive individual caller interruption. If the last waiter leaves while a lookup is pending, it is interrupted and its scope closed. Missing-key lookups, including `refresh`, now run in daemon fibers like `Cache`, so children forked by a lookup end with it rather than with the caller. + +- [#8488](https://github.com/Effect-TS/effect/pull/8488) [`c8a9f85`](https://github.com/Effect-TS/effect/commit/c8a9f8543de5a93e021e1738af248b2f4e0a5b63) Thanks @gcanti! - - Allow `Config.withDefault` and `Config.option` to recover `Config.all` groups with missing children even when other children have values. Validation and source errors still propagate. A group default replaces the entire group; put defaults on individual children to preserve supplied values, or use `Config.schema(Schema.Struct(...))` to reject incomplete objects. + - Make `Config.orElse` adopt the fallback's result, allowing a later default or option to handle an absent fallback even after invalid input was recovered. + - Return `None` for `ShardingConfig.runnerListenAddress` when `listenHost` is absent and `listenPort` is valid. Supply `listenHost` to configure the listen address. + +- [#8433](https://github.com/Effect-TS/effect/pull/8433) [`0c0aa5e`](https://github.com/Effect-TS/effect/commit/0c0aa5ebb0b833435156c63536be0a967cbe19a4) Thanks @front-depiction! - Avoid redundant context lookups for the tracer and tracing-enabled flag when creating spans. + +- [#8356](https://github.com/Effect-TS/effect/pull/8356) [`d426feb`](https://github.com/Effect-TS/effect/commit/d426feb658448593980da79937b6456fbef4a258) Thanks @tim-smart! - Split the encoding API into `effect/encoding/Base64`, `effect/encoding/Base64Url`, `effect/encoding/Hex`, and `effect/encoding/EncodingError`. The former `effect/Encoding` module has been removed; migrate each helper to its format module and use the shared error entrypoint to name or narrow encoding failures. + + Two migrations need special attention: `randomHex` is now `Hex.random`, and the runtime value of `EncodingErrorTypeId` changed from `~effect/Encoding/EncodingError` to `~effect/encoding/EncodingError`. + +- [#8529](https://github.com/Effect-TS/effect/pull/8529) [`2a8f5c7`](https://github.com/Effect-TS/effect/commit/2a8f5c748d83d8de22bf865618aa76ec77b000b2) Thanks @tim-smart! - Roll back SQLite transactions left open by a failed COMMIT, such as a deferred foreign key violation, so the connection can be reused. + +- [#8445](https://github.com/Effect-TS/effect/pull/8445) [`6332de2`](https://github.com/Effect-TS/effect/commit/6332de29a8138cf220576656b418eda17aa1c4ba) Thanks @front-depiction! - Keep long chains of `Stream.concat` linear in their length. + +- [#8416](https://github.com/Effect-TS/effect/pull/8416) [`c00c8ff`](https://github.com/Effect-TS/effect/commit/c00c8ffd48fe17f40ec1ba56a27bf1f2efed31b8) Thanks @front-depiction! - Fix `Stream.forever` and `Stream.repeat` slowing down and retaining pull layers with each repetition by using a constant-depth loop. + +- [#8411](https://github.com/Effect-TS/effect/pull/8411) [`aa7de26`](https://github.com/Effect-TS/effect/commit/aa7de2663104e65677ec69a4a47f5df9a3adadc7) Thanks @front-depiction! - Fix concurrent `Stream.mapEffect` hanging when the mapping function throws synchronously. Run the function inside its forked fiber so the throw is reported as a defect. + +- [#8410](https://github.com/Effect-TS/effect/pull/8410) [`6f44ca0`](https://github.com/Effect-TS/effect/commit/6f44ca0b98b27a4222c677bf0b89e85a16790ebb) Thanks @front-depiction! - Fix `Stream.broadcast`, `Stream.broadcastN`, `Stream.share` and `Stream.toPubSubTake` subscribers that never end. + + The upstream's exit was published as an ordinary message, so a subscriber that arrived after it, or whose bounded `dropping` PubSub was full, waited forever. The new `PubSub.end` ends a PubSub with a final message that every subscriber, current or future, receives after draining its buffered messages, and the broadcast operators now use it for the exit. + +- [#8405](https://github.com/Effect-TS/effect/pull/8405) [`98792c3`](https://github.com/Effect-TS/effect/commit/98792c3bf8bf80237362c51505fc080d2a89ac1d) Thanks @tim-smart! - Add a `probabilityPrecision` option to `DecisionModel.make` that accepts and rescales the sum drift caused by a provider rounding each probability. OpenRouter and TypeSafe decision models set it to two decimals, so rounded distributions such as `0.02 / 0.93 / 0.04` no longer fail with `InvalidOutputError`. Providers that do not opt in keep strict sum validation. + +- [#8531](https://github.com/Effect-TS/effect/pull/8531) [`1f76040`](https://github.com/Effect-TS/effect/commit/1f760401be854f8e50c414902be6eeea1d479682) Thanks @tim-smart! - `Toolkit.handle` now requires tool handler services on the outer Effect as well as the returned Stream. If a parameter decoder needs a service, provide it when running the outer `handle` Effect, not only when consuming the Stream. + +- [#8530](https://github.com/Effect-TS/effect/pull/8530) [`cf7cfd6`](https://github.com/Effect-TS/effect/commit/cf7cfd6144faac0c0a4c236382706f38e246795d) Thanks @tim-smart! - Expose `AiError` in the error type of streams returned by `Toolkit.handle` in return mode. Result-encoding failures already fail the stream at runtime; consumers that treated the extracted stream as infallible must handle or propagate that error. + +- [#8387](https://github.com/Effect-TS/effect/pull/8387) [`6bc0eee`](https://github.com/Effect-TS/effect/commit/6bc0eeea42fa557cab193c84646d2c4dae7f5b20) Thanks @front-depiction! - End tracing spans and restore fiber context when interruption occurs as a traced region starts. Ensure `HttpMiddleware.tracer` ends its span on interruption. + +- [#8370](https://github.com/Effect-TS/effect/pull/8370) [`88b79d7`](https://github.com/Effect-TS/effect/commit/88b79d74f19567de3fb00c84e71df9f21455fe95) Thanks @front-depiction! - Build `Mime` lookup tables on first lookup so bundlers can drop the MIME data when no lookup is used. + +- [#8361](https://github.com/Effect-TS/effect/pull/8361) [`db7b69e`](https://github.com/Effect-TS/effect/commit/db7b69efffa6b3c104dbe5f6f1192db5630df272) Thanks @xia-chao! - Add `isNonEmpty` predicates to `TxHashSet`, `TxPubSub`, and `TxQueue`. + +- [#8452](https://github.com/Effect-TS/effect/pull/8452) [`43bbb25`](https://github.com/Effect-TS/effect/commit/43bbb257c22d5a819e423526a358091be629206b) Thanks @front-depiction! - Fix `Effect.tx` never committing a `TxRef` write of `-0` over `0`, and only wake retry waiters for refs a transaction wrote, not refs it only read. + +- [#8372](https://github.com/Effect-TS/effect/pull/8372) [`6ecd4b3`](https://github.com/Effect-TS/effect/commit/6ecd4b380947ff4db0e8f2144c330526824e67d7) Thanks @front-depiction! - Fix `Effect.txRetry` blocking forever when a `TxRef` the transaction read was changed by another commit while the transaction was suspended. The transaction now reruns immediately instead of waiting for a later change. + +- [#8348](https://github.com/Effect-TS/effect/pull/8348) [`7b81a95`](https://github.com/Effect-TS/effect/commit/7b81a95de1d0666e102ea9b29cd9a1fa05b2649c) Thanks @xia-chao! - Add `TxChunk.isTxChunk`, a type guard for `TxChunk` values. The other transactional data types already expose an equivalent `isTx*` guard. + +- [#8446](https://github.com/Effect-TS/effect/pull/8446) [`dd8990b`](https://github.com/Effect-TS/effect/commit/dd8990bb1b3379e5bb09f384005e45b85980fe4d) Thanks @front-depiction! - `TxPriorityQueue.offerAll` merges new values into the sorted queue instead of re-sorting existing values. + +- [#8334](https://github.com/Effect-TS/effect/pull/8334) [`19835f2`](https://github.com/Effect-TS/effect/commit/19835f2b92a9fca55a965903d74dda074b17e8ea) Thanks @nikelborm! - Added `Config.flatMap` + +- [#8455](https://github.com/Effect-TS/effect/pull/8455) [`1a3eb9e`](https://github.com/Effect-TS/effect/commit/1a3eb9ea60dcfcfe09406bf0b766f806883cbf87) Thanks @henningpokriefke! - Derive workflow execution IDs from a length-prefixed workflow tag and idempotency key. + +## 4.0.0-rc.117 + +### Patch Changes + +- [#8331](https://github.com/Effect-TS/effect/pull/8331) [`9953c92`](https://github.com/Effect-TS/effect/commit/9953c92b860061ec928098b121279087ee56e5e9) Thanks @tim-smart! - Bound local sends that wait for an unregistered cluster entity type. These sends now fail with an `Entity type ... not registered` defect at the shared runner registration deadline. Once that startup deadline has elapsed, sends to not-yet-registered dynamic entity types fail immediately. + +- [#8319](https://github.com/Effect-TS/effect/pull/8319) [`c6e8b20`](https://github.com/Effect-TS/effect/commit/c6e8b20c27b08e94549a9431c38e867aa014b724) Thanks @fubhy! - Extend `NetAddress.toCanonical` to accept internet addresses. IPv4-mapped IPv6 addresses become IPv4 while retaining the port. Other addresses retain their identity and IPv6 scope metadata. + +- [#8317](https://github.com/Effect-TS/effect/pull/8317) [`8d40572`](https://github.com/Effect-TS/effect/commit/8d40572de3762d51b142cfff30e6927ef21e37a3) Thanks @tim-smart! - Fix cluster workflows stalling after request resets. + +- [#8323](https://github.com/Effect-TS/effect/pull/8323) [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1) Thanks @fubhy! - Add validated multicast address refinements for IP and MAC addresses. + +- [#8310](https://github.com/Effect-TS/effect/pull/8310) [`3b155e3`](https://github.com/Effect-TS/effect/commit/3b155e3e24e42b603d48dff5d3280715944998f0) Thanks @williamrobertson13! - Fix memory leaks in `Pool.makeWithTTL` with the usage strategy by releasing retired resources and consumed acquisition errors from the TTL queue. + +- [#8326](https://github.com/Effect-TS/effect/pull/8326) [`895d944`](https://github.com/Effect-TS/effect/commit/895d94410f90c45546aec593730e5630f200ca1b) Thanks @lloydrichards! - Normalize identified MCP output schemas to object roots across protocol versions. + Support identified object schemas in MCP elicitation requests. + +- [#8318](https://github.com/Effect-TS/effect/pull/8318) [`705fb68`](https://github.com/Effect-TS/effect/commit/705fb6852ac24791352bd78889d57f07cbe949a9) Thanks @fubhy! - Add `Queue.shutdownUnsafe` to synchronously discard buffered messages and settle pending queue operations from callbacks. Both `Queue.shutdown` and `Queue.shutdownUnsafe` return `false` when the queue has already been shut down or completed; previously, `Queue.shutdown` always returned `true`. + +- [#8325](https://github.com/Effect-TS/effect/pull/8325) [`87912c0`](https://github.com/Effect-TS/effect/commit/87912c014132648694015a5636177b2ba2bdeb2a) Thanks @fubhy! - Add `NetAddress.formatNativeHost` and `NetAddress.formatMulticastInterface` with explicit platform and scope ID map parameters for native socket APIs. + +- [#8308](https://github.com/Effect-TS/effect/pull/8308) [`106af64`](https://github.com/Effect-TS/effect/commit/106af64cf7117dfad53d3b11b61bb3d17d63bfca) Thanks @tristanz! - Release savepoints after nested transactions succeed or successfully roll back in PostgreSQL, PGlite, MySQL, libSQL, and the Node, Bun, React Native, and WASM SQLite clients. This frees PostgreSQL transaction locks before the outer transaction completes. + + Custom SQL clients can opt in through the new `releaseSavepoint` option. Clients that omit it are unchanged. + +- [#8323](https://github.com/Effect-TS/effect/pull/8323) [`1b21e0d`](https://github.com/Effect-TS/effect/commit/1b21e0df6ab777d6a1664dfabdea88511f2d25d1) Thanks @fubhy! - Add generic branded NetAddress classifications and named validating schemas. + Singleton address constants retain their base-family types; guards expose + classifications on demand. Operations that derive new address bits return only + the IPv4 or IPv6 family until callers revalidate the classification. + +- [#8305](https://github.com/Effect-TS/effect/pull/8305) [`3d59ae6`](https://github.com/Effect-TS/effect/commit/3d59ae6d5f9ff3e52cb6ed4a9f325320580218d5) Thanks @tim-smart! - Make `criteria` optional for `Decision.probability`. When supplied, `criteria` still requires descriptions for both `false` and `true`. + + `Decision.Probability.criteria` is now optional, so consumers reading outcome descriptions (for example, `decision.criteria.true`) must first guard against `undefined`. + +- [#8329](https://github.com/Effect-TS/effect/pull/8329) [`89c9a39`](https://github.com/Effect-TS/effect/commit/89c9a398412f1f5e7f9c6f697a1badd982bcd741) Thanks @tim-smart! - Warn when conflicting cluster workflow definitions reuse a tag. + +## 4.0.0-rc.116 + +### Patch Changes + +- [#7908](https://github.com/Effect-TS/effect/pull/7908) [`c19c63f`](https://github.com/Effect-TS/effect/commit/c19c63fb710422aaf00b8d923188aa2e52a6776f) Thanks @gcanti! - Add experimental JIT and AOT schema compilers that work through the existing + `SchemaParser` APIs and share a decoder registry. Enable JIT globally with + `effect/unstable/schema/SchemaJITCompiler/enable`, selectively with + `SchemaJITCompiler.enable(ast)`, or install generated AOT decoders without + requiring dynamic function construction. The new + `effect/unstable/schema/SchemaAOTCompiler/Build` entrypoint discovers direct + Schema exports from explicit module loaders and writes a self-installing AOT + module through Effect's `FileSystem` and `Path` services. Compiled decoders can + provide optional synchronous `decode` and `make` operations; normal + `SchemaParser` calls consume them transparently and retain `decodeEffect` and + `makeEffect` as the detailed fallbacks. AOT targets declare the operations to + prepare, so generated modules contain only those operation families and use + the interpreter if an omitted operation is later called. + + ### Breaking changes + + `SchemaGetter.Getter` is now a tagged union that distinguishes synchronous, + optional, and effectful transformations. Getter values now expose only `pipe`. + Use the dual `SchemaGetter.map`, `SchemaGetter.compose`, and `SchemaGetter.run` + functions instead of the former methods. Keeping these operations standalone + lets bundlers remove composition code when an application does not use it. + + The public `Getter` constructor is removed. Use + `SchemaGetter.transformOptionalEffect` instead of `new SchemaGetter.Getter`. + `SchemaGetter.onSome` and `SchemaGetter.onNone` are also removed. Use + `transformEffect` for an effectful transformation of present values and + `transformOptionalEffect` when the transformation handles missing values. + + `Transformation#compose` is replaced by the dual standalone function + `SchemaTransformation.composeTransformation`. Replace `first.compose(second)` + with `SchemaTransformation.composeTransformation(first, second)` or + `SchemaTransformation.composeTransformation(second)(first)`. + + `SchemaTransformation.make` is renamed to + `SchemaTransformation.makeTransformation`. `SchemaTransformation.Transformation` + and `SchemaTransformation.Middleware` now implement `Pipeable`, so both values + can be passed through standalone combinators with `.pipe(...)`. + + `SchemaAST.Context.constructorDefault` now stores the constructor-default + `Effect` directly instead of wrapping it in a `SchemaAST.Link`. Constructor + defaults apply only during construction, so the direct representation avoids + giving them encoding semantics and lets construction reuse already-completed + synchronous Effects. Code that constructs or inspects `SchemaAST.Context` + should pass or read the default `Effect` directly. + +- [#8287](https://github.com/Effect-TS/effect/pull/8287) [`8cb0a4f`](https://github.com/Effect-TS/effect/commit/8cb0a4f28fba991e15659d08ecc09da28cf742d2) Thanks @tim-smart! - Add `Decision` and `DecisionModel` to `effect/unstable/ai` for batched classification, rating, and probability estimates over schema-encoded input. + +- [#8221](https://github.com/Effect-TS/effect/pull/8221) [`8f420bb`](https://github.com/Effect-TS/effect/commit/8f420bb3dc3c9be8c4a48d57dccee201dcb0260d) Thanks @gcanti! - Add `Arbitrary.array(item, { minLength, maxLength })` for variable-length arrays of custom Arbitraries. Shrinking removes blocks of commands while preserving the remaining values and their order, and also simplifies individual elements. + + Schema-derived arrays now also try removing prefixes and interior blocks. Shrinking composed values and Schema objects preserves child candidates that were previously lost when exploring another branch. + + Shrunk results and replay paths may change from earlier native releases. Re-run affected properties to obtain new replay tokens, and preserve important failing inputs as regression tests. + +- [#8233](https://github.com/Effect-TS/effect/pull/8233) [`1393080`](https://github.com/Effect-TS/effect/commit/1393080f1cc8f47d459119fcb759e2cc00fd7356) Thanks @gold-beyond! - Add `Crypto.randomULID` to generate ULIDs from the current `Clock` timestamp and cryptographically secure random bytes. + +- [#8256](https://github.com/Effect-TS/effect/pull/8256) [`ccae354`](https://github.com/Effect-TS/effect/commit/ccae35423188f58d7c3dec5db3e36ed4bf42bcdf) Thanks @tim-smart! - Align the `Effect` and `Stream` APIs and fix several Stream type signatures. + + - `Effect.orElseSucceed` now passes the error to the fallback function, matching `Stream.orElseSucceed`. + - `Effect.isEffect` narrows to `Effect` instead of `any`. + - `Stream.bind`, `Stream.bindEffect` and `Stream.let` allow re-binding an existing field and produce the same record type as their `Effect` counterparts. + - `Stream.Success`, `Stream.Error` and `Stream.Services` are unconstrained and distributive like the `Effect` versions. + - `Stream.partition` returns `[passes, fails]` and takes a `capacity` option, matching `Stream.partitionQueue` and `Stream.partitionEffect`. Its default capacity remains 16. + - `Stream.mapBoth` takes `onElement` / `onError`, matching `Stream.tapBoth`. + - `Stream.scan` and `Stream.scanEffect` take a lazy initial state. + - `Stream.catchTags` rejects unknown tag keys like `Effect.catchTags`. + - Fixed the data-first overloads of `Stream.runIntoPubSub` (error type was dropped), `Stream.cross` (swapped type parameter names) and `Stream.mapAccumArray` (`onHalt` return type). + - Added `Stream.as`, `Stream.tapDefect`, `Stream.tapErrorTag` and `Stream.unwrapReason`. + - Stream concurrency options use the `Types.Concurrency` alias, and JSDoc categories were consolidated across both modules. + +- [#8235](https://github.com/Effect-TS/effect/pull/8235) [`553c403`](https://github.com/Effect-TS/effect/commit/553c403f1d9199df738f73446dacd090da2e698b) Thanks @tim-smart! - Retain durable deferred completions received before a workflow owner's first local run so replay can complete while the deferred reply is still being persisted. + + Keep pending completions in a cache keyed weakly by cluster activation. Handler rebuilds retain completions, and overlapping activations cannot clear each other's results. Results can be collected once their activation scope becomes unreachable; closing a scope alone does not guarantee collection. + + Release RPC stream and queue consumers when their request write fiber is interrupted. + +- [#8243](https://github.com/Effect-TS/effect/pull/8243) [`45b5103`](https://github.com/Effect-TS/effect/commit/45b510352d42ac55718f9f5f43573975b323143f) Thanks @tim-smart! - Fix lost durable deferred wake-ups when a ClusterWorkflowEngine execution suspends before its run reply is persisted. Deferred completions now wait for the current run reply before resuming, so discarded executions can replay without relying on caller retries. + +- [#8254](https://github.com/Effect-TS/effect/pull/8254) [`77a5612`](https://github.com/Effect-TS/effect/commit/77a56120354d1d3f7341b117266f211143a3734a) Thanks @tim-smart! - Fix file response content types: honor the `contentType` option and preserve explicit headers, including MIME types set by `HttpStaticServer`. The default `HttpPlatform.layer` now infers missing content types from file extensions. + + Web file responses on the default, Node, and Deno platforms prefer explicit content types, then nonempty `File.type`, then the file extension. + + Removed the unused `contentLength` option from `HttpServerResponse.file`; lengths are calculated from the file and requested range. + +- [#8229](https://github.com/Effect-TS/effect/pull/8229) [`1076170`](https://github.com/Effect-TS/effect/commit/10761707b5cae0a66ef605abd1737ae59a18f5ac) Thanks @tim-smart! - Fix a deadlock in the memory workflow engine when a durable deferred is completed from a finalizer in the workflow awaiting it, including `DurableDeferred.into` inside `DurableDeferred.raceAll`. + +- [#8206](https://github.com/Effect-TS/effect/pull/8206) [`f110af1`](https://github.com/Effect-TS/effect/commit/f110af1ac5a54a7d62c2b96e35d4521a09f3fa06) Thanks @tim-smart! - Fix multipart file streams hanging on body read errors and preserve error causes when persisting files. + +- [#8284](https://github.com/Effect-TS/effect/pull/8284) [`0045152`](https://github.com/Effect-TS/effect/commit/0045152cdf796f20be5e690c69c40da611f813b8) Thanks @tim-smart! - `HttpServerResponse.toWeb` now keeps a raw `Response`'s headers and `Set-Cookie` values when the body is omitted (HEAD, or status 204, 205 or 304). Bodyless outer statuses keep the outer status and status text; HEAD keeps the raw `Response`'s. Outer cookies are appended to native `Set-Cookie` headers instead of replacing them. + +- [#8202](https://github.com/Effect-TS/effect/pull/8202) [`51d4a2f`](https://github.com/Effect-TS/effect/commit/51d4a2f08a5c7691dc876415bc9fc0ecf467e153) Thanks @xia-chao! - Fix `RcRef.make` to treat `idleTimeToLive: 0` like `Duration.zero` and `"0 millis"` instead of an omitted option. + +- [#8298](https://github.com/Effect-TS/effect/pull/8298) [`4d4c4e8`](https://github.com/Effect-TS/effect/commit/4d4c4e8a4436d49997a5a7234860ede651467537) Thanks @gcanti! - Fix `Schema.Redacted` to wrap the transformed result of its inner schema during decoding and encoding. + +- [#8227](https://github.com/Effect-TS/effect/pull/8227) [`ccfe152`](https://github.com/Effect-TS/effect/commit/ccfe152d11bed497f2d26aba8ef1a3613d0d6746) Thanks @tim-smart! - Release non-persisted, interruptible `RunnerServer` handlers and mailbox slots when callers disconnect. + + Preserve dynamic `WithTransaction` annotations during replay and re-delivery. + +- [#8261](https://github.com/Effect-TS/effect/pull/8261) [`d30a0c8`](https://github.com/Effect-TS/effect/commit/d30a0c880f8ffe06d7f0695c17f35910f1fcfbc9) Thanks @nikhilsnayak! - Add HTTP `QUERY` support to clients, routers, HttpApi endpoints, and AI request metadata. Configure CORS support through `allowedMethods`; defaults are unchanged. + + OpenAPI 3.1 output represents `QUERY` through `x-oai-additionalOperations`, which requires consumer support for that extension. The OpenAPI generator accepts the extension and the native OpenAPI 3.2 `query` field. + +- [#8255](https://github.com/Effect-TS/effect/pull/8255) [`84fe64a`](https://github.com/Effect-TS/effect/commit/84fe64a5fbfdecd23b66c207d0daa848d59dd825) Thanks @tim-smart! - Preserve literal suffixes such as `:wait` in `/operations/:id:wait` across `HttpApiClient`, `HttpApiBuilder`, and OpenAPI paths. + + Server routes without a params schema keep their existing matching for `RouteContext` consumers. Schemas whose keys cannot be enumerated keep the existing fallback. + +- [#8228](https://github.com/Effect-TS/effect/pull/8228) [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72) Thanks @lloydrichards! - Support prompt titles in `McpServer.prompt` and `McpServer.registerPrompt`. + +- [#8228](https://github.com/Effect-TS/effect/pull/8228) [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72) Thanks @lloydrichards! - Add an `instructions` option to MCP servers for initialization and discovery responses. + +- [#7265](https://github.com/Effect-TS/effect/pull/7265) [`a2c4154`](https://github.com/Effect-TS/effect/commit/a2c4154cf8bcbe455bd43bf7f3f12d9cbf38247c) Thanks @lloydrichards! - Add server support for MCP protocol version 2026-07-28 through 2026_07_28 + +- [#8272](https://github.com/Effect-TS/effect/pull/8272) [`23a58c0`](https://github.com/Effect-TS/effect/commit/23a58c020b25dde573ce523c2d157032676de0a3) Thanks @fubhy! - Add `NetAddress.formatHost` and `NetAddress.inetAddressFromHostString` for socket APIs that accept numeric hosts and ports separately, preserving IPv6 scope IDs. Add `NetAddress.scopeIdsFromInterfaces` to build a scope map for resolving named IPv6 zones from supplied network interface entries without performing operating-system lookups. + +- [#8293](https://github.com/Effect-TS/effect/pull/8293) [`feef90c`](https://github.com/Effect-TS/effect/commit/feef90ccbd86e5ab49b7e3cc3845591e23bdcb1b) Thanks @gcanti! - Treat `Never` as an uninhabited branch during `Arbitrary.schema` derivation. Schemas with another finite generation path, including optional properties, unions, and empty collections, no longer fail derivation. + +- [#8212](https://github.com/Effect-TS/effect/pull/8212) [`755e863`](https://github.com/Effect-TS/effect/commit/755e863a793e5621183e7992cb3f85d29030ad7b) Thanks @IMax153! - Restrict `ByteSize.Input` strings to canonical non-negative integers with recognized units, rejecting malformed literals at compile time. Parse external strings and fractional quantities with `ByteSize.fromString` or `ByteSize.fromStringUnsafe` before passing them to APIs accepting `ByteSize.Input`. + +- [#8228](https://github.com/Effect-TS/effect/pull/8228) [`49e4b37`](https://github.com/Effect-TS/effect/commit/49e4b37b831a573567e0b67d3ec4593403dc2e72) Thanks @lloydrichards! - Honor `Tool.Strict` in MCP input schemas and argument validation. Strict dynamic tools require Effect schemas; raw JSON Schema is rejected at registration. + + Support identified input schemas for non-strict tools. Invalid arguments use `InvalidParams` on protocols before 2025-11-25 and `isError: true` results on newer protocols. + + Distinguish validation failures from declared handler failures. Declared failures return `isError: true` without `structuredContent`: error mode uses `Error.message` or schema-encoded text, and return mode uses the encoded payload. Declared failures do not produce internal-error diagnostics. + + Log and report internal failures, including defects and encoding errors, while keeping client messages generic. + + Allow `Toolkit.handle` to accept `SchemaAST.ParseOptions` for parameter decoding. Expose the `Toolkit.FailureOrigin` cause annotation and shared `Tool.FailureOrigin` type, with the same origin available in `Tool.HandlerResult.failureOrigin` on returned failures. + +- [#8269](https://github.com/Effect-TS/effect/pull/8269) [`9ad9891`](https://github.com/Effect-TS/effect/commit/9ad9891e24058065bcd445772e005f8ce4b3e42f) Thanks @tim-smart! - Add `HttpApi.ParseOptions` to configure server and client codecs at the API, group, or endpoint level. + + `Sse.decodeSchema` and `ChannelSchema.decode` accept parse options, and `Schema.Cause` encodes reasons to their wire fields. + + SSE decoding omits absent IDs, including with default options. Use `Schema.optional(Schema.String)` instead of `Schema.UndefinedOr(Schema.String)` for IDs. With `onExcessProperty: "error"`, declare `event` (default: `"message"`) and any `id`, including inherited IDs. + + `HttpApiSchema.StreamSse` data-mode types and OpenAPI schemas now make `id` optional. + +- [#8296](https://github.com/Effect-TS/effect/pull/8296) [`0beded0`](https://github.com/Effect-TS/effect/commit/0beded04f5cffe8dd263f989c38a609e27f27fa5) Thanks @gcanti! - Improve Schema-derived BigInt and BigDecimal arbitraries with wider magnitude, precision, and exponent coverage and numeric boundary shrinking. + + Cover intermediate magnitudes up to large one-sided BigInt bounds, handle BigDecimal bounds with widely different scales, and refine decimal counterexamples between simple values. + +- [#8260](https://github.com/Effect-TS/effect/pull/8260) [`2940742`](https://github.com/Effect-TS/effect/commit/2940742c3f8529bc3b024b379904fbefd40ece55) Thanks @xia-chao! - Fix repeated `text`, `json`, `arrayBuffer`, and `urlParamsBody` reads in `HttpServerResponse.toClientResponse` for raw Web `Response` bodies, preserving the original response for serving. + +- [#8270](https://github.com/Effect-TS/effect/pull/8270) [`63c1566`](https://github.com/Effect-TS/effect/commit/63c15662f90ae970969de29761207c99ecdbf66b) Thanks @tim-smart! - Fix YAML indentless sequences, multiline scalar folding, and comments. Reject unsupported compact nested sequences (`- - value`) and document streams. + + Previously accepted values such as `description: Use when: deploy` and `description: Deploy:` now throw `SyntaxError`: YAML forbids colons followed by whitespace or end-of-value in plain scalars. Quote these values, for example `description: "Use when: deploy"`. + ## 4.0.0-rc.115 ### Patch Changes diff --git a/repos/effect/packages/effect/CONFIG.md b/repos/effect/packages/effect/CONFIG.md index bcb4145213..0285dc5bce 100644 --- a/repos/effect/packages/effect/CONFIG.md +++ b/repos/effect/packages/effect/CONFIG.md @@ -115,7 +115,7 @@ The same rule applies when a `Config` is yielded as an `Effect`: the config uses ### `Config.withDefault` — Fallback for Absent Input -Triggers when the config cannot resolve and none of its relevant provider input is present. Validation errors and partially supplied groups still propagate. +Triggers when the config is absent. For `Config.all`, any absent child makes the group absent unless another child fails. A group default replaces the entire group; apply defaults to individual children to keep other supplied values. Validation and source errors still propagate. ```ts import { Config, ConfigProvider, Effect } from "effect" @@ -151,7 +151,7 @@ const upperHost = Config.String("HOST").pipe( ### `Config.orElse` — Fallback on Any Error -Unlike `withDefault`, this catches **all** `ConfigError`s: +Unlike `withDefault`, this handles absence and **all** `ConfigError`s. The fallback's result replaces the original failure. If the fallback is absent, a later `withDefault` or `option` can recover it. If the fallback fails, only its error propagates: ```ts import { Config } from "effect" @@ -218,7 +218,7 @@ Effect.runSync(config.parse(provider)) // "localhost" ### `Config.all` — Combine Multiple Configs -Accepts a record or a tuple. A wholly absent group can be handled by `Config.withDefault` or `Config.option`. If any child reads provider input, every other required child must also resolve; partial groups fail instead of silently replacing user input with a whole-group default. +Accepts a record, a tuple, or an iterable. A group is absent when any child is absent and no child fails. `Config.withDefault` replaces the entire absent group, and `Config.option` returns `None`. Validation and source errors propagate even when another child is absent. ```ts import { Config } from "effect" @@ -234,7 +234,7 @@ const appConfig = Config.all({ const pair = Config.all([Config.String("a"), Config.Int("b")]) ``` -For example, providing only `host` is an error here: +For example, providing only `host` selects the default for the entire group here: ```ts import { Config } from "effect" @@ -247,7 +247,9 @@ const database = Config.all({ ) ``` -The default applies when both keys are absent, but not when only one key is present. Defaults on individual children do not count as provider input: +If the provider contains `{ host: "db.internal" }`, the result is `{ host: "localhost", port: 5432 }`. To keep the supplied host and default only the port, put `Config.withDefault(5432)` on the `port` child instead. + +Child defaults resolve missing children before the group decides whether it is absent: ```ts const listener = Config.all({ @@ -256,17 +258,17 @@ const listener = Config.all({ }).pipe(Config.option) ``` -`listener` is `None` when both keys are absent, `Some` when `host` is present, and fails when only `port` is present. +`listener` is `None` when `host` is absent, including when a valid `port` is present. It is `Some` when `host` is present and `port` resolves to either a supplied value or its default. An invalid `port` fails even if `host` is absent. ### How Absence Is Decided Configuration evaluation distinguishes three situations before producing the public `Effect`: 1. **Resolved** — decoding succeeded. The value may legitimately be `undefined`, `{}`, or `[]`. -2. **Absent** — the config could not resolve and no relevant provider representation was found. -3. **Failed** — the provider failed, input was invalid, or a combined config was only partially supplied. +2. **Absent** — a schema could not resolve because its relevant provider representation was unavailable, or a combined config has an absent child and no failed children. +3. **Failed** — the provider failed, input was invalid, or an effectful transformation failed. -`Config.withDefault` and `Config.option` handle only the second case. `Config.orElse` handles both absence and failures. +`Config.withDefault` and `Config.option` handle only the second case. `Config.orElse` handles both absence and failures and adopts the fallback's result. Once a failure is recovered, it does not influence enclosing groups or later defaults. At the lookup path of a `Config.schema`, an unavailable representation is passed to the schema decoder as `undefined`. This includes a missing node and a present node whose shape cannot represent the schema: for example, an array node cannot represent a struct. Missing properties inside an object remain omitted so the schema's property semantics still apply. The decoder runs before absence is decided. Consequently: @@ -274,10 +276,10 @@ At the lookup path of a `Config.schema`, an unavailable representation is passed - An explicitly present empty object can decode to `{}` when the schema permits it. - Wrapping either successful result in `Config.option` produces `Some`, because decoding succeeded. - If the schema rejects `undefined` and no relevant representation was found, `Config.withDefault` uses its fallback and `Config.option` returns `None`. -- Present invalid data and partially supplied `Config.all` groups are failures. +- Present invalid data is a failure. A `Config.all` group with missing children is absent unless another child fails. - `SourceError` is always a failure and is never replaced by `withDefault` or `option`. -`Config.schema(Schema.Struct(...))` and `Config.all(...)` share the same decoder-first rule but describe different lookup models. A struct schema owns one structured input, so an explicitly present empty object is relevant input and its required fields are validated. `Config.all` evaluates independent child configs; an empty parent object does not make the group present when every child is absent. Field optionality in `Config.all` is expressed on each child with `Config.option` or `Config.withDefault`. +`Config.schema(Schema.Struct(...))` and `Config.all(...)` describe different lookup models. A struct schema validates one structured input. An existing object, including an empty object, fails validation if required fields are missing. `Config.all` evaluates independent child configs and can recover a partially supplied group through `Config.withDefault` or `Config.option`. Field optionality in `Config.all` is expressed on each child with `Config.option` or `Config.withDefault`. ### How Schema Input Is Loaded @@ -730,7 +732,7 @@ const program = Config.Int("PORT").parse( ) ``` -**Important**: `Config.withDefault` and `Config.option` recover only from semantic absence. They do not classify `SchemaIssue` values as “missing.” Validation errors, source failures, and partially supplied groups still propagate. +**Important**: `Config.withDefault` and `Config.option` recover only from semantic absence. They do not classify `SchemaIssue` values as “missing.” Validation and source errors still propagate unless explicitly recovered with `Config.orElse`. ## Practical Example: Web Server Config diff --git a/repos/effect/packages/effect/HTTPAPI.md b/repos/effect/packages/effect/HTTPAPI.md index 6567439ec1..2864513dbe 100644 --- a/repos/effect/packages/effect/HTTPAPI.md +++ b/repos/effect/packages/effect/HTTPAPI.md @@ -44,8 +44,8 @@ HttpApi ("MyApi") ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" // Definition @@ -152,9 +152,9 @@ Once you've defined an API, you can generate a fully typed client from it using ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { FetchHttpClient } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { FetchHttpClient } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -329,8 +329,8 @@ Use `HttpApiEndpoint.get` to create a GET endpoint. Provide a name (used as the ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" // Define a schema representing a User entity @@ -387,8 +387,8 @@ When a group has more than one endpoint, use `.handleAll` to register all the ha ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -460,8 +460,8 @@ Use `HttpApiEndpoint.delete` to create an endpoint that removes a resource. ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -539,8 +539,8 @@ Use `HttpApiEndpoint.patch` to create an endpoint that partially updates a resou ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -633,8 +633,8 @@ Path parameters let you capture dynamic values from the URL. For example, `/user ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -698,8 +698,8 @@ Set the path to `"*"` to match any URL that no other endpoint handles. This is u ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -803,8 +803,8 @@ Prefixes let you prepend a common path segment to endpoints, groups, or an entir ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -864,8 +864,8 @@ Query parameters are the `?key=value` pairs appended to a URL. Use the `query` o ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -925,8 +925,8 @@ A single query parameter can carry multiple values (e.g., `?a=1&a=2`). Wrap the ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -997,8 +997,8 @@ Use the `headers` option to declare a record of fields or a full schema for the ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -1058,15 +1058,8 @@ To accept file uploads, mark the payload as multipart with `HttpApiSchema.asMult ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter, Multipart } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiGroup, - HttpApiScalar, - HttpApiSchema -} from "effect/unstable/httpapi" +import { HttpRouter, Multipart } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -1125,15 +1118,8 @@ By default, request bodies are JSON. To accept a different format — like form- ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiGroup, - HttpApiScalar, - HttpApiSchema -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -1199,8 +1185,8 @@ Inside a handler, `ctx.request` gives you access to the raw incoming HTTP reques ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi").add( @@ -1246,7 +1232,7 @@ There is no `cookies` option on endpoints. Instead, validated cookie access goes ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Context, Effect, Layer, Redacted, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" +import { HttpRouter } from "effect/http" import { HttpApi, HttpApiBuilder, @@ -1255,7 +1241,7 @@ import { HttpApiMiddleware, HttpApiScalar, HttpApiSecurity -} from "effect/unstable/httpapi" +} from "effect/http-api" import { createServer } from "node:http" // Define the service providing the current user @@ -1340,8 +1326,8 @@ For quick, unvalidated access you can read cookies directly from `ctx.request.co ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("api").add( @@ -1385,8 +1371,8 @@ The payload schema still describes the endpoint in the generated documentation, ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema, Stream } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("myApi").add( @@ -1445,15 +1431,8 @@ Success responses default to `200 OK`. To use a different status code, annotate ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiGroup, - HttpApiScalar, - HttpApiSchema -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -1503,15 +1482,8 @@ Responses default to JSON. To return a different format — like CSV or plain te ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiGroup, - HttpApiScalar, - HttpApiSchema -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -1582,8 +1554,8 @@ Wrap the success schema with `HttpApiSchema.WithHeaders(schema, headers)`. The h ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -1653,8 +1625,8 @@ The body schema is authoritative for everything wire-level: status, content type ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" class UserNotFound extends Schema.TaggedError()("UserNotFound", { @@ -1734,8 +1706,8 @@ For headers that should not appear in the API contract, call `HttpEffect.appendP ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpEffect, HttpRouter, HttpServerResponse } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpEffect, HttpRouter, HttpServerResponse } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("api").add( @@ -1780,8 +1752,8 @@ Set cookies on the response using `HttpEffect.appendPreResponseHandler` together ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpEffect, HttpRouter, HttpServerResponse } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpEffect, HttpRouter, HttpServerResponse } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("api").add( @@ -1828,9 +1800,9 @@ To redirect the client to a different URL, return an `HttpServerResponse.redirec ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpServerResponse } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpServerResponse } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi").add( @@ -1886,8 +1858,8 @@ With a streaming success schema, the handler returns a `Stream` directly, and th ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schedule, Stream } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("myApi").add( @@ -1943,8 +1915,8 @@ The response will stream data (`a`, `b`, `c`) with a 500ms interval between each ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schedule, Schema, Stream } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const Message = Schema.Struct({ @@ -2004,7 +1976,7 @@ yield * Stream.runForEach(events, (message) => Console.log(message.text)) ### Typed Stream Failures -`HttpApiSchema.StreamSse` accepts an `error` schema describing typed stream failures. Since the HTTP status is already sent when the stream fails, the failure cannot travel as a normal error response. Instead, the server encodes the full `Cause` into a reserved `effect/httpapi/stream/failure` event, and the derived client decodes that event and fails the stream with the original cause. +`HttpApiSchema.StreamSse` accepts an `error` schema describing typed stream failures. Since the HTTP status is already sent when the stream fails, the failure cannot travel as a normal error response. Instead, the server encodes the full `Cause` into a reserved `effect/http-api/stream/failure` event, and the derived client decodes that event and fails the stream with the original cause. **Example** (Declaring a Typed Stream Failure) @@ -2029,7 +2001,7 @@ Things to know: - Set a custom status or content type with `HttpApiSchema.status(code)` and the `contentType` option, for example `HttpApiSchema.status(206)(HttpApiSchema.StreamUint8Array({ contentType: "application/custom-bytes" }))`. - Wrap a stream schema with `HttpApiSchema.WithHeaders` to declare typed response headers (see [Setting Response Headers](#setting-response-headers)). The handler returns `HttpApiSchema.withHeaders({ body, headers })` with the stream as `body`, and the client resolves to the same shape. - An endpoint may declare at most one streaming success schema, `HEAD` endpoints cannot declare one, and streaming schemas are not supported in error responses. -- The `effect/httpapi/stream/failure` event name is reserved: `events` schemas may not declare it, and the client treats such an event as a stream failure only when its `data` decodes to a `Cause`. +- The `effect/http-api/stream/failure` event name is reserved: `events` schemas may not declare it, and the client treats such an event as a stream failure only when its `data` decodes to a `Cause`. # Error Handling @@ -2042,15 +2014,8 @@ Endpoints can declare the errors they may return. Each error is a schema annotat ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiGroup, - HttpApiScalar, - HttpApiSchema -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar, HttpApiSchema } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -2139,15 +2104,8 @@ The `HttpApiError` module provides ready-made error schemas for common HTTP stat ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiError, - HttpApiGroup, - HttpApiScalar -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -2223,15 +2181,8 @@ Each predefined error also has a `NoContent` variant that responds with the stat ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { - HttpApi, - HttpApiBuilder, - HttpApiEndpoint, - HttpApiError, - HttpApiGroup, - HttpApiScalar -} from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -2295,7 +2246,7 @@ In this example, if a client sends a non-integer `id` query parameter, the API r ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" +import { HttpRouter } from "effect/http" import { HttpApi, HttpApiBuilder, @@ -2304,7 +2255,7 @@ import { HttpApiMiddleware, HttpApiScalar, HttpApiSchema -} from "effect/unstable/httpapi" +} from "effect/http-api" import { createServer } from "node:http" // Define a custom error for validation failures @@ -2388,7 +2339,7 @@ Middleware lets you run shared logic — like logging or authentication — befo ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter, HttpServerRequest } from "effect/unstable/http" +import { HttpRouter, HttpServerRequest } from "effect/http" import { HttpApi, HttpApiBuilder, @@ -2397,7 +2348,7 @@ import { HttpApiMiddleware, HttpApiScalar, HttpApiSchema -} from "effect/unstable/httpapi" +} from "effect/http-api" import { createServer } from "node:http" class Logger extends HttpApiMiddleware.Service()("Http/Logger", { @@ -2476,7 +2427,7 @@ When you attach interdependent middleware to an endpoint, group, or API, the mid ```ts import { Context, Effect, Layer, Schema } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/unstable/httpapi" +import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/http-api" class AuthInfo extends Context.Service("User")({ id: Schema.Finite }) {} @@ -2602,7 +2553,7 @@ To enforce a security scheme, implement its middleware as a `Layer`. The layer r ```ts import { Context, Effect, Layer, Redacted, Schema } from "effect" -import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi" +import { HttpApiMiddleware, HttpApiSecurity } from "effect/http-api" class User extends Schema.Class("User")({ id: Schema.Finite }) {} @@ -2658,7 +2609,7 @@ Use `HttpApiSecurity.annotate` to attach metadata — like a description — to ```ts import { Context, Schema } from "effect" -import { HttpApiMiddleware, HttpApiSecurity, OpenApi } from "effect/unstable/httpapi" +import { HttpApiMiddleware, HttpApiSecurity, OpenApi } from "effect/http-api" class User extends Schema.Class("User")({ id: Schema.Finite }) {} @@ -2695,7 +2646,7 @@ Use `HttpApiBuilder.securitySetCookie` to set a security cookie from a handler. ```ts import { Redacted, Schema } from "effect" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSecurity } from "effect/unstable/httpapi" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSecurity } from "effect/http-api" const Api = HttpApi.make("MyApi") .add( @@ -2732,8 +2683,8 @@ Handlers can access any Effect service. Because `HttpApiBuilder.group` returns a ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Context, Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -2798,8 +2749,8 @@ Add interactive API documentation with `HttpApiScalar` (Scalar UI) or `HttpApiSw ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import { createServer } from "node:http" const User = Schema.Struct({ @@ -2911,7 +2862,7 @@ Below is a list of available annotations for a top-level `HttpApi`. They can be ```ts import { Schema } from "effect" -import { HttpApi, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, OpenApi } from "effect/http-api" const api = HttpApi.make("api") // Provide additional schemas @@ -3014,7 +2965,7 @@ The following annotations can be added to an `HttpApiGroup`: **Example** (Annotating a Group) ```ts -import { HttpApi, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiGroup, OpenApi } from "effect/http-api" const api = HttpApi.make("api") .add( @@ -3090,7 +3041,7 @@ For an `HttpApiEndpoint`, you can use the following annotations: ```ts import { Schema } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" const api = HttpApi.make("api").add( HttpApiGroup.make("group") @@ -3212,7 +3163,7 @@ The default response description is "Success". You can override this by annotati ```ts import { Schema } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" const User = Schema.Struct({ id: Schema.Finite, @@ -3312,7 +3263,7 @@ When a group is `topLevel`, its name is not prepended to operation IDs in the Op ```ts import { Schema } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" const api = HttpApi.make("api").add( // Mark the group as top-level @@ -3391,9 +3342,9 @@ The `HttpApiClient` module generates a fully typed client from your API definiti ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { FetchHttpClient } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { FetchHttpClient } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -3450,9 +3401,9 @@ When a group is `topLevel`, its endpoints are exposed as top-level methods on th ```ts import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { Effect, Layer, Schema } from "effect" -import { HttpRouter } from "effect/unstable/http" -import { FetchHttpClient } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" +import { HttpRouter } from "effect/http" +import { FetchHttpClient } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" import { createServer } from "node:http" const Api = HttpApi.make("MyApi") @@ -3499,8 +3450,8 @@ If you need to plug your API into an existing HTTP server (instead of using `Nod ```ts import { Effect, Layer, Schema } from "effect" -import { HttpRouter, HttpServer } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/unstable/httpapi" +import { HttpRouter, HttpServer } from "effect/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiScalar } from "effect/http-api" import * as http from "node:http" const Api = HttpApi.make("myApi").add( diff --git a/repos/effect/packages/effect/MCP.md b/repos/effect/packages/effect/MCP.md index 53f77d24fc..5460022953 100644 --- a/repos/effect/packages/effect/MCP.md +++ b/repos/effect/packages/effect/MCP.md @@ -11,7 +11,7 @@ Here is an example of a MCP server implementation: ```typescript import { NodeRuntime, NodeStdio } from "@effect/platform-node" import { Effect, Layer, Logger, Schema } from "effect" -import { McpProtocol, McpServer, Tool, Toolkit } from "effect/unstable/ai" +import { McpProtocol, McpServer, Tool, Toolkit } from "effect/ai" // Define a simple tool const DemoTool = Tool.make("DemoTool", { @@ -99,7 +99,7 @@ parameters, completions, and content generation. ```typescript import { Effect, Schema } from "effect" -import { McpSchema, McpServer } from "effect/unstable/ai" +import { McpSchema, McpServer } from "effect/ai" const SimpleResource = McpServer.resource({ uri: "file:///demo.txt", @@ -140,7 +140,7 @@ generation logic in a declarative way. ```typescript import { Effect, Schema } from "effect" -import { McpServer } from "effect/unstable/ai" +import { McpServer } from "effect/ai" const DemoPrompt = McpServer.prompt({ name: "Demo Prompt", @@ -167,7 +167,7 @@ grouped into toolkits, which can be combined and converted into layers. ```typescript import { Effect, Layer, Schema } from "effect" -import { McpServer, Tool, Toolkit } from "effect/unstable/ai" +import { McpServer, Tool, Toolkit } from "effect/ai" const DemoTool = Tool.make("DemoTool", { description: "This is a demo tool for the documentation", @@ -214,7 +214,7 @@ validated user input. ```typescript import { Effect, Schema } from "effect" -import { McpServer } from "effect/unstable/ai" +import { McpServer } from "effect/ai" const DemoElicitation = McpServer.elicit({ message: `Please answer the question ("yes" | "no") (default "no"):`, @@ -240,7 +240,7 @@ Here's a complete, copy/pastable MCP server example that combines all the concep ```typescript import { NodeRuntime, NodeStdio } from "@effect/platform-node" import { Effect, Layer, Logger, Schema } from "effect" -import { McpProtocol, McpSchema, McpServer, Tool, Toolkit } from "effect/unstable/ai" +import { McpProtocol, McpSchema, McpServer, Tool, Toolkit } from "effect/ai" // Define tools const GreetTool = Tool.make("GreetTool", { diff --git a/repos/effect/packages/effect/README.md b/repos/effect/packages/effect/README.md index d702d36b63..b3c60740f1 100644 --- a/repos/effect/packages/effect/README.md +++ b/repos/effect/packages/effect/README.md @@ -4,10 +4,12 @@ Effect is a library for building robust, maintainable, type-safe, and production The `effect` package is the core of the framework. It provides primitives for managing side effects, errors, concurrency, resources, and structured data, alongside a rich standard library. +> **Effect 4.x is a long-term support (LTS) release**, with at least three years of bug and security fixes. If you are upgrading from Effect 3.x, follow the [migration guide](https://github.com/Effect-TS/effect/blob/main/MIGRATION.md). + ## Installation ```sh -npm install effect@rc +npm install effect ``` ## Requirements @@ -24,10 +26,21 @@ npm install effect@rc } ``` -## Documentation +## Links + +- [Website](https://effect.website): documentation, guides, and news. +- [Reference](https://effect.website/docs/v4/api/effect): API documentation for this package. +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team. +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own. +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests. + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. -- [Effect website](https://effect.website) -- [API reference](https://effect.website/docs/v4/api/effect) +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. ## Overview @@ -44,4 +57,4 @@ The `effect` package is a collection of modules. Some of the core ones: | Scope | Manages the lifecycle of resources, ensuring proper acquisition and release. | | Schema | A powerful library for defining, validating, and transforming structured data with type-safe encoding and decoding. | -In v4, functionality that previously lived in separate packages ships inside `effect` under the `effect/unstable/*` namespaces, including `http`, `httpapi`, `rpc`, `cluster`, `workflow`, `cli`, `ai`, `sql`, and `reactivity`. +In v4, functionality that previously lived in separate packages ships inside `effect` under the `effect/*` namespaces, including `http`, `http-api`, `rpc`, `cluster`, `workflow`, `cli`, `ai`, `sql`, and `reactivity`. diff --git a/repos/effect/packages/effect/SCHEMA.md b/repos/effect/packages/effect/SCHEMA.md index 2a25c9b9f4..9fb8b0cf51 100644 --- a/repos/effect/packages/effect/SCHEMA.md +++ b/repos/effect/packages/effect/SCHEMA.md @@ -35,7 +35,7 @@ Use Schema to: 11. **Middlewares** — intercept decoding/encoding to provide fallbacks or inject services. 12. **Advanced topics** — internal type model and type hierarchy (for library authors). 13. **Integrations** — working examples for TanStack Form and Elysia. -14. **Migration from v3** — API mapping from Schema v3 to v4. +14. **[Migration from v3](../../migration/schema.md)** — API mapping from Schema v3 to v4. ## Runtime Performance @@ -51,24 +51,151 @@ Values are microseconds per operation and lower is better. Results vary between machines, so they are most useful for understanding relative costs. A dash means that the library does not provide that benchmark. -| Scenario | Effect Schema | Valibot | Zod 4 | -| ------------------------------------- | ------------: | ---------: | ---------: | -| Create a schema | 118.23 | **40.24** | 318.56 | -| Create a schema and parser | **130.50** | — | — | -| Validate valid data | **5.415** | 5.63 | — | -| Validate invalid data | 1.348 | **0.2431** | — | -| Parse valid data and collect errors | 5.366 | **5.22** | 7.16 | -| Parse invalid data and collect errors | **9.100** | 15.70 | 41.58 | -| Parse valid data and stop early | **5.294** | 5.37 | — | -| Parse invalid data and stop early | 1.352 | **0.2572** | — | -| Standard Schema, valid data | 5.935 | 5.35 | **3.83** | -| Standard Schema, invalid data | **15.203** | 16.51 | 32.85 | -| Standard Schema, valid, stop early | **5.843** | — | — | -| Standard Schema, invalid, stop early | **2.244** | — | — | -| Encode with a typed codec | 0.3420 | — | **0.0405** | -| Decode with a typed codec | 0.3762 | — | **0.0463** | -| Encode unknown input | **0.3472** | — | — | -| Decode unknown input | **0.3637** | — | — | +| Scenario | Effect Schema | Valibot 1.5.0 | Zod 4.6.2 | +| ------------------------------------- | ------------: | ------------: | --------: | +| Create a schema | 60.1297 | 1.0926 | 83.7496 | +| Validate valid data | 4.1473 | 3.5389 | — | +| Validate invalid data | 0.2571 | 0.1823 | — | +| Parse valid data and collect errors | 5.0261 | 3.5393 | 7.0073 | +| Parse invalid data and collect errors | 7.4838 | 5.7206 | 19.3064 | +| Parse valid data and stop early | 4.1559 | 3.5941 | — | +| Parse invalid data and stop early | 0.2525 | 0.1917 | — | +| Standard Schema, valid data | **5.3408** | 3.5565 | 3.5755 | +| Standard Schema, invalid data | 11.7723 | 5.4422 | 15.9974 | +| Encode with a typed codec | 0.0827 | — | 0.0441 | +| Decode with a typed codec | 0.1063 | — | 0.0427 | + +## Experimental schema compilers + +Enable JIT compilation at application startup with a side-effect import: + +```ts +import "effect/schema/SchemaJITCompiler/enable" +``` + +Alternatively, `SchemaJITCompiler.enable(schema.ast)` enables one AST and the +dependencies reached while parsing it. Importing `SchemaJITCompiler` or the +`schema` barrel alone does not enable compilation. Operations are +prepared on first use. If dynamic function construction is blocked or compilation +fails, the interpreter remains available. Exceptions from executing a parser are +not treated as compilation failures and do not trigger a retry. + +JIT and AOT use the same source generator. To generate an AOT module at build +time, call `SchemaAOTCompiler.compile(targets)` with an ordered array of ASTs +and the operations to prepare: + +```ts +SchemaAOTCompiler.compile([ + { ast: User.ast, operations: ["decode"] }, + { ast: SchemaAST.toType(User.ast), operations: ["is", "make"] } +]) +``` + +The module exports `install(asts)`. Call it with the target ASTs in the same +order before using normal `SchemaParser` functions. Generated modules contain +only the requested operation families and their dependencies. Operations that +were not requested use the interpreter if they are called. Generated modules +do not import the generator and work where `new Function` is forbidden. + +The low-level installation trusts the supplied root order and AST definitions. +Target `SchemaAST.toType(schema.ast)` with `is` or `make` for guards and +construction. Target `SchemaAST.flip(schema.ast)` with `decode` for encoding. + +`effect/schema/SchemaAOTCompiler/Build` provides the higher-level +workflow. Its `build` function loads direct Schema exports, writes a +self-installing module through `FileSystem`, and prepares decoding by default: + +```ts +import * as SchemaAOTCompilerBuild from "effect/schema/SchemaAOTCompiler/Build" + +SchemaAOTCompilerBuild.build({ + modules: { + "./schemas/User.js": () => import("./schemas/User.js"), + "./schemas/Order.js": () => import("./schemas/Order.js") + }, + baseUrl: import.meta.url, + outFile: "./generated/schema-aot.js" +}) +``` + +Import the generated file at application startup. Module keys identify imports +relative to `baseUrl`; each loader must return that same module during the +build. The lazy record produced by `import.meta.glob` can be passed directly. +Request `encode`, `is`, or `make` explicitly when those directions also need +AOT roots. Loading executes the selected application modules during the build. +Run the returned Effect with the platform's `FileSystem` and `Path` services, +and ensure the bundler retains the generated side-effect import. + +Regenerate AOT modules when schema definitions or the Effect version change. +Callbacks and symbols are read from runtime ASTs, not serialized. + +### One registry for all implementations + +A single `WeakMap` associates each exact AST with its decoder entry. The cache +stores functions, never parsing results. The interpreter, JIT, AOT and +`SchemaCompiler.set(ast, decoder)` all use it. + +| Operation | Result | Purpose | +| -------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `decodeEffect` | `Effect` with output or detailed issues | Required complete decoding, including asynchronous work and transformations. | +| `decode` | Output or `SchemaCompiler.invalid` | Optional synchronous decoding fast path without detailed diagnostics. | +| `is` | Boolean | Optional validation without constructing output. | +| `make` | Output or `SchemaCompiler.invalid` | Optional synchronous construction fast path without detailed diagnostics. | +| `makeEffect` | `Effect` with a constructed value or issues | Optional specialized construction. The registry caches the interpreted constructor when absent. | + +Decoding tries `decode` when available. Success provides the output directly; +failure calls `decodeEffect` for diagnostics. The diagnostic traversal uses child +decoders directly, without restarting their validation fast paths. A boolean +guard prefers `is`, otherwise it uses ordinary decoding (including `decode` +when available). An `invalid` result needs that diagnostic fallback because the +marker is also a possible input value. Composite checks +can require stripped, reconstructed values, so `is` is omitted when it cannot +avoid constructing those values safely. + +Each operation initializes independently. Synchronous construction tries `make` +when available and falls back to `makeEffect` for detailed issues. Compilers omit +`make` whenever replay could repeat defaults, Class constructors, +transformations, middleware, or other effects. `makeEffect` itself never uses +validation replay. Field defaults belong to the parent occurrence, not to +construction of the root. Runtime parse options, including product concurrency, +retain the interpreter's semantics. + +Installing a decoder replaces the entry for that AST. Existing consumers that +already captured an entry retain it. Late installation is allowed, but startup +installation is needed to optimize every consumer. Custom decoders supplied to +`set` are trusted to implement the AST's semantics. + +An AOT module installs entries for explicitly requested roots and their static +dependencies. Each generated operation initializes on first use. All entries +live in the same `WeakMap`: there is no separate AOT cache, and a later +`SchemaCompiler.set` for the same AST replaces any compiled entry in the same way. + +### What is specialized + +Encoding-free graphs of supported primitives, Objects, Arrays, tuples, Unions +and template literals can use generated validators. Struct and homogeneous Array +decoding and construction also have generated loops. Pure fixed Struct and +homogeneous Array constructors can additionally use the synchronous `make` fast +path; composite children are resolved through the same registry. These loops +share the interpreter's diagnostic and asynchronous continuation helpers. +Other detailed traversals and constructors use the existing interpreter with +registry-resolved children; there is no separate diagnostic interpreter in the +compiler. + +Transformations and middleware never participate in validation replay. A single +synchronous transformation between supported leaf types can use generated +orchestration directly. Other transformations and middleware use the interpreted +orchestration, while pure child checkpoints can still use generated validators. +Suspend is resolved lazily by JIT. AOT does not evaluate Suspend thunks at build +time, so dynamically reached schemas fall back to the interpreter unless installed +separately. Declaration callbacks remain runtime code; their type parameters can +be compiled. + +Checks and property getters in replayable validation must be deterministic and +free of side effects. Proxy inputs and modifications to built-in object behavior +are not supported by the optimization contract. Large or unsupported graphs +retain interpreted paths. AOT removes dynamic source generation, not all parser +initialization or the need for runtime schema objects. # Defining Elementary Schemas @@ -175,11 +302,11 @@ import { Schema } from "effect" Schema.String.check(Schema.isMaxLength(5)) Schema.String.check(Schema.isMinLength(5)) -Schema.String.check(Schema.isLengthBetween(5, 5)) +Schema.String.check(Schema.isBetweenLength(5, 5)) Schema.String.check(Schema.isPattern(/^[a-z]+$/)) -Schema.String.check(Schema.isStartsWith("aaa")) -Schema.String.check(Schema.isEndsWith("zzz")) -Schema.String.check(Schema.isIncludes("---")) +Schema.String.check(Schema.isStartingWith("aaa")) +Schema.String.check(Schema.isEndingWith("zzz")) +Schema.String.check(Schema.isIncluding("---")) Schema.String.check(Schema.isUppercased()) Schema.String.check(Schema.isLowercased()) ``` @@ -2684,6 +2811,15 @@ const refined = Schema.Array(Schema.String).pipe( Use `Schema.brand` to add a brand to a schema. +The identifier must be a single concrete string literal. `Schema.brand` adds +a nominal distinction to the decoded TypeScript type. It does not add runtime +validation or metadata to the schema AST. Apply it once per identifier when a +type has multiple brands. + +Because branding is type-only, `SchemaRepresentation` does not preserve it. +Reapply `Schema.brand` after rebuilding a representation or generating schema +code when the branded TypeScript type is still required. + **Example** (Brand a string as a UserId) ```ts @@ -2694,6 +2830,32 @@ import { Schema } from "effect" const branded = Schema.String.pipe(Schema.brand("UserId")) ``` +### Using Brand constructors + +Use `Schema.fromBrand` to reuse the checks from a `Brand.Constructor`. The +constructor must have exactly one concrete brand key, and the identifier must +match that key. Apply `Schema.fromBrand` once per constructor to compose +distinct brands. Use `Schema.Union` for alternatives instead. + +With a string enum brand key, pass the enum member rather than its string value. + +**Example** (Compose checked brands) + +```ts +import { Brand, Schema } from "effect" + +type Int = number & Brand.Brand<"Int"> +const Int = Brand.check(Schema.isInt()) + +type Positive = number & Brand.Brand<"Positive"> +const Positive = Brand.check(Schema.isGreaterThan(0)) + +const PositiveInt = Schema.Number.pipe( + Schema.fromBrand("Int", Int), + Schema.fromBrand("Positive", Positive) +) +``` + ## Structural Filters Some filters check the structure of a value rather than its contents — for example, the number of items in an array or the number of keys in an object. These are called **structural filters**. @@ -3080,57 +3242,65 @@ Transformation - `RD`: the context used while decoding - `RE`: the context used while encoding -A `Transformation` consists of two `Getter` functions: +A `Transformation` consists of two `Getter` values: - `decode: Getter` — transforms a value during decoding - `encode: Getter` — transforms a value during encoding -Each `Getter` receives an input and an optional context and returns either a value or an error. Getters can be composed to build more complex logic. +Each `Getter` is a tagged description of one operation: + +- `Transform` transforms a present value synchronously. +- `TransformOptional` transforms an `Option` synchronously and can handle a missing value. +- `TransformEffect` and `TransformOptionalEffect` are the corresponding effectful forms. +- `Passthrough` returns its input unchanged. + +Getter values expose `pipe`. Use the dual standalone functions `SchemaGetter.map` and `SchemaGetter.compose` to build +larger transformations. `SchemaGetter.run` executes a getter directly and always returns an `Effect`; schemas execute +their getters through `SchemaParser` instead. **Example** (Implementation of `Transformation.trim`) ```ts +import { SchemaGetter, SchemaTransformation } from "effect" + /** * @category String transformations * @since 4.0.0 */ -export function trim(): Transformation { - return new Transformation(Getter.trim(), Getter.passthrough()) +export function trim(): SchemaTransformation.Transformation { + return new SchemaTransformation.Transformation(SchemaGetter.trim(), SchemaGetter.passthrough()) } ``` In this case: -- The `decode` process uses `Getter.trim()` to remove leading and trailing whitespace. -- The `encode` process uses `Getter.passthrough()`, which returns the input as is. +- The `decode` process uses `SchemaGetter.trim()` to remove leading and trailing whitespace. +- The `encode` process uses `SchemaGetter.passthrough()`, which returns the input as is. ## Composing Transformations -You can combine transformations using the `.compose` method. The resulting transformation applies the `decode` and `encode` logic of both transformations in sequence. +You can combine transformations using `SchemaTransformation.composeTransformation`. The resulting transformation applies the `decode` and `encode` logic of both transformations in sequence. **Example** (Trim and lowercase a string) ```ts -import { Option, SchemaTransformation } from "effect" +import { Schema, SchemaTransformation } from "effect" // Compose two transformations: trim followed by toLowerCase -const trimToLowerCase = SchemaTransformation.trim().compose(SchemaTransformation.toLowerCase()) +const trimToLowerCase = SchemaTransformation.composeTransformation( + SchemaTransformation.trim(), + SchemaTransformation.toLowerCase() +) +const schema = Schema.String.pipe(Schema.decode(trimToLowerCase)) -// Run the decode logic manually to inspect the result -console.log(trimToLowerCase.decode.run(Option.some(" Abc"), {})) -/* -{ - _id: 'Exit', - _tag: 'Success', - value: { _id: 'Option', _tag: 'Some', value: 'abc' } -} -*/ +Schema.decodeUnknownSync(schema)(" Abc") +// "abc" ``` In this example: -- The `decode` logic applies `Getter.trim()` followed by `Getter.toLowerCase()`, producing a string that is trimmed and lowercased. -- The `encode` logic is `Getter.passthrough()`, which simply returns the input as-is. +- The `decode` logic applies `SchemaGetter.trim()` followed by `SchemaGetter.toLowerCase()`, producing a string that is trimmed and lowercased. +- The `encode` logic is `SchemaGetter.passthrough()`, which returns the input unchanged. ## Transforming One Schema into Another @@ -5128,8 +5298,8 @@ Properties not modeled by an object schema use `onExcessProperty: "ignore"` by default, matching the decoder default. This emits `additionalProperties: true`. Pass `{ onExcessProperty: "error" }` to the generator and decoder to reject unmatched properties whenever the key space is representable. An -index-signature key check that cannot be represented leaves unmatched -properties open so that JSON Schema does not reject inputs Effect may accept. +index-signature key check that cannot be translated to an exact selector uses +a conservative fallback so that JSON Schema does not reject inputs Effect may accept. The generator does not merge conjunctive key patterns into a new regular expression. With `onExcessProperty: "ignore"`, it leaves that index signature open. With `onExcessProperty: "error"`, it uses the generated key schemas under @@ -5138,9 +5308,11 @@ open. With `onExcessProperty: "error"`, it uses the generated key schemas under decoder enforces the exact association between keys and values. Known differences include Unicode code-point versus UTF-16 string length, -JavaScript RegExp flags, property checks applied before versus after decoding, -and `oneOf` with overlapping members. Custom `toJsonSchema` callbacks are also -responsible for the semantics they emit. +JavaScript RegExp flags, and property checks applied before versus after decoding. +When a `oneOf` branch has a known approximation, the compiler emits `anyOf` so +that newly overlapping branches cannot reject valid values. It retains `oneOf` +when all branches are exact. Custom `toJsonSchema` callbacks declare approximate +results with `[schema, true]`; see [Check exporters and approximation](#check-exporters-and-approximation). The result is a data structure including: @@ -5867,6 +6039,13 @@ console.log(decoded.representation._tag) Consequently, rebuilding `encoded` produces a schema for the string representation; it does not recreate the original string-to-number transformation. +Representations describe runtime schema structure, so they do not preserve +TypeScript-only distinctions. `Schema.brand` is absent because it does not +change the AST. A check introduced by `Schema.refine` can remain part of the +runtime representation, but its narrowed TypeScript type cannot be recovered. +Reapply these type-level operations after rebuilding a representation or +generating schema code when needed. + ### Live and persisted documents A live `Document` can contain functions in its ordinary annotations. These callbacks allow compilers to handle custom @@ -5934,10 +6113,12 @@ inline even when the same AST occurs more than once. Recursive schemas always re available, the converter assigns a synthetic name such as `Objects_` or `Suspend_`. The default policy uses an explicit `identifier` as the reference name. Reusing the same schema shares its reference. -Context-only copies created through `SchemaAST.replaceContext` retain the original AST as their reference owner, including -across several successive context changes. Context still belongs to each occurrence and does not, by itself, create a new -candidate. Independently constructed ASTs are not canonicalized merely because they are structurally equal. When distinct -schemas request the same name, the first schema keeps it and later schemas receive numeric suffixes in encounter order, +AST copies that change only their own `context` or `encoding` share a decoded body. Type and encoded projections preserve +this sharing while keeping each occurrence's context and following its actual encoding chain. Changing checks, value +annotations, or children creates a distinct body; child contexts are part of the parent's structure. Reference owners omit +their own encoding, so reference policies inspect the represented body. Independently constructed ASTs are not +canonicalized merely because they are structurally equal. When distinct schemas request the same name, the first schema +keeps it and later schemas receive numeric suffixes in encounter order, such as `Value_1` and `Value_2`. Internal `~identifier` annotations are fallback allocation hints; their generated names use the `Encoded` suffix and follow the same collision rules. @@ -6092,25 +6273,63 @@ different allocation rule. Generated JSON Schema is a preliminary validation layer. The Effect decoder remains the final authority because string length, RegExp flags, -decoded-object property checks, and `oneOf` can differ between the two -validators. The default `onExcessProperty: "ignore"` emits +and decoded-object property checks can differ between the two validators. +If a `oneOf` branch contains a known approximation, the compiler emits `anyOf`. +This prevents the approximation from rejecting values by making multiple +branches match. Unions whose branches are all exact retain `oneOf`. + +The default `onExcessProperty: "ignore"` emits `additionalProperties: true`; use `onExcessProperty: "error"` in both generation and decoding to reject unmatched properties whenever the key space -is representable. Unrepresentable index-signature key checks leave unmatched -properties open under the default mode. The compiler does not merge -conjunctive key patterns into a new regular expression. In `error` mode it uses -the generated key schemas under `propertyNames`. For properties not otherwise -selected, it accepts any index-signature value schema and leaves the exact -key-value association to the Effect decoder. +has an exact selector. For `Schema.Record(Key, Value)`, a `patternProperties` +selector must represent the key check exactly. A looser pattern could select +extra keys and impose `Value` constraints on properties the Effect decoder ignores. + +When a key cannot be translated to an exact selector, including conjunctive key +patterns that the compiler does not merge into a single regular expression: + +- With `onExcessProperty: "ignore"`, the compiler omits that index-signature constraint. +- With `onExcessProperty: "error"`, it emits the generated key schemas under + `propertyNames`, also allowing explicitly declared property names. Properties + not selected by `properties` or exact `patternProperties` selectors may satisfy + any candidate index-signature value schema. The Effect decoder enforces the + exact association between keys and values. + +Exact selectors from other index signatures remain active. An exact key selector +is also retained when only its value schema is approximate. At the lower level, `SchemaRepresentation.toJsonSchemaDocument(document)` compiles a live `Document`, and `toJsonSchemaMultiDocument` compiles a live `MultiDocument`. Check-level `toJsonSchema` callbacks contribute JSON Schema constraints. Opaque declarations that have not been structurally lowered compile to an unconstrained JSON Schema. Callback authors are responsible for the semantics of their output. -`toJsonSchema` callbacks must treat their input schemas as immutable and return a valid JSON Schema object graph. After a -callback returns, it must not mutate that object or anything reachable from it; returning a new graph is the supported way -to produce different output during a later compilation. The compiler may cache structural comparisons while +#### Check exporters and approximation + +Check-level `toJsonSchema` callbacks return `SchemaRepresentation.ToJsonSchema.CheckOutput`: + +| Return value | Meaning | +| ---------------- | ---------------------------------------------------------------------------- | +| `schema` | The fragment represents the check exactly. | +| `[schema, true]` | The fragment accepts every value accepted by the check, and may accept more. | + +Use the tightest safe approximation available. Return `[{}, true]` when the +constraint must be omitted. Returning a bare `{}` instead declares that the +check imposes no constraint. The compiler trusts this declaration; it does not +prove that a custom export is exact or safely looser. + +Approximation propagates through grouped checks, array elements, object +properties, unions, and references, including recursive definitions. It also +propagates from dependencies listed in a check's `representation.schemas`, even +if its callback returns a plain fragment. The callback receives those compiled +dependencies in its `schemas` input. Filters without a `toJsonSchema` callback +and opaque declarations without a structural codec are treated as approximate. + +This information controls the `oneOf` and record-key fallbacks described above. +It is used during compilation and adds no metadata to the emitted JSON Schema. + +`toJsonSchema` callbacks must treat their input schemas as immutable and return a valid JSON Schema fragment, either +directly or in the tuple above. After a callback returns, it must not mutate that fragment or anything reachable from it; +returning a new graph is the supported way to produce different output during a later compilation. The compiler may cache structural comparisons while deduplicating completed definitions, so mutating a previously returned graph can make equality results stale. Definitions are compared only with definitions in the same internal fallback-identifier group. Equal definitions in @@ -6126,6 +6345,10 @@ schema with revivers first. `SchemaRepresentation.fromJsonSchemaDocument` imports a JSON Schema Draft 2020-12 document as a runtime `Schema.Top`. It does not return a representation document. +The input is assumed to be a valid Draft 2020-12 document and is not validated +against the meta-schema. Instance validation assumes JSON-compatible JavaScript +values produced by `JSON.parse`. + Only direct local references to top-level definitions in the form `#/$defs/` are supported. Root references, external references, and pointers below a definition are rejected with the supported reference format. Missing definitions are reported by name. References reached inside a nested schema @@ -6158,10 +6381,11 @@ validation or a lossless round trip: result to a finite set of properties. - `minProperties`, `maxProperties`, and `propertyNames` use the existing checks on the decoded object, after excess properties have been stripped. No separate validation of the original object is added. -- String length checks count UTF-16 code units, not Unicode code points. For example, `"😀"` satisfies an imported - `minLength: 2` and fails an imported `maxLength: 1`, unlike JSON Schema validation. +- String `minLength` and `maxLength` count Unicode code points through `Schema.isMinCodePoints` and + `Schema.isMaxCodePoints`, except that `minLength: 1` uses the equivalent non-empty check `Schema.isMinLength(1)`. - `integer` uses `Schema.isInt` and rejects integers outside JavaScript's safe integer range. -- Applied patterns use the existing `Schema.isPattern` check without adding a Unicode flag. +- Applied patterns use `Schema.isPattern` with the `u` flag. Patterns that cannot be compiled in Unicode mode are + rejected as unsupported translations, with their source path, rather than interpreted with different semantics. An Effect struct exported with `additionalProperties: true` therefore imports with a JSON-valued index signature, even if the original struct had none. The imported decoder retains additional properties that the original decoder diff --git a/repos/effect/packages/effect/benchmark/http/multipart.ts b/repos/effect/packages/effect/benchmark/http/multipart.ts index 22ef22378d..bb0291f340 100644 --- a/repos/effect/packages/effect/benchmark/http/multipart.ts +++ b/repos/effect/packages/effect/benchmark/http/multipart.ts @@ -1,6 +1,6 @@ import { Effect, Stream } from "effect" -import * as Multipart from "effect/unstable/http/Multipart" -import * as MultipartParser from "effect/unstable/http/MultipartParser" +import * as Multipart from "effect/http/Multipart" +import * as MultipartParser from "effect/http/MultipartParser" import { Bench } from "tinybench" const bench = new Bench() diff --git a/repos/effect/packages/effect/benchmark/http/serverAllocations.ts b/repos/effect/packages/effect/benchmark/http/serverAllocations.ts index 927a0cac53..e06dd3a93c 100644 --- a/repos/effect/packages/effect/benchmark/http/serverAllocations.ts +++ b/repos/effect/packages/effect/benchmark/http/serverAllocations.ts @@ -1,4 +1,4 @@ -import { HttpRouter, HttpServerResponse } from "effect/unstable/http" +import { HttpRouter, HttpServerResponse } from "effect/http" import * as inspector from "node:inspector/promises" const WARMUP = 5_000 diff --git a/repos/effect/packages/effect/benchmark/http/tracer.ts b/repos/effect/packages/effect/benchmark/http/tracer.ts index ef9b4bbaf5..f6a2591088 100644 --- a/repos/effect/packages/effect/benchmark/http/tracer.ts +++ b/repos/effect/packages/effect/benchmark/http/tracer.ts @@ -1,10 +1,10 @@ import { Effect } from "effect" +import * as Headers from "effect/http/Headers" +import * as HttpMiddleware from "effect/http/HttpMiddleware" +import * as HttpServerRequest from "effect/http/HttpServerRequest" +import * as HttpServerResponse from "effect/http/HttpServerResponse" +import * as HttpTraceContext from "effect/http/HttpTraceContext" import * as Tracer from "effect/Tracer" -import * as Headers from "effect/unstable/http/Headers" -import * as HttpMiddleware from "effect/unstable/http/HttpMiddleware" -import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest" -import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse" -import * as HttpTraceContext from "effect/unstable/http/HttpTraceContext" import { Bench } from "tinybench" const bench = new Bench() diff --git a/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts b/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts index 0b41490a86..cffa08dbaa 100644 --- a/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts +++ b/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts @@ -1,6 +1,6 @@ import { Effect, Schema, Stream } from "effect" -import { HttpClient, HttpClientResponse } from "effect/unstable/http" -import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" +import { HttpClient, HttpClientResponse } from "effect/http" +import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api" import { Bench } from "tinybench" const Event = Schema.Struct({ diff --git a/repos/effect/packages/effect/benchmark/net/NetAddress.ts b/repos/effect/packages/effect/benchmark/net/NetAddress.ts new file mode 100644 index 0000000000..81ee5e69b8 --- /dev/null +++ b/repos/effect/packages/effect/benchmark/net/NetAddress.ts @@ -0,0 +1,93 @@ +// Run from the repository root on each runtime: +// +// node packages/effect/benchmark/net/NetAddress.ts +// bun packages/effect/benchmark/net/NetAddress.ts +// deno run -A packages/effect/benchmark/net/NetAddress.ts +import { Equal, Hash } from "effect" +import { NetAddress } from "effect/net" +import { Bench } from "tinybench" + +const runtime = (globalThis as any).Deno !== undefined + ? `deno ${(globalThis as any).Deno.version.deno}` + : (globalThis as any).Bun !== undefined + ? `bun ${(globalThis as any).Bun.version}` + : `node ${process.versions.node}` + +// Results are accumulated so the engine cannot drop the measured work. +let sink = 0 +const consume = (value: unknown) => { + if (value === undefined) sink++ +} + +const ipv4String = "192.168.1.42" +const ipv4Octets = [192, 168, 1, 42] as const +const ipv4Bytes = new Uint8Array(ipv4Octets) + +const ipv6FullString = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" +const ipv6CompressedString = "2001:db8::8a2e:370:7334" +const ipv6MappedString = "::ffff:192.168.1.42" +const ipv6Segments = [0x2001, 0x0db8, 0x85a3, 0, 0, 0x8a2e, 0x0370, 0x7334] as const +const ipv6Bytes = new Uint8Array(ipv6Segments.flatMap((segment) => [segment >> 8, segment & 0xff])) + +// Distinct objects with the same value, plus one differing only in the last +// byte so the comparison has to check the whole address. +const ipv4 = NetAddress.ipv4FromBytesUnsafe(ipv4Bytes) +const ipv4Same = NetAddress.ipv4FromBytesUnsafe(ipv4Bytes) +const ipv4Other = NetAddress.ipv4FromBytesUnsafe(new Uint8Array([192, 168, 1, 43])) +const ipv6 = NetAddress.ipv6FromBytesUnsafe(ipv6Bytes) +const ipv6Same = NetAddress.ipv6FromBytesUnsafe(ipv6Bytes) +const ipv6OtherBytes = new Uint8Array(ipv6Bytes) +ipv6OtherBytes[15] ^= 1 +const ipv6Other = NetAddress.ipv6FromBytesUnsafe(ipv6OtherBytes) + +// Equal.equals and Hash.hash cache results per object in WeakMaps, so calling +// them repeatedly on the same addresses only measures the cache. The "method" +// cases call the address implementations directly, which is the code the +// storage change affects. The "public" cases build a fresh address each +// iteration, so the first (uncached) call is measured; subtract the matching +// "ipvN fromBytesUnsafe" case to isolate the call itself. +const equalsMethod = (self: Equal.Equal, that: Equal.Equal) => self[Equal.symbol](that) +const hashMethod = (self: Hash.Hash) => self[Hash.symbol]() + +const bench = new Bench({ name: `NetAddress (${runtime})` }) + +bench + // Typed-array allocation cost, for comparison with Uint8Array-backed storage. + .add("baseline new Uint8Array(4)", () => consume(new Uint8Array(4))) + .add("baseline new Uint8Array(16)", () => consume(new Uint8Array(16))) + // IPv4 construction + .add("ipv4 fromString", () => consume(NetAddress.ipv4FromString(ipv4String))) + .add("ipv4 fromOctets", () => consume(NetAddress.ipv4FromOctets(ipv4Octets))) + .add("ipv4 fromBytesUnsafe", () => consume(NetAddress.ipv4FromBytesUnsafe(ipv4Bytes))) + .add("ip fromString (ipv4)", () => consume(NetAddress.ipFromString(ipv4String))) + // IPv6 construction + .add("ipv6 fromString (full)", () => consume(NetAddress.ipv6FromString(ipv6FullString))) + .add("ipv6 fromString (compressed)", () => consume(NetAddress.ipv6FromString(ipv6CompressedString))) + .add("ipv6 fromString (ipv4-mapped)", () => consume(NetAddress.ipv6FromString(ipv6MappedString))) + .add("ipv6 fromSegments", () => consume(NetAddress.ipv6FromSegments(ipv6Segments))) + .add("ipv6 fromBytesUnsafe", () => consume(NetAddress.ipv6FromBytesUnsafe(ipv6Bytes))) + .add("ip fromString (ipv6 compressed)", () => consume(NetAddress.ipFromString(ipv6CompressedString))) + // Equality and hashing on the address implementations + .add("ipv4 equals method (equal)", () => consume(equalsMethod(ipv4, ipv4Same))) + .add("ipv4 equals method (different)", () => consume(equalsMethod(ipv4, ipv4Other))) + .add("ipv4 hash method", () => consume(hashMethod(ipv4))) + .add("ipv6 equals method (equal)", () => consume(equalsMethod(ipv6, ipv6Same))) + .add("ipv6 equals method (different)", () => consume(equalsMethod(ipv6, ipv6Other))) + .add("ipv6 hash method", () => consume(hashMethod(ipv6))) + // Uncached public Equal.equals and Hash.hash, including construction + .add( + "ipv4 Equal.equals public (fresh)", + () => consume(Equal.equals(NetAddress.ipv4FromBytesUnsafe(ipv4Bytes), ipv4Same)) + ) + .add("ipv4 Hash.hash public (fresh)", () => consume(Hash.hash(NetAddress.ipv4FromBytesUnsafe(ipv4Bytes)))) + .add( + "ipv6 Equal.equals public (fresh)", + () => consume(Equal.equals(NetAddress.ipv6FromBytesUnsafe(ipv6Bytes), ipv6Same)) + ) + .add("ipv6 Hash.hash public (fresh)", () => consume(Hash.hash(NetAddress.ipv6FromBytesUnsafe(ipv6Bytes)))) + +await bench.run() + +console.log(bench.name) +console.table(bench.table()) +if (sink !== 0) console.log(sink) diff --git a/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts b/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts index e0665b14d9..a2057a7088 100644 --- a/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts +++ b/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts @@ -1,5 +1,5 @@ import { Effect, Exit, Schema } from "effect" -import { Rpc, RpcSerialization } from "effect/unstable/rpc" +import { Rpc, RpcSerialization } from "effect/rpc" import assert from "node:assert/strict" import { cpus } from "node:os" import { Bench } from "tinybench" diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts b/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts index 7077ec325d..366b798326 100644 --- a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts +++ b/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts @@ -1,5 +1,5 @@ import { Effect, Schema, Stream } from "effect" -import { Ndjson, SchemaBinary } from "effect/unstable/encoding" +import { Ndjson, SchemaBinary } from "effect/encoding" import assert from "node:assert/strict" import { gzipSync, zstdCompressSync } from "node:zlib" import protobuf, { type Message, type Type } from "protobufjs" diff --git a/repos/effect/packages/effect/package.json b/repos/effect/packages/effect/package.json index 96edea2fa6..6534bb1991 100644 --- a/repos/effect/packages/effect/package.json +++ b/repos/effect/packages/effect/package.json @@ -1,7 +1,7 @@ { "name": "effect", "type": "module", - "version": "4.0.0-rc.115", + "version": "4.0.0", "license": "MIT", "description": "The missing standard library for TypeScript, for writing production-grade software.", "homepage": "https://effect.website", @@ -25,34 +25,36 @@ "concurrency", "observability" ], - "sideEffects": [], + "sideEffects": [ + "./src/schema/SchemaJITCompiler/enable.ts", + "./dist/schema/SchemaJITCompiler/enable.js" + ], "exports": { "./package.json": "./package.json", ".": "./src/index.ts", "./testing": "./src/testing/index.ts", - "./unstable/ai": "./src/unstable/ai/index.ts", - "./unstable/arbitrary": "./src/unstable/arbitrary/index.ts", - "./unstable/cli": "./src/unstable/cli/index.ts", - "./unstable/cluster": "./src/unstable/cluster/index.ts", - "./unstable/devtools": "./src/unstable/devtools/index.ts", - "./unstable/encoding": "./src/unstable/encoding/index.ts", - "./unstable/eventlog": "./src/unstable/eventlog/index.ts", - "./unstable/http": "./src/unstable/http/index.ts", - "./unstable/httpapi": "./src/unstable/httpapi/index.ts", - "./unstable/net": "./src/unstable/net/index.ts", - "./unstable/observability": "./src/unstable/observability/index.ts", - "./unstable/persistence": "./src/unstable/persistence/index.ts", - "./unstable/process": "./src/unstable/process/index.ts", - "./unstable/reactivity": "./src/unstable/reactivity/index.ts", - "./unstable/rpc": "./src/unstable/rpc/index.ts", - "./unstable/schema": "./src/unstable/schema/index.ts", - "./unstable/socket": "./src/unstable/socket/index.ts", - "./unstable/sql": "./src/unstable/sql/index.ts", - "./unstable/workflow": "./src/unstable/workflow/index.ts", - "./unstable/workers": "./src/unstable/workers/index.ts", + "./ai": "./src/ai/index.ts", + "./cli": "./src/cli/index.ts", + "./cluster": "./src/cluster/index.ts", + "./devtools": "./src/devtools/index.ts", + "./encoding": "./src/encoding/index.ts", + "./eventlog": "./src/eventlog/index.ts", + "./http": "./src/http/index.ts", + "./http-api": "./src/http-api/index.ts", + "./net": "./src/net/index.ts", + "./observability": "./src/observability/index.ts", + "./persistence": "./src/persistence/index.ts", + "./process": "./src/process/index.ts", + "./reactivity": "./src/reactivity/index.ts", + "./rpc": "./src/rpc/index.ts", + "./schema": "./src/schema/index.ts", + "./socket": "./src/socket/index.ts", + "./sql": "./src/sql/index.ts", + "./workflow": "./src/workflow/index.ts", + "./workers": "./src/workers/index.ts", "./*": "./src/*.ts", - "./unstable/cli/internal/*": null, - "./unstable/cluster/internal/*": null, + "./cli/internal/*": null, + "./cluster/internal/*": null, "./internal/*": null, "./index": null, "./*/index": null @@ -74,30 +76,29 @@ "./package.json": "./package.json", ".": "./dist/index.js", "./testing": "./dist/testing/index.js", - "./unstable/ai": "./dist/unstable/ai/index.js", - "./unstable/arbitrary": "./dist/unstable/arbitrary/index.js", - "./unstable/cli": "./dist/unstable/cli/index.js", - "./unstable/cluster": "./dist/unstable/cluster/index.js", - "./unstable/devtools": "./dist/unstable/devtools/index.js", - "./unstable/encoding": "./dist/unstable/encoding/index.js", - "./unstable/eventlog": "./dist/unstable/eventlog/index.js", - "./unstable/http": "./dist/unstable/http/index.js", - "./unstable/httpapi": "./dist/unstable/httpapi/index.js", - "./unstable/net": "./dist/unstable/net/index.js", - "./unstable/observability": "./dist/unstable/observability/index.js", - "./unstable/persistence": "./dist/unstable/persistence/index.js", - "./unstable/process": "./dist/unstable/process/index.js", - "./unstable/reactivity": "./dist/unstable/reactivity/index.js", - "./unstable/rpc": "./dist/unstable/rpc/index.js", - "./unstable/schema": "./dist/unstable/schema/index.js", - "./unstable/socket": "./dist/unstable/socket/index.js", - "./unstable/sql": "./dist/unstable/sql/index.js", - "./unstable/workflow": "./dist/unstable/workflow/index.js", - "./unstable/workers": "./dist/unstable/workers/index.js", + "./ai": "./dist/ai/index.js", + "./cli": "./dist/cli/index.js", + "./cluster": "./dist/cluster/index.js", + "./devtools": "./dist/devtools/index.js", + "./encoding": "./dist/encoding/index.js", + "./eventlog": "./dist/eventlog/index.js", + "./http": "./dist/http/index.js", + "./http-api": "./dist/http-api/index.js", + "./net": "./dist/net/index.js", + "./observability": "./dist/observability/index.js", + "./persistence": "./dist/persistence/index.js", + "./process": "./dist/process/index.js", + "./reactivity": "./dist/reactivity/index.js", + "./rpc": "./dist/rpc/index.js", + "./schema": "./dist/schema/index.js", + "./socket": "./dist/socket/index.js", + "./sql": "./dist/sql/index.js", + "./workflow": "./dist/workflow/index.js", + "./workers": "./dist/workers/index.js", "./*": "./dist/*.js", "./internal/*": null, - "./unstable/cli/internal/*": null, - "./unstable/cluster/internal/*": null, + "./cli/internal/*": null, + "./cluster/internal/*": null, "./index": null, "./*/index": null } @@ -109,13 +110,13 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@types/node": "^26.4.1", + "@types/node": "^26.6.3", "ajv": "^8.20.0", "ajv-draft-04": "^1.0.0", "ast-types": "^0.16.3", "immer": "^11.1.18", "protobufjs": "^8.8.0", - "tinybench": "^6.1.6", + "tinybench": "^6.2.0", "valibot": "^1.5.0" } } diff --git a/repos/effect/packages/effect/runtimeperf/README.md b/repos/effect/packages/effect/runtimeperf/README.md index 8265911a73..cdda394960 100644 --- a/repos/effect/packages/effect/runtimeperf/README.md +++ b/repos/effect/packages/effect/runtimeperf/README.md @@ -42,6 +42,7 @@ pnpm runtimeperf object-32-valid pnpm runtimeperf schema/object-32-valid-effect pnpm runtimeperf --family arrays pnpm runtimeperf --implementation zod4 +pnpm runtimeperf --implementation zod4-compiled ``` Override measurement settings: @@ -56,6 +57,13 @@ Compare Effect `HEAD` with the working tree: pnpm runtimeperf-compare schema/object-32-valid-effect ``` +Measure excess-property checking and symbol enumeration in interpreted and JIT parsers: + +```sh +pnpm runtimeperf-compare schema-excess-properties +pnpm runtimeperf-compare schema-excess-properties --family jit +``` + Compare explicit refs: ```sh @@ -86,6 +94,13 @@ adapters, recursion and cold paths. The `schema-benchmarks` suite contains the complete timing matrices exposed by the upstream Effect, Valibot and Zod adapters. +The `moltar-parse-safe` and `moltar-assert-loose` suites preserve Moltar's +object shape. Their valid cases reproduce the upstream timed operation; the +extra-property and invalid cases are Effect extensions. They compare +interpreted Effect, Effect JIT and AOT, Valibot, ordinary and jitless Zod where +applicable, and Zod `compile`, using the dependency versions recorded in each +report. + The `arbitrary` suite compares the native public API with direct fast-check v4 arbitraries in separate processes. It measures derivation through the first recursive sample, steady-state recursive sampling, optional-Struct sampling, fixed-length string generation to exercise constraint pushdown, @@ -103,6 +118,11 @@ end-to-end public API comparison, but the work is not identical: native replay verifies the original failure and its full shrink path, while fast-check can start directly from its recorded path. +Array cases cover passing checks and shrinking a four-command sequence while preserving an ordered pair. The latter +starts from the same explicit sequence in both engines, using a fixed seed for native generation and an example for +fast-check. Base/head validation preserves the failure without requiring identical shrunk outputs, so its timing also +reflects changes in how much of the shrink tree is explored. The runtime regression tests assert the smaller result. + The regular-expression fixtures validate the same language, and both fixed-seed warm fixtures must reach multiple lengths and every alternative family. Their exact distributions remain implementation-defined, so cross-library timings are diagnostic while base/head comparisons protect the cost of each implementation's established behavior. @@ -118,13 +138,18 @@ Zod parsing cases import `zod/v4` and call `safeParse` with `{ jitless: true }`; its Standard Schema and codec cases use their native APIs. Valibot uses the corresponding `is`, `safeParse` and Standard Schema APIs. The focused Effect adapter family measures the overhead of public APIs that wrap parser issues. +The compiler comparison calls `z.compile(schema, { strict: true })` and uses +Zod's `validate` API for boolean checks. Ten representative scenarios also run +against equivalent Valibot schemas using `parse` and `is`. ## Measurement model -Each worker validates the fixture before and after measuring. Calibration finds -a batch large enough for the configured target duration. Each implementation -uses its own calibrated batch and executes in a separate process, with rotating -order within the scenario. +Each worker validates the fixture before and after measuring. The Effect +fixture calibrates one batch size that every implementation in the same +scenario uses. This keeps the enclosing loop identical across implementations; +V8 can otherwise optimize sub-10 ns callbacks differently at different batch +sizes. Each implementation executes in a separate process, with rotating order +within the scenario. Tinybench measures one synchronous batched task. The primary process result is: diff --git a/repos/effect/packages/effect/runtimeperf/compare.mts b/repos/effect/packages/effect/runtimeperf/compare.mts index 7c2cf346ea..a37cf5373b 100644 --- a/repos/effect/packages/effect/runtimeperf/compare.mts +++ b/repos/effect/packages/effect/runtimeperf/compare.mts @@ -1,13 +1,14 @@ import { spawnSync } from "node:child_process" -import { existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from "node:fs" +import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from "node:fs" import os from "node:os" -import { join } from "node:path" +import { dirname, join } from "node:path" import process from "node:process" import { materializeFixture } from "./materialize.mts" import { analyzePairs } from "./stats.mts" import { aggregateMeasurements, calibrateFixture, + comparePath, configPath, coverageSummary, effectDir, @@ -16,6 +17,7 @@ import { libraryVersions, loadRegistry, makeRunId, + materializePath, measureFixture, parseArgs, printTable, @@ -25,9 +27,12 @@ import { resolveDefaults, selectFixtures, sha256, + statsPath, + utilsPath, workerPath, writeJson } from "./utils.mts" +import { applyWorktreeDiff, readWorktreeDiff } from "./worktree-diff.mts" const usage = `Usage: pnpm runtimeperf-compare [suite[/fixture]|scenario] [options] @@ -90,8 +95,17 @@ const createWorktree = (runRoot, name, sha) => { return path } +const applyWorktreeChanges = (path, untracked) => { + applyWorktreeDiff(path, readWorktreeDiff(repoRoot)) + for (const file of untracked) { + const target = join(path, file.path) + mkdirSync(dirname(target), { recursive: true }) + cpSync(join(repoRoot, file.path), target, { recursive: true }) + } +} + const worktreeState = () => { - const diff = runGit(["diff", "--binary", "HEAD", "--"]) + const diff = readWorktreeDiff(repoRoot) const untrackedOutput = runGit([ "ls-files", "--others", @@ -136,16 +150,13 @@ const main = () => { try { const baseRoot = createWorktree(runRoot, "base", baseSha) worktrees.push(baseRoot) - const headRoot = options.head === "worktree" - ? repoRoot - : createWorktree(runRoot, "head", headSha) - if (headRoot !== repoRoot) worktrees.push(headRoot) + const headRoot = createWorktree(runRoot, "head", headSha) + worktrees.push(headRoot) + if (options.head === "worktree") applyWorktreeChanges(headRoot, state.untracked) for (const fixture of selected) { const baseFixturePath = materializeFixture(baseRoot, fixture) - const headFixturePath = headRoot === repoRoot - ? fixture.fixturePath - : materializeFixture(headRoot, fixture) + const headFixturePath = materializeFixture(headRoot, fixture) const baseCalibration = calibrateFixture(fixture, defaults, baseFixturePath) const headCalibration = calibrateFixture(fixture, defaults, headFixturePath) const batchSize = Math.max(baseCalibration.batchSize, headCalibration.batchSize) @@ -227,7 +238,11 @@ const main = () => { artifactMode: "repository", coverage: coverageSummary(selected), hashes: { + compare: hashFile(comparePath), config: hashFile(configPath), + materialize: hashFile(materializePath), + stats: hashFile(statsPath), + utils: hashFile(utilsPath), worker: hashFile(workerPath), fixtures: Object.fromEntries( [...new Set(selected.map((fixture) => fixture.fixturePath))] diff --git a/repos/effect/packages/effect/runtimeperf/config.json b/repos/effect/packages/effect/runtimeperf/config.json index 516e43c7c7..e7fac7c637 100644 --- a/repos/effect/packages/effect/runtimeperf/config.json +++ b/repos/effect/packages/effect/runtimeperf/config.json @@ -11,6 +11,164 @@ "maxRegressionPercent": 5 }, "suites": [ + { + "name": "schema-checked-sources", + "fixtures": [ + { + "file": "suites/schema-checked-sources/fixtures/cases.ts", + "defaults": { + "tier": 1, + "family": "jit", + "implementation": "effect-jit", + "astTags": ["Arrays", "Objects", "String", "Number"], + "operation": "decode", + "path": "valid", + "size": 1000 + }, + "cases": [ + { + "name": "digits", + "export": "digits", + "scenario": "checked-sources-digits" + }, + { + "name": "checked-source", + "export": "checkedSource", + "scenario": "checked-sources-checked-source" + }, + { + "name": "unchecked-source", + "export": "uncheckedSource", + "scenario": "checked-sources-unchecked-source" + }, + { + "name": "two-passes", + "export": "twoPasses", + "scenario": "checked-sources-two-passes" + }, + { + "name": "checked-template-target", + "export": "checkedTemplateTarget", + "scenario": "checked-sources-checked-template-target", + "astTags": ["Arrays", "Objects", "String", "TemplateLiteral"] + }, + { + "name": "unchecked-template-target", + "export": "uncheckedTemplateTarget", + "scenario": "checked-sources-unchecked-template-target", + "astTags": ["Arrays", "Objects", "String", "TemplateLiteral"] + } + ] + } + ] + }, + { + "name": "schema-excess-properties", + "fixtures": [ + { + "file": "suites/schema-excess-properties/fixtures/cases.ts", + "defaults": { + "tier": 1, + "family": "interpreted", + "implementation": "effect", + "astTags": ["Objects", "String", "Number", "Boolean"], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "interpreted-struct-default", + "export": "interpretedStructDefault", + "scenario": "excess-struct-default" + }, + { + "name": "jit-struct-default", + "export": "jitStructDefault", + "scenario": "excess-struct-default", + "family": "jit", + "implementation": "effect-jit" + }, + { + "name": "interpreted-struct-strict", + "export": "interpretedStructStrict", + "scenario": "excess-struct-strict" + }, + { + "name": "jit-struct-strict", + "export": "jitStructStrict", + "scenario": "excess-struct-strict", + "family": "jit", + "implementation": "effect-jit" + }, + { + "name": "interpreted-extra-strict", + "export": "interpretedExtraStrict", + "scenario": "excess-extra-strict", + "path": "invalid", + "size": 4 + }, + { + "name": "jit-extra-strict", + "export": "jitExtraStrict", + "scenario": "excess-extra-strict", + "family": "jit", + "implementation": "effect-jit", + "path": "invalid", + "size": 4 + }, + { + "name": "interpreted-strings-strict", + "export": "interpretedStringsStrict", + "scenario": "excess-strings-strict", + "astTags": ["Objects", "String", "Number"], + "size": 32 + }, + { + "name": "jit-strings-strict", + "export": "jitStringsStrict", + "scenario": "excess-strings-strict", + "family": "jit", + "implementation": "effect-jit", + "astTags": ["Objects", "String", "Number"], + "size": 32 + }, + { + "name": "interpreted-symbols-default", + "export": "interpretedSymbolsDefault", + "scenario": "excess-symbols-default", + "astTags": ["Objects", "Symbol", "Number"], + "size": 32 + }, + { + "name": "jit-symbols-default", + "export": "jitSymbolsDefault", + "scenario": "excess-symbols-default", + "family": "jit", + "implementation": "effect-jit", + "astTags": ["Objects", "Symbol", "Number"], + "size": 32 + }, + { + "name": "interpreted-symbols-strict", + "export": "interpretedSymbolsStrict", + "scenario": "excess-symbols-strict", + "astTags": ["Objects", "Symbol", "Number"], + "size": 32 + }, + { + "name": "jit-symbols-strict", + "export": "jitSymbolsStrict", + "scenario": "excess-symbols-strict", + "family": "jit", + "implementation": "effect-jit", + "astTags": ["Objects", "Symbol", "Number"], + "size": 32 + } + ] + } + ] + }, { "name": "arbitrary", "fixtures": [ @@ -322,6 +480,21 @@ ], "size": 1 }, + { + "name": "array-check-pass-100-native", + "export": "arrayCheckPass100", + "scenario": "arbitrary-array-check-pass-100", + "operation": "check-array", + "size": 50 + }, + { + "name": "array-check-falsify-shrink-native", + "export": "arrayCheckFalsifyAndShrink", + "scenario": "arbitrary-array-check-falsify-shrink", + "operation": "check-array-and-shrink", + "path": "failure", + "size": 4 + }, { "name": "check-pass-100-native", "export": "checkPass100", @@ -571,6 +744,21 @@ "path": "failure", "size": 1 }, + { + "name": "array-check-pass-100-fast-check-v4", + "export": "arrayCheckPass100", + "scenario": "arbitrary-array-check-pass-100", + "operation": "check-array", + "size": 50 + }, + { + "name": "array-check-falsify-shrink-fast-check-v4", + "export": "arrayCheckFalsifyAndShrink", + "scenario": "arbitrary-array-check-falsify-shrink", + "operation": "check-array-and-shrink", + "path": "failure", + "size": 4 + }, { "name": "check-pass-100-fast-check-v4", "export": "checkPass100", @@ -1283,6 +1471,24 @@ "implementation": "effect", "operation": "schema" }, + { + "name": "schema-creation-encoded-object-8-effect", + "export": "effectSchemaCreationEncodedObject8", + "scenario": "schema-creation-encoded-object-8", + "implementation": "effect", + "operation": "schema", + "astTags": ["Objects", "Arrays", "String", "Number", "Boolean"], + "size": 8 + }, + { + "name": "first-make-encoded-object-8-effect", + "export": "effectFirstMakeEncodedObject8", + "scenario": "first-make-encoded-object-8", + "implementation": "effect", + "operation": "first-make", + "astTags": ["Objects", "Arrays", "String", "Number", "Boolean"], + "size": 8 + }, { "name": "first-make-object-2-effect", "export": "effectFirstMakeObject2", @@ -1673,6 +1879,1536 @@ ] } ] + }, + { + "name": "moltar-parse-safe", + "fixtures": [ + { + "file": "suites/moltar/fixtures/interpreted.ts", + "defaults": { + "tier": 2, + "family": "moltar-parse-safe", + "implementation": "effect", + "operation": "decode-unknown-sync", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-valid", + "export": "parseValid", + "scenario": "moltar-parse-safe-valid", + "path": "valid" + }, + { + "name": "effect-extra-valid", + "export": "parseExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-invalid", + "export": "parseInvalid", + "scenario": "moltar-parse-safe-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/jit.ts", + "defaults": { + "tier": 2, + "family": "moltar-parse-safe", + "implementation": "effect-jit", + "operation": "decode-unknown-sync", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-jit-valid", + "export": "parseValid", + "scenario": "moltar-parse-safe-valid", + "path": "valid" + }, + { + "name": "effect-jit-extra-valid", + "export": "parseExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-jit-invalid", + "export": "parseInvalid", + "scenario": "moltar-parse-safe-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/aot.ts", + "defaults": { + "tier": 2, + "family": "moltar-parse-safe", + "implementation": "effect-aot", + "operation": "decode-unknown-sync", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-aot-valid", + "export": "parseValid", + "scenario": "moltar-parse-safe-valid", + "path": "valid" + }, + { + "name": "effect-aot-extra-valid", + "export": "parseExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-aot-invalid", + "export": "parseInvalid", + "scenario": "moltar-parse-safe-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/valibot.ts", + "defaults": { + "tier": 2, + "family": "moltar-parse-safe", + "implementation": "valibot", + "operation": "parse", + "astTags": [], + "size": "moltar-object" + }, + "cases": [ + { + "name": "valibot-valid", + "export": "parseValid", + "scenario": "moltar-parse-safe-valid", + "path": "valid" + }, + { + "name": "valibot-extra-valid", + "export": "parseExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "path": "extra-valid" + }, + { + "name": "valibot-invalid", + "export": "parseInvalid", + "scenario": "moltar-parse-safe-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/zod.ts", + "defaults": { + "tier": 2, + "family": "moltar-parse-safe", + "astTags": [], + "size": "moltar-object" + }, + "cases": [ + { + "name": "zod-valid", + "export": "parseValid", + "scenario": "moltar-parse-safe-valid", + "implementation": "zod4", + "operation": "parse", + "path": "valid" + }, + { + "name": "zod-extra-valid", + "export": "parseExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "implementation": "zod4", + "operation": "parse", + "path": "extra-valid" + }, + { + "name": "zod-invalid", + "export": "parseInvalid", + "scenario": "moltar-parse-safe-invalid", + "implementation": "zod4", + "operation": "parse", + "path": "invalid" + }, + { + "name": "zod-jitless-valid", + "export": "parseJitlessValid", + "scenario": "moltar-parse-safe-valid", + "implementation": "zod4-jitless", + "operation": "parse-jitless", + "path": "valid" + }, + { + "name": "zod-jitless-extra-valid", + "export": "parseJitlessExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "implementation": "zod4-jitless", + "operation": "parse-jitless", + "path": "extra-valid" + }, + { + "name": "zod-jitless-invalid", + "export": "parseJitlessInvalid", + "scenario": "moltar-parse-safe-invalid", + "implementation": "zod4-jitless", + "operation": "parse-jitless", + "path": "invalid" + }, + { + "name": "zod-compiled-valid", + "export": "parseCompiledValid", + "scenario": "moltar-parse-safe-valid", + "implementation": "zod4-compiled", + "operation": "parse-compiled", + "path": "valid" + }, + { + "name": "zod-compiled-extra-valid", + "export": "parseCompiledExtraValid", + "scenario": "moltar-parse-safe-extra-valid", + "implementation": "zod4-compiled", + "operation": "parse-compiled", + "path": "extra-valid" + }, + { + "name": "zod-compiled-invalid", + "export": "parseCompiledInvalid", + "scenario": "moltar-parse-safe-invalid", + "implementation": "zod4-compiled", + "operation": "parse-compiled", + "path": "invalid" + } + ] + } + ] + }, + { + "name": "moltar-assert-loose", + "fixtures": [ + { + "file": "suites/moltar/fixtures/interpreted.ts", + "defaults": { + "tier": 2, + "family": "moltar-assert-loose", + "implementation": "effect", + "operation": "is", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-valid", + "export": "isValid", + "scenario": "moltar-assert-loose-valid", + "path": "valid" + }, + { + "name": "effect-extra-valid", + "export": "isExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-invalid", + "export": "isInvalid", + "scenario": "moltar-assert-loose-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/jit.ts", + "defaults": { + "tier": 2, + "family": "moltar-assert-loose", + "implementation": "effect-jit", + "operation": "is", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-jit-valid", + "export": "isValid", + "scenario": "moltar-assert-loose-valid", + "path": "valid" + }, + { + "name": "effect-jit-extra-valid", + "export": "isExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-jit-invalid", + "export": "isInvalid", + "scenario": "moltar-assert-loose-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/aot.ts", + "defaults": { + "tier": 2, + "family": "moltar-assert-loose", + "implementation": "effect-aot", + "operation": "is", + "astTags": ["Objects", "String", "Number", "Boolean"], + "size": "moltar-object" + }, + "cases": [ + { + "name": "effect-aot-valid", + "export": "isValid", + "scenario": "moltar-assert-loose-valid", + "path": "valid" + }, + { + "name": "effect-aot-extra-valid", + "export": "isExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "path": "extra-valid" + }, + { + "name": "effect-aot-invalid", + "export": "isInvalid", + "scenario": "moltar-assert-loose-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/valibot.ts", + "defaults": { + "tier": 2, + "family": "moltar-assert-loose", + "implementation": "valibot", + "operation": "is", + "astTags": [], + "size": "moltar-object" + }, + "cases": [ + { + "name": "valibot-valid", + "export": "isValid", + "scenario": "moltar-assert-loose-valid", + "path": "valid" + }, + { + "name": "valibot-extra-valid", + "export": "isExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "path": "extra-valid" + }, + { + "name": "valibot-invalid", + "export": "isInvalid", + "scenario": "moltar-assert-loose-invalid", + "path": "invalid" + } + ] + }, + { + "file": "suites/moltar/fixtures/zod.ts", + "defaults": { + "tier": 2, + "family": "moltar-assert-loose", + "astTags": [], + "size": "moltar-object" + }, + "cases": [ + { + "name": "zod-parse-valid", + "export": "assertParseValid", + "scenario": "moltar-assert-loose-valid", + "implementation": "zod4", + "operation": "parse-and-assert", + "path": "valid" + }, + { + "name": "zod-parse-extra-valid", + "export": "assertParseExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "implementation": "zod4", + "operation": "parse-and-assert", + "path": "extra-valid" + }, + { + "name": "zod-jitless-validate-valid", + "export": "isJitlessValid", + "scenario": "moltar-assert-loose-valid", + "implementation": "zod4-jitless", + "operation": "validate-jitless", + "path": "valid" + }, + { + "name": "zod-jitless-validate-extra-valid", + "export": "isJitlessExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "implementation": "zod4-jitless", + "operation": "validate-jitless", + "path": "extra-valid" + }, + { + "name": "zod-jitless-validate-invalid", + "export": "isJitlessInvalid", + "scenario": "moltar-assert-loose-invalid", + "implementation": "zod4-jitless", + "operation": "validate-jitless", + "path": "invalid" + }, + { + "name": "zod-validate-valid", + "export": "isValid", + "scenario": "moltar-assert-loose-valid", + "implementation": "zod4-validate", + "operation": "validate", + "path": "valid" + }, + { + "name": "zod-validate-extra-valid", + "export": "isExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "implementation": "zod4-validate", + "operation": "validate", + "path": "extra-valid" + }, + { + "name": "zod-validate-invalid", + "export": "isInvalid", + "scenario": "moltar-assert-loose-invalid", + "implementation": "zod4-validate", + "operation": "validate", + "path": "invalid" + }, + { + "name": "zod-compiled-valid", + "export": "isCompiledValid", + "scenario": "moltar-assert-loose-valid", + "implementation": "zod4-compiled", + "operation": "validate-compiled", + "path": "valid" + }, + { + "name": "zod-compiled-extra-valid", + "export": "isCompiledExtraValid", + "scenario": "moltar-assert-loose-extra-valid", + "implementation": "zod4-compiled", + "operation": "validate-compiled", + "path": "extra-valid" + }, + { + "name": "zod-compiled-invalid", + "export": "isCompiledInvalid", + "scenario": "moltar-assert-loose-invalid", + "implementation": "zod4-compiled", + "operation": "validate-compiled", + "path": "invalid" + } + ] + } + ] + }, + { + "name": "compiler-rebuild", + "fixtures": [ + { + "file": "suites/compiler-rebuild/fixtures/interpreted.ts", + "defaults": { + "tier": 1, + "family": "interpreted", + "implementation": "effect", + "astTags": [ + "Objects", + "String", + "Number", + "Boolean" + ], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "interpreted-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "interpreted-parseExtra", + "export": "parseExtra", + "scenario": "compiler-rebuild-parseExtra", + "size": 4 + }, + { + "name": "interpreted-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "interpreted-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "interpreted-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "interpreted-encode", + "export": "encode", + "scenario": "compiler-rebuild-encode", + "operation": "encode" + }, + { + "name": "interpreted-strict", + "export": "strict", + "scenario": "compiler-rebuild-strict" + }, + { + "name": "interpreted-nested", + "export": "nested", + "scenario": "compiler-rebuild-nested", + "astTags": [ + "Objects", + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 2 + }, + { + "name": "interpreted-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "interpreted-arrayInvalid", + "export": "arrayInvalid", + "scenario": "compiler-rebuild-arrayInvalid", + "path": "invalid", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 33 + }, + { + "name": "interpreted-tuple", + "export": "tuple", + "scenario": "compiler-rebuild-tuple", + "astTags": [ + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 34 + }, + { + "name": "interpreted-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "interpreted-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "astTags": [ + "Objects", + "String" + ], + "size": 32 + }, + { + "name": "interpreted-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + }, + { + "name": "interpreted-oneOf", + "export": "oneOf", + "scenario": "compiler-rebuild-oneOf", + "astTags": [ + "Union", + "Objects", + "String", + "Number" + ], + "size": 2 + }, + { + "name": "interpreted-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "astTags": [ + "Objects", + "String", + "Number" + ], + "size": 32 + }, + { + "name": "interpreted-transformInvalid", + "export": "transformInvalid", + "scenario": "compiler-rebuild-transformInvalid", + "path": "invalid", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "interpreted-rootTransform", + "export": "rootTransform", + "scenario": "compiler-rebuild-rootTransform", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "interpreted-middleware", + "export": "middleware", + "scenario": "compiler-rebuild-middleware" + }, + { + "name": "interpreted-recursive", + "export": "recursive", + "scenario": "compiler-rebuild-recursive", + "astTags": [ + "Objects", + "Arrays", + "Suspend", + "Number" + ], + "size": 31 + }, + { + "name": "interpreted-suspendRoot", + "export": "suspendRoot", + "scenario": "compiler-rebuild-suspendRoot", + "astTags": [ + "Suspend", + "Objects", + "Number" + ], + "size": 1 + }, + { + "name": "interpreted-declaration", + "export": "declaration", + "scenario": "compiler-rebuild-declaration", + "astTags": [ + "Declaration", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "interpreted-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "astTags": [ + "Objects", + "Number" + ], + "size": 32 + }, + { + "name": "interpreted-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "interpreted-makeUnion", + "export": "makeUnion", + "scenario": "compiler-rebuild-makeUnion", + "operation": "make", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + } + ] + }, + { + "file": "suites/compiler-rebuild/fixtures/jit.ts", + "defaults": { + "tier": 1, + "family": "jit", + "implementation": "effect-jit", + "astTags": [ + "Objects", + "String", + "Number", + "Boolean" + ], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "jit-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "jit-parseExtra", + "export": "parseExtra", + "scenario": "compiler-rebuild-parseExtra", + "size": 4 + }, + { + "name": "jit-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "jit-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "jit-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "jit-encode", + "export": "encode", + "scenario": "compiler-rebuild-encode", + "operation": "encode" + }, + { + "name": "jit-strict", + "export": "strict", + "scenario": "compiler-rebuild-strict" + }, + { + "name": "jit-nested", + "export": "nested", + "scenario": "compiler-rebuild-nested", + "astTags": [ + "Objects", + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 2 + }, + { + "name": "jit-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "jit-arrayInvalid", + "export": "arrayInvalid", + "scenario": "compiler-rebuild-arrayInvalid", + "path": "invalid", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 33 + }, + { + "name": "jit-tuple", + "export": "tuple", + "scenario": "compiler-rebuild-tuple", + "astTags": [ + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 34 + }, + { + "name": "jit-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "jit-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "astTags": [ + "Objects", + "String" + ], + "size": 32 + }, + { + "name": "jit-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + }, + { + "name": "jit-oneOf", + "export": "oneOf", + "scenario": "compiler-rebuild-oneOf", + "astTags": [ + "Union", + "Objects", + "String", + "Number" + ], + "size": 2 + }, + { + "name": "jit-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "astTags": [ + "Objects", + "String", + "Number" + ], + "size": 32 + }, + { + "name": "jit-transformInvalid", + "export": "transformInvalid", + "scenario": "compiler-rebuild-transformInvalid", + "path": "invalid", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "jit-rootTransform", + "export": "rootTransform", + "scenario": "compiler-rebuild-rootTransform", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "jit-middleware", + "export": "middleware", + "scenario": "compiler-rebuild-middleware" + }, + { + "name": "jit-recursive", + "export": "recursive", + "scenario": "compiler-rebuild-recursive", + "astTags": [ + "Objects", + "Arrays", + "Suspend", + "Number" + ], + "size": 31 + }, + { + "name": "jit-suspendRoot", + "export": "suspendRoot", + "scenario": "compiler-rebuild-suspendRoot", + "astTags": [ + "Suspend", + "Objects", + "Number" + ], + "size": 1 + }, + { + "name": "jit-declaration", + "export": "declaration", + "scenario": "compiler-rebuild-declaration", + "astTags": [ + "Declaration", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "jit-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "astTags": [ + "Objects", + "Number" + ], + "size": 32 + }, + { + "name": "jit-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "jit-makeUnion", + "export": "makeUnion", + "scenario": "compiler-rebuild-makeUnion", + "operation": "make", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + } + ] + }, + { + "file": "suites/compiler-rebuild/fixtures/aot.ts", + "defaults": { + "tier": 1, + "family": "aot", + "implementation": "effect-aot", + "astTags": [ + "Objects", + "String", + "Number", + "Boolean" + ], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "aot-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "aot-parseExtra", + "export": "parseExtra", + "scenario": "compiler-rebuild-parseExtra", + "size": 4 + }, + { + "name": "aot-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "aot-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "aot-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "aot-encode", + "export": "encode", + "scenario": "compiler-rebuild-encode", + "operation": "encode" + }, + { + "name": "aot-strict", + "export": "strict", + "scenario": "compiler-rebuild-strict" + }, + { + "name": "aot-nested", + "export": "nested", + "scenario": "compiler-rebuild-nested", + "astTags": [ + "Objects", + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 2 + }, + { + "name": "aot-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "aot-arrayInvalid", + "export": "arrayInvalid", + "scenario": "compiler-rebuild-arrayInvalid", + "path": "invalid", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 33 + }, + { + "name": "aot-tuple", + "export": "tuple", + "scenario": "compiler-rebuild-tuple", + "astTags": [ + "Arrays", + "String", + "Number", + "Boolean" + ], + "size": 34 + }, + { + "name": "aot-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "aot-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "astTags": [ + "Objects", + "String" + ], + "size": 32 + }, + { + "name": "aot-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + }, + { + "name": "aot-oneOf", + "export": "oneOf", + "scenario": "compiler-rebuild-oneOf", + "astTags": [ + "Union", + "Objects", + "String", + "Number" + ], + "size": 2 + }, + { + "name": "aot-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "astTags": [ + "Objects", + "String", + "Number" + ], + "size": 32 + }, + { + "name": "aot-transformInvalid", + "export": "transformInvalid", + "scenario": "compiler-rebuild-transformInvalid", + "path": "invalid", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "aot-rootTransform", + "export": "rootTransform", + "scenario": "compiler-rebuild-rootTransform", + "astTags": [ + "String", + "Number" + ], + "size": 1 + }, + { + "name": "aot-middleware", + "export": "middleware", + "scenario": "compiler-rebuild-middleware" + }, + { + "name": "aot-recursive", + "export": "recursive", + "scenario": "compiler-rebuild-recursive", + "astTags": [ + "Objects", + "Arrays", + "Suspend", + "Number" + ], + "size": 31 + }, + { + "name": "aot-suspendRoot", + "export": "suspendRoot", + "scenario": "compiler-rebuild-suspendRoot", + "astTags": [ + "Suspend", + "Objects", + "Number" + ], + "size": 1 + }, + { + "name": "aot-declaration", + "export": "declaration", + "scenario": "compiler-rebuild-declaration", + "astTags": [ + "Declaration", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "aot-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "astTags": [ + "Objects", + "Number" + ], + "size": 32 + }, + { + "name": "aot-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "astTags": [ + "Arrays", + "Objects", + "String", + "Number", + "Boolean" + ], + "size": 32 + }, + { + "name": "aot-makeUnion", + "export": "makeUnion", + "scenario": "compiler-rebuild-makeUnion", + "operation": "make", + "astTags": [ + "Union", + "Objects", + "Literal", + "Number" + ], + "size": 8 + } + ] + }, + { + "file": "suites/compiler-rebuild/fixtures/valibot.ts", + "defaults": { + "tier": 1, + "family": "valibot", + "implementation": "valibot", + "astTags": [], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "valibot-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "valibot-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "valibot-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "valibot-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "valibot-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "size": 32 + }, + { + "name": "valibot-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "valibot-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "size": 32 + }, + { + "name": "valibot-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "size": 8 + }, + { + "name": "valibot-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "size": 32 + }, + { + "name": "valibot-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "size": 32 + }, + { + "name": "valibot-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "size": 32 + } + ] + }, + { + "file": "suites/compiler-rebuild/fixtures/zod-jitless.ts", + "defaults": { + "tier": 1, + "family": "zod-jitless", + "implementation": "zod4-jitless", + "astTags": [], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "zod-jitless-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "zod-jitless-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "zod-jitless-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "zod-jitless-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "zod-jitless-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "size": 32 + }, + { + "name": "zod-jitless-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "zod-jitless-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "size": 32 + }, + { + "name": "zod-jitless-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "size": 8 + }, + { + "name": "zod-jitless-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "size": 32 + }, + { + "name": "zod-jitless-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "size": 32 + }, + { + "name": "zod-jitless-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "size": 32 + } + ] + }, + { + "file": "suites/compiler-rebuild/fixtures/zod-compiled.ts", + "defaults": { + "tier": 1, + "family": "zod-compiled", + "implementation": "zod4-compiled", + "astTags": [], + "operation": "decode", + "path": "valid", + "size": 3 + }, + "cases": [ + { + "name": "zod-compiled-parseValid", + "export": "parseValid", + "scenario": "compiler-rebuild-parseValid" + }, + { + "name": "zod-compiled-parseExtra", + "export": "parseExtra", + "scenario": "compiler-rebuild-parseExtra", + "size": 4 + }, + { + "name": "zod-compiled-parseInvalid", + "export": "parseInvalid", + "scenario": "compiler-rebuild-parseInvalid", + "path": "invalid" + }, + { + "name": "zod-compiled-isValid", + "export": "isValid", + "scenario": "compiler-rebuild-isValid", + "operation": "is" + }, + { + "name": "zod-compiled-isInvalid", + "export": "isInvalid", + "scenario": "compiler-rebuild-isInvalid", + "operation": "is", + "path": "invalid" + }, + { + "name": "zod-compiled-encode", + "export": "encode", + "scenario": "compiler-rebuild-encode", + "operation": "encode" + }, + { + "name": "zod-compiled-strict", + "export": "strict", + "scenario": "compiler-rebuild-strict" + }, + { + "name": "zod-compiled-nested", + "export": "nested", + "scenario": "compiler-rebuild-nested", + "size": 2 + }, + { + "name": "zod-compiled-array", + "export": "array", + "scenario": "compiler-rebuild-array", + "size": 32 + }, + { + "name": "zod-compiled-arrayInvalid", + "export": "arrayInvalid", + "scenario": "compiler-rebuild-arrayInvalid", + "path": "invalid", + "size": 33 + }, + { + "name": "zod-compiled-record", + "export": "record", + "scenario": "compiler-rebuild-record", + "size": 32 + }, + { + "name": "zod-compiled-recordTransformedKeys", + "export": "recordTransformedKeys", + "scenario": "compiler-rebuild-recordTransformedKeys", + "size": 32 + }, + { + "name": "zod-compiled-union", + "export": "union", + "scenario": "compiler-rebuild-union", + "size": 8 + }, + { + "name": "zod-compiled-transform", + "export": "transform", + "scenario": "compiler-rebuild-transform", + "size": 32 + }, + { + "name": "zod-compiled-transformInvalid", + "export": "transformInvalid", + "scenario": "compiler-rebuild-transformInvalid", + "path": "invalid", + "size": 1 + }, + { + "name": "zod-compiled-declaration", + "export": "declaration", + "scenario": "compiler-rebuild-declaration", + "size": 32 + }, + { + "name": "zod-compiled-makeStruct", + "export": "makeStruct", + "scenario": "compiler-rebuild-makeStruct", + "operation": "make", + "size": 32 + }, + { + "name": "zod-compiled-makeArray", + "export": "makeArray", + "scenario": "compiler-rebuild-makeArray", + "operation": "make", + "size": 32 + }, + { + "name": "zod-compiled-makeUnion", + "export": "makeUnion", + "scenario": "compiler-rebuild-makeUnion", + "operation": "make", + "size": 8 + } + ] + } + ] } ] } diff --git a/repos/effect/packages/effect/runtimeperf/run.mts b/repos/effect/packages/effect/runtimeperf/run.mts index b97665f60b..3cac08601d 100644 --- a/repos/effect/packages/effect/runtimeperf/run.mts +++ b/repos/effect/packages/effect/runtimeperf/run.mts @@ -18,7 +18,11 @@ import { relativeToRepo, reportPath, resolveDefaults, + runPath, + scenarioBatchSize, selectFixtures, + statsPath, + utilsPath, workerPath, writeJson } from "./utils.mts" @@ -31,7 +35,7 @@ Options: --warmup-time --tier <0-3> --family - --implementation + --implementation ` const rotate = (items, offset) => items.map((_, index) => items[(index + offset) % items.length]) @@ -52,12 +56,12 @@ const main = () => { for (const [scenario, group] of groups) { const calibrations = new Map(group.map((fixture) => [fixture, calibrateFixture(fixture, defaults)])) + const batchSize = scenarioBatchSize(group, calibrations) const byTarget = new Map(group.map((fixture) => [fixture.target, []])) for (let round = 0; round < defaults.rounds; round++) { for (const fixture of rotate(group, round % group.length)) { - const calibration = calibrations.get(fixture) - const measurement = measureFixture(fixture, defaults, calibration.batchSize) + const measurement = measureFixture(fixture, defaults, batchSize) byTarget.get(fixture.target).push(measurement) executionOrder.push({ scenario, round: round + 1, target: fixture.target }) } @@ -68,7 +72,7 @@ const main = () => { const measurements = byTarget.get(fixture.target) results.push({ fixture, - batchSize: calibration.batchSize, + batchSize, calibration, measurements, aggregate: aggregateMeasurements(measurements) @@ -81,7 +85,7 @@ const main = () => { const effect = group.find((result) => result.fixture.implementation === "effect") if (!effect) continue for (const candidate of group) { - if (candidate === effect) continue + if (candidate.fixture.implementation === "effect") continue crossLibrary.push({ scenario, implementation: candidate.fixture.implementation, @@ -116,16 +120,14 @@ const main = () => { cpu: os.cpus()[0]?.model ?? "unknown" }, libraries: libraryVersions(), - crossLibraryDecodeApis: { - effect: "SchemaParser.decodeUnknownExit (SchemaIssue)", - valibot: "safeParser", - zod4: "safeParse ({ jitless: true })" - }, artifactMode: "repository", git: currentGitState(), coverage: coverageSummary(selected), hashes: { config: hashFile(configPath), + run: hashFile(runPath), + stats: hashFile(statsPath), + utils: hashFile(utilsPath), worker: hashFile(workerPath), fixtures: Object.fromEntries( [...new Set(selected.map((fixture) => fixture.fixturePath))] @@ -140,11 +142,12 @@ const main = () => { writeJson(path, report) const comparisons = new Map(crossLibrary.map((item) => [`${item.scenario}/${item.implementation}`, item])) printTable( - ["scenario", "implementation", "ns/op", "mad", "vs Effect"], + ["scenario", "family", "implementation", "ns/op", "mad", "vs Effect"], results.map((result) => { const comparison = comparisons.get(`${result.fixture.scenario}/${result.fixture.implementation}`) return [ result.fixture.scenario, + result.fixture.family, result.fixture.implementation, formatNs(result.aggregate.median), formatNs(result.aggregate.mad), diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts b/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts index 374da26c4d..5418e34f38 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts @@ -376,6 +376,33 @@ export const checkPass100 = () => { } } +export const arrayCheckPass100 = () => { + const property = FastCheck.property(FastCheck.array(FastCheck.integer(), { maxLength: 50 }), () => true) + return { + run: () => FastCheck.check(property, { numRuns: 100, seed }), + validate: (result: FastCheck.RunDetails<[Array]>) => { + assert.equal(result.failed, false) + assert.equal(result.numRuns, 100) + assert.equal(result.numSkips, 0) + } + } +} + +export const arrayCheckFalsifyAndShrink = () => { + const arbitrary = FastCheck.array(FastCheck.constantFrom(8, 27, 0, 1), { maxLength: 4 }) + const property = FastCheck.property( + arbitrary, + (values) => !(values.includes(27) && values.indexOf(0) > values.indexOf(27)) + ) + return { + run: () => FastCheck.check(property, { examples: [[[8, 27, 0, 1]]], numRuns: 1, seed }), + validate: (result: FastCheck.RunDetails<[Array]>) => { + assert.equal(result.failed, true) + assert.deepEqual(result.counterexample, [[27, 0]]) + } + } +} + export const testSchemaVerifyGeneration100 = () => ({ run: () => { const schema = Schema.Int diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts b/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts index 79d46504b2..b5a9152b22 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts @@ -1,8 +1,8 @@ +import * as Arbitrary from "effect/Arbitrary" import * as Effect from "effect/Effect" import * as Result from "effect/Result" import * as Schema from "effect/Schema" import * as TestSchema from "effect/testing/TestSchema" -import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import assert from "node:assert/strict" import { makeBigDecimalSchema, @@ -391,6 +391,34 @@ export const checkPass100 = () => { } } +export const arrayCheckPass100 = () => { + const arbitrary = Arbitrary.schema(Schema.Array(Schema.Int)) + const program = Arbitrary.checkEffect(arbitrary, () => true, { runs: 100, seed, size: 50 }) + return { + run: () => Effect.runSync(program), + validate: (result: Arbitrary.CheckResult, never>) => { + assert.deepEqual(result, { _tag: "Passed", runs: 100, discards: 0 }) + } + } +} + +export const arrayCheckFalsifyAndShrink = () => { + const arbitrary = Arbitrary.schema(Schema.Array(Schema.Literals([8, 27, 0, 1]))) + const property = (values: ReadonlyArray) => !(values.includes(27) && values.indexOf(0) > values.indexOf(27)) + const program = Arbitrary.checkEffect(arbitrary, property, { runs: 1, seed: 1967, size: 4 }) + return { + run: () => Effect.runSync(program), + validate: (result: Arbitrary.CheckResult, never>) => { + assert.equal(result._tag, "Falsified") + if (result._tag !== "Falsified") return + assert.deepEqual(result.initialInput, [8, 27, 0, 1]) + // Both revisions must preserve the failure; the regression test checks the new, smaller result. + assert.equal(property(result.shrunkInput), false) + assert.ok(result.shrunkInput.length <= 3) + } + } +} + export const testSchemaVerifyGeneration100 = () => { const asserts = new TestSchema.Asserts(Schema.Int) return { diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md new file mode 100644 index 0000000000..25997e2c25 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md @@ -0,0 +1,81 @@ +# Compiler rebuild comparison + +The `compiler-rebuild` suite measures 25 public SchemaParser operations with the +interpreter, selective JIT and generated AOT modules. Nineteen cases also run +against `z.compile(schema, { strict: true })`; eleven representative cases run +against Valibot and Zod with `{ jitless: true }`. The fixture families name the +execution mode. Cases +cover simple and nested Structs, Arrays, tuples, Records, transformed Record +keys, anyOf/oneOf Unions, transformations, middleware, recursive and suspended +schemas, Declarations, construction, +and successful and failing validation. + +The Effect tuple has a trailing element after its rest element, Zod compilation +does not support recursive schemas or `z.xor`, and Zod has no equivalent +decoding middleware. Those four cases intentionally have no Zod fixture. Zod +compilation only accelerates forward parsing, so the encode case measures its +documented runtime fallback. Zod parsing stands in for Effect construction +because Zod has no separate constructor operation. Valibot parsing with defaults +is used for the same comparison. + +```sh +pnpm runtimeperf-compare compiler-rebuild --base schema-compiler +pnpm runtimeperf-compare compiler-rebuild --base main --family interpreted +pnpm runtimeperf compiler-rebuild +``` + +The first command compares the current working tree with the archived compiler +branch. The second isolates changes to interpreted parsing against main. Schema +and parser construction are outside the steady-state measurements. AOT fixtures +generate their version-specific module in a separate process before loading it. + +The standard paired harness records five alternating rounds, validates every +fixture before and after measurement, uses a common batch within each pair, and +reports bootstrap confidence intervals. Cross-mode rankings are descriptive; +the paired base/head comparison is the regression evidence. + +## Retained heap and first use + +For a current cross-library comparison of retained heap, V8 code memory, peak +RSS and preparation CPU, run: + +```sh +node packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts \ + tmp/schema-compiler-resources.json 5 100,500 +node packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts \ + tmp/schema-compiler-resources.json +``` + +Each sample runs in a fresh process. The report calculates median per-schema +slopes between 100 and 500 distinct schemas, which removes fixed module and +lazy initialization costs. Fixture inputs are released before retained-memory +measurement. Preparation CPU includes adapter creation and first use; AOT also +includes loading and installing the generated module. AOT source generation is +reported separately because it runs at build time. Hot synchronous CPU is +already represented by the normal runtime measurements. + +The older Effect-only probe below remains available for comparisons with its +existing archived results. + +The standalone cost probe accepts a checkout root, mode, operation, shape and +schema count. Run several fresh processes per combination and alternate the +checkout order. Supported modes are `interpreted`, `jit` and `aot`; operations +are `decode`, `invalid`, `is` and `make`; shapes are `struct`, `array`, `transform` +and `default`. + +```sh +node --expose-gc packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts heap "$PWD" jit decode struct 500 +node --expose-gc packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts cold "$PWD" jit decode struct 500 +``` + +Heap measurements exclude schema construction and module imports, and retain +both schema objects and public parser functions. They include operation setup +and first use. Forced GC removes transient parse output and issues. The AOT +generator runs in a separate process, so its retained heap is not attributed to +the runtime application. + +First-use measurements include constructing a fresh schema, installing its +compiler when requested, creating a public parser and calling it. They exclude +module imports and AOT source generation. Repeated shapes can benefit from V8's +source cache, so this is not process startup latency. Treat these timing probes +as exploratory, separately from the paired throughput results. diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts new file mode 100644 index 0000000000..f7c5ef99ee --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts @@ -0,0 +1,127 @@ +import assert from "node:assert/strict" +import { execFileSync } from "node:child_process" +import { mkdtempSync, rmSync, writeFileSync } from "node:fs" +import { join } from "node:path" +import { fileURLToPath, pathToFileURL } from "node:url" + +const [command, root, mode, operation, shape = "struct", countString = "500"] = process.argv.slice(2) +const count = Number(countString) +const includes = (values: ReadonlyArray, value: string | undefined) => + value !== undefined && values.includes(value) +if (command !== "heap" && command !== "cold" && command !== "generate") { + throw new Error("command must be heap, cold or generate") +} +if (root === undefined) throw new Error("root is required") +if (!includes(["interpreted", "jit", "aot"], mode)) { + throw new Error("mode must be interpreted, jit or aot") +} +if (!includes(["decode", "invalid", "is", "make"], operation)) { + throw new Error("operation must be decode, invalid, is or make") +} +if (!includes(["struct", "array", "transform", "default"], shape)) { + throw new Error("shape must be struct, array, transform or default") +} +if (!Number.isSafeInteger(count) || count <= 0) throw new Error("count must be a positive integer") +const load = (path: string) => import(pathToFileURL(join(root, "packages/effect/src", path + ".ts")).href) +const Schema = await load("Schema") +const Parser = await load("SchemaParser") +const AST = await load("SchemaAST") +const Effect = await load("Effect") +const create = () => { + const struct = Schema.Struct({ name: Schema.String, age: Schema.Number, active: Schema.Boolean }) + return shape === "array" ? Schema.Array(struct) + : shape === "transform" ? Schema.Struct({ value: Schema.NumberFromString }) + : shape === "default" ? Schema.Struct({ value: Schema.Number.pipe(Schema.withConstructorDefault(Effect.succeed(1))) }) + : struct +} +const valid = shape === "array" ? Array.from({ length: 32 }, () => ({ name: "Ada", age: 37, active: true })) + : shape === "transform" ? { value: "1" } + : shape === "default" ? { value: 1 } + : { name: "Ada", age: 37, active: true } +const typeValid = shape === "transform" ? { value: 1 } : valid +const input = operation === "invalid" ? { name: "Ada", age: "bad", active: true } + : operation === "make" && shape === "default" ? {} + : operation === "make" || operation === "is" ? typeValid + : valid +const expected = shape === "transform" || shape === "default" ? { value: 1 } : valid + +if (command === "generate") { + const AOT = await load("schema/SchemaAOTCompiler") + const schema = create() + const targetOperation = operation === "make" ? "make" : operation === "is" ? "is" : "decode" + const ast = targetOperation === "decode" ? schema.ast : AST.toType(schema.ast) + writeFileSync(process.argv[8], AOT.compile([{ ast, operations: [targetOperation] }])) +} else { + for (let i = 0; i < 5; i++) globalThis.gc?.() + const beforeImport = process.memoryUsage().heapUsed + let enable: ((ast: unknown) => void) | undefined + let install: ((asts: Array) => void) | undefined + let directory: string | undefined + if (mode === "jit") { + enable = (await load("schema/SchemaJITCompiler")).enable + } else if (mode === "aot") { + directory = mkdtempSync(join(root, "packages/effect/.compiler-memory-")) + const generated = join(directory, "generated.mjs") + execFileSync(process.execPath, [fileURLToPath(import.meta.url), "generate", root, mode, operation, shape, countString, generated]) + install = (await import(pathToFileURL(generated).href)).install + } + for (let i = 0; i < 5; i++) globalThis.gc?.() + const moduleBytes = process.memoryUsage().heapUsed - beforeImport + const prepare = (schema: any) => { + const ast = operation === "make" || operation === "is" ? AST.toType(schema.ast) : schema.ast + if (enable) enable(ast) + if (install) install([ast]) + return operation === "make" ? Parser.make(schema) + : operation === "is" ? Parser.is(schema) + : Parser.decodeUnknownSync(schema) + } + const run = (parse: (input: unknown) => unknown) => { + try { + return parse(input) + } catch (error) { + if (operation !== "invalid") throw error + return error + } + } + const validate = (value: unknown) => { + if (operation === "invalid") { + assert.ok(value instanceof Error) + assert.equal(value.message, "Schema validation failed") + assert.equal("cause" in value, true) + } + else if (operation === "is") assert.equal(value, true) + else assert.deepEqual(value, expected) + } + try { + for (let i = 0; i < 20; i++) validate(run(prepare(create()))) + if (command === "heap") { + assert.equal(typeof globalThis.gc, "function") + const schemas = Array.from({ length: count }, create) + for (let i = 0; i < 5; i++) globalThis.gc!() + const before = process.memoryUsage().heapUsed + const parsers = schemas.map((schema) => { + const parse = prepare(schema) + validate(run(parse)) + return parse + }) + for (let i = 0; i < 5; i++) globalThis.gc!() + const after = process.memoryUsage().heapUsed + // Keep schemas and adapters alive across the measurement. + assert.equal(parsers.length, count) + assert.equal(schemas.length, count) + process.stdout.write(JSON.stringify({ mode, operation, shape, count, moduleBytes, bytesPerSchema: (after - before) / count })) + } else if (command === "cold") { + const samples = [] + for (let round = 0; round < 7; round++) { + let value: unknown + const start = process.hrtime.bigint() + for (let i = 0; i < count; i++) value = run(prepare(create())) + samples.push(Number(process.hrtime.bigint() - start) / count) + validate(value) + } + process.stdout.write(JSON.stringify({ mode, operation, shape, count, nsPerSchema: samples })) + } + } finally { + if (directory) rmSync(directory, { recursive: true, force: true }) + } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts new file mode 100644 index 0000000000..cb95c7d3fd --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts @@ -0,0 +1,39 @@ +import { execFileSync } from "node:child_process" +import { mkdtempSync, rmSync } from "node:fs" +import { join } from "node:path" +import { fileURLToPath, pathToFileURL } from "node:url" +import { fixture, roots } from "./cases.ts" +const directory = mkdtempSync(fileURLToPath(new URL("./.aot-", import.meta.url))) +try { + const file = join(directory, "generated.mjs") + execFileSync(process.execPath, [fileURLToPath(new URL("./generate.mts", import.meta.url)), file]) + const generated = await import(pathToFileURL(file).href) + generated.install(roots) +} finally { + rmSync(directory, { recursive: true, force: true }) +} +export const parseValid = () => fixture("parseValid") +export const parseExtra = () => fixture("parseExtra") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const encode = () => fixture("encode") +export const strict = () => fixture("strict") +export const nested = () => fixture("nested") +export const array = () => fixture("array") +export const arrayInvalid = () => fixture("arrayInvalid") +export const tuple = () => fixture("tuple") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const oneOf = () => fixture("oneOf") +export const transform = () => fixture("transform") +export const transformInvalid = () => fixture("transformInvalid") +export const rootTransform = () => fixture("rootTransform") +export const middleware = () => fixture("middleware") +export const recursive = () => fixture("recursive") +export const suspendRoot = () => fixture("suspendRoot") +export const declaration = () => fixture("declaration") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") +export const makeUnion = () => fixture("makeUnion") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts new file mode 100644 index 0000000000..16e22ae4fc --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts @@ -0,0 +1,143 @@ +import * as Effect from "effect/Effect" +import * as Schema from "effect/Schema" +import * as SchemaAST from "effect/SchemaAST" +import * as SchemaParser from "effect/SchemaParser" +import * as SchemaTransformation from "effect/SchemaTransformation" +import assert from "node:assert/strict" + +export const person = () => Schema.Struct({ name: Schema.String, age: Schema.Number, active: Schema.Boolean }) +export const input = { name: "Ada", age: 37, active: true } +const small = person() +const nested = Schema.Struct({ user: person(), tags: Schema.Array(Schema.String) }) +const array = Schema.Array(person()) +const arrayInput = Array.from({ length: 32 }, () => ({ ...input })) +const tuple = Schema.TupleWithRest(Schema.Tuple([Schema.String]), [Schema.Number, Schema.Boolean]) +const tupleInput = ["head", ...Array.from({ length: 32 }, (_, i) => i), true] +const record = Schema.Record(Schema.String, person()) +const recordInput = Object.fromEntries(arrayInput.map((value, i) => [String(i), value])) +const transformedKeyRecord = Schema.Record( + Schema.String.pipe(Schema.decode(SchemaTransformation.snakeToCamel())), + Schema.String +) +const transformedKeyRecordInput = Object.fromEntries( + Array.from({ length: 32 }, (_, index) => [`field_${index}_value`, `value${index}`]) +) +const transformedKeyRecordOutput = Object.fromEntries( + Array.from({ length: 32 }, (_, index) => [`field${index}Value`, `value${index}`]) +) +const union = Schema.Union( + Array.from({ length: 8 }, (_, i) => Schema.Struct({ tag: Schema.Literal(i), value: Schema.Number })) +) +const oneOf = Schema.Union([Schema.Struct({ a: Schema.String }), Schema.Struct({ b: Schema.Number })], { + mode: "oneOf" +}) +const fields = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, Schema.NumberFromString])) +const transformed = Schema.Struct(fields) +const transformedInput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, String(i)])) +const transformedOutput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, i])) +const checkedTransform = Schema.String.pipe( + Schema.decodeTo( + Schema.Number.check(Schema.isGreaterThan(0)), + SchemaTransformation.transform({ decode: Number, encode: String }) + ) +) +const rootTransform = Schema.FiniteFromString +const middleware = small.pipe(Schema.middlewareDecoding((effect) => effect)) +const declaration = Schema.ReadonlySet(person()) +const declarationInput = new Set(arrayInput) +interface Node { + readonly value: number + readonly children: ReadonlyArray +} +const recursive: Schema.Codec = Schema.Struct({ + value: Schema.Number, + children: Schema.Array(Schema.suspend(() => recursive)) +}) +const node = (depth: number): Node => ({ + value: depth, + children: depth === 0 ? [] : [node(depth - 1), node(depth - 1)] +}) +const recursiveInput = node(4) +const suspendRoot = Schema.suspend(() => Schema.Struct({ value: Schema.Number })) +const defaults = Schema.Struct( + Object.fromEntries( + Array.from( + { length: 32 }, + (_, i) => [`v${i}`, Schema.Number.pipe(Schema.withConstructorDefault(Effect.succeed(i)))] + ) + ) +) + +type Case = { + schema: Schema.Constraint + input: unknown + expected: unknown + operation?: "is" | "make" | "encode" + invalid?: boolean + options?: SchemaAST.ParseOptions +} + +export const cases: Record = { + parseValid: { schema: small, input, expected: input }, + parseExtra: { schema: small, input: { ...input, extra: 1 }, expected: input }, + parseInvalid: { schema: small, input: { ...input, age: "bad" }, expected: true, invalid: true }, + isValid: { schema: small, input, expected: true, operation: "is" }, + isInvalid: { schema: small, input: { ...input, age: "bad" }, expected: false, operation: "is" }, + encode: { schema: small, input, expected: input, operation: "encode" }, + strict: { schema: small, input, expected: input, options: { onExcessProperty: "error" } }, + nested: { schema: nested, input: { user: input, tags: ["a", "b"] }, expected: { user: input, tags: ["a", "b"] } }, + array: { schema: array, input: arrayInput, expected: arrayInput }, + arrayInvalid: { schema: array, input: [...arrayInput, { ...input, age: "bad" }], expected: true, invalid: true }, + tuple: { schema: tuple, input: tupleInput, expected: tupleInput }, + record: { schema: record, input: recordInput, expected: recordInput }, + recordTransformedKeys: { + schema: transformedKeyRecord, + input: transformedKeyRecordInput, + expected: transformedKeyRecordOutput + }, + union: { schema: union, input: { tag: 7, value: 1 }, expected: { tag: 7, value: 1 } }, + oneOf: { schema: oneOf, input: { b: 1 }, expected: { b: 1 } }, + transform: { schema: transformed, input: transformedInput, expected: transformedOutput }, + transformInvalid: { schema: checkedTransform, input: "-1", expected: true, invalid: true }, + rootTransform: { schema: rootTransform, input: "123", expected: 123 }, + middleware: { schema: middleware, input, expected: input }, + recursive: { schema: recursive, input: recursiveInput, expected: recursiveInput }, + suspendRoot: { schema: suspendRoot, input: { value: 1 }, expected: { value: 1 } }, + declaration: { schema: declaration, input: declarationInput, expected: declarationInput }, + makeStruct: { schema: defaults, input: {}, expected: transformedOutput, operation: "make" }, + makeArray: { schema: array, input: arrayInput, expected: arrayInput, operation: "make" }, + makeUnion: { schema: union, input: { tag: 7, value: 1 }, expected: { tag: 7, value: 1 }, operation: "make" } +} + +export const targets = Object.values(cases).map(({ operation, schema }) => + operation === "encode" + ? { ast: SchemaAST.flip(schema.ast), operations: ["decode"] as const } + : operation === "is" || operation === "make" + ? { ast: SchemaAST.toType(schema.ast), operations: [operation] as const } + : { ast: schema.ast, operations: ["decode"] as const } +) +export const roots = targets.map((target) => target.ast) + +export const fixture = (name: string) => { + const { schema, input, expected, operation, invalid, options } = cases[name] + const parse = operation === "is" ? + SchemaParser.is(schema) + : operation === "make" ? + SchemaParser.make(schema) + : operation === "encode" ? + SchemaParser.encodeUnknownSync(schema as Schema.ConstraintEncoder) + : SchemaParser.decodeUnknownSync(schema as Schema.ConstraintDecoder, options) + return { + run: invalid ? + () => { + try { + parse(input) + return false + } catch { + return true + } + } : + () => parse(input), + validate: (result: unknown) => assert.deepEqual(result, expected) + } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts new file mode 100644 index 0000000000..a549452143 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts @@ -0,0 +1,5 @@ +import { writeFileSync } from "node:fs" +import { compile } from "effect/schema/SchemaAOTCompiler" +import { targets } from "./cases.ts" + +writeFileSync(process.argv[2], compile(targets)) diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts new file mode 100644 index 0000000000..8412c30a95 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts @@ -0,0 +1,26 @@ +import { fixture } from "./cases.ts" +export const parseValid = () => fixture("parseValid") +export const parseExtra = () => fixture("parseExtra") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const encode = () => fixture("encode") +export const strict = () => fixture("strict") +export const nested = () => fixture("nested") +export const array = () => fixture("array") +export const arrayInvalid = () => fixture("arrayInvalid") +export const tuple = () => fixture("tuple") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const oneOf = () => fixture("oneOf") +export const transform = () => fixture("transform") +export const transformInvalid = () => fixture("transformInvalid") +export const rootTransform = () => fixture("rootTransform") +export const middleware = () => fixture("middleware") +export const recursive = () => fixture("recursive") +export const suspendRoot = () => fixture("suspendRoot") +export const declaration = () => fixture("declaration") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") +export const makeUnion = () => fixture("makeUnion") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts new file mode 100644 index 0000000000..1970495ef1 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts @@ -0,0 +1,28 @@ +import { enable } from "effect/schema/SchemaJITCompiler" +import { fixture, roots } from "./cases.ts" +for (const ast of roots) enable(ast) +export const parseValid = () => fixture("parseValid") +export const parseExtra = () => fixture("parseExtra") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const encode = () => fixture("encode") +export const strict = () => fixture("strict") +export const nested = () => fixture("nested") +export const array = () => fixture("array") +export const arrayInvalid = () => fixture("arrayInvalid") +export const tuple = () => fixture("tuple") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const oneOf = () => fixture("oneOf") +export const transform = () => fixture("transform") +export const transformInvalid = () => fixture("transformInvalid") +export const rootTransform = () => fixture("rootTransform") +export const middleware = () => fixture("middleware") +export const recursive = () => fixture("recursive") +export const suspendRoot = () => fixture("suspendRoot") +export const declaration = () => fixture("declaration") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") +export const makeUnion = () => fixture("makeUnion") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts new file mode 100644 index 0000000000..e729d5fffb --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts @@ -0,0 +1,93 @@ +import * as Str from "effect/String" +import assert from "node:assert/strict" +import * as v from "valibot" + +const person = () => v.object({ name: v.string(), age: v.number(), active: v.boolean() }) +const input = { name: "Ada", age: 37, active: true } +const small = person() +const arraySchema = v.array(person()) +const arrayInput = Array.from({ length: 32 }, () => ({ ...input })) +const recordSchema = v.record(v.string(), person()) +const recordInput = Object.fromEntries(arrayInput.map((value, i) => [String(i), value])) +const transformedKeyRecordSchema = v.record( + v.pipe(v.string(), v.transform(Str.snakeToCamel)), + v.string() +) +const transformedKeyRecordInput = Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`field_${i}_value`, String(i)]) +) +const transformedKeyRecordOutput = Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`field${i}Value`, String(i)]) +) +const unionSchema = v.variant( + "tag", + Array.from({ length: 8 }, (_, i) => v.object({ tag: v.literal(i), value: v.number() })) +) +const transformedSchema = v.object( + Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`v${i}`, v.pipe(v.string(), v.transform(Number))]) + ) +) +const transformedInput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, String(i)])) +const transformedOutput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, i])) +const defaultsSchema = v.object( + Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, v.optional(v.number(), i)])) +) + +type Case = { + readonly schema: v.BaseSchema> + readonly input: unknown + readonly expected: unknown + readonly operation?: "is" | "make" + readonly invalid?: boolean +} + +const cases: Record = { + parseValid: { schema: small, input, expected: input }, + parseInvalid: { schema: small, input: { ...input, age: "bad" }, expected: true, invalid: true }, + isValid: { schema: small, input, expected: true, operation: "is" }, + isInvalid: { schema: small, input: { ...input, age: "bad" }, expected: false, operation: "is" }, + array: { schema: arraySchema, input: arrayInput, expected: arrayInput }, + record: { schema: recordSchema, input: recordInput, expected: recordInput }, + recordTransformedKeys: { + schema: transformedKeyRecordSchema, + input: transformedKeyRecordInput, + expected: transformedKeyRecordOutput + }, + union: { schema: unionSchema, input: { tag: 7, value: 1 }, expected: { tag: 7, value: 1 } }, + transform: { schema: transformedSchema, input: transformedInput, expected: transformedOutput }, + makeStruct: { schema: defaultsSchema, input: {}, expected: transformedOutput, operation: "make" }, + makeArray: { schema: arraySchema, input: arrayInput, expected: arrayInput, operation: "make" } +} + +const fixture = (name: string) => { + const { schema, input, expected, operation, invalid } = cases[name] + const parse = operation === "is" + ? (input: unknown) => v.is(schema, input) + : (input: unknown) => v.parse(schema, input) + return { + run: invalid + ? () => { + try { + parse(input) + return false + } catch { + return true + } + } + : () => parse(input), + validate: (result: unknown) => assert.deepEqual(result, expected) + } +} + +export const parseValid = () => fixture("parseValid") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const array = () => fixture("array") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const transform = () => fixture("transform") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts new file mode 100644 index 0000000000..b2e271b59e --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts @@ -0,0 +1,133 @@ +import * as Str from "effect/String" +import assert from "node:assert/strict" +import * as z from "zod/v4" + +const person = () => z.object({ name: z.string(), age: z.number(), active: z.boolean() }) +const input = { name: "Ada", age: 37, active: true } +const small = person() +const nestedSchema = z.object({ user: person(), tags: z.array(z.string()) }) +const arraySchema = z.array(person()) +const arrayInput = Array.from({ length: 32 }, () => ({ ...input })) +const recordSchema = z.record(z.string(), person()) +const recordInput = Object.fromEntries(arrayInput.map((value, i) => [String(i), value])) +const transformedKeyRecordSchema = z.record( + z.string().overwrite(Str.snakeToCamel), + z.string() +) +const transformedKeyRecordInput = Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`field_${i}_value`, String(i)]) +) +const transformedKeyRecordOutput = Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`field${i}Value`, String(i)]) +) +const unionSchema = z.union( + Array.from({ length: 8 }, (_, i) => z.object({ tag: z.literal(i), value: z.number() })) as [ + z.ZodObject, + z.ZodObject, + ...Array + ] +) +const fields = Object.fromEntries( + Array.from({ length: 32 }, (_, i) => [`v${i}`, z.string().transform(Number)]) +) +const transformedSchema = z.object(fields) +const transformedInput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, String(i)])) +const transformedOutput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, i])) +const checkedTransform = z.string().transform(Number).pipe(z.number().positive()) +const declarationSchema = z.set(person()) +const declarationInput = new Set(arrayInput) +const defaultsSchema = z.object( + Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, z.number().default(i)])) +) + +type Case = { + readonly schema: z.ZodType + readonly input: unknown + readonly expected: unknown + readonly operation?: "is" | "make" | "encode" + readonly invalid?: boolean +} + +const cases: Record = { + parseValid: { schema: small, input, expected: input }, + parseExtra: { schema: small, input: { ...input, extra: 1 }, expected: input }, + parseInvalid: { schema: small, input: { ...input, age: "bad" }, expected: true, invalid: true }, + isValid: { schema: small, input, expected: true, operation: "is" }, + isInvalid: { schema: small, input: { ...input, age: "bad" }, expected: false, operation: "is" }, + encode: { schema: small, input, expected: input, operation: "encode" }, + strict: { + schema: z.strictObject({ name: z.string(), age: z.number(), active: z.boolean() }), + input, + expected: input + }, + nested: { + schema: nestedSchema, + input: { user: input, tags: ["a", "b"] }, + expected: { user: input, tags: ["a", "b"] } + }, + array: { schema: arraySchema, input: arrayInput, expected: arrayInput }, + arrayInvalid: { + schema: arraySchema, + input: [...arrayInput, { ...input, age: "bad" }], + expected: true, + invalid: true + }, + record: { schema: recordSchema, input: recordInput, expected: recordInput }, + recordTransformedKeys: { + schema: transformedKeyRecordSchema, + input: transformedKeyRecordInput, + expected: transformedKeyRecordOutput + }, + union: { schema: unionSchema, input: { tag: 7, value: 1 }, expected: { tag: 7, value: 1 } }, + transform: { schema: transformedSchema, input: transformedInput, expected: transformedOutput }, + transformInvalid: { schema: checkedTransform, input: "-1", expected: true, invalid: true }, + declaration: { schema: declarationSchema, input: declarationInput, expected: declarationInput }, + makeStruct: { schema: defaultsSchema, input: {}, expected: transformedOutput, operation: "make" }, + makeArray: { schema: arraySchema, input: arrayInput, expected: arrayInput, operation: "make" }, + makeUnion: { + schema: unionSchema, + input: { tag: 7, value: 1 }, + expected: { tag: 7, value: 1 }, + operation: "make" + } +} + +const makeFixture = ( + { input, expected, invalid }: Case, + parse: (input: unknown) => unknown +) => { + return { + run: invalid ? + () => { + try { + parse(input) + return false + } catch { + return true + } + } : + () => parse(input), + validate: (result: unknown) => assert.deepEqual(result, expected) + } +} + +export const compiledFixture = (name: string) => { + const value = cases[name] + const schema = z.compile(value.schema, { strict: true }) + const parse = value.operation === "is" ? + (input: unknown) => z.validate(schema, input) + : value.operation === "encode" ? + (input: unknown) => z.encode(schema, input) + : (input: unknown) => schema.parse(input) + return makeFixture(value, parse) +} + +export const jitlessFixture = (name: string) => { + const value = cases[name] + const parse = value.operation === "is" ? + (input: unknown) => z.validate(value.schema, input, { jitless: true }) + : value.operation === "encode" ? + (input: unknown) => z.encode(value.schema, input, { jitless: true }) + : (input: unknown) => value.schema.parse(input, { jitless: true }) + return makeFixture(value, parse) +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts new file mode 100644 index 0000000000..4fa0b3f8d1 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts @@ -0,0 +1,21 @@ +import { compiledFixture as fixture } from "./zod-cases.ts" + +export const parseValid = () => fixture("parseValid") +export const parseExtra = () => fixture("parseExtra") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const encode = () => fixture("encode") +export const strict = () => fixture("strict") +export const nested = () => fixture("nested") +export const array = () => fixture("array") +export const arrayInvalid = () => fixture("arrayInvalid") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const transform = () => fixture("transform") +export const transformInvalid = () => fixture("transformInvalid") +export const declaration = () => fixture("declaration") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") +export const makeUnion = () => fixture("makeUnion") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts new file mode 100644 index 0000000000..fc84ea4731 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts @@ -0,0 +1,13 @@ +import { jitlessFixture as fixture } from "./zod-cases.ts" + +export const parseValid = () => fixture("parseValid") +export const parseInvalid = () => fixture("parseInvalid") +export const isValid = () => fixture("isValid") +export const isInvalid = () => fixture("isInvalid") +export const array = () => fixture("array") +export const record = () => fixture("record") +export const recordTransformedKeys = () => fixture("recordTransformedKeys") +export const union = () => fixture("union") +export const transform = () => fixture("transform") +export const makeStruct = () => fixture("makeStruct") +export const makeArray = () => fixture("makeArray") diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts new file mode 100644 index 0000000000..99e2aa6f0a --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts @@ -0,0 +1,235 @@ +import { readFileSync } from "node:fs" +import { resolve } from "node:path" + +type Sample = { + readonly aotBuild?: { + readonly generateCpu: { readonly cpuMicros: number } + readonly maxRssBytes: number + readonly schemaCpu: { readonly cpuMicros: number } + readonly sourceBytes: number + } + readonly case: string + readonly count: number + readonly cpu: { + readonly aotModule?: { readonly cpuMicros: number } + readonly firstCall: { readonly cpuMicros: number } + readonly prepare: { readonly cpuMicros: number } + } + readonly implementation: string + readonly memory: { + readonly compilerPerSchema: Memory + readonly module: Memory + readonly retainedLibraryPerSchema: Memory + } + readonly round: number +} + +type Memory = { + readonly bytecodeBytes: number + readonly codeBytes: number + readonly externalSourceBytes: number + readonly heapBytes: number + readonly maxRssBytes: number +} + +type Report = { + readonly environment: Record + readonly measurement: { + readonly cases: ReadonlyArray + readonly counts: ReadonlyArray + readonly implementations: ReadonlyArray + readonly rounds: number + } + readonly results: ReadonlyArray +} + +const path = resolve(process.argv[2] ?? "tmp/schema-compiler-resources.json") +const report = JSON.parse(readFileSync(path, "utf8")) as Report +const lowCount = Math.min(...report.measurement.counts) +const highCount = Math.max(...report.measurement.counts) +if (lowCount === highCount) throw new Error("the report needs at least two schema counts") + +const median = (values: ReadonlyArray) => { + const sorted = values.toSorted((a, b) => a - b) + const middle = Math.floor(sorted.length / 2) + return sorted.length % 2 === 0 ? (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle] +} + +const samples = (implementation: string, caseName: string) => { + const pairs = [] + for (let round = 0; round < report.measurement.rounds; round++) { + const low = report.results.find((sample) => + sample.round === round && sample.implementation === implementation && sample.case === caseName && + sample.count === lowCount + ) + const high = report.results.find((sample) => + sample.round === round && sample.implementation === implementation && sample.case === caseName && + sample.count === highCount + ) + if (low === undefined || high === undefined) throw new Error(`missing samples for ${implementation}/${caseName}`) + pairs.push({ high, low }) + } + return pairs +} + +const perSchemaSlope = ( + implementation: string, + caseName: string, + value: (sample: Sample) => number +) => median(samples(implementation, caseName).map(({ high, low }) => + (value(high) * highCount - value(low) * lowCount) / (highCount - lowCount))) + +const totalSlope = ( + implementation: string, + caseName: string, + value: (sample: Sample) => number +) => median(samples(implementation, caseName).map(({ high, low }) => + (value(high) - value(low)) / (highCount - lowCount))) + +const heap = (implementation: string, caseName: string) => + perSchemaSlope(implementation, caseName, (sample) => sample.memory.retainedLibraryPerSchema.heapBytes) / 1024 +const code = (implementation: string, caseName: string) => + perSchemaSlope( + implementation, + caseName, + (sample) => { + const memory = sample.memory.retainedLibraryPerSchema + return memory.bytecodeBytes + memory.codeBytes + memory.externalSourceBytes + } + ) / 1024 +const peakRss = (implementation: string, caseName: string) => + perSchemaSlope(implementation, caseName, (sample) => sample.memory.retainedLibraryPerSchema.maxRssBytes) / 1024 +const startupCpu = (implementation: string, caseName: string) => + totalSlope( + implementation, + caseName, + (sample) => + sample.cpu.prepare.cpuMicros + sample.cpu.firstCall.cpuMicros + + (implementation === "effect-aot" ? sample.cpu.aotModule?.cpuMicros ?? 0 : 0) + ) + +const fixedDelta = ( + left: string, + right: string, + value: (sample: Sample) => number +) => { + const differences = [] + for (const caseName of report.measurement.cases) { + for (let round = 0; round < report.measurement.rounds; round++) { + for (const count of report.measurement.counts) { + const find = (implementation: string) => report.results.find((sample) => + sample.implementation === implementation && sample.case === caseName && sample.round === round && + sample.count === count + )! + differences.push(value(find(left)) - value(find(right))) + } + } + } + return median(differences) +} + +const names: Record = { + "array-decode": "Array of 32 Structs", + "default-make": "Construct 32 defaults", + "struct-decode": "Struct decode", + "struct-invalid": "Struct invalid decode", + "struct-is": "Struct guard", + "transform-decode": "32 transformations", + "union-decode": "Discriminated Union (8)" +} + +const table = ( + implementations: ReadonlyArray, + value: (implementation: string, caseName: string) => number, + unit: string, + digits: number +) => { + const labels: Record = { + "effect-aot": "Effect AOT", + "effect-interpreted": "Effect interpreted", + "effect-jit": "Effect JIT", + "valibot": "Valibot", + "zod-compiled": "Zod compile", + "zod-jitless": "Zod jitless" + } + const lines = [ + `| Case | ${implementations.map((implementation) => labels[implementation]).join(" | ")} |`, + `|---|${implementations.map(() => "---:").join("|")}|` + ] + for (const caseName of report.measurement.cases) { + lines.push( + `| ${names[caseName]} | ${ + implementations.map((implementation) => `${value(implementation, caseName).toFixed(digits)} ${unit}`).join(" | ") + } |` + ) + } + return lines.join("\n") +} + +const interpreted = ["effect-interpreted", "valibot", "zod-jitless"] +const compiled = ["effect-jit", "effect-aot", "zod-compiled"] +const environment = report.environment + +process.stdout.write(`# Schema compiler resource comparison + +Incremental costs are median per-schema slopes between ${lowCount} and ${highCount} distinct schemas across ${ + report.measurement.rounds +} fresh-process rounds. Lower is better. Fixed module imports and fixture inputs are excluded. + +Environment: Node ${environment.node}, V8 ${environment.v8}, ${environment.cpu}, ${environment.platform} ${ + environment.arch +}; Valibot ${environment.valibot}, Zod ${environment.zod}. + +Importing the JIT compiler retains ${ + (fixedDelta("effect-jit", "effect-interpreted", (sample) => sample.memory.module.heapBytes) / 1024).toFixed(1) +} KiB of additional fixed JavaScript heap and ${ + (fixedDelta("effect-jit", "effect-interpreted", (sample) => + sample.memory.module.codeBytes + sample.memory.module.bytecodeBytes + sample.memory.module.externalSourceBytes) / 1024) + .toFixed(1) +} KiB of V8 code, bytecode and source in this source-tree setup. + +## Retained JavaScript heap + +### Interpreted + +${table(interpreted, heap, "KiB/schema", 2)} + +### Compiled + +${table(compiled, heap, "KiB/schema", 2)} + +## Retained V8 code, bytecode and source + +${table(compiled, code, "KiB/schema", 2)} + +## Runtime preparation CPU + +This includes adapter creation and the first call. AOT also includes importing and installing the generated module. AOT build-time generation is excluded. + +### Interpreted + +${table(interpreted, startupCpu, "µs/schema", 1)} + +### Compiled + +${table(compiled, startupCpu, "µs/schema", 1)} + +## Peak runtime RSS growth + +${table(compiled, peakRss, "KiB/schema", 1)} + +## AOT build cost + +| Case | CPU | Generated source | Peak RSS growth | +|---|---:|---:|---:| +${report.measurement.cases.map((caseName) => { + const cpu = totalSlope( + "effect-aot", + caseName, + (sample) => (sample.aotBuild?.schemaCpu.cpuMicros ?? 0) + (sample.aotBuild?.generateCpu.cpuMicros ?? 0) + ) + const source = totalSlope("effect-aot", caseName, (sample) => sample.aotBuild?.sourceBytes ?? 0) / 1024 + const rss = totalSlope("effect-aot", caseName, (sample) => sample.aotBuild?.maxRssBytes ?? 0) / 1024 + return `| ${names[caseName]} | ${cpu.toFixed(1)} µs/schema | ${source.toFixed(2)} KiB/schema | ${rss.toFixed(1)} KiB/schema |` +}).join("\n")} +`) diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts new file mode 100644 index 0000000000..37e6032bb9 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts @@ -0,0 +1,479 @@ +import assert from "node:assert/strict" +import { execFileSync } from "node:child_process" +import { mkdtempSync, rmSync, writeFileSync } from "node:fs" +import { join } from "node:path" +import { fileURLToPath, pathToFileURL } from "node:url" +import * as v8 from "node:v8" + +const implementations = [ + "effect-interpreted", + "effect-jit", + "effect-aot", + "valibot", + "zod-jitless", + "zod-compiled" +] as const +type Implementation = typeof implementations[number] + +const cases = [ + "struct-decode", + "struct-invalid", + "struct-is", + "array-decode", + "union-decode", + "transform-decode", + "default-make" +] as const +type CaseName = typeof cases[number] + +type BuiltCase = { + readonly schema: unknown + input: unknown + expected: unknown + readonly invalid?: boolean + readonly ast?: unknown +} + +type Parser = (input: unknown) => unknown + +const [command, root, implementationArgument, caseArgument, countArgument, output] = process.argv.slice(2) +const includes = (values: ReadonlyArray, value: string | undefined): value is A => + value !== undefined && values.includes(value as A) + +if (command !== "measure" && command !== "generate") { + throw new Error("command must be measure or generate") +} +if (root === undefined) throw new Error("root is required") +if (!includes(implementations, implementationArgument)) { + throw new Error(`implementation must be one of: ${implementations.join(", ")}`) +} +if (!includes(cases, caseArgument)) { + throw new Error(`case must be one of: ${cases.join(", ")}`) +} +const implementation: Implementation = implementationArgument +const caseName: CaseName = caseArgument +const count = Number(countArgument) +if (!Number.isSafeInteger(count) || count <= 0) throw new Error("count must be a positive integer") + +const forceGc = () => { + assert.equal(typeof globalThis.gc, "function", "run this probe with --expose-gc") + for (let i = 0; i < 5; i++) globalThis.gc!() +} + +type CpuSample = { + readonly cpuMicros: number + readonly systemMicros: number + readonly userMicros: number + readonly wallNanos: number +} + +const measureCpu = async (f: () => A | Promise): Promise => { + const cpuStart = process.cpuUsage() + const wallStart = process.hrtime.bigint() + const value = await f() + const wallNanos = Number(process.hrtime.bigint() - wallStart) + const cpu = process.cpuUsage(cpuStart) + return [value, { + cpuMicros: cpu.user + cpu.system, + systemMicros: cpu.system, + userMicros: cpu.user, + wallNanos + }] +} + +type MemorySample = { + readonly bytecodeBytes: number + readonly codeBytes: number + readonly externalSourceBytes: number + readonly heapBytes: number + readonly maxRssBytes: number + readonly rssBytes: number +} + +const memory = (): MemorySample => { + const usage = process.memoryUsage() + const code = v8.getHeapCodeStatistics() + return { + bytecodeBytes: code.bytecode_and_metadata_size, + codeBytes: code.code_and_metadata_size, + externalSourceBytes: code.external_script_source_size, + heapBytes: usage.heapUsed, + maxRssBytes: process.resourceUsage().maxRSS * 1024, + rssBytes: usage.rss + } +} + +const delta = (after: MemorySample, before: MemorySample) => ({ + bytecodeBytes: after.bytecodeBytes - before.bytecodeBytes, + codeBytes: after.codeBytes - before.codeBytes, + externalSourceBytes: after.externalSourceBytes - before.externalSourceBytes, + heapBytes: after.heapBytes - before.heapBytes, + maxRssBytes: after.maxRssBytes - before.maxRssBytes, + rssBytes: after.rssBytes - before.rssBytes +}) + +const perSchema = (sample: ReturnType) => ({ + bytecodeBytes: sample.bytecodeBytes / count, + codeBytes: sample.codeBytes / count, + externalSourceBytes: sample.externalSourceBytes / count, + heapBytes: sample.heapBytes / count, + maxRssBytes: sample.maxRssBytes / count, + rssBytes: sample.rssBytes / count +}) + +const loadEffectModule = (path: string) => + import(pathToFileURL(join(root, "packages/effect/src", `${path}.ts`)).href) + +const loadEffectSchemaModules = async () => ({ + Effect: await loadEffectModule("Effect"), + Schema: await loadEffectModule("Schema"), + SchemaAST: await loadEffectModule("SchemaAST") +}) + +type EffectSchemaModules = Awaited> + +const buildEffectCase = ({ Effect, Schema, SchemaAST }: EffectSchemaModules, index: number): BuiltCase => { + const suffix = String(index) + const name = `name${suffix}` + const age = `age${suffix}` + const active = `active${suffix}` + const person = () => Schema.Struct({ [name]: Schema.String, [age]: Schema.Number, [active]: Schema.Boolean }) + const value = { [name]: "Ada", [age]: 37, [active]: true } + switch (caseName) { + case "struct-decode": { + const schema = person() + return { schema, ast: schema.ast, input: value, expected: value } + } + case "struct-invalid": { + const schema = person() + return { schema, ast: schema.ast, input: { ...value, [age]: "bad" }, expected: true, invalid: true } + } + case "struct-is": { + const schema = person() + return { schema, ast: SchemaAST.toType(schema.ast), input: value, expected: true } + } + case "array-decode": { + const schema = Schema.Array(person()) + const input = Array.from({ length: 32 }, () => ({ ...value })) + return { schema, ast: schema.ast, input, expected: input } + } + case "union-decode": { + const tag = `tag${suffix}` + const schema = Schema.Union( + Array.from({ length: 8 }, (_, member) => + Schema.Struct({ [tag]: Schema.Literal(member), [`value${suffix}`]: Schema.Number })) + ) + const input = { [tag]: 7, [`value${suffix}`]: 1 } + return { schema, ast: schema.ast, input, expected: input } + } + case "transform-decode": { + const schema = Schema.Struct( + Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, Schema.NumberFromString])) + ) + const input = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, String(field)])) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, ast: schema.ast, input, expected } + } + case "default-make": { + const schema = Schema.Struct( + Object.fromEntries( + Array.from( + { length: 32 }, + (_, field) => [ + `v${suffix}_${field}`, + Schema.Number.pipe(Schema.withConstructorDefault(Effect.succeed(field))) + ] + ) + ) + ) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, ast: SchemaAST.toType(schema.ast), input: {}, expected } + } + } +} + +const loadEffect = async (jit: boolean) => { + const modules = await loadEffectSchemaModules() + const SchemaParser = await loadEffectModule("SchemaParser") + const enable = jit ? (await loadEffectModule("schema/SchemaJITCompiler")).enable : undefined + + const prepare = (built: ReadonlyArray): ReadonlyArray => { + return built.map(({ ast, schema }) => { + if (enable !== undefined) enable(ast) + if (caseName === "struct-is") return SchemaParser.is(schema) + if (caseName === "default-make") return SchemaParser.make(schema) + return SchemaParser.decodeUnknownSync(schema) + }) + } + + return { build: (index: number) => buildEffectCase(modules, index), prepare } +} + +const loadZod = async (compiled: boolean) => { + const z = await import("zod/v4") + + const build = (index: number): BuiltCase => { + const suffix = String(index) + const name = `name${suffix}` + const age = `age${suffix}` + const active = `active${suffix}` + const person = () => z.object({ [name]: z.string(), [age]: z.number(), [active]: z.boolean() }) + const value = { [name]: "Ada", [age]: 37, [active]: true } + switch (caseName) { + case "struct-decode": + return { schema: person(), input: value, expected: value } + case "struct-invalid": + return { schema: person(), input: { ...value, [age]: "bad" }, expected: true, invalid: true } + case "struct-is": + return { schema: person(), input: value, expected: true } + case "array-decode": { + const input = Array.from({ length: 32 }, () => ({ ...value })) + return { schema: z.array(person()), input, expected: input } + } + case "union-decode": { + const tag = `tag${suffix}` + const schema = z.union( + Array.from({ length: 8 }, (_, member) => + z.object({ [tag]: z.literal(member), [`value${suffix}`]: z.number() })) as [ + ReturnType, + ReturnType, + ...Array> + ] + ) + const input = { [tag]: 7, [`value${suffix}`]: 1 } + return { schema, input, expected: input } + } + case "transform-decode": { + const schema = z.object( + Object.fromEntries( + Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, z.string().transform(Number)]) + ) + ) + const input = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, String(field)])) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, input, expected } + } + case "default-make": { + const schema = z.object( + Object.fromEntries( + Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, z.number().default(field)]) + ) + ) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, input: {}, expected } + } + } + } + + const prepare = (built: ReadonlyArray): ReadonlyArray => { + return built.map(({ schema }) => { + const target = compiled ? z.compile(schema, { strict: true }) : schema + if (caseName === "struct-is") { + return compiled + ? (input: unknown) => z.validate(target, input) + : (input: unknown) => z.validate(target, input, { jitless: true }) + } + return compiled + ? (input: unknown) => target.parse(input) + : (input: unknown) => target.parse(input, { jitless: true }) + }) + } + + return { build, prepare } +} + +const loadValibot = async () => { + const v = await import("valibot") + + const build = (index: number): BuiltCase => { + const suffix = String(index) + const name = `name${suffix}` + const age = `age${suffix}` + const active = `active${suffix}` + const person = () => v.object({ [name]: v.string(), [age]: v.number(), [active]: v.boolean() }) + const value = { [name]: "Ada", [age]: 37, [active]: true } + switch (caseName) { + case "struct-decode": + return { schema: person(), input: value, expected: value } + case "struct-invalid": + return { schema: person(), input: { ...value, [age]: "bad" }, expected: true, invalid: true } + case "struct-is": + return { schema: person(), input: value, expected: true } + case "array-decode": { + const input = Array.from({ length: 32 }, () => ({ ...value })) + return { schema: v.array(person()), input, expected: input } + } + case "union-decode": { + const tag = `tag${suffix}` + const schema = v.variant( + tag, + Array.from({ length: 8 }, (_, member) => + v.object({ [tag]: v.literal(member), [`value${suffix}`]: v.number() })) + ) + const input = { [tag]: 7, [`value${suffix}`]: 1 } + return { schema, input, expected: input } + } + case "transform-decode": { + const schema = v.object( + Object.fromEntries( + Array.from({ length: 32 }, (_, field) => [ + `v${suffix}_${field}`, + v.pipe(v.string(), v.transform(Number)) + ]) + ) + ) + const input = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, String(field)])) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, input, expected } + } + case "default-make": { + const schema = v.object( + Object.fromEntries( + Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, v.optional(v.number(), field)]) + ) + ) + const expected = Object.fromEntries(Array.from({ length: 32 }, (_, field) => [`v${suffix}_${field}`, field])) + return { schema, input: {}, expected } + } + } + } + + const prepare = (built: ReadonlyArray): ReadonlyArray => { + return built.map(({ schema }) => + caseName === "struct-is" + ? (input: unknown) => v.is(schema, input) + : (input: unknown) => v.parse(schema, input)) + } + + return { build, prepare } +} + +const runParser = (parser: Parser, built: BuiltCase): unknown => { + if (!built.invalid) return parser(built.input) + try { + parser(built.input) + return false + } catch { + return true + } +} + +const validate = (actual: unknown, built: BuiltCase) => assert.deepEqual(actual, built.expected) + +if (command === "generate") { + if (output === undefined) throw new Error("output is required for generate") + const [modules, moduleCpu] = await measureCpu(async () => { + return { + ...await loadEffectSchemaModules(), + AOT: await loadEffectModule("schema/SchemaAOTCompiler") + } + }) + const [built, schemaCpu] = await measureCpu(() => + Array.from({ length: count }, (_, index) => buildEffectCase(modules, index).ast) + ) + const operation = caseName === "struct-is" ? "is" : caseName === "default-make" ? "make" : "decode" + const [source, generateCpu] = await measureCpu(() => + modules.AOT.compile(built.map((ast) => ({ ast, operations: [operation] })))) + writeFileSync(output, source) + process.stdout.write(JSON.stringify({ + generateCpu, + heapBytes: process.memoryUsage().heapUsed, + maxRssBytes: process.resourceUsage().maxRSS * 1024, + moduleCpu, + schemaCpu, + sourceBytes: Buffer.byteLength(source) + })) +} else { + forceGc() + const beforeModule = memory() + const [library, moduleCpu] = await measureCpu(() => { + switch (implementation) { + case "effect-interpreted": + return loadEffect(false) + case "effect-jit": + case "effect-aot": + return loadEffect(implementation === "effect-jit") + case "zod-jitless": + case "zod-compiled": + return loadZod(implementation === "zod-compiled") + case "valibot": + return loadValibot() + } + }) + forceGc() + const afterModule = memory() + + const [built, schemaCpu] = await measureCpu(() => Array.from({ length: count }, (_, index) => library.build(index))) + forceGc() + const afterSchemas = memory() + + let aotBuild: unknown + let aotModuleCpu: CpuSample | undefined + let directory: string | undefined + let parsers: ReadonlyArray + let prepareCpu: CpuSample + try { + if (implementation === "effect-aot") { + directory = mkdtempSync(join(root, "packages/effect/.compiler-resources-")) + const generated = join(directory, "generated.mjs") + aotBuild = JSON.parse(execFileSync( + process.execPath, + ["--expose-gc", fileURLToPath(import.meta.url), "generate", root, implementation, caseName, String(count), generated], + { encoding: "utf8" } + )) + const [installed, measuredModuleCpu] = await measureCpu(async () => { + const generatedModule = await import(pathToFileURL(generated).href) + generatedModule.install(built.map((value) => value.ast)) + }) + void installed + aotModuleCpu = measuredModuleCpu + } + const prepared = await measureCpu(() => library.prepare(built)) + parsers = prepared[0] + prepareCpu = prepared[1] + forceGc() + const afterPrepare = memory() + + const [results, firstCallCpu] = await measureCpu(() => parsers.map((parser, index) => runParser(parser, built[index]))) + results.forEach((actual, index) => validate(actual, built[index])) + results.length = 0 + forceGc() + const afterFirstCall = memory() + + for (const value of built) { + value.input = undefined + value.expected = undefined + } + forceGc() + const afterRelease = memory() + + assert.equal(built.length, count) + assert.equal(parsers.length, count) + process.stdout.write(JSON.stringify({ + aotBuild, + case: caseName, + count, + cpu: { + aotModule: aotModuleCpu, + firstCall: firstCallCpu, + module: moduleCpu, + prepare: prepareCpu, + schema: schemaCpu + }, + implementation, + memory: { + compilerPerSchema: perSchema(delta(afterFirstCall, afterSchemas)), + firstCallPerSchema: perSchema(delta(afterFirstCall, afterPrepare)), + module: delta(afterModule, beforeModule), + preparePerSchema: perSchema(delta(afterPrepare, afterSchemas)), + retainedLibraryPerSchema: perSchema(delta(afterRelease, afterModule)), + schemaPerSchema: perSchema(delta(afterSchemas, afterModule)), + totalWithFixturePerSchema: perSchema(delta(afterFirstCall, afterModule)) + } + })) + } finally { + if (directory !== undefined) { + rmSync(directory, { recursive: true, force: true }) + } + } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts new file mode 100644 index 0000000000..daa0ccb472 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts @@ -0,0 +1,93 @@ +import { execFileSync } from "node:child_process" +import { mkdirSync, readFileSync, writeFileSync } from "node:fs" +import os from "node:os" +import { dirname, join, resolve } from "node:path" +import { createRequire } from "node:module" +import { fileURLToPath } from "node:url" + +const implementations = [ + "effect-interpreted", + "valibot", + "zod-jitless", + "effect-jit", + "effect-aot", + "zod-compiled" +] as const + +const cases = [ + "struct-decode", + "struct-invalid", + "struct-is", + "array-decode", + "union-decode", + "transform-decode", + "default-make" +] as const + +const [outputArgument = "tmp/schema-compiler-resources.json", roundsArgument = "5", countsArgument = "100,500"] = + process.argv.slice(2) +const rounds = Number(roundsArgument) +const counts = countsArgument.split(",").map(Number) +if (!Number.isSafeInteger(rounds) || rounds <= 0) throw new Error("rounds must be a positive integer") +if (counts.some((count) => !Number.isSafeInteger(count) || count <= 0)) { + throw new Error("counts must be comma-separated positive integers") +} + +const root = process.cwd() +const output = resolve(outputArgument) +const worker = fileURLToPath(new URL("./resources.mts", import.meta.url)) +const require = createRequire(import.meta.url) +const packageVersion = (name: string) => { + let directory = dirname(require.resolve(name)) + while (true) { + try { + const metadata = JSON.parse(readFileSync(join(directory, "package.json"), "utf8")) + if (metadata.name === name) return metadata.version as string + } catch { + // Continue at the parent directory. + } + const parent = dirname(directory) + if (parent === directory) throw new Error(`package.json not found for ${name}`) + directory = parent + } +} + +const results: Array = [] +for (let round = 0; round < rounds; round++) { + const orderedImplementations = round % 2 === 0 ? implementations : implementations.toReversed() + const orderedCounts = round % 2 === 0 ? counts : counts.toReversed() + for (const count of orderedCounts) { + for (const caseName of cases) { + for (const implementation of orderedImplementations) { + const sample = JSON.parse(execFileSync( + process.execPath, + ["--expose-gc", worker, "measure", root, implementation, caseName, String(count)], + { encoding: "utf8", maxBuffer: 16 * 1024 * 1024 } + )) + results.push({ round, ...sample }) + } + } + process.stderr.write(`resource round ${round + 1}/${rounds}, count ${count} complete\n`) + } +} + +mkdirSync(dirname(output), { recursive: true }) +writeFileSync(output, JSON.stringify({ + environment: { + arch: process.arch, + cpu: os.cpus()[0]?.model ?? "unknown", + node: process.version, + platform: process.platform, + valibot: packageVersion("valibot"), + v8: process.versions.v8, + zod: packageVersion("zod") + }, + measurement: { + cases, + counts, + implementations, + rounds + }, + results +}, null, 2)) +process.stdout.write(`${output}\n`) diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts new file mode 100644 index 0000000000..1a40211255 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts @@ -0,0 +1,17 @@ +import { execFileSync } from "node:child_process" +import { mkdtempSync, rmSync } from "node:fs" +import { join } from "node:path" +import { fileURLToPath, pathToFileURL } from "node:url" +import { roots } from "./cases.ts" + +const directory = mkdtempSync(fileURLToPath(new URL("./.aot-", import.meta.url))) +try { + const file = join(directory, "generated.mjs") + execFileSync(process.execPath, [fileURLToPath(new URL("./generate.mts", import.meta.url)), file]) + const generated = await import(pathToFileURL(file).href) + generated.install(roots) +} finally { + rmSync(directory, { recursive: true, force: true }) +} + +export { isExtraValid, isInvalid, isValid, parseExtraValid, parseInvalid, parseValid } from "./cases.ts" diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts new file mode 100644 index 0000000000..ba38d5ea0b --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts @@ -0,0 +1,65 @@ +import * as Schema from "effect/Schema" +import * as SchemaAST from "effect/SchemaAST" +import * as SchemaParser from "effect/SchemaParser" +import assert from "node:assert/strict" +import { invalidData, validData, validDataWithExtras } from "./data.ts" + +export const schema = Schema.Struct({ + number: Schema.Number, + negNumber: Schema.Number, + maxNumber: Schema.Number, + string: Schema.String, + longString: Schema.String, + boolean: Schema.Boolean, + deeplyNested: Schema.Struct({ + foo: Schema.String, + num: Schema.Number, + bool: Schema.Boolean + }) +}) + +export const targets = [ + { ast: schema.ast, operations: ["decode"] }, + { ast: SchemaAST.toType(schema.ast), operations: ["is"] }, + { ast: SchemaAST.flip(schema.ast), operations: ["decode"] } +] as const +export const roots = targets.map((target) => target.ast) + +const parseCase = (input: unknown, invalid = false) => () => { + const parse = SchemaParser.decodeUnknownSync(schema) + return { + run: invalid + ? () => { + try { + parse(input) + return false + } catch { + return true + } + } + : () => parse(input), + validate: invalid + ? (result: unknown) => assert.equal(result, true) + : (result: unknown) => assert.deepEqual(result, validData) + } +} + +const guardCase = (input: unknown, expected: boolean) => () => { + const guard = SchemaParser.is(schema) + return { + run: expected + ? () => { + if (!guard(input)) throw new Error("Invalid") + return true + } + : () => guard(input), + validate: (result: unknown) => assert.equal(result, expected) + } +} + +export const parseValid = parseCase(validData) +export const parseExtraValid = parseCase(validDataWithExtras) +export const parseInvalid = parseCase(invalidData, true) +export const isValid = guardCase(validData, true) +export const isExtraValid = guardCase(validDataWithExtras, true) +export const isInvalid = guardCase(invalidData, false) diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts new file mode 100644 index 0000000000..d247f4eb01 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts @@ -0,0 +1,31 @@ +// Extracted from moltar/typescript-runtime-type-benchmarks at +// d1791e68fc1108ef47da50547e80900e177a9d10. +// Upstream license: MIT, declared in package.json at that commit. +export const validData = Object.freeze({ + number: 1, + negNumber: -1, + maxNumber: Number.MAX_VALUE, + string: "string", + longString: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Vivendum intellegat et qui, ei denique consequuntur vix. Semper aeterno percipit ut his, sea ex utinam referrentur repudiandae. No epicuri hendrerit consetetur sit, sit dicta adipiscing ex, in facete detracto deterruisset duo. Quot populo ad qui. Sit fugit nostrum et. Ad per diam dicant interesset, lorem iusto sensibus ut sed. No dicam aperiam vis. Pri posse graeco definitiones cu, id eam populo quaestio adipiscing, usu quod malorum te. Ex nam agam veri, dicunt efficiantur ad qui, ad legere adversarium sit. Commune platonem mel id, brute adipiscing duo an. Vivendum intellegat et qui, ei denique consequuntur vix. Offendit eleifend moderatius ex vix, quem odio mazim et qui, purto expetendis cotidieque quo cu, veri persius vituperata ei nec. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.", + boolean: true, + deeplyNested: { + foo: "bar", + num: 1, + bool: false + } +}) + +export const validDataWithExtras = Object.freeze({ + ...validData, + extraAttribute: "foo", + deeplyNested: { + ...validData.deeplyNested, + extraNestedAttribute: "bar" + } +}) + +export const invalidData = Object.freeze({ + ...validData, + number: "invalid" +}) diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts new file mode 100644 index 0000000000..a549452143 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts @@ -0,0 +1,5 @@ +import { writeFileSync } from "node:fs" +import { compile } from "effect/schema/SchemaAOTCompiler" +import { targets } from "./cases.ts" + +writeFileSync(process.argv[2], compile(targets)) diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts new file mode 100644 index 0000000000..448732fdab --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts @@ -0,0 +1 @@ +export { isExtraValid, isInvalid, isValid, parseExtraValid, parseInvalid, parseValid } from "./cases.ts" diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts new file mode 100644 index 0000000000..9b17c1db9c --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts @@ -0,0 +1,6 @@ +import { enable } from "effect/schema/SchemaJITCompiler" +import { roots } from "./cases.ts" + +for (const ast of roots) enable(ast) + +export { isExtraValid, isInvalid, isValid, parseExtraValid, parseInvalid, parseValid } from "./cases.ts" diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts new file mode 100644 index 0000000000..4de6e0cb25 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts @@ -0,0 +1,53 @@ +import assert from "node:assert/strict" +import * as v from "valibot" +import { invalidData, validData, validDataWithExtras } from "./data.ts" + +const shape = { + number: v.number(), + negNumber: v.number(), + maxNumber: v.number(), + string: v.string(), + longString: v.string(), + boolean: v.boolean(), + deeplyNested: v.object({ + foo: v.string(), + num: v.number(), + bool: v.boolean() + }) +} + +const parseSchema = v.object(shape) +const guardSchema = v.looseObject({ ...shape, deeplyNested: v.looseObject(shape.deeplyNested.entries) }) + +const parseCase = (input: unknown, invalid = false) => () => ({ + run: invalid + ? () => { + try { + v.parse(parseSchema, input) + return false + } catch { + return true + } + } + : () => v.parse(parseSchema, input), + validate: invalid + ? (result: unknown) => assert.equal(result, true) + : (result: unknown) => assert.deepEqual(result, validData) +}) + +const guardCase = (input: unknown, expected: boolean) => () => ({ + run: expected + ? () => { + if (!v.is(guardSchema, input)) throw new Error("Invalid") + return true + } + : () => v.is(guardSchema, input), + validate: (result: unknown) => assert.equal(result, expected) +}) + +export const parseValid = parseCase(validData) +export const parseExtraValid = parseCase(validDataWithExtras) +export const parseInvalid = parseCase(invalidData, true) +export const isValid = guardCase(validData, true) +export const isExtraValid = guardCase(validDataWithExtras, true) +export const isInvalid = guardCase(invalidData, false) diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts new file mode 100644 index 0000000000..245da3a858 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts @@ -0,0 +1,117 @@ +import assert from "node:assert/strict" +import * as z from "zod/v4" +import { invalidData, validData, validDataWithExtras } from "./data.ts" + +const makeShape = () => ({ + number: z.number(), + negNumber: z.number(), + maxNumber: z.number(), + string: z.string(), + longString: z.string(), + boolean: z.boolean(), + deeplyNested: z.object({ + foo: z.string(), + num: z.number(), + bool: z.boolean() + }) +}) + +const makeParseSchema = () => z.object(makeShape()) +const makeGuardSchema = () => { + const shape = makeShape() + return z.object({ ...shape, deeplyNested: shape.deeplyNested.passthrough() }).passthrough() +} + +const parseCase = ( + compile: (schema: ReturnType) => (input: unknown) => unknown, + input: unknown, + invalid = false +) => +() => { + const parse = compile(makeParseSchema()) + return { + run: invalid + ? () => { + try { + parse(input) + return false + } catch { + return true + } + } + : () => parse(input), + validate: invalid + ? (result: unknown) => assert.equal(result, true) + : (result: unknown) => assert.deepEqual(result, validData) + } +} + +const guardCase = ( + compile: (schema: ReturnType) => (input: unknown) => boolean, + input: unknown, + expected: boolean +) => +() => { + const validate = compile(makeGuardSchema()) + return { + run: expected + ? () => { + if (!validate(input)) throw new Error("Invalid") + return true + } + : () => validate(input), + validate: (result: unknown) => assert.equal(result, expected) + } +} + +const assertParseCase = ( + compile: (schema: ReturnType) => (input: unknown) => unknown, + input: unknown +) => +() => { + const parse = compile(makeGuardSchema()) + return { + run: () => { + parse(input) + return true + }, + validate: (result: unknown) => assert.equal(result, true) + } +} + +const parse = (schema: ReturnType) => (input: unknown) => schema.parse(input) +const parseJitless = (schema: ReturnType) => (input: unknown) => + schema.parse(input, { jitless: true }) +const parseCompiled = (schema: ReturnType) => { + const compiled = z.compile(schema, { strict: true }) + return (input: unknown) => compiled.parse(input) +} +const validate = (schema: ReturnType) => (input: unknown) => z.validate(schema, input) +const validateJitless = (schema: ReturnType) => (input: unknown) => + z.validate(schema, input, { jitless: true }) +const validateCompiled = (schema: ReturnType) => { + const compiled = z.compile(schema, { strict: true }) + return (input: unknown) => z.validate(compiled, input) +} +const assertParse = (schema: ReturnType) => (input: unknown) => schema.parse(input) + +export const parseValid = parseCase(parse, validData) +export const parseExtraValid = parseCase(parse, validDataWithExtras) +export const parseInvalid = parseCase(parse, invalidData, true) +export const parseJitlessValid = parseCase(parseJitless, validData) +export const parseJitlessExtraValid = parseCase(parseJitless, validDataWithExtras) +export const parseJitlessInvalid = parseCase(parseJitless, invalidData, true) +export const parseCompiledValid = parseCase(parseCompiled, validData) +export const parseCompiledExtraValid = parseCase(parseCompiled, validDataWithExtras) +export const parseCompiledInvalid = parseCase(parseCompiled, invalidData, true) +export const isValid = guardCase(validate, validData, true) +export const isExtraValid = guardCase(validate, validDataWithExtras, true) +export const isInvalid = guardCase(validate, invalidData, false) +export const isJitlessValid = guardCase(validateJitless, validData, true) +export const isJitlessExtraValid = guardCase(validateJitless, validDataWithExtras, true) +export const isJitlessInvalid = guardCase(validateJitless, invalidData, false) +export const isCompiledValid = guardCase(validateCompiled, validData, true) +export const isCompiledExtraValid = guardCase(validateCompiled, validDataWithExtras, true) +export const isCompiledInvalid = guardCase(validateCompiled, invalidData, false) +export const assertParseValid = assertParseCase(assertParse, validData) +export const assertParseExtraValid = assertParseCase(assertParse, validDataWithExtras) diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts index b21c0dd2d6..06ddb89a54 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts @@ -66,7 +66,10 @@ const parsingCase = (input, errors, success) => () => { const run = Schema.decodeUnknownOption(makeSchema()) return { run: () => run(input, { errors }), - validate: (result) => assert.equal(Option.isSome(result), success) + validate: (result) => { + assert.equal(Option.isSome(result), success) + if (success && Option.isSome(result)) assert.deepEqual(result.value, validData) + } } } diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts index fdff6a1b55..25676aad23 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts @@ -16,7 +16,7 @@ const makeSchema = () => { }) const rating = v.object({ id: v.number(), - stars: v.pipe(v.number(), v.minValue(1), v.maxValue(5)), + stars: v.pipe(v.number(), v.minValue(0), v.maxValue(5)), title: v.pipe(v.string(), v.minLength(1), v.maxLength(100)), text: v.pipe(v.string(), v.minLength(1), v.maxLength(1000)), images: v.array(image) @@ -56,7 +56,10 @@ const parsingCase = (input, options, success) => () => { const schema = makeSchema() return { run: () => v.safeParse(schema, input, options), - validate: (result) => assert.equal(result.success, success) + validate: (result) => { + assert.equal(result.success, success) + if (success) assert.deepEqual(result.output, validData) + } } } @@ -72,6 +75,7 @@ const standardCase = (input, success) => () => { validate: (result) => { assert.equal(typeof result?.then, "undefined") assert.equal(result.issues === undefined, success) + if (success) assert.deepEqual(result.value, validData) } } } diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts index 81e56f4821..609f06c497 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts @@ -29,7 +29,7 @@ const makeSchema = () => { description: z.string().min(1).max(500), price: z.number().min(1).max(10000), discount: z.number().min(1).max(100).nullable(), - quantity: z.number().min(0).max(10), + quantity: z.number().min(1).max(10), tags: z.array(z.string().min(1).max(30)), images: z.array(image), ratings: z.array(rating) @@ -46,7 +46,10 @@ const parsingCase = (input, success) => () => { const options = { jitless: true } return { run: () => schema.safeParse(input, options), - validate: (result) => assert.equal(result.success, success) + validate: (result) => { + assert.equal(result.success, success) + if (success) assert.deepEqual(result.data, validData) + } } } @@ -60,6 +63,7 @@ const standardCase = (input, success) => () => { validate: (result) => { assert.equal(typeof result?.then, "undefined") assert.equal(result.issues === undefined, success) + if (success) assert.deepEqual(result.value, validData) } } } diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md b/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md new file mode 100644 index 0000000000..f364b399ae --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md @@ -0,0 +1,29 @@ +# Checked transformation sources + +This suite reconstructs the motivating shape from PR #8597: decode an Array of +1,000 rows, each containing eleven pattern-checked string fields and one `n` +field. The sibling fields match `/^[a-z][a-z0-9]{1,15}$/`, and the checked numeric +source matches `/^[0-9]{1,6}$/`. Rows contain `value0` through `value999` and +numeric strings `1` through `1000`. Schemas, inputs and parsers are prepared +outside the measured callback. Each operation decodes all 1,000 rows. + +- `digits` validates the checked string without transforming it. +- `checked-source` converts the checked numeric string to a number. +- `unchecked-source` converts a plain string to a number. +- `two-passes` first validates all fields, then decodes the validated output + with a transformations-only Struct. +- `checked-template-target` and `unchecked-template-target` convert the `n` + field to a TemplateLiteral string prefixed with `x-`. They measure valid + input through the parser fallback required to preserve template diagnostics. + +Every worker validates the complete output before and after measuring. The +comparison harness copies the same fixtures into both revisions and alternates +base/head order in fresh Node processes. + +```sh +pnpm runtimeperf-compare schema-checked-sources --base main --head HEAD \ + --rounds 16 --time 500 --warmup-time 150 +``` + +These fixtures use repository sources. The PR's original figures used packed +packages on Node 26.10, so compare relative changes rather than absolute times. diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts b/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts new file mode 100644 index 0000000000..0c5bf688d6 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts @@ -0,0 +1,63 @@ +import * as Schema from "effect/Schema" +import { enable } from "effect/schema/SchemaJITCompiler" +import * as SchemaParser from "effect/SchemaParser" +import * as SchemaTransformation from "effect/SchemaTransformation" +import assert from "node:assert/strict" + +type Kind = "digits" | "checked" | "unchecked" | "two-passes" | "checked-template" | "unchecked-template" + +const fixture = (kind: Kind) => { + const text = Schema.String.check(Schema.isPattern(/^[a-z][a-z0-9]{1,15}$/)) + const digits = Schema.String.check(Schema.isPattern(/^[0-9]{1,6}$/)) + const toNumber = SchemaTransformation.transform({ decode: Number, encode: String }) + const toTemplate = SchemaTransformation.transform({ + decode: (s: string): `x-${string}` => `x-${s}`, + encode: (s: `x-${string}`) => s.slice(2) + }) + const template = Schema.TemplateLiteral(["x-", Schema.String]) + const names = Array.from({ length: 11 }, (_, index) => `s${index}`) + const checkedFields = Object.fromEntries(names.map((name) => [name, text])) + const uncheckedFields = Object.fromEntries(names.map((name) => [name, Schema.String])) + const source = kind === "unchecked" || kind === "unchecked-template" ? Schema.String : digits + const field = kind === "digits" || kind === "two-passes" + ? digits + : kind === "checked-template" || kind === "unchecked-template" + ? source.pipe(Schema.decodeTo(template, toTemplate)) + : source.pipe(Schema.decodeTo(Schema.Number, toNumber)) + const schema = Schema.Array(Schema.Struct({ ...checkedFields, n: field })) + enable(schema.ast) + const decode = SchemaParser.decodeUnknownSync(schema) + const input = Array.from({ length: 1000 }, (_, index) => ({ + ...Object.fromEntries(names.map((name) => [name, `value${index}`])), + n: String(index + 1) + })) + const expected = input.map((row) => ({ + ...row, + n: kind === "digits" + ? row.n + : kind === "checked-template" || kind === "unchecked-template" + ? `x-${row.n}` + : Number(row.n) + })) + let run = () => decode(input) + if (kind === "two-passes") { + const transformations = Schema.Array(Schema.Struct({ + ...uncheckedFields, + n: Schema.String.pipe(Schema.decodeTo(Schema.Number, toNumber)) + })) + enable(transformations.ast) + const transform = SchemaParser.decodeUnknownSync(transformations) + run = () => transform(decode(input)) + } + return { + run, + validate: (result: unknown) => assert.deepEqual(result, expected) + } +} + +export const digits = () => fixture("digits") +export const checkedSource = () => fixture("checked") +export const uncheckedSource = () => fixture("unchecked") +export const twoPasses = () => fixture("two-passes") +export const checkedTemplateTarget = () => fixture("checked-template") +export const uncheckedTemplateTarget = () => fixture("unchecked-template") diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md new file mode 100644 index 0000000000..9b620062ec --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md @@ -0,0 +1,232 @@ +# AST allocation and copying, 2026-09-25 + +The existing AST classes already provide cheap allocation with stable field layouts. The first architectural change +worth investigating is a shared, inexpensive path for immutable updates. Descriptor-based copying is substantially +more expensive than creating the nodes themselves. Specialized constructors and a generic value-copy prototype both +remove most of this cost in the measured workloads. The measurements do not establish that rewriting every node into +a new class hierarchy would improve schema startup further. + +This is an investigation of `SchemaAST.ts` at `80670a2b6f`, after the duplicate-check and `mapOrSame` optimizations. +The initial experiments used isolated source copies. Their patches, scripts and raw measurements are retained under +`tmp/ast-layout-study/` and `tmp/ast-layout-classes-vs-assign/` locally. The final section records the subsequent +implementation and its validation. + +## Current representation + +All 21 AST variants already use classes derived from `ASTNodeImpl`. Their class expressions execute once at module +initialization. Renaming them to `ObjectsImpl`, for example, would help internal references but would not itself make +allocation faster. Common fields are present even when their values are `undefined`, and methods live on prototypes. + +| Node family | Construction work | Implication for copying | +| -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `Null`, `Undefined`, `Void`, `Never`, `Unknown`, `Any`, `String`, `Number`, `Boolean`, `BigInt`, `Symbol`, `ObjectKeyword` | Store common fields and `_tag` | Natural candidates for a direct allocation path; checks on scalar schemas use these frequently | +| `Objects`, `Union`, `Declaration` | Store common fields and child references, options or declaration callbacks | Share children and callbacks; preserve `encodingChecks` and `encodingRun` | +| `Literal`, `Enum` | Validate finite numeric values, then store payload | Copies with unchanged payload should not need repeated validation | +| `UniqueSymbol` | Store the symbol | Share its identity | +| `Arrays` | Store children and validate optional/required ordering | Metadata updates must not rescan an unchanged tuple | +| `TemplateLiteral` | Validate parts, allocate encoded parts and literals, compute suffix lengths | Metadata copies should share prepared data; rerunning the public constructor would redo work | +| `Suspend` | Reject checks and wrap the thunk in a memoizing closure | Copies must share the existing thunk; recreating its wrapper changes sharing and adds closures | + +`PropertySignature`, `IndexSignature`, `Context`, `Link`, `Filter` and `FilterGroup` also already use classes. +`IndexSignature` validates its parameter on the type and encoded sides and checks optionality. `PropertySignature` +only stores its name and type. The fixed shape of an `Objects` node does not depend on its number of fields: those live +in `propertySignatures`, rather than becoming JavaScript properties of the node itself. + +The expensive generic path is `modifyOwnPropertyDescriptors`, used by `replaceChecks`, `replaceEncoding`, +`replaceContext`, `annotate` and check promotion inside `toType`. It creates the descriptor table and descriptor +objects, modifies values, and defines the properties on another object. + +`toType` can first copy a node to remove encoding, then rebuild it after visiting children, then copy it again to +promote encoding checks. `flipEncoding` also removes encoding through a copy before recursion. These are opportunities +to reduce the number of allocations, independently of how each allocation is implemented. + +`Schema.make` remains a separate source of construction work. It creates a function object, sets its prototype, adds +options and adapters, and calls `SchemaAST.toType` through `SchemaParser.make`. Making AST allocation cheap does not +remove all of this work. + +## Direct V8 inspection + +Environment: Node v24.12.0, V8 13.6.233.17-node.37, Apple M3, Darwin 23.6.0 arm64. +The diagnostic used `%HaveSameMap`, `%HasFastProperties` and `%DebugPrint` in a separate process with +`--allow-natives-syntax`. That flag was not used for timing runs. + +For the tested `String`, `Number`, `Literal`, `Objects`, `Arrays` and `Union` nodes: + +- Repeated constructor calls share a V8 map in the tested fixtures. +- A descriptor-based copy and its constructor-created source have different maps. +- Copies still have fast properties. Descriptor copying did not put these nodes into dictionary mode. + +For the inspected `Objects` node, after constructor slack tracking: + +| Allocation | Fields inside the object | Additional property storage | Same map as the original | +| ------------------------------- | -----------------------: | ----------------------------------- | ------------------------ | +| `new Objects(...)` | 9 | None | Yes | +| Descriptor copy | 4 | `PropertyArray[6]`, five slots used | No | +| Copy through `new Objects(...)` | 9 | None | Yes | + +An `Object.create` plus `Object.assign` copy also has a different map from the constructor-created node. This supports +investigating constructor copies, but does not prove a material end-to-end advantage over all generic copies. +See the retained `v8-layout.txt` for the actual dump. These details are observations of this V8 version, not language +guarantees. V8 describes the relevant mechanisms in [fast properties](https://v8.dev/blog/fast-properties) and +[hidden classes](https://v8.dev/docs/hidden-classes). + +## Allocation experiment + +The constructor prototype replaces metadata copies with explicit constructor calls for 18 variants. `Enum`, +`TemplateLiteral` and `Suspend` retain descriptor copying. It then updates the requested field on the new, unpublished +object. This prototype deliberately assumes standard built-in nodes. It is not a compatible replacement for arbitrary +AST objects. + +Each case has 20 paired rounds in fresh Node processes. Pair order alternates and case order rotates. AST cases retain +10,000 results; Schema cases retain 1,000 new root schemas. There is no explicit warmup, but imports can warm shared +code, and V8 can optimize during the batch. Imports, process startup and decoding are excluded from elapsed time. +Valid decode, invalid input rejection and type-side construction run in separate validation processes. Prepared +property signatures are reused only in the two AST-only cases. Nested and composed Schema cases include child creation. + +All 280 measured processes and 14 validation processes completed. Delta is the median paired ratio, with a 95% +process-bootstrap interval. It is not the ratio of the two separately reported medians. The improvement threshold is 2%. + +| Case | Baseline batch ms | Constructor-copy batch ms | Paired change | 95% interval | +| ------------------------------------------- | ----------------: | ------------------------: | ------------: | ---------------------------------------- | +| Raw `Objects`, 8 ready properties | 0.863 | 0.816 | -6.6% | -9.6% to -0.8%, below evidence threshold | +| Copy `Objects` to change checks | 23.126 | 1.091 | -95.4% | -95.4% to -95.2% | +| `Schema.Struct`, 8 fields | 1.382 | 1.350 | -1.6% | -10.7% to +0.8%, inconclusive | +| Nested small structs | 4.172 | 4.314 | +2.0% | -0.3% to +5.0%, inconclusive | +| Structs, checks, arrays and optional keys | 16.334 | 8.716 | -47.2% | -47.9% to -45.6% | +| New string-to-number transformations | 11.992 | 6.000 | -44.2% | -51.1% to -40.5% | +| Union of 8 newly constructed tagged members | 20.030 | 16.824 | -15.0% | -17.9% to -12.3% | + +The raw constructor is unchanged between versions, so its apparent improvement is not evidence of a better constructor. +The candidate also changes module-initialization objects and therefore can change V8 feedback before timing starts. +The large gains demonstrate the potential of cheaper copying in these fixtures. They do not measure complete application +startup or identify how much of the gain comes from fewer descriptors versus different object layouts. + +## Do specialized classes beat a simpler copy? + +A second experiment substitutes `Object.assign(Object.create(Object.getPrototypeOf(ast)), ast)` for the prototype's +constructor switch. This uses the same metadata update callers. Twenty paired rounds compare it directly with the +constructor prototype, with the same timing boundaries. All 160 measured and eight validation processes completed. + +| Case | Assign relative to constructor copying | 95% interval | +| ------------------------------- | -------------------------------------: | --------------- | +| Copy `Objects` to change checks | +5.9% | +3.8% to +10.5% | +| Composed schema | -0.7% | -3.0% to +2.7% | +| Encoded schema | +0.2% | -1.1% to +2.9% | +| Union of 8 members | +1.6% | +0.4% to +3.6% | + +None meets the configured 5% regression threshold with its entire interval. In these Schema workloads, specialized +constructor copies have no demonstrated material advantage over this small generic helper. This matters for the +preference for simple code and a small bundle. Bundle size was not measured for these diagnostic prototypes. + +## Proposed design + +Keep one class per AST kind, defined once, with fixed own fields, immutable child references and prototype methods. +The existing representation is a useful foundation. A uniform class containing every variant's fields would increase +node size and does not have evidence supporting it here. Moving every parser method off the prototypes or replacing +string tags with numbers also has no measured justification from this study. + +Make node allocation and immutable updates one coherent internal mechanism: + +1. Separate validation and preparation from storage. Public construction still rejects invalid literals, tuple ordering + and template parts at the current time. An internal allocation path can reuse already validated payloads. It must + not skip validation when an operation actually changes the relevant children. +2. Route metadata updates through that path, preserving unchanged arrays, callbacks, template preparation and suspended + thunks. Cover scalar nodes as well as composite nodes. Restricting this to `Objects`, `Arrays` and `Union` would miss + `.check` and encoding copies on common scalar schemas. +3. Build at most one final node for a projection when possible. `toType` should compute the final children, encoding + and checks together, while preserving idempotence, structural-check promotion and context ownership. It should + return the original node when nothing changes. This is a separate unmeasured optimization. +4. Keep parser preparation and derived caches outside ordinary node construction. Existing identity-based caches are + useful for DAG sharing. Adding cache properties after construction, eagerly allocating more per-node caches, or + recreating closure wrappers during copying would work against this goal. Moving template preprocessing to first + use is a separate startup/first-use tradeoff, not an automatic improvement. + +Start with the smallest common copy mechanism whose contract is acceptable. The generic value-copy candidate deserves +consideration before adding a per-kind copying method or constructor switch. If specialized copies are adopted, use +direct calls to the relevant internal class; dynamically invoking `this.constructor` is not equivalent to a known +constructor, and does not safely handle subclass contracts. + +There is a concrete compatibility decision before adopting either prototype. The current descriptor path preserves +extra non-enumerable properties, getter definitions, descriptor flags and custom prototypes. The local contract probe +confirmed that constructor copies drop extensions and subclasses; assign preserves enumerable symbols and the prototype +but drops non-enumerable properties, invokes getters, and changes descriptor flags. Frozen source nodes also expose +different property flags in the resulting copy. Neither prototype is therefore a drop-in implementation. + +A fast path must have a defensible definition of a standard internal node. Merely checking `_tag`, its prototype or +membership in a WeakSet does not prove that descriptors have not been changed after construction. Scanning every +descriptor to establish this on each copy could consume the intended saving. That contract should be settled explicitly +when implementing the shared copy path; the measured upper potential is not a reason to silently narrow it. + +## Artifacts and validation limits + +- `tmp/ast-layout-study/results.json`: baseline versus constructor copies, hashes, environment and raw paired timings. +- `tmp/ast-layout-classes-vs-assign/results.json`: constructor copies versus generic value copies. +- Each directory contains the measured `SchemaAST.ts` sources, patches, runner, statistics code and fixtures. +- `tmp/ast-layout-study/v8-layout.txt` and `contract.json`: representation and observable compatibility diagnostics. + +The diagnostic workers checked normal fixture behavior before measurement in separate processes. Those prototype +checks are not a full Schema/compiler regression test or a claim of production compatibility. The prototype +measurements establish no bundle-size conclusion. Their temporary worktrees and runnable scratch files were removed +after archiving the evidence. + +## Implemented value copies + +The implementation replaces the descriptor helper with +`Object.assign(Object.create(Object.getPrototypeOf(ast)), ast)`. Checks, encoding, context, annotations and the existing +check-promotion step in `toType` copy through this helper and update the new node before publishing it. The class +hierarchy and traversal algorithm are unchanged. + +The copy contract preserves the prototype and enumerable values, including symbol keys. It shares child arrays, +prepared template data and suspended thunks. It does not preserve extra non-enumerable properties or descriptor flags; +enumerable getters are evaluated and copied as values. This is an intentional change from descriptor copying. + +Validation passed: `pnpm lint-fix`, `pnpm check`, and 988 tests across `SchemaAST.test.ts`, `Schema.test.ts`, +`toCodec.test.ts`, `toIso.test.ts`, `SchemaCompilerConstruction.test.ts` and `SchemaJITCompiler.test.ts`. Six added tests +cover frozen source nodes and custom prototypes, unchanged-node identity, encoding removal, context ownership, +template preparation and shared suspended thunks. After type-only corrections, the 69 AST tests also passed again. + +The runtime comparison uses `pnpm runtimeperf-compare` against `HEAD` at +`80670a2b6f881f846c3945bb0cfcc77b87a47161`, with the modified working tree as head. Environment is Node v24.12.0, +V8 13.6.233.17-node.37, Darwin arm64 on Apple M3. Every comparison completed all 20 paired rounds in separate processes, +alternating base/head order. Each worker validates the fixture before and after timing. Initial runs use 150 ms warmup +and 500 ms timing. These measure repeated construction of fresh schemas after warmup, excluding imports, process +startup and decoding. They do not measure complete serverless startup. + +| Case | Base median | Updated median | Paired change | 95% interval | Classification | +| ---------------------------------------------------- | ----------: | -------------: | ------------: | ------------------ | -------------- | +| Product with small nested structs, checks and arrays | 63.96 µs | 40.90 µs | -33.79% | -37.71% to -32.87% | Improvement | +| Record with a newly constructed key transformation | 6.62 µs | 3.95 µs | -40.60% | -45.23% to -34.76% | Improvement | +| Plain two-field Struct, initial run | 1.38 µs | 1.41 µs | +6.26% | -2.84% to +14.61% | Inconclusive | +| Plain two-field Struct, longer run | 1.33 µs | 1.39 µs | +3.24% | -2.50% to +7.72% | Inconclusive | + +The longer control run uses 500 ms warmup and 1,500 ms timing per worker. It was run because the initial control was +too variable. Both results are retained; a small regression on plain Struct construction cannot be excluded. The +paired change is computed from paired log ratios, not the ratio of the two displayed medians. Confidence intervals +use 10,000 deterministic bootstrap samples, with the standard 2% improvement and 5% regression thresholds. + +Reproduction commands: + +```sh +pnpm runtimeperf-compare schema-benchmarks/initialization-schema --rounds 20 --time 500 --warmup-time 150 +pnpm runtimeperf-compare schema/schema-creation-encoded-record-effect --rounds 20 --time 500 --warmup-time 150 +pnpm runtimeperf-compare schema/schema-creation-object-2-effect --rounds 20 --time 500 --warmup-time 150 +pnpm runtimeperf-compare schema/schema-creation-object-2-effect --rounds 20 --time 1500 --warmup-time 500 +``` + +`tmp/schema-ast-copy/results.json` summarizes the runs and names each full report under `tmp/runtimeperf/results/`. +The directory also retains the measured `SchemaAST.ts` and the two investigation-local bundle fixtures. + +The selected bundle comparison rebuilt both the current working tree and the same baseline commit with `pnpm build`: + +```sh +pnpm bundle-compare-selected --base HEAD scratchpad/schema-ast-copy-struct.ts scratchpad/schema-ast-copy-composed.ts +``` + +| Fixture | Base gzip size | Updated gzip size | Difference | +| ----------------------------------------------------------------------- | -------------: | ----------------: | ---------------: | +| Exported eight-field Struct | 17.90 KB | 17.88 KB | -0.02 KB, -0.12% | +| Exported composed schema with checks, annotations, context and encoding | 19.30 KB | 19.28 KB | -0.02 KB, -0.10% | + +Sizes use the tool's decimal KB units and rounding. The full output is `tmp/schema-ast-copy/bundle.log`. Both builds +and comparisons passed. The workflow removed its temporary base checkout; the scratch fixtures and summary script +were removed after archiving the measurements. diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md new file mode 100644 index 0000000000..c6f765846c --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md @@ -0,0 +1,97 @@ +# Schema construction costs + +This diagnostic measures the current implementation before proposing changes. It leaves library sources unchanged. +It measures elapsed construction time in ordinary fresh Node processes, then uses separate V8 CPU-profiled processes +to locate the work. Profile shares are estimates of sampled execution, not directly timed phases or predicted savings. + +Run from the repository root: + +```sh +node packages/effect/runtimeperf/suites/schema-construction/run.mts tmp/schema-construction 24 1000 +``` + +Arguments are a new output directory, rounds, root count, and an optional comma-separated list of case names. +The output directory's parent must exist. Existing output directories are rejected. The source tree must be clean. +Every raw observation is saved in `results.json`; raw `.cpuprofile` files can be opened in a CPU profile viewer. +Copies of the measured harness files are retained in `harness/` to preserve uncommitted analysis code. +The report records the commit, source-tree object, harness and lockfile hashes, Node/V8 versions, machine and settings. +Interrupted or failed runs retain `complete: false` and must not be reported as complete measurements. + +## Scope + +Each worker imports Schema, prepares fixture keys and validation inputs, and preallocates a root array before timing. +It then builds 1,000 new root schemas and retains them until the process exits. The timer includes field-map creation, +child schemas, checks, transformations, options and all eager internal work triggered by the declaration. +There is no explicit warmup. Schema's own import-time initialization has already run and can warm shared constructors +and caches. Node startup, TypeScript loading, module imports, decoder preparation and decoding are excluded. + +Fixtures cover literals; structs with 2, 32 and 256 fields; nested structs; arrays with checks, optional keys and literals; +new string-to-number transformations; and unions of eight newly constructed tagged members. Root counts are not counts +of every intermediate schema. Shared primitive schemas such as `Schema.String` are imported constants. The `struct-N` +fixtures build field maps with `Object.fromEntries` during timing. The additional `struct-ready-N` fixtures reuse a field +map prepared before timing, to isolate `Schema.Struct(fields)` with ready inputs. Primitive schemas, field-name strings +and sample values are prepared beforehand in both cases. Dynamically generating fields is a distinct caller cost; it +does not model a hand-written object literal's cost. + +Each fixture is validated in a separate process before measurement, including valid decode, type-side `.make`, and invalid +input rejection. Measured workers check root count, object kind and distinctness after timing. No prior validation warms +the measured worker. This protocol makes no claim about complete HttpApi/Confect startup, memory usage or warmed throughput. + +## Measurement and attribution + +Each round runs ordinary Node, profiling at a requested 100 microsecond interval, and profiling at 500 microseconds. +Workers run serially. The six possible orders rotate by round; fixture order also rotates. The default 24 rounds +balance all six orders. Do not run other benchmarks, builds or tests alongside the measurement. + +Elapsed times are medians across independent processes, with median absolute deviation. Profiler perturbation is measured +through paired ratios against the ordinary process in the same round, using the existing deterministic bootstrap with +10,000 resamples. This is a check on measurement bias, not a comparison of optimized code. + +Each CPU sample is assigned to one category: + +| Category | Included work | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `type-projection` | `toType` and its recursive traversal, clones and memoization, during synchronous maker preparation | +| `ast` | Initial AST construction and changes made by checks, annotations, optional keys and transformations | +| `schema-object` | Central schema constructor after subtracting identifiable child phases: function object, prototype, options, property and closure setup | +| `make-sync` | Identifiable synchronous maker factory frames outside the type projection | +| `make-effect` | Identifiable effectful maker factory frames outside the Option adapter | +| `make-option` | Option adapter factory, including its separately allocated effectful maker | +| `schema-api` | Other Schema API work, including composition wrappers and check factories outside AST constructors | +| `fixture` | Caller-attributed samples: field maps, member lists, the loop, and library work inlined into those frames | +| `gc` | Garbage collector samples inside the construction window | +| `runtime` | Other runtime samples without an attributable Schema stack | + +Child work is never added again to its parent. V8 can omit an inlined `SchemaParser.make` frame. In these fixtures, +the only eager AST/Function subtree beneath central `make` is `toType`; this path is classified as projection even when +the parser wrapper is absent. The fixtures do not use options with getters that could invoke arbitrary AST work. +Completely inlined work that cannot be separated remains with the caller. The `fixture` category is therefore an +unresolved caller category, not a measurement of pure benchmark overhead or field-map creation. A ready-field fixture +whose body only calls `Schema.Struct(fields)` still receives caller-attributed samples. Small or zero factory shares do not prove +that their allocations are free; allocation effects can appear in central construction or GC. + +Profiles are delimited by the first and last samples containing the worker's `constructBatch` frame. Samples outside +that span are excluded, including inspector setup. Interior GC/runtime samples are retained. Inspector timestamps and +`hrtime` are not directly compared because their origins can differ. Missing boundary samples, especially GC before the +first or after the last observed construction frame, are a limitation. The report keeps sample counts, sampled span +coverage and counts of processes with fewer than ten samples. A profile with no construction samples remains recorded +and is excluded from share estimates. One sample has zero measured span coverage and is marked sparse. + +Shares are unweighted sample proportions within each process, averaged across processes. Their 95% intervals bootstrap +whole processes, not individual correlated samples. Categories sum to 100% for the point estimates; interval endpoints +need not sum to 100%. These intervals cover sampling variability, not profiler bias or attribution errors. Compare the +two sampling frequencies and their overhead before interpreting the shares. Do not convert them into exact milliseconds +or predicted speedups. Short fixtures may support a reliable elapsed time without a useful internal breakdown. + +Top leaf functions and source-position ticks are retained as secondary diagnostics. Source-position ticks are not an +additional additive cost breakdown and may accumulate at a function's entry after optimization. + +## Validation + +```sh +node --test packages/effect/runtimeperf/test/*.test.mts +pnpm exec dprint check --includes-override 'packages/effect/runtimeperf/suites/schema-construction/*.mts' 'packages/effect/runtimeperf/test/schema-construction.test.mts' +``` + +The attribution tests cover non-overlapping phases, inlined maker frames, Option's nested effect maker, profile boundaries, +interior garbage collection, missing samples and deterministic confidence intervals. diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md new file mode 100644 index 0000000000..c8f932d2f6 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md @@ -0,0 +1,122 @@ +# Schema construction profile, 2026-09-25 + +The largest attributable costs depend on the schema. AST creation and copying dominate the ready-field wide structs +and the checked/encoded fixtures. Central schema-object construction is substantial when many small schemas are composed. +Type projection is more prominent with new transformations than with ordinary checks. These measurements do not support +assigning one universal percentage to `Schema.make` or predicting an optimization's speedup from CPU shares. + +Only measurement code was added. The measured library is `main` at +`e36c35c656bb5cc6dd1170d863ea8abb75c24279`. Environment: Apple M3, macOS Darwin 23.6.0 arm64, +Node v24.12.0, V8 13.6.233.17-node.37. The source tree was checked clean and the revision unchanged before and after each run. + +## Elapsed construction time + +Times below are medians from 24 ordinary fresh Node processes per fixture, without the profiler. +Each process constructs 1,000 roots. MAD is the median absolute deviation, not a confidence interval. +Imports and first decode are excluded. Child construction is included. + +| Fixture | Median ms | MAD ms | +| ------------------------------------------ | --------: | -----: | +| Literal | 0.648 | 0.008 | +| Struct, 2 dynamically generated fields | 1.249 | 0.018 | +| Struct, 32 dynamically generated fields | 6.126 | 0.412 | +| Struct, 256 dynamically generated fields | 43.040 | 0.655 | +| Nested structs | 5.356 | 0.110 | +| Arrays, checks, optional keys and literals | 18.455 | 0.310 | +| New string-to-number transformations | 12.360 | 0.108 | +| Union of 8 new tagged members | 22.030 | 0.180 | +| Struct, 32 ready fields | 3.955 | 0.043 | +| Struct, 256 ready fields | 26.528 | 0.197 | + +The dynamic-field fixtures include `Object.fromEntries` and its input arrays. The ready-field fixtures isolate +`Schema.Struct(fields)` with a previously prepared field map. Neither dynamic generation nor reused fields is a timing +model for a freshly written object literal. The ready-field cases were measured in a subsequent cohort; the difference +between the two tables' cases is not a paired estimate of field-map creation cost. + +## Attributable internal work + +These are mean per-process sample percentages at a requested 100 microsecond interval, rounded to one decimal place. +Every sample belongs to one category. Other includes API preparation, separately identifiable maker factories, the caller, +and runtime frames. Library work that V8 folds into the caller can remain in Other. + +| Fixture | Initial AST and AST changes | Central schema constructor | `toType` | GC | Other | +| ------------------------------------------ | --------------------------: | -------------------------: | -------: | ----: | ----: | +| Struct, 256 ready fields | 49.7% | 1.7% | 9.3% | 15.8% | 23.5% | +| Nested structs | 14.4% | 26.6% | 6.9% | 26.9% | 25.2% | +| Arrays, checks, optional keys and literals | 33.6% | 21.4% | 3.7% | 23.1% | 18.2% | +| New string-to-number transformations | 30.9% | 21.9% | 19.3% | 12.5% | 15.4% | + +Representative 95% process-bootstrap intervals: + +| Fixture | Initial AST and AST changes | `toType` | +| ------------------------------------------ | --------------------------- | -------------- | +| Struct, 256 ready fields | 48.8% to 50.6% | 8.4% to 10.2% | +| Arrays, checks, optional keys and literals | 32.5% to 34.8% | 3.1% to 4.4% | +| New string-to-number transformations | 28.8% to 33.0% | 17.4% to 21.1% | + +The intervals describe process-to-process sampling variability. They do not include profiler bias, missed boundary +samples, or attribution errors caused by optimized code. These shares must not be multiplied by ordinary elapsed time +and presented as directly measured phase durations. + +The retained stacks point to these concrete operations: + +- Checked schemas spend many AST samples in `modifyOwnPropertyDescriptors` and `replaceChecks` in + [SchemaAST.ts](../../../src/SchemaAST.ts). Updating checks copies property descriptors and creates another AST object. +- Encoded schemas also spend substantial time in `replaceEncoding`. The stacks separate copies needed while defining + transformations from copies made later by `toType` while preparing `.make`. +- Ready-field structs spend many AST samples in `struct` and the `Objects` constructor. Those enumerate fields, create + property signatures and check duplicate names. `toType` then traverses the property signatures, using memoized children. +- The [central constructor](../../../src/internal/schema/make.ts) remains a substantial cost for nested, checked, + encoded and union fixtures. Its samples include function/prototype/property setup and inseparable inlined work. + They do not isolate the cost of each property assignment or closure allocation. +- GC is visible in several fixtures. The CPU profile cannot assign a GC pause to the allocation that caused it. + Small or zero samples in `makeEffect`/`makeOption` therefore do not establish that their allocations are cheap. + +## Profiler perturbation and resolution + +Both frequencies were paired with ordinary execution in the same round. Entries are median paired changes in elapsed +time, with 95% intervals. This is measurement overhead, not a source-code regression. + +| Fixture | Profiling at 100 us | Profiling at 500 us | +| ------------------------------------------ | ---------------------- | ---------------------- | +| Literal | +13.8%, 12.4% to 16.5% | +13.0%, 12.0% to 15.4% | +| Struct, 2 dynamic fields | +15.7%, 11.9% to 18.2% | +14.6%, 12.6% to 17.6% | +| Nested structs | +6.2%, 3.7% to 7.7% | +2.2%, 0.6% to 4.5% | +| Arrays, checks, optional keys and literals | +6.1%, 3.8% to 7.6% | +3.2%, 1.1% to 4.7% | +| New string-to-number transformations | +6.4%, 5.4% to 8.0% | +4.4%, 2.4% to 5.9% | +| Struct, 256 ready fields | +5.8%, 4.0% to 7.0% | +2.9%, 2.2% to 3.3% | + +The checked and encoded fixtures give similar broad proportions at both frequencies. At 500 us, their `toType` shares +are 3.9% and 19.4%, respectively. This supports distinguishing the workloads, not treating the percentages as exact costs. + +Literal has only 147 total construction samples at 100 us and 26 at 500 us across all 24 processes. The 500 us median +sampled span is zero because most processes have only one observed construction sample. Struct-2 and ready-field Struct-32 +also have sparse 500 us profiles. Their precise elapsed times do not provide a comparably precise internal decomposition. + +Union-8 exposes a boundary limitation: the 500 us median sampled span covers about 81% of elapsed construction time. +Its GC share changes from 34.7% at 100 us to 26.1% at 500 us, while central construction stays near 28%. +Do not use its GC point estimate to quantify a prospective allocation optimization. Caller-only attribution is also +ambiguous: even ready-field Struct-256 assigns about 21% to its tiny calling function, showing that this includes inlined +library work. It must not be described as 21% benchmark overhead. + +## Reproduction and validation + +The [protocol](./README.md) describes timing boundaries, attribution and sparse-profile handling. Two complete cohorts +ran sequentially, with no builds, tests or other benchmarks started alongside them. They contain 720 measured processes +and 10 separate semantic validation processes. All configured rounds succeeded; no observations were discarded. +An earlier two-round pilot and an incomplete sampler-resolution probe are separate from these results. + +```sh +node packages/effect/runtimeperf/suites/schema-construction/run.mts tmp/schema-construction-rerun 24 1000 literal,struct-2,struct-32,struct-256,nested,composed,encoded,union-8 +node packages/effect/runtimeperf/suites/schema-construction/run.mts tmp/schema-construction-ready-rerun 24 1000 struct-ready-32,struct-ready-256 +``` + +Raw reports: [main cohort](../../../../../tmp/schema-construction-main/results.json) and +[ready-field cohort](../../../../../tmp/schema-construction-ready/results.json). Their sibling `profiles/` directories +contain the CPU profiles. Their `harness/` directories preserve the measured analysis files. The fixture list and runner +archival support were extended between cohorts; each report records its own hashes. Library sources and measurement +settings were unchanged. + +Validation passed: all 26 runtimeperf infrastructure tests, including four attribution tests; valid/invalid fixture +checks and type-side makers in separate workers; post-timing root checks; `pnpm lint-fix`; focused `.mts` formatting; +and `pnpm check`. No library optimization was implemented or measured. diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md new file mode 100644 index 0000000000..6e38acce52 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md @@ -0,0 +1,217 @@ +# Removing intermediate toType copies, 2026-09-25 + +Follow-up diagnosis below qualifies the initial timing conclusion: the spike removes measurable work, but the +reported 11% reduction in mean time is not a stable estimate across sessions. + +This spike changes only the projection order inside `SchemaAST.toType`. It projects children before stripping the +node's encoding. A rebuilt composite node already has no outer encoding, so the preliminary copy can be skipped. +When encoding checks need promotion, removing encoding shares that final metadata copy. Unchanged children still +preserve all encoding checks; changed children retain only the existing structural-check subset. + +The class hierarchy, recursive rebuilding methods and memoization helper are unchanged. The spike does not attempt +to combine a composite rebuild and subsequent check promotion into one allocation. + +## Comparison + +Base is `7cf1abdbf4e0a7a86c66f8cb668bf42a2047110e`; head is the working tree on that commit with the spike applied. +Measurements use Node v24.12.0, V8 13.6.233.17-node.37, Darwin arm64 on Apple M3. Each comparison completes 20 paired +rounds in fresh processes, with alternating base/head order. Both sides use the same fixture and batch size. No +builds or tests ran concurrently with these timing commands. + +The added eight-field fixture constructs a JSON-string codec around a Struct with fresh string-to-number +transformations, checks, an array, a nested Struct and an optional property. The first-make case constructs that same +schema and calls `.make()` once with decoded input. Each measured operation constructs a new root schema. Fixture +validation checks decoding, successful construction and rejection of a negative number outside the timed callback. + +These are warmed construction measurements. They exclude process startup, module loading and imports. First-make +timings include schema creation and constructor preparation through the first result, not JSON decoding. They do +not establish a complete serverless startup improvement or isolate the cost of `.make()` alone. + +The initial runs use 150 ms warmup and 500 ms timing per process: + +| Case | Base median | Spike median | Paired change | 95% interval | Classification | +| -------------------------------------------------------- | ----------: | -----------: | ------------: | ----------------- | -------------- | +| Encoded eight-field schema, creation | 13.55 µs | 12.52 µs | -6.18% | -8.77% to -1.90% | Inconclusive | +| Encoded eight-field schema, creation and first make | 19.55 µs | 17.26 µs | -6.38% | -15.96% to +1.71% | Inconclusive | +| Record with a new key transformation, creation | 3.90 µs | 4.33 µs | +5.14% | -4.52% to +24.91% | Inconclusive | +| Product with nested structs, checks and arrays, creation | 40.75 µs | 39.57 µs | -0.09% | -1.36% to +0.96% | Inconclusive | +| Plain two-field Struct, creation and first make | 2.03 µs | 2.02 µs | -0.21% | -6.22% to +4.08% | Inconclusive | + +The three transformation cases were repeated with 500 ms warmup and 1,500 ms timing per process because the initial +comparisons were inconclusive. All three repeats completed the same 20 paired rounds: + +| Case | Base median | Spike median | Paired change | 95% interval | Classification | +| --------------------------------------------------- | ----------: | -----------: | ------------: | ----------------- | -------------- | +| Encoded eight-field schema, creation | 13.56 µs | 13.37 µs | -2.63% | -5.17% to +0.78% | Inconclusive | +| Encoded eight-field schema, creation and first make | 21.03 µs | 18.52 µs | -10.99% | -25.73% to -7.77% | Improvement | +| Record with a new key transformation, creation | 3.70 µs | 3.71 µs | -4.05% | -7.57% to -0.13% | Inconclusive | + +All eight comparisons completed, totaling 320 measured worker processes. No initial results were discarded or pooled +with the longer runs. Deltas are medians of paired log ratios, not ratios of the separately displayed medians. The +95% intervals use 10,000 bootstrap samples. Classification requires the whole interval to exceed a 2% improvement +or 5% regression threshold. Thus an interval entirely below zero can still be inconclusive at the improvement threshold. + +The longer run supports an improvement through the first `.make()` for the encoded eight-field fixture. The scope of +that finding is one workflow; the creation-only cases do not establish a gain above the configured threshold. No +comparison classified a regression. The wide first-make interval also limits precision of the estimated saving. + +## Validation and artifacts + +Passed `pnpm lint-fix`, `pnpm check`, and 991 tests across `SchemaAST.test.ts`, `Schema.test.ts`, `toCodec.test.ts`, +`toIso.test.ts`, `SchemaCompilerConstruction.test.ts` and `SchemaJITCompiler.test.ts`. The three added regression tests +cover outer encoding with unchanged children, combined outer/child encoding with mixed checks, and lazy recursive +suspended nodes with identity preservation. The 72 AST tests also passed after a test typing correction. + +The runtimeperf registry and fixture materialization tests passed, 11 tests in total. This includes loading, running +and validating every registered fixture export, including the two added cases. + +Commands for the three transformation cases, using either `--time 500 --warmup-time 150` for the initial run or +`--time 1500 --warmup-time 500` for the repeat: + +```sh +pnpm runtimeperf-compare schema/schema-creation-encoded-object-8-effect --rounds 20 --time 1500 --warmup-time 500 +pnpm runtimeperf-compare schema/first-make-encoded-object-8-effect --rounds 20 --time 1500 --warmup-time 500 +pnpm runtimeperf-compare schema/schema-creation-encoded-record-effect --rounds 20 --time 1500 --warmup-time 500 +pnpm runtimeperf-compare schema-benchmarks/initialization-schema --rounds 20 --time 500 --warmup-time 150 +pnpm runtimeperf-compare schema/first-make-object-2-effect --rounds 20 --time 500 --warmup-time 150 +``` + +These commands use the current working tree as head and `HEAD` as base. Set `--base 7cf1abdbf4` to reproduce the +comparison after committing the spike. + +`tmp/schema-to-type/results.json` retains the summaries, configuration and paths to all full reports under +`tmp/runtimeperf/results/`. The same directory retains the candidate source, tests, fixture definitions, registry, +patch and bundle fixtures. + +## Bundle comparison + +`pnpm bundle-compare-selected --base HEAD scratchpad/schema-to-type-struct.ts scratchpad/schema-to-type-encoded.ts` +rebuilt both revisions and compared two self-contained, investigation-local entrypoints. Both builds and comparisons +passed. The first exports a plain eight-field Struct; the second exports a JSON-string codec around a checked, +encoded eight-field Struct. + +| Fixture | Base gzip size | Spike gzip size | Difference | +| -------------- | -------------: | --------------: | ---------------: | +| Plain Struct | 17.88 KB | 17.89 KB | +0.01 KB, +0.04% | +| Encoded Struct | 19.55 KB | 19.55 KB | +0.01 KB, +0.03% | + +Sizes and deltas use the tool's decimal KB units, rounded separately. Full output is in +`tmp/schema-to-type/bundle.log`. The temporary bundle checkout and scratch files were removed; fixture sources remain +archived with the measurements. The spike, regression tests and two benchmark cases remain in the working tree. + +## Follow-up: explaining the first-make result + +The investigation compared three versions of the same eight-field fixture: + +- **Base:** commit `7cf1abdbf4`. +- **Direct:** a diagnostic variant of base that still makes the preliminary encoding-free copies, but calls the + projection body directly for those temporary copies instead of recursively entering the memoized wrapper. +- **Spike:** the existing uncommitted change, which also skips the redundant composite copy. + +The direct variant was confined to a temporary checkout. It is an experiment, not an additional production change. + +### Deterministic work counts + +Counters were added only to isolated copies of the source. `WeakMap.get` and `WeakMap.set` were counted separately; +`WeakMap.has` distinguished a new key from an overwrite. These counts are not timings. + +| Work during one schema creation | Base | Direct | Spike | +| ------------------------------- | ---: | -----: | ----: | +| Calls to AST `copy` | 7 | 7 | 6 | +| Copies of an `Objects` node | 2 | 2 | 1 | +| Projection-body executions | 10 | 10 | 8 | +| WeakMap reads | 32 | 30 | 30 | +| WeakMap writes | 20 | 16 | 16 | +| Newly inserted WeakMap keys | 12 | 11 | 11 | + +The four fewer writes do not mean four fewer cache entries: only one distinct key disappears. The memoization +helper caches both input and output, and several writes overwrite an existing output entry. + +During the first `.make()`, all three versions resolve the registry 12 times and compile the same eight parsers. +On subsequent freshly constructed schemas, already cached primitive parsers reduce that count to five in all three +versions. A second `.make()` on the same schema performs none of these counted operations. No `toType` work occurs +during these `.make()` calls: `internal/schema/make.ts` already calls `SchemaParser.make`, which projects the AST +while creating the schema. + +The projected graphs match for node tags, check counts, encoding counts, optionality, property order and child +sharing in this fixture. Valid input, decoding and invalid-input rejection also pass in the diagnostic variant. +The production spike retains the regression-test coverage listed above. + +This establishes two mechanisms: less copying and less memoization of temporary objects. It does not establish +fewer parser compilations or less validation work on first use. + +### Repetition and GC diagnostics + +A fresh standard comparison with the original 20-round, 500 ms warmup / 1,500 ms timing configuration produced +24.95 µs versus 18.30 µs, a paired -26.47% change with interval -28.58% to -9.23%. The direction repeated, but the +magnitude differed substantially from the earlier -10.99%. Both reports retain unusually long tail samples. + +To investigate the cause, the diagnostic experiment separated the creation, first-make and second-make phases; +compared a continuous loop with retained batches; and then returned to Tinybench with the original batch size of +eight. Counters were removed before every timing run. Explicit GC was used only in the separate-phase experiment, +outside the measured phases. It was not used in either Tinybench experiment. + +The final experiment ran the repository's **unmodified worker** and a copy with a GC observer in separate processes. +Both used the same fixture, time settings and batch size. Each mode completed 12 rounds across base, direct and +spike; the six possible execution orders were balanced. GC observation is diagnostic and can affect execution. +The unmodified-worker results remain the control for this experiment. + +| Unmodified worker metric | Base median | Direct median | Spike median | Direct paired change (95% interval) | Spike paired change (95% interval) | +| ------------------------ | ----------: | ------------: | -----------: | ----------------------------------- | ---------------------------------- | +| Mean time per operation | 19.28 µs | 18.78 µs | 19.09 µs | -1.66% (-8.51% to +3.87%) | -3.80% (-12.56% to +1.41%) | +| Within-process p50 | 7.55 µs | 7.14 µs | 7.06 µs | -4.71% (-8.17% to -2.99%) | -7.08% (-10.52% to -4.78%) | + +The means are inconclusive; the p50 reductions exceed the configured 2% threshold. The p50 is a median of batch +latencies divided by eight, not an isolated single-call latency or a cold-start result. In a separate Tinybench +diagnostic driver, the spike's p50 improvement was -4.42% (-5.63% to -3.26%), while its mean was inconclusive. +These exploratory metrics were selected during diagnosis and should not replace the mean as the original primary +endpoint. + +With the GC observer attached to a copy of the original worker, medians were: + +| Measured quantity | Base | Direct | Spike | +| ---------------------------------- | -------: | -------: | -------: | +| Mean time per operation | 19.18 µs | 18.80 µs | 18.30 µs | +| Observed GC duration per operation | 11.11 µs | 11.19 µs | 10.78 µs | +| Time after subtracting observed GC | 8.07 µs | 7.84 µs | 7.62 µs | + +Each row is aggregated separately, so displayed medians need not add up. GC events were filtered to the measurement +loop's wall-clock interval, excluding warmup and post-run statistics. The subtraction is approximate: events can +occur between callbacks, and event duration does not account for every concurrent collector cost. The observer uses +Node's [GC performance entries](https://nodejs.org/docs/latest-v24.x/api/perf_hooks.html#garbage-collection-gc-details). +It records event durations, not total allocated bytes or exact attribution to a specific AST allocation. + +Observed GC accounts for more than half of the mean time in this workload. However, the paired difference in GC +duration for spike versus base was -6.51% with interval -13.46% to +2.39%, so a consistent GC reduction is **not** +established. The separate-phase experiment was also inconclusive for first-make alone. Neither experiment proves +that a particular V8 collector or JIT mechanism caused the original 11% result. + +The supported conclusion is narrower: the change eliminates one AST copy, two cache reads, four writes and one +cache entry per schema in this fixture; bypassing only the intermediate memoization already reduces the typical +batch latency. Repeated creation plus first use is heavily affected by GC, and its aggregate improvement varies +across cohorts. The original 11% must not be presented as a stable first-make or startup improvement. Comparing +independent creation-only and creation-plus-make medians cannot isolate the cost of `.make()`. + +### Diagnostic artifacts and validation + +`tmp/schema-to-type-diagnosis/` retains source snapshots, all probe scripts, the direct variant, counter outputs and +full per-process results. In particular: + +- `original-stats.json`: reanalysis of the original long run and its fresh standard repetition, including p50 and tails. +- `base-counts.json`, `direct-counts.json`, `head-counts.json`: work counts and normalized output graphs. +- `phases/results.json`: 10 paired rounds per mode, 60 workers, including the experiments with explicit GC. +- `tinybench/results.json`: 12 rounds of three variants, with/without GC observation, 72 workers. +- `exact/results.json`: another 72 workers, using the original worker and its GC-instrumented copy. + +The final experiment's first aggregation attempt mislabeled the observer mode because the worker's `mode: measure` +field overwrote it. All 72 workers had completed successfully. The summaries were regenerated from the saved files, +whose names identify the correct modes; no measurements were rerun or discarded to repair this reporting error. + +The fresh standard report is +`tmp/runtimeperf/results/2026-09-25T12-58-09-246Z-8508-78c08b-compare-schema-first-make-encoded-object-8-effect.json`. +No builds or tests ran during the diagnostic timing cohorts. A temporary checkout was created near the end of the +fresh standard repetition; that run alone is not the basis of the diagnosis. The isolated counter instrumentation +was restored before timings, and the temporary checkouts and scratch probes were removed after archiving. + +No runtime source or regression test changed during this follow-up. The earlier runtime validation still applies; +the report-only follow-up was checked with `pnpm lint-fix` and `git diff --check`. diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts new file mode 100644 index 0000000000..fe6861df35 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts @@ -0,0 +1,137 @@ +import assert from "node:assert/strict" +import * as Schema from "../../../src/Schema.ts" +import * as SchemaTransformation from "../../../src/SchemaTransformation.ts" + +export const caseNames = [ + "literal", + "struct-2", + "struct-32", + "struct-256", + "nested", + "composed", + "encoded", + "union-8", + "struct-ready-32", + "struct-ready-256" +] + +export function fixture(name: string) { + if (name === "literal") { + return { + build: (index: number) => Schema.Literal(index), + input: (index: number) => index, + output: (index: number) => index, + invalid: null + } + } + if (name.startsWith("struct-")) { + const ready = name.startsWith("struct-ready-") + const size = Number(name.slice(ready ? "struct-ready-".length : "struct-".length)) + assert.ok([2, 32, 256].includes(size)) + const keys = Array.from({ length: size }, (_, index) => `field${index}`) + const value = Object.fromEntries(keys.map((key) => [key, "value"])) + if (ready) { + const fields = Object.fromEntries(keys.map((key) => [key, Schema.String])) + return { + build: () => Schema.Struct(fields), + input: () => value, + output: () => value, + invalid: {} + } + } + return { + build: () => Schema.Struct(Object.fromEntries(keys.map((key) => [key, Schema.String]))), + input: () => value, + output: () => value, + invalid: {} + } + } + if (name === "nested") { + const value = (index: number) => ({ + endpoint: index, + payload: { name: "Ada", count: 1, location: { country: "IT", city: "Rome" } } + }) + return { + build: (index: number) => + Schema.Struct({ + endpoint: Schema.Literal(index), + payload: Schema.Struct({ + name: Schema.String, + count: Schema.Number, + location: Schema.Struct({ country: Schema.Literal("IT"), city: Schema.String }) + }) + }), + input: value, + output: value, + invalid: {} + } + } + if (name === "composed") { + const value = (index: number) => ({ + endpoint: index, + name: "Ada", + items: [{ sku: "A", quantity: 1 }], + status: "open" + }) + return { + build: (index: number) => + Schema.Struct({ + endpoint: Schema.Literal(index), + name: Schema.String.check(Schema.isMinLength(1)), + items: Schema.Array(Schema.Struct({ + sku: Schema.String.check(Schema.isMinLength(1)), + quantity: Schema.Number.check(Schema.isGreaterThanOrEqualTo(1)) + })), + note: Schema.optionalKey(Schema.String), + status: Schema.Literals(["open", "closed"]) + }), + input: value, + output: value, + invalid: { endpoint: 0, name: "", items: [{ sku: "", quantity: 0 }], status: "unknown" } + } + } + if (name === "encoded") { + return { + build: (index: number) => { + const amount = Schema.String.pipe(Schema.decodeTo( + Schema.Number.check(Schema.isGreaterThanOrEqualTo(0)), + SchemaTransformation.numberFromString + )) + return Schema.Struct({ + endpoint: Schema.Literal(index), + amount, + values: Schema.Array(amount), + note: Schema.optionalKey(Schema.String) + }) + }, + input: (index: number) => ({ endpoint: index, amount: "1", values: ["2"] }), + output: (index: number) => ({ endpoint: index, amount: 1, values: [2] }), + invalid: { endpoint: 0, amount: "-1", values: ["-2"] } + } + } + if (name === "union-8") { + const value = (index: number) => ({ kind: index * 8 + 7, value: "Ada" }) + return { + build: (index: number) => + Schema.Union(Array.from({ length: 8 }, (_, member) => + Schema.Struct({ + kind: Schema.Literal(index * 8 + member), + value: Schema.String + }))), + input: value, + output: value, + invalid: { kind: -1, value: "Ada" } + } + } + throw new Error(`Unknown fixture: ${name}`) +} + +export function validate(name: string) { + const { build, input, output, invalid } = fixture(name) + for (const index of [0, 1, 999]) { + const schema = build(index) + assert.deepEqual(Schema.decodeUnknownSync(schema)(input(index)), output(index)) + assert.deepEqual(schema.make(output(index) as never), output(index)) + assert.throws(() => Schema.decodeUnknownSync(schema)(invalid)) + } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts new file mode 100644 index 0000000000..d02ae6078d --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts @@ -0,0 +1,145 @@ +import assert from "node:assert/strict" +import type { Profiler, Runtime } from "node:inspector" +import { percentile } from "../../stats.mts" + +export const categories = [ + "type-projection", + "ast", + "schema-object", + "make-sync", + "make-effect", + "make-option", + "schema-api", + "fixture", + "gc", + "runtime" +] as const +type Category = typeof categories[number] +type Frame = Runtime.CallFrame + +const inFile = (frame: Frame, suffix: string) => frame.url.endsWith(suffix) +const parserFunction = (frame: Frame, name: string) => + inFile(frame, "/src/SchemaParser.ts") && frame.functionName === name + +// Innermost first. Each sample belongs to exactly one category; parents are +// never added to child costs. The Option adapter includes its own makeEffect. +export function classify(stack: ReadonlyArray): Category { + if (stack[0]?.functionName === "(garbage collector)") return "gc" + const sync = stack.findIndex((frame) => parserFunction(frame, "make")) + if (sync >= 0) { + if (stack.slice(0, sync).some((frame) => parserFunction(frame, "makeConstructorSync"))) return "make-sync" + return sync > 0 ? "type-projection" : "make-sync" + } + if (stack.some((frame) => parserFunction(frame, "makeOption"))) return "make-option" + if (stack.some((frame) => parserFunction(frame, "makeEffect"))) return "make-effect" + const centralMake = stack.findIndex((frame) => + inFile(frame, "/src/internal/schema/make.ts") && frame.functionName === "make" + ) + // V8 can inline SchemaParser.make and omit its frame. In these fixtures, + // central make has no getter-bearing options: its only eager AST/Function + // subtree is toType, including the WeakMap memoizer in Function.ts. + if ( + centralMake > 0 && + stack.slice(0, centralMake).some((frame) => inFile(frame, "/src/SchemaAST.ts") || inFile(frame, "/src/Function.ts")) + ) return "type-projection" + if (stack.some((frame) => inFile(frame, "/src/SchemaAST.ts"))) return "ast" + if (centralMake >= 0) { + return "schema-object" + } + if (stack.some((frame) => frame.url.includes("/packages/effect/src/"))) return "schema-api" + if ( + stack.some((frame) => inFile(frame, "/schema-construction/fixtures.mts") || frame.functionName === "constructBatch") + ) { + return "fixture" + } + return "runtime" +} + +export function analyzeProfile(profile: Profiler.Profile) { + const { nodes, samples, timeDeltas } = profile + assert.ok(samples && timeDeltas && samples.length === timeDeltas.length) + const byId = new Map(nodes.map((node) => [node.id, node])) + const parents = new Map() + for (const node of nodes) for (const child of node.children ?? []) parents.set(child, node.id) + const stacks = new Map>() + for (const node of nodes) { + const frames: Array = [] + const seen = new Set() + let id: number | undefined = node.id + while (id !== undefined) { + assert.ok(!seen.has(id), "Cycle in CPU profile") + seen.add(id) + const current = byId.get(id) + assert.ok(current, "Missing profile node") + frames.push(current.callFrame) + id = parents.get(id) + } + stacks.set(node.id, frames) + } + const inBatch = samples.map((id) => + stacks.get(id)!.some((frame) => + frame.functionName === "constructBatch" && inFile(frame, "/schema-construction/worker.mts") + ) + ) + const first = inBatch.indexOf(true) + const last = inBatch.lastIndexOf(true) + const counts = Object.fromEntries(categories.map((category) => [category, 0])) as Record + const sites = new Map() + const lineTicks = new Map() + const sampledNodes = new Set() + if (first < 0) { + return { counts, totalSamples: 0, spanUs: 0, excludedSamples: samples.length, sites: [], lineTicks: [] } + } + let spanUs = 0 + for (let index = first; index <= last; index++) { + const stack = stacks.get(samples[index])! + const category = classify(stack) + sampledNodes.add(samples[index]) + counts[category]++ + if (index > first) spanUs += timeDeltas[index] + const frame = stack[0] + const key = `${category}:${frame.url}:${frame.lineNumber}:${frame.columnNumber}:${frame.functionName}` + const site = sites.get(key) ?? { category, frame, samples: 0 } + site.samples++ + sites.set(key, site) + } + for (const id of sampledNodes) { + const node = byId.get(id)! + const stack = stacks.get(id)! + if (!stack.some((frame) => frame.functionName === "constructBatch")) continue + for (const position of node.positionTicks ?? []) { + const category = classify(stack) + const url = node.callFrame.url + const key = `${category}:${url}:${position.line}` + const entry = lineTicks.get(key) ?? { category, url, line: position.line, ticks: 0 } + entry.ticks += position.ticks + lineTicks.set(key, entry) + } + } + return { + counts, + totalSamples: last - first + 1, + spanUs, + excludedSamples: samples.length - (last - first + 1), + sites: [...sites.values()].sort((a, b) => b.samples - a.samples), + lineTicks: [...lineTicks.values()].sort((a, b) => b.ticks - a.ticks) + } +} + +// Resample whole fresh processes, preserving correlation among their samples. +// The point estimate is the mean process share, giving each process equal weight. +export function shareInterval(values: ReadonlyArray, iterations = 10_000) { + assert.ok(values.length > 0 && values.every((value) => Number.isFinite(value) && value >= 0 && value <= 1)) + const mean = values.reduce((a, b) => a + b, 0) / values.length + let state = 0x5eed1234 + const draws = new Array(iterations) + for (let iteration = 0; iteration < iterations; iteration++) { + let sum = 0 + for (let index = 0; index < values.length; index++) { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0 + sum += values[Math.floor(state / 0x100000000 * values.length)] + } + draws[iteration] = sum / values.length + } + return { mean, low: percentile(draws, 0.025), high: percentile(draws, 0.975) } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts new file mode 100644 index 0000000000..54abb0a30d --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts @@ -0,0 +1,144 @@ +import assert from "node:assert/strict" +import { execFileSync } from "node:child_process" +import { createHash } from "node:crypto" +import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs" +import os from "node:os" +import { basename, join, resolve } from "node:path" +import { fileURLToPath } from "node:url" +import { aggregate, analyzePairs, median } from "../../stats.mts" +import { caseNames } from "./fixtures.mts" +import { analyzeProfile, categories, shareInterval } from "./profile.mts" + +const [outputArgument = "tmp/schema-construction", roundsArgument = "24", countArgument = "1000", namesArgument] = + process.argv.slice(2) +const rounds = Number(roundsArgument) +const count = Number(countArgument) +assert.ok(Number.isSafeInteger(rounds) && rounds > 0) +assert.ok(Number.isSafeInteger(count) && count > 1) +const names = namesArgument ? namesArgument.split(",") : caseNames +assert.ok(names.length > 0 && names.every((name) => caseNames.includes(name)) && new Set(names).size === names.length) +const root = process.cwd() +const output = resolve(outputArgument) +const worker = fileURLToPath(new URL("./worker.mts", import.meta.url)) +const git = (...args: Array) => execFileSync("git", args, { encoding: "utf8" }).trim() +const revision = git("rev-parse", "HEAD") +const sourceTree = git("rev-parse", "HEAD:packages/effect/src") +assert.equal( + git("status", "--porcelain", "--untracked-files=all", "--", "packages/effect/src"), + "", + "Source must be clean" +) +const sha256 = (path: string) => createHash("sha256").update(readFileSync(path)).digest("hex") +const harnessFiles = ["fixtures.mts", "worker.mts", "profile.mts", "run.mts"].map((name) => + fileURLToPath(new URL(name, import.meta.url)) +) +const hashes = Object.fromEntries( + [...harnessFiles, "packages/effect/runtimeperf/stats.mts", "pnpm-lock.yaml"].map((path) => [path, sha256(path)]) +) +const intervals = [0, 100, 500] +// All six permutations balance both absolute order and pair order. +const orders = [[0, 100, 500], [500, 100, 0], [100, 0, 500], [500, 0, 100], [0, 500, 100], [100, 500, 0]] +const runWorker = (args: Array) => + JSON.parse(execFileSync(process.execPath, [worker, ...args], { + encoding: "utf8", + timeout: 120_000, + maxBuffer: 16 * 1024 * 1024 + })) +mkdirSync(output, { recursive: false }) +mkdirSync(join(output, "profiles")) +mkdirSync(join(output, "harness")) +for (const file of [...harnessFiles, "packages/effect/runtimeperf/stats.mts"]) { + copyFileSync(file, join(output, "harness", basename(file))) +} +const report = { + complete: false, + environment: { + date: new Date().toISOString(), + node: process.version, + v8: process.versions.v8, + arch: process.arch, + platform: process.platform, + release: os.release(), + cpu: os.cpus()[0]?.model, + execArgv: process.execArgv, + nodeOptions: process.env.NODE_OPTIONS ?? "" + }, + source: { revision, sourceTree, hashes, status: git("status", "--short") }, + measurement: { rounds, count, names, intervals, orders, warmup: false, bootstrapIterations: 10_000 }, + validations: [] as Array, + results: [] as Array, + summaries: [] as Array +} +const save = () => writeFileSync(join(output, "results.json"), JSON.stringify(report, null, 2)) +try { + for (const name of names) report.validations.push(runWorker(["validate", name])) + save() + for (let round = 0; round < rounds; round++) { + const rotation = round % names.length + for (const name of [...names.slice(rotation), ...names.slice(0, rotation)]) { + for (const interval of orders[round % orders.length]) { + const profilePath = interval > 0 ? join(output, "profiles", `${name}-${round}-${interval}.cpuprofile`) : "" + const sample = runWorker(["measure", name, String(count), String(interval), profilePath]) + assert.equal(sample.name, name) + assert.equal(sample.count, count) + assert.equal(sample.interval, interval) + assert.ok(Number.isFinite(sample.elapsedMs) && sample.elapsedMs > 0) + report.results.push({ + round, + ...sample, + ...(profilePath ? { profile: analyzeProfile(JSON.parse(readFileSync(profilePath, "utf8"))) } : {}) + }) + } + } + save() + process.stderr.write(`construction round ${round + 1}/${rounds} complete\n`) + } + assert.equal(git("rev-parse", "HEAD"), revision, "HEAD changed during measurement") + assert.equal(git("status", "--porcelain", "--untracked-files=all", "--", "packages/effect/src"), "", "Source changed") + for (const [path, hash] of Object.entries(hashes)) { + assert.equal(sha256(path), hash, `${path} changed during measurement`) + } + assert.equal(report.results.length, rounds * names.length * intervals.length) + for (const name of names) { + const baseline = report.results.filter((sample) => sample.name === name && sample.interval === 0) + report.summaries.push({ + name, + baselineMs: aggregate(baseline.map((sample) => sample.elapsedMs)), + profiles: intervals.filter((interval) => interval > 0).map((interval) => { + const samples = report.results.filter((sample) => sample.name === name && sample.interval === interval) + const observed = samples.filter((sample) => sample.profile.totalSamples > 0) + assert.deepEqual(samples.map((sample) => sample.round), baseline.map((sample) => sample.round)) + const sites = new Map() + for (const sample of samples) { + for (const site of sample.profile.sites) { + const frame = site.frame + const key = `${site.category}:${frame.url}:${frame.lineNumber}:${frame.columnNumber}:${frame.functionName}` + const existing = sites.get(key) ?? { ...site, samples: 0 } + existing.samples += site.samples + sites.set(key, existing) + } + } + return { + interval, + elapsedMs: aggregate(samples.map((sample) => sample.elapsedMs)), + overhead: analyzePairs(baseline.map((sample) => sample.elapsedMs), samples.map((sample) => sample.elapsedMs)), + sampleCount: samples.reduce((sum, sample) => sum + sample.profile.totalSamples, 0), + observedProcesses: observed.length, + sparseProcesses: samples.filter((sample) => sample.profile.totalSamples < 10).length, + medianSpanCoverage: median(samples.map((sample) => sample.profile.spanUs / 1000 / sample.elapsedMs)), + shares: Object.fromEntries(categories.map((category) => [ + category, + observed.length > 0 + ? shareInterval(observed.map((sample) => sample.profile.counts[category] / sample.profile.totalSamples)) + : null + ])), + topSites: [...sites.values()].sort((a, b) => b.samples - a.samples).slice(0, 20) + } + }) + }) + } + report.complete = true +} finally { + save() +} +process.stdout.write(`${join(output, "results.json")}\n`) diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts new file mode 100644 index 0000000000..8c6b75f55f --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts @@ -0,0 +1,48 @@ +import assert from "node:assert/strict" +import { writeFileSync } from "node:fs" +import { Session } from "node:inspector/promises" +import { fixture, validate } from "./fixtures.mts" + +const [mode, name, countArgument = "1000", intervalArgument = "0", profilePath] = process.argv.slice(2) +if (mode === "validate") { + validate(name) + process.stdout.write(JSON.stringify({ validated: name })) +} else { + assert.equal(mode, "measure") + const count = Number(countArgument) + const interval = Number(intervalArgument) + assert.ok(Number.isSafeInteger(count) && count > 0) + assert.ok(Number.isSafeInteger(interval) && interval >= 0) + const { build } = fixture(name) + const roots = new Array(count) + // This frame delimits the construction samples. Inspector and hrtime clocks + // have different origins on some platforms, so they must not be compared. + function constructBatch() { + const start = process.hrtime.bigint() + for (let index = 0; index < count; index++) roots[index] = build(index) + return Number(process.hrtime.bigint() - start) / 1e6 + } + const session = new Session() + try { + if (interval > 0) { + assert.ok(profilePath) + session.connect() + await session.post("Profiler.enable") + await session.post("Profiler.setSamplingInterval", { interval }) + await session.post("Profiler.start") + } + const elapsedMs = constructBatch() + if (interval > 0) { + const { profile } = await session.post("Profiler.stop") + writeFileSync(profilePath, JSON.stringify(profile)) + } + // Do not prepare a decoder, traverse ASTs, or warm the construction path + // before timing. Full semantic validation runs in separate processes. + assert.equal(roots.length, count) + for (const root of roots) assert.equal(typeof root, "function") + assert.notEqual(roots[0], count > 1 ? roots[count - 1] : undefined) + process.stdout.write(JSON.stringify({ name, count, interval, elapsedMs, profilePath })) + } finally { + session.disconnect() + } +} diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts b/repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts new file mode 100644 index 0000000000..6a6f6f52d2 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts @@ -0,0 +1,53 @@ +import * as Schema from "effect/Schema" +import { enable } from "effect/schema/SchemaJITCompiler" +import * as SchemaIssue from "effect/SchemaIssue" +import * as SchemaParser from "effect/SchemaParser" +import assert from "node:assert/strict" + +const fixture = (kind: "struct" | "extra" | "strings" | "symbols", strict: boolean, jit: boolean) => { + const schema = kind === "symbols" + ? Schema.Record(Schema.Symbol, Schema.Number) + : kind === "strings" + ? Schema.Record(Schema.String, Schema.Number) + : Schema.Struct({ name: Schema.String, age: Schema.Number, active: Schema.Boolean }) + const input = kind === "symbols" || kind === "strings" + ? Object.fromEntries(Array.from({ length: 32 }, (_, i) => [kind === "symbols" ? Symbol(`key${i}`) : `key${i}`, i])) + : kind === "extra" + ? { name: "Ada", age: 37, active: true, extra: 1 } + : { name: "Ada", age: 37, active: true } + if (jit) enable(schema.ast) + const decode = SchemaParser.decodeUnknownSync(schema, strict ? { onExcessProperty: "error" } : undefined) + return { + run: kind === "extra" + ? () => { + try { + return decode(input) + } catch (error) { + return error + } + } + : () => decode(input), + validate: (result: unknown) => { + if (kind === "extra") { + assert.ok(result instanceof Error) + assert.ok(SchemaIssue.isIssue(result.cause)) + assert.equal(SchemaIssue.defaultFormatter(result.cause), "Expected no excess property\n at [\"extra\"]") + } else { + assert.deepEqual(result, input) + } + } + } +} + +export const interpretedStructDefault = () => fixture("struct", false, false) +export const jitStructDefault = () => fixture("struct", false, true) +export const interpretedStructStrict = () => fixture("struct", true, false) +export const jitStructStrict = () => fixture("struct", true, true) +export const interpretedExtraStrict = () => fixture("extra", true, false) +export const jitExtraStrict = () => fixture("extra", true, true) +export const interpretedStringsStrict = () => fixture("strings", true, false) +export const jitStringsStrict = () => fixture("strings", true, true) +export const interpretedSymbolsDefault = () => fixture("symbols", false, false) +export const jitSymbolsDefault = () => fixture("symbols", false, true) +export const interpretedSymbolsStrict = () => fixture("symbols", true, false) +export const jitSymbolsStrict = () => fixture("symbols", true, true) diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts index e5d891cd71..f9fd2ac059 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts @@ -28,7 +28,10 @@ export const exitValid = () => { const run = Schema.decodeUnknownExit(schema) return { run: () => run(input), - validate: (result) => assert.equal(result._tag, "Success") + validate: (result) => { + assert.equal(result._tag, "Success") + assert.deepEqual(result.value, input) + } } } @@ -44,7 +47,10 @@ export const optionValid = () => { const run = Schema.decodeUnknownOption(schema) return { run: () => run(input), - validate: (result) => assert.equal(Option.isSome(result), true) + validate: (result) => { + assert.equal(Option.isSome(result), true) + if (Option.isSome(result)) assert.deepEqual(result.value, input) + } } } @@ -60,7 +66,10 @@ export const resultValid = () => { const run = Schema.decodeUnknownResult(schema) return { run: () => run(input), - validate: (result) => assert.equal(Result.isSuccess(result), true) + validate: (result) => { + assert.equal(Result.isSuccess(result), true) + if (Result.isSuccess(result)) assert.deepEqual(result.success, input) + } } } diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts index 773fdce532..dc7895913b 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts @@ -7,25 +7,37 @@ import assert from "node:assert/strict" const decodeCase = (schema, input, success, options) => () => { const run = Schema.decodeUnknownExit(schema, options) + const isOutput = success ? Schema.is(schema) : undefined return { run: () => run(input), - validate: (result) => assert.equal(result._tag, success ? "Success" : "Failure") + validate: (result) => { + assert.equal(result._tag, success ? "Success" : "Failure") + if (result._tag === "Success") assert.equal(isOutput?.(result.value), true) + } } } const decodeParserCase = (schema, input, success, options) => () => { const run = SchemaParser.decodeUnknownExit(schema, options) + const isOutput = success ? Schema.is(schema) : undefined return { run: () => run(input), - validate: (result) => assert.equal(result._tag, success ? "Success" : "Failure") + validate: (result) => { + assert.equal(result._tag, success ? "Success" : "Failure") + if (result._tag === "Success") assert.equal(isOutput?.(result.value), true) + } } } const encodeParserCase = (schema, input, success, options) => () => { const run = SchemaParser.encodeUnknownExit(schema, options) + const isOutput = success ? Schema.is(Schema.flip(schema)) : undefined return { run: () => run(input), - validate: (result) => assert.equal(result._tag, success ? "Success" : "Failure") + validate: (result) => { + assert.equal(result._tag, success ? "Success" : "Failure") + if (result._tag === "Success") assert.equal(isOutput?.(result.value), true) + } } } @@ -146,7 +158,7 @@ export const optionalPresentValid = decodeCase( export const optionalPresentInvalid = decodeCase(optionalStruct, { required: "value", optionalKey: 1 }, false) const suspendedString = Schema.String.pipe(Schema.decode({ - decode: new SchemaGetter.Getter((input) => Effect.suspend(() => Effect.succeed(input))), + decode: SchemaGetter.transformOptionalEffect((input) => Effect.suspend(() => Effect.succeed(input))), encode: SchemaGetter.passthrough() })) const suspendedObjectFields = Object.fromEntries( diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts index 59dc535f96..f07080d6d7 100644 --- a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts +++ b/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts @@ -30,6 +30,34 @@ const makeEffectObject2Schema = () => age: Schema.Number }) +const makeEffectEncodedObject8Schema = () => { + const amount = Schema.String.pipe(Schema.decodeTo( + Schema.Number.check(Schema.isGreaterThanOrEqualTo(0)), + SchemaTransformation.numberFromString + )) + return Schema.fromJsonString(Schema.Struct({ + id: Schema.String, + name: Schema.String.check(Schema.isMinLength(1)), + price: amount, + quantity: amount, + active: Schema.Boolean, + address: Schema.Struct({ city: Schema.String, country: Schema.String }), + tags: Schema.Array(Schema.String).check(Schema.isMinLength(1)), + note: Schema.optionalKey(Schema.String) + })) +} + +const encodedObject8Value = { + id: "product-1", + name: "Product", + price: 12, + quantity: 2, + active: true, + address: { city: "Rome", country: "IT" }, + tags: ["new"] +} +const encodedObject8Input = JSON.stringify({ ...encodedObject8Value, price: "12", quantity: "2" }) + const makeObjectFields = (size: number) => Object.fromEntries(Array.from({ length: size }, (_, index) => [`field${index}`, Schema.NonEmptyString])) @@ -108,9 +136,26 @@ export const effectFirstMakeObject2 = () => ({ validate: (result) => assert.deepEqual(result, object2Input) }) +export const effectSchemaCreationEncodedObject8 = () => ({ + run: makeEffectEncodedObject8Schema, + validate: (schema) => { + assert.deepEqual(Schema.decodeUnknownSync(schema)(encodedObject8Input), encodedObject8Value) + assert.deepEqual(schema.make(encodedObject8Value), encodedObject8Value) + assert.throws(() => schema.make({ ...encodedObject8Value, price: -1 })) + } +}) + +export const effectFirstMakeEncodedObject8 = () => ({ + run: () => makeEffectEncodedObject8Schema().make(encodedObject8Value), + validate: (result) => assert.deepEqual(result, encodedObject8Value) +}) + export const effectFirstDecodeCheckedObject32 = () => ({ run: () => SchemaParser.decodeUnknownExit(makeEffectCheckedSchema())(input), - validate: (result) => assert.equal(result._tag, "Success") + validate: (result) => { + assert.equal(result._tag, "Success") + assert.deepEqual(result.value, input) + } }) export const effectFirstDecodeTemplateLiteral = () => ({ @@ -120,17 +165,26 @@ export const effectFirstDecodeTemplateLiteral = () => ({ export const effectFirstDecodeRecord32 = () => ({ run: () => SchemaParser.decodeUnknownExit(makeEffectRecordSchema())(input), - validate: (result) => assert.equal(result._tag, "Success") + validate: (result) => { + assert.equal(result._tag, "Success") + assert.deepEqual(result.value, input) + } }) export const effectFirstDecodeLiteral100 = () => ({ run: () => SchemaParser.decodeUnknownExit(makeEffectLiteral100Schema())("value99"), - validate: (result) => assert.equal(result._tag, "Success") + validate: (result) => { + assert.equal(result._tag, "Success") + assert.equal(result.value, "value99") + } }) export const effectFirstDecodeTagged100 = () => ({ run: () => SchemaParser.decodeUnknownExit(makeEffectTagged100Schema())(taggedInput), - validate: (result) => assert.equal(result._tag, "Success") + validate: (result) => { + assert.equal(result._tag, "Success") + assert.deepEqual(result.value, taggedInput) + } }) export const effectFirstDecodeEncodingChain8 = () => ({ diff --git a/repos/effect/packages/effect/runtimeperf/test/registry.test.mts b/repos/effect/packages/effect/runtimeperf/test/registry.test.mts index 1598b63440..4596e8bc9b 100644 --- a/repos/effect/packages/effect/runtimeperf/test/registry.test.mts +++ b/repos/effect/packages/effect/runtimeperf/test/registry.test.mts @@ -2,14 +2,33 @@ import assert from "node:assert/strict" import { readFile } from "node:fs/promises" import { describe, it } from "node:test" import { pathToFileURL } from "node:url" -import { loadRegistry } from "../utils.mts" +import { loadRegistry, scenarioBatchSize } from "../utils.mts" describe("runtimeperf registry", () => { + it("uses the Effect calibration for every implementation in a scenario", () => { + const zod = { implementation: "zod4-compiled" } + const effect = { implementation: "effect" } + assert.equal(scenarioBatchSize([zod, effect], new Map([ + [zod, { batchSize: 4_096 }], + [effect, { batchSize: 256 }] + ])), 256) + }) + it("uses unique fixture targets and valid implementations", () => { const { fixtures } = loadRegistry() assert.equal(new Set(fixtures.map((fixture) => fixture.target)).size, fixtures.length) for (const fixture of fixtures) { - assert.ok(["effect", "fast-check-v4", "valibot", "zod4"].includes(fixture.implementation)) + assert.ok([ + "effect", + "effect-aot", + "effect-jit", + "fast-check-v4", + "valibot", + "zod4", + "zod4-compiled", + "zod4-jitless", + "zod4-validate" + ].includes(fixture.implementation)) } }) @@ -19,7 +38,7 @@ describe("runtimeperf registry", () => { fixtures.filter((fixture) => fixture.suite === "arbitrary"), (fixture) => fixture.scenario ) - assert.equal(scenarios.size, 31) + assert.equal(scenarios.size, 33) for (const fixtures of scenarios.values()) { assert.deepEqual(fixtures.map((fixture) => fixture.implementation).sort(), ["effect", "fast-check-v4"]) const metadata = (fixture) => ({ @@ -103,7 +122,7 @@ describe("runtimeperf registry", () => { const { fixtures } = loadRegistry() const zodFiles = new Set( fixtures - .filter((fixture) => fixture.implementation === "zod4") + .filter((fixture) => fixture.suite === "schema-benchmarks" && fixture.implementation === "zod4") .map((fixture) => fixture.fixturePath) ) assert.ok(zodFiles.size > 0) @@ -115,6 +134,34 @@ describe("runtimeperf registry", () => { } }) + it("uses strict Zod compilation for the compiler comparison fixtures", async () => { + const { fixtures } = loadRegistry() + const compiled = fixtures.filter((fixture) => + fixture.suite === "compiler-rebuild" && fixture.implementation === "zod4-compiled" + ) + assert.equal(compiled.length, 19) + const paths = new Set(compiled.map((fixture) => fixture.fixturePath)) + assert.equal(paths.size, 1) + const source = await readFile([...paths][0], "utf8") + assert.match(source, /from "\.\/zod-cases\.ts"/) + const shared = await readFile(new URL("../suites/compiler-rebuild/fixtures/zod-cases.ts", import.meta.url), "utf8") + assert.match(shared, /from "zod\/v4"/) + assert.match(shared, /z\.compile\(value\.schema, \{ strict: true \}\)/) + }) + + it("uses interpreted Zod for the jitless compiler comparison fixtures", async () => { + const { fixtures } = loadRegistry() + const jitless = fixtures.filter((fixture) => + fixture.suite === "compiler-rebuild" && fixture.implementation === "zod4-jitless" + ) + assert.equal(jitless.length, 11) + const paths = new Set(jitless.map((fixture) => fixture.fixturePath)) + assert.equal(paths.size, 1) + const source = await readFile(new URL("../suites/compiler-rebuild/fixtures/zod-cases.ts", import.meta.url), "utf8") + assert.match(source, /z\.validate\(value\.schema, input, \{ jitless: true \}\)/) + assert.match(source, /value\.schema\.parse\(input, \{ jitless: true \}\)/) + }) + it("loads, runs and validates every fixture export", async () => { const { fixtures } = loadRegistry() const modules = new Map() diff --git a/repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts b/repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts new file mode 100644 index 0000000000..460f09b714 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts @@ -0,0 +1,68 @@ +import assert from "node:assert/strict" +import type { Profiler, Runtime } from "node:inspector" +import { describe, it } from "node:test" +import { analyzeProfile, classify, shareInterval } from "../suites/schema-construction/profile.mts" + +const frame = (functionName: string, file = ""): Runtime.CallFrame => ({ + functionName, + url: file ? `file:///repo/packages/effect/${file}` : "", + scriptId: "1", + lineNumber: 0, + columnNumber: 0 +}) +const make = frame("make", "src/internal/schema/make.ts") +const sync = frame("make", "src/SchemaParser.ts") +const effect = frame("makeEffect", "src/SchemaParser.ts") +const option = frame("makeOption", "src/SchemaParser.ts") +const ast = frame("Objects", "src/SchemaAST.ts") + +describe("schema construction attribution", () => { + it("attributes the type traversal to the maker, without counting it again as AST construction", () => { + assert.equal(classify([ast, sync, make]), "type-projection") + assert.equal(classify([frame("", "src/Function.ts"), sync, make]), "type-projection") + assert.equal(classify([ast, make]), "type-projection") + assert.equal(classify([frame("", "src/Function.ts"), make]), "type-projection") + assert.equal(classify([ast, frame("Struct", "src/Schema.ts")]), "ast") + assert.equal(classify([sync, make]), "make-sync") + assert.equal(classify([frame("makeConstructorSync", "src/SchemaParser.ts"), sync, make]), "make-sync") + }) + it("keeps the second effect maker inside the Option adapter", () => { + assert.equal(classify([effect, option, make]), "make-option") + assert.equal(classify([effect, make]), "make-effect") + assert.equal(classify([make]), "schema-object") + }) + it("excludes profiler setup and includes GC only between construction samples", () => { + const node = (id: number, callFrame: Runtime.CallFrame, children: Array = []): Profiler.ProfileNode => ({ + id, + callFrame, + children + }) + const profile: Profiler.Profile = { + startTime: 1000, + endTime: 2000, + nodes: [ + node(1, frame("(root)"), [2, 3, 4]), + node(2, frame("dispatch")), + node(3, frame("constructBatch", "runtimeperf/suites/schema-construction/worker.mts"), [5]), + node(4, frame("(garbage collector)")), + node(5, make) + ], + samples: [2, 4, 5, 4, 5, 2, 4], + timeDeltas: [100, 100, 100, 100, 100, 100, 100] + } + const result = analyzeProfile(profile) + assert.equal(result.totalSamples, 3) + assert.equal(result.excludedSamples, 4) + assert.equal(result.spanUs, 200) + assert.equal(result.counts["schema-object"], 2) + assert.equal(result.counts.gc, 1) + assert.equal(Object.values(result.counts).reduce((a, b) => a + b, 0), result.totalSamples) + assert.equal(analyzeProfile({ ...profile, samples: [2], timeDeltas: [100] }).totalSamples, 0) + }) + it("retains zero shares and uses reproducible process-level intervals", () => { + assert.deepEqual(shareInterval([0, 0], 100), { mean: 0, low: 0, high: 0 }) + assert.deepEqual(shareInterval([0.2, 0.5, 0.8], 100), shareInterval([0.2, 0.5, 0.8], 100)) + assert.equal(shareInterval([0, 1], 100).mean, 0.5) + assert.throws(() => shareInterval([]), /assert|false/i) + }) +}) diff --git a/repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts b/repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts new file mode 100644 index 0000000000..761e6066d7 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts @@ -0,0 +1,57 @@ +import assert from "node:assert/strict" +import { spawnSync } from "node:child_process" +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { describe, it } from "node:test" +import { applyWorktreeDiff, readWorktreeDiff } from "../worktree-diff.mts" + +const git = (cwd: string, args: Array) => { + const result = spawnSync("git", ["-c", "user.name=test", "-c", "user.email=test@example.com", ...args], { + cwd, + encoding: "utf8" + }) + assert.equal(result.status, 0, result.stderr) +} + +// Commits `before` in a repository, adds a second worktree at that commit, +// writes `after` into the first one and replays its diff onto the second. +const replay = (before: Record, after: Record) => { + const root = mkdtempSync(join(tmpdir(), "effect-runtimeperf-diff-")) + try { + const source = join(root, "source") + const target = join(root, "target") + git(root, ["init", "-q", source]) + for (const [file, content] of Object.entries(before)) writeFileSync(join(source, file), content) + git(source, ["add", "."]) + git(source, ["commit", "-q", "-m", "before"]) + git(source, ["worktree", "add", "-q", "--detach", target, "HEAD"]) + for (const [file, content] of Object.entries(after)) writeFileSync(join(source, file), content) + + applyWorktreeDiff(target, readWorktreeDiff(source)) + + for (const file of Object.keys(after)) { + assert.deepEqual(readFileSync(join(target, file)), readFileSync(join(source, file)), file) + } + } finally { + rmSync(root, { recursive: true, force: true }) + } +} + +describe("runtimeperf worktree diff", () => { + it("replays a text change", () => { + replay({ "a.txt": "a\nb\nc\n" }, { "a.txt": "a\nB\nc\n" }) + }) + + it("replays a patch whose last context line is blank", () => { + replay({ "a.txt": "a\nb\n\n" }, { "a.txt": "A\nb\n\n" }) + }) + + it("replays an added whitespace-only last line", () => { + replay({ "a.txt": "a\n" }, { "a.txt": "a\n \n" }) + }) + + it("replays a binary change", () => { + replay({ "b.bin": new Uint8Array([0, 1, 2, 3]) }, { "b.bin": new Uint8Array([0, 9, 2, 3, 4]) }) + }) +}) diff --git a/repos/effect/packages/effect/runtimeperf/utils.mts b/repos/effect/packages/effect/runtimeperf/utils.mts index 2dc6210b02..2df3859b58 100644 --- a/repos/effect/packages/effect/runtimeperf/utils.mts +++ b/repos/effect/packages/effect/runtimeperf/utils.mts @@ -8,6 +8,11 @@ import { aggregate } from "./stats.mts" export const runtimeperfDir = dirname(fileURLToPath(import.meta.url)) export const effectDir = resolve(runtimeperfDir, "..") export const repoRoot = resolve(effectDir, "../..") +export const runPath = join(runtimeperfDir, "run.mts") +export const comparePath = join(runtimeperfDir, "compare.mts") +export const materializePath = join(runtimeperfDir, "materialize.mts") +export const statsPath = join(runtimeperfDir, "stats.mts") +export const utilsPath = fileURLToPath(import.meta.url) export const workerPath = join(runtimeperfDir, "worker.mts") export const configPath = join(runtimeperfDir, "config.json") export const resultsRoot = join(repoRoot, "tmp", "runtimeperf", "results") @@ -147,7 +152,7 @@ export const selectFixtures = (fixtures, options, { effectOnly = false } = {}) = selected = selected.filter((fixture) => fixture.implementation === options.implementation) } if (effectOnly) { - selected = selected.filter((fixture) => fixture.implementation === "effect") + selected = selected.filter((fixture) => fixture.implementation.startsWith("effect")) } if (selected.length === 0) { throw new Error("No runtimeperf fixtures matched the selection") @@ -216,6 +221,13 @@ export const measureFixture = (fixture, defaults, batchSize, fixturePath = fixtu export const aggregateMeasurements = (measurements) => aggregate(measurements.map((item) => item.nsPerOp)) +export const scenarioBatchSize = (fixtures, calibrations) => { + const reference = fixtures.find((fixture) => fixture.implementation === "effect") ?? fixtures[0] + const calibration = calibrations.get(reference) + if (calibration === undefined) throw new Error("Missing scenario reference calibration") + return calibration.batchSize +} + export const coverageSummary = (fixtures) => ({ tiers: [...new Set(fixtures.map((fixture) => fixture.tier))].sort(), families: [...new Set(fixtures.map((fixture) => fixture.family))].sort(), @@ -223,7 +235,7 @@ export const coverageSummary = (fixtures) => ({ effectAstTags: [ ...new Set( fixtures - .filter((fixture) => fixture.implementation === "effect") + .filter((fixture) => fixture.implementation.startsWith("effect")) .flatMap((fixture) => fixture.astTags) ) ].sort() diff --git a/repos/effect/packages/effect/runtimeperf/worktree-diff.mts b/repos/effect/packages/effect/runtimeperf/worktree-diff.mts new file mode 100644 index 0000000000..d702430c50 --- /dev/null +++ b/repos/effect/packages/effect/runtimeperf/worktree-diff.mts @@ -0,0 +1,25 @@ +import { spawnSync } from "node:child_process" + +// Returns stdout untouched: a patch ends in newlines and can end in a context +// line that is a single space, so trimming it corrupts the patch. +const gitRaw = (cwd, args, input) => { + const result = spawnSync("git", args, { + cwd, + encoding: "utf8", + maxBuffer: 50 * 1024 * 1024, + input + }) + if (result.error) throw result.error + if (result.status !== 0) { + throw new Error(`${result.stdout}${result.stderr}`.trim()) + } + return result.stdout +} + +/** The uncommitted changes to tracked files of `root`, as a binary patch. */ +export const readWorktreeDiff = (root) => gitRaw(root, ["diff", "--binary", "HEAD", "--"]) + +/** Applies a patch read by `readWorktreeDiff` to the worktree at `root`. */ +export const applyWorktreeDiff = (root, diff) => { + if (diff !== "") gitRaw(root, ["apply", "--binary", "-"], diff) +} diff --git a/repos/effect/packages/effect/src/unstable/arbitrary/Arbitrary.ts b/repos/effect/packages/effect/src/Arbitrary.ts similarity index 78% rename from repos/effect/packages/effect/src/unstable/arbitrary/Arbitrary.ts rename to repos/effect/packages/effect/src/Arbitrary.ts index 14c0a75824..42d7bc1356 100644 --- a/repos/effect/packages/effect/src/unstable/arbitrary/Arbitrary.ts +++ b/repos/effect/packages/effect/src/Arbitrary.ts @@ -1,23 +1,25 @@ /** * Derives, samples, and checks generated values from Effect Schema. * + * @stability unstable * @since 4.0.0 */ -import * as Cause from "../../Cause.ts" -import type * as Effect from "../../Effect.ts" -import type * as Filter from "../../Filter.ts" -import * as Formatter from "../../Formatter.ts" -import { dual } from "../../Function.ts" -import type * as Model from "../../internal/arbitrary/model.ts" -import * as Internal from "../../internal/arbitrary/runner.ts" -import type { Pipeable } from "../../Pipeable.ts" -import { hasProperty, type Predicate, type Refinement } from "../../Predicate.ts" -import type * as Schema_ from "../../Schema.ts" -import type * as Types from "../../Types.ts" +import * as Cause from "./Cause.ts" +import type * as Effect from "./Effect.ts" +import type * as Filter from "./Filter.ts" +import * as Formatter from "./Formatter.ts" +import { dual } from "./Function.ts" +import type * as Model from "./internal/arbitrary/model.ts" +import * as Internal from "./internal/arbitrary/runner.ts" +import type { Pipeable } from "./Pipeable.ts" +import { hasProperty, type Predicate, type Refinement } from "./Predicate.ts" +import type * as Schema_ from "./Schema.ts" +import type * as Types from "./Types.ts" /** * Runtime type identifier for `Arbitrary` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -26,6 +28,7 @@ export const TypeId: TypeId = Internal.TypeId /** * Type of the runtime identifier for `Arbitrary` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -43,13 +46,16 @@ export type TypeId = "~effect/arbitrary/Arbitrary" * * Arbitraries implement `Pipeable`, so data-last combinators can be composed with `.pipe(...)`. * + * @stability unstable * @category models * @since 4.0.0 */ export interface Arbitrary extends Pipeable { readonly [TypeId]: TypeId readonly "~A": Types.Covariant - /** @internal */ + /** + * @internal + */ readonly gen: Model.Generator } @@ -61,6 +67,7 @@ export interface Arbitrary extends Pipeable { * `shrink` returns the immediate semantic simplifications of a failing value. Each returned candidate is validated * against the decoded side of the original Schema before it can reach the property. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -68,6 +75,23 @@ export interface SchemaOptions { readonly shrink?: ((value: A) => ReadonlyArray) | undefined } +/** + * Configures the lengths generated and preserved during shrinking by {@link array}. + * + * **Details** + * + * `minLength` defaults to zero. Generation grows with `size`, honors `minLength` even at size zero, and is capped by + * `maxLength` when provided. Both bounds must be integers between zero and 4294967295, with `minLength <= maxLength`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ArrayOptions { + readonly minLength?: number | undefined + readonly maxLength?: number | undefined +} + /** * Checks whether a value is an `Arbitrary`. * @@ -75,6 +99,7 @@ export interface SchemaOptions { * * Use when accepting both Arbitrary values and other input descriptions. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -89,6 +114,7 @@ export const isArbitrary = (u: unknown): u is Arbitrary => hasProperty( * or object property observes the same size independently, while recursive branches share one recursion allowance. * Explicit Schema minima and required members are still honored, while explicit maxima clamp generation. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -107,6 +133,7 @@ export interface SampleOptions { * The effective `seed` can be passed to {@link sampleEffect} to reproduce the exhausted run, including when sampling * originally selected a seed from the Effect `Random` service. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -134,6 +161,7 @@ export interface SampleError { * * Replay compatibility is not guaranteed across releases of this unstable module. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -160,6 +188,7 @@ export type Replay = string * When `replay` is present, its recorded seed, attempt, size, and shrink path control the run. The `runs`, `size`, * `maxDiscards`, `maxShrinks`, and `seed` options are ignored. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -172,9 +201,41 @@ export interface CheckOptions { readonly replay?: Replay | undefined } +/** + * Independent defaults for property checking and sampling, excluding replay tokens. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface GlobalOptions { + readonly check?: Omit | undefined + readonly sample?: SampleOptions | undefined +} + +/** + * Replaces the defaults used by {@link checkEffect} and {@link sampleEffect}. + * + * **Details** + * + * Options are resolved once per execution: explicit non-`undefined` values, then configured defaults, then built-in + * defaults. The supplied configuration is copied. Pass `{}` to reset it. Replay tokens retain control of replayed checks. + * + * **Gotchas** + * + * Configure defaults before starting concurrent tests. Changes affect subsequent executions, including Effects created + * earlier, but do not affect active runs. + * + * @stability unstable + * @category configuration + * @since 4.0.0 + */ +export const configureGlobal = Internal.configureGlobal + /** * Identifies a property that returned `false`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -185,6 +246,7 @@ export interface ReturnedFalse { /** * Preserves a typed failure produced by an effectful property. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -196,6 +258,7 @@ export interface PropertyError { /** * Represents the reason a property was falsified. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -204,6 +267,7 @@ export type PropertyFailure = ReturnedFalse | PropertyError /** * Reports that every requested property run passed. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -224,6 +288,7 @@ export interface Passed { * `runs` counts main property evaluations through the falsifying evaluation. It excludes evaluations performed while * shrinking. A replay reports one run. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -246,6 +311,7 @@ export interface Falsified { * The effective `seed` can be passed to {@link checkEffect} to reproduce the exhausted run, including when checking * originally selected a seed from the Effect `Random` service. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -265,6 +331,7 @@ export interface Exhausted { * - `ShrinkPassed` means that the root switched failure class, or that a recorded shrink either passed or switched * failure class. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -280,6 +347,7 @@ export interface ReplayMismatch { * * Defects and fiber interruption are not converted to this data type and continue through the returned `Effect`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -292,6 +360,7 @@ export type CheckResult = Passed | Falsified | Exhausted | ReplayMis * * Use when integrating `checkEffect` with a test runner or another reporting interface. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -340,6 +409,7 @@ export function formatCheckFailure(result: CheckResult): string | un * A custom shrinker replaces Schema-derived shrinking. It is evaluated lazily after a property failure and must be * synchronous, deterministic, terminating, and free of mutation. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -362,6 +432,7 @@ export function schema( * Every generation returns the same value. Objects are not cloned, so properties must not mutate them. * * @see {@link flatMap} for selecting dependent Arbitraries + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -369,6 +440,55 @@ export function Constant(value: A): Arbitrary { return Internal.constant(value) } +/** + * Generates variable-length arrays from an existing element `Arbitrary`. + * + * **When to use** + * + * Use when generating command sequences or collections whose elements are built with Arbitrary composition. + * + * **Details** + * + * Shrinking tries removing blocks, including prefixes and interior blocks, while preserving the order and values of + * retained elements. It also tries the elements' own shrink candidates. Length bounds remain valid during shrinking, + * and elements share the generation budget for recursion. + * + * **Gotchas** + * + * Invalid length bounds throw a `RangeError` when this function is called. Retained objects are not cloned, so + * properties must not mutate generated values. Bounded shrinking does not guarantee a globally minimal failure. + * + * **Example** (Removing irrelevant commands) + * + * ```ts import.meta.vitest + * import { Arbitrary, Effect, Schema } from "effect" + * + * const command = Arbitrary.schema(Schema.Literals(["Add", "Reset", "Stop"])) + * const commands = Arbitrary.array(command, { maxLength: 50 }) + * const result = await Effect.runPromise( + * Arbitrary.checkEffect(commands, (values) => { + * // The state machine fails when Reset occurs before a later Stop. + * const reset = values.indexOf("Reset") + * return reset === -1 || !values.slice(reset + 1).includes("Stop") + * }, { runs: 1, size: 4, seed: 0 }) + * ) + * + * result._tag // => "Falsified" + * if (result._tag === "Falsified") { + * result.initialInput // => ["Add", "Add", "Reset", "Stop"] + * result.shrunkInput // => ["Reset", "Stop"] + * } + * ``` + * + * @see {@link ArrayOptions} for length bounds + * @see {@link all} for fixed tuples and records of Arbitraries + * @see {@link schema} for deriving arrays described by Schema + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const array: (item: Arbitrary, options?: ArrayOptions) => Arbitrary> = Internal.array + /** * Transforms every generated value and its shrink candidates. * @@ -377,6 +497,7 @@ export function Constant(value: A): Arbitrary { * Use when you want to derive generated values from an existing `Arbitrary` without changing its generation or shrink * structure. * + * @stability unstable * @category mapping * @since 4.0.0 */ @@ -398,6 +519,7 @@ export const map: { * may generate matching values directly. * * @see {@link filterMap} for transforming and filtering simultaneously + * @stability unstable * @category filtering * @since 4.0.0 */ @@ -422,6 +544,7 @@ export const filter: { * * @see {@link map} for transformations that cannot reject * @see {@link filter} for retaining original values that satisfy a condition + * @stability unstable * @category filtering * @since 4.0.0 */ @@ -453,6 +576,7 @@ export const filterMap: { * * @see {@link map} for total transformations that do not select another Arbitrary * @see {@link Constant} for dependent branches that return an existing value + * @stability unstable * @category sequencing * @since 4.0.0 */ @@ -479,6 +603,8 @@ export const flatMap: { * Iterable inputs are consumed when `all` is called. If any member discards a generated root, the complete generated * value is discarded. * + * @see {@link array} for variable-length arrays that shrink by removing elements + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -504,6 +630,7 @@ export function all> | Record( * it repeatedly and does not restore mutable services between evaluations. Stateful properties should acquire and * release an independent fixture inside each evaluation. * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/Array.ts b/repos/effect/packages/effect/src/Array.ts index 8813ded8ce..65dd5016d1 100644 --- a/repos/effect/packages/effect/src/Array.ts +++ b/repos/effect/packages/effect/src/Array.ts @@ -3720,7 +3720,7 @@ export const getSomes: >, X = any>( * ``` * * @see {@link getSuccesses} — extract success values - * @see {@link separate} — split into failures and successes + * @see {@link separate} — split into successes and failures * * @category filtering * @since 4.0.0 @@ -3756,7 +3756,7 @@ export const getFailures = >>( * ``` * * @see {@link getFailures} — extract failure values - * @see {@link separate} — split into failures and successes + * @see {@link separate} — split into successes and failures * * @category filtering * @since 4.0.0 @@ -3795,7 +3795,7 @@ export const getSuccesses = >>( * ``` * * @see {@link filter} — keep original elements matching a predicate - * @see {@link partition} for keeping both failures and successes + * @see {@link partition} for keeping both successes and failures * * @category filtering * @since 2.0.0 @@ -3862,16 +3862,16 @@ export const filter: { ) /** - * Splits an iterable using a `Filter` into failures and successes. + * Splits an iterable using a `Filter` into successes and failures. * * **When to use** * * Use to partition an iterable by evaluating each element with a - * `Result`-returning filter and keeping both failure and success values. + * `Result`-returning filter and keeping both success and failure values. * * **Details** * - * Returns `[excluded, satisfying]`. The filter receives `(element, index)`. + * Returns `[passes, fails]`. The filter receives `(element, index)`. * * **Example** (Partitioning with a filter) * @@ -3880,7 +3880,7 @@ export const filter: { * * Array.partition([1, -2, 3], (n, i) => * n > 0 ? Result.succeed(n + i) : Result.fail(`negative:${n}`) - * ) // => [["negative:-2"], [1, 5]] + * ) // => [[1, 5], ["negative:-2"]] * ``` * * @see {@link filter} — keep only matching elements @@ -3893,42 +3893,42 @@ export const filter: { export const partition: { ( f: (input: NoInfer, i: number) => Result.Result - ): (self: Iterable) => [excluded: Array, satisfying: Array] + ): (self: Iterable) => [passes: Array, fails: Array] ( self: Iterable, f: (input: A, i: number) => Result.Result - ): [excluded: Array, satisfying: Array] + ): [passes: Array, fails: Array] } = dual( 2, ( self: Iterable, f: (input: A, i: number) => Result.Result - ): [excluded: Array, satisfying: Array] => { - const excluded: Array = [] - const satisfying: Array = [] + ): [passes: Array, fails: Array] => { + const passes: Array = [] + const fails: Array = [] let i = 0 for (const a of self) { const result = f(a, i++) if (Result.isSuccess(result)) { - satisfying.push(result.success) + passes.push(result.success) } else { - excluded.push(result.failure) + fails.push(result.failure) } } - return [excluded, satisfying] + return [passes, fails] } ) /** - * Separates an iterable of `Result`s into failure values and success values. + * Separates an iterable of `Result`s into success values and failure values. * * **When to use** * - * Use to split an iterable of `Result` values into failure and success arrays. + * Use to split an iterable of `Result` values into success and failure arrays. * * **Details** * - * Returns `[failures, successes]`. This is equivalent to + * Returns `[successes, failures]`. This is equivalent to * `partition(identity)`. * * **Example** (Separating Results) @@ -3936,7 +3936,7 @@ export const partition: { * ```ts import.meta.vitest * import { Array, Result } from "effect" * - * Array.separate([Result.succeed(1), Result.fail("error"), Result.succeed(2)]) // => [["error"], [1, 2]] + * Array.separate([Result.succeed(1), Result.fail("error"), Result.succeed(2)]) // => [[1, 2], ["error"]] * ``` * * @see {@link getFailures} — extract only failures @@ -3949,8 +3949,8 @@ export const partition: { export const separate: >>( self: T ) => [ - failures: Array>>, - successes: Array>> + successes: Array>>, + failures: Array>> ] = partition(identity) /** @@ -4492,18 +4492,14 @@ export const dedupeWith: { } = dual( 2, (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Array => { - const input = fromIterable(self) - if (isReadonlyArrayNonEmpty(input)) { - const out: NonEmptyArray = [headNonEmpty(input)] - const rest = tailNonEmpty(input) - for (const r of rest) { - if (out.every((a) => !isEquivalent(r, a))) { - out.push(r) - } + const out: Array = [] + next: for (const r of fromIterable(self)) { + for (let i = 0; i < out.length; i++) { + if (isEquivalent(r, out[i])) continue next } - return out + out.push(r) } - return [] + return out } ) diff --git a/repos/effect/packages/effect/src/BigDecimal.ts b/repos/effect/packages/effect/src/BigDecimal.ts index f7d6604753..729fcdae8a 100644 --- a/repos/effect/packages/effect/src/BigDecimal.ts +++ b/repos/effect/packages/effect/src/BigDecimal.ts @@ -58,10 +58,10 @@ const BigDecimalProto: Omit = { [TypeId]: TypeId, [Hash.symbol](this: BigDecimal): number { const normalized = normalize(this) - return Hash.combine(Hash.hash(normalized.value), Hash.number(normalized.scale)) + return Hash.combine(Hash.string(String(normalized.value)), Hash.number(normalized.scale)) }, [Equal.symbol](this: BigDecimal, that: unknown): boolean { - return isBigDecimal(that) && equals(this, that) + return isBigDecimal(that) && compare(this, that) === 0 }, toString(this: BigDecimal) { return `BigDecimal(${format(this)})` @@ -146,6 +146,12 @@ export const make = (value: bigint, scale: number): BigDecimal => { return o } +const makeNormalized = (value: bigint, scale: number): BigDecimal => { + const o = make(value, scale) + o.normalized = o + return o +} + /** * Internal function used to create pre-normalized `BigDecimal`s. * @@ -156,9 +162,7 @@ export const makeNormalizedUnsafe = (value: bigint, scale: number): BigDecimal = throw new RangeError("Value must be normalized") } - const o = make(value, scale) - o.normalized = o - return o + return makeNormalized(value, scale) } const bigint0 = BigInt(0) @@ -168,7 +172,7 @@ const bigint2 = BigInt(2) const bigint5 = BigInt(5) const bigint_5 = BigInt(-5) const bigint10 = BigInt(10) -const zero = makeNormalizedUnsafe(bigint0, 0) +const zero = makeNormalized(bigint0, 0) const one = makeNormalizedUnsafe(bigint1, 0) /** @@ -202,23 +206,9 @@ export const normalize = (self: BigDecimal): BigDecimal => { self.normalized = zero } else { const digits = `${self.value}` - - let trail = 0 - for (let i = digits.length - 1; i >= 0; i--) { - if (digits[i] === "0") { - trail++ - } else { - break - } - } - - if (trail === 0) { - self.normalized = self - } - - const value = BigInt(digits.substring(0, digits.length - trail)) - const scale = self.scale - trail - self.normalized = makeNormalizedUnsafe(value, scale) + let end = digits.length + while (digits[end - 1] === "0") end-- + self.normalized = makeNormalized(BigInt(digits.slice(0, end)), self.scale - (digits.length - end)) } } @@ -649,12 +639,7 @@ const compareMagnitude = (self: BigDecimal, that: BigDecimal): Ordering => { if (exponentDifference !== bigint0) return exponentDifference < bigint0 ? -1 : 1 const length = Math.max(selfDigits.length, thatDigits.length) - for (let i = 0; i < length; i++) { - const selfDigit = i < selfDigits.length ? selfDigits.charCodeAt(i) : 48 - const thatDigit = i < thatDigits.length ? thatDigits.charCodeAt(i) : 48 - if (selfDigit !== thatDigit) return selfDigit < thatDigit ? -1 : 1 - } - return 0 + return order.String(selfDigits.padEnd(length, "0"), thatDigits.padEnd(length, "0")) } const compare = (self: BigDecimal, that: BigDecimal): Ordering => { @@ -1407,27 +1392,12 @@ export const format = (n: BigDecimal): string => { } const negative = normalized.value < bigint0 - const absolute = negative ? `${normalized.value}`.substring(1) : `${normalized.value}` - - let before: string - let after: string - - if (normalized.scale >= absolute.length) { - before = "0" - after = "0".repeat(normalized.scale - absolute.length) + absolute - } else { - const location = absolute.length - normalized.scale - if (location > absolute.length) { - const zeros = location - absolute.length - before = `${absolute}${"0".repeat(zeros)}` - after = "" - } else { - after = absolute.slice(location) - before = absolute.slice(0, location) - } - } - - const complete = after === "" ? before : `${before}.${after}` + const absolute = `${negative ? -normalized.value : normalized.value}` + const digits = normalized.scale > 0 + ? absolute.padStart(normalized.scale + 1, "0") + : absolute.padEnd(absolute.length - normalized.scale, "0") + const point = digits.length - normalized.scale + const complete = normalized.scale > 0 ? `${digits.slice(0, point)}.${digits.slice(point)}` : digits return negative ? `-${complete}` : complete } @@ -1457,17 +1427,12 @@ export const toExponential = (n: BigDecimal): string => { } const normalized = normalize(n) - const digits = `${abs(normalized).value}` - const head = digits.slice(0, 1) - const tail = digits.slice(1) - - let output = `${isNegative(normalized) ? "-" : ""}${head}` - if (tail !== "") { - output += `.${tail}` - } - + const digits = `${normalized.value}` + const point = normalized.value < bigint0 ? 2 : 1 + const head = digits.slice(0, point) + const tail = digits.slice(point) const exp = tail.length - normalized.scale - return `${output}e${exp >= 0 ? "+" : ""}${exp}` + return `${head}${tail === "" ? "" : `.${tail}`}e${exp >= 0 ? "+" : ""}${exp}` } /** diff --git a/repos/effect/packages/effect/src/ByteSize.ts b/repos/effect/packages/effect/src/ByteSize.ts index ae7dfc1125..ddacee0f73 100644 --- a/repos/effect/packages/effect/src/ByteSize.ts +++ b/repos/effect/packages/effect/src/ByteSize.ts @@ -32,10 +32,45 @@ export type ByteSize = Brand.Branded /** * Values accepted by byte-size decoding operations. * + * **Details** + * + * String inputs require a non-negative decimal integer without leading zeros, + * followed by a canonical unit symbol or lowercase unit name, optionally + * separated by one space. Numeric inputs are validated at runtime. + * + * @see {@link fromString} for external strings and fractional quantities + * @see {@link fromStringUnsafe} for throwing string validation * @category models * @since 4.0.0 */ -export type Input = ByteSize | bigint | number | string +export type Input = ByteSize | bigint | number | `${IntegerQuantity}${"" | " "}${InputUnit}` + +// Combining these constraints excludes signs, radix prefixes, leading zeros, +// fractions, and exponent notation without limiting the number of digits. +type IntegerQuantity = "0" | (`${bigint}` & `${"1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"}${string}`) + +type InputUnit = + | Unit + | `${ + | "" + | "kilo" + | "mega" + | "giga" + | "tera" + | "peta" + | "exa" + | "zetta" + | "yotta" + | "ronna" + | "quetta" + | "kibi" + | "mebi" + | "gibi" + | "tebi" + | "pebi" + | "exbi" + | "zebi" + | "yobi"}byte${"" | "s"}` /** * Canonical decimal byte unit symbols. @@ -172,7 +207,21 @@ const fromQuantity = (quantity: number | bigint, unit: UnitInfo): ByteSize => { return make(BigInt(value)) } -const parse = (input: string): ByteSize => { +/** + * Parses a byte-size string and throws for invalid syntax, unsupported units, + * or quantities that do not represent non-negative integral bytes. + * + * **Details** + * + * Accepts decimal fractions, canonical SI and IEC symbols, lowercase unit + * names, and whitespace around the quantity and unit. Parsing uses exact + * bigint arithmetic, including above the safe integer range. + * + * @see {@link fromString} for non-throwing validation + * @category constructors + * @since 4.0.0 + */ +export const fromStringUnsafe = (input: string): ByteSize => { const match = /^\s*(\d+)(?:\.(\d+))?\s*([A-Za-z]+)\s*$/.exec(input) if (match === null) return invalid(`unsupported syntax ${JSON.stringify(input)}`) const unit = unitsByName.get(match[3]) @@ -186,6 +235,21 @@ const parse = (input: string): ByteSize => { return make(numerator / scale) } +/** + * Parses a byte-size string, returning `None` for invalid syntax, unsupported + * units, or quantities that do not represent non-negative integral bytes. + * + * **Details** + * + * Accepts external strings and fractional quantities using the same syntax + * as `fromStringUnsafe`. + * + * @see {@link fromStringUnsafe} for throwing validation + * @category constructors + * @since 4.0.0 + */ +export const fromString: (input: string) => Option.Option = Option.liftThrowable(fromStringUnsafe) + /** * Decodes a trusted input into a byte size and throws for invalid input. * @@ -200,7 +264,7 @@ export const fromInputUnsafe = (input: Input): ByteSize => { case "number": return fromNumber(input) case "string": - return parse(input) + return fromStringUnsafe(input) } return invalid(`unsupported input ${input}`) } diff --git a/repos/effect/packages/effect/src/Cache.ts b/repos/effect/packages/effect/src/Cache.ts index 0bc335802d..504d98dde5 100644 --- a/repos/effect/packages/effect/src/Cache.ts +++ b/repos/effect/packages/effect/src/Cache.ts @@ -425,13 +425,16 @@ export const get: { (self: Cache, key: Key): Effect.Effect => core.withFiber((fiber) => { const oentry = MutableHashMap.get(self.map, key) - if (Option.isSome(oentry) && !hasExpired(oentry.value, fiber)) { - // Move the entry to the end of the map to keep it fresh + if (Option.isSome(oentry)) { MutableHashMap.remove(self.map, key) - MutableHashMap.set(self.map, key, oentry.value) - return oentry.value.await() + if (!hasExpired(oentry.value, fiber)) { + // Move the entry to the end of the map to keep it fresh + MutableHashMap.set(self.map, key, oentry.value) + return oentry.value.await() + } } const entry = new EntryImpl(fiber, self.lookup(key)) + let skipCache = false entry.fiber.addObserver((exit) => { if (effect.exitHasInterrupts(exit)) { const current = MutableHashMap.get(self.map, key) @@ -441,14 +444,18 @@ export const get: { return } const ttl = self.timeToLive(exit, key) - if (Duration.isFinite(ttl)) { + if (Duration.isZero(ttl)) { + skipCache = true + const current = MutableHashMap.get(self.map, key) + if (Option.isSome(current) && current.value === entry) { + MutableHashMap.remove(self.map, key) + } + } else if (Duration.isFinite(ttl)) { entry.expiresAt = fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) - } else if (Duration.isZero(ttl)) { - MutableHashMap.remove(self.map, key) } }) const exit = entry.fiber.pollUnsafe() - if (exit === undefined || !effect.exitHasInterrupts(exit)) { + if (!skipCache && (exit === undefined || !effect.exitHasInterrupts(exit))) { MutableHashMap.set(self.map, key, entry) } if (Number.isFinite(self.capacity)) { diff --git a/repos/effect/packages/effect/src/Channel.ts b/repos/effect/packages/effect/src/Channel.ts index 5845d91ba3..53357cc35e 100644 --- a/repos/effect/packages/effect/src/Channel.ts +++ b/repos/effect/packages/effect/src/Channel.ts @@ -21,7 +21,7 @@ import type * as Filter from "./Filter.ts" import type { LazyArg } from "./Function.ts" import { constant, constTrue, constVoid, dual, identity as identity_ } from "./Function.ts" import * as Count from "./internal/count.ts" -import { ClockRef, endSpan, scopeFinalizerCountUnsafe } from "./internal/effect.ts" +import { ClockRef, endSpan } from "./internal/effect.ts" import { addSpanStackTrace } from "./internal/tracer.ts" import * as Iterable from "./Iterable.ts" import * as Latch from "./Latch.ts" @@ -2165,7 +2165,8 @@ const mapEffectConcurrent = < yield* semaphore.take(1).pipe( Effect.flatMap(() => pull), Effect.flatMap((value) => { - trackFiber(runFork(handle(f(value, i++)))) + const index = i++ + trackFiber(runFork(handle(Effect.suspend(() => f(value, index))))) return Effect.void }), Effect.forever({ disableYield: true }), @@ -2203,7 +2204,8 @@ const mapEffectConcurrent = < yield* pull.pipe( Effect.flatMap((value) => { if (errorCause) return Effect.failCause(errorCause) - const fiber = runFork(f(value, i++)) + const index = i++ + const fiber = runFork(Effect.suspend(() => f(value, index))) trackFiber(fiber) fiber.addObserver(onExit) return Queue.offer(effects, Fiber.join(fiber)) @@ -2512,8 +2514,8 @@ const flatMapSequential = < }) const catchHalt = Pull.catchDone((_) => { childPull = undefined - // we can reuse the scope if the only finalizer is the "fork" one - if (childScope!.state._tag === "Open" && scopeFinalizerCountUnsafe(childScope!) === 1) { + // the scope can be reused if the inner channel left no finalizers behind + if (childScope!.state._tag === "Empty") { return makePull } const close = Scope.close(childScope!, Exit.void) @@ -3219,31 +3221,42 @@ export const repeat: { Schedule.toStepWithMetadata(typeof schedule === "function" ? schedule(identity_) : schedule).pipe( Effect.map((step) => { let meta = Schedule.CurrentMetadata.defaultValue() - const loop: Channel< - OutElem, - OutErr | SE, - OutDone, - InElem, - InErr, - InDone, - Env | SR - > = concatWith( + return repeatLoop( provideServiceEffect(self, Schedule.CurrentMetadata, Effect.sync(() => meta)), (done) => step(done).pipe( Effect.map((meta_) => { meta = meta_ - return loop }), - Pull.catchDone(() => Effect.succeed(end(done))), - unwrap + Pull.catchDone(() => Cause.done(done)) ) ) - return loop }), unwrap )) +const repeatLoop = ( + self: Channel, + onDone: (done: OutDone) => Pull.Pull +): Channel => + fromTransform((upstream, scope) => + Effect.sync(() => { + let currentPull: Pull.Pull | undefined + const makePull = (): Pull.Pull => { + const runScope = Scope.forkUnsafe(scope) + return Effect.flatMap(toTransform(self)(upstream, runScope), (pull) => { + currentPull = Pull.catchDone(pull, (done) => + Scope.close(runScope, Exit.void).pipe( + Effect.flatMap(() => onDone(done as OutDone)), + Effect.flatMap(makePull) + )) + return currentPull + }) + } + return Effect.suspend(() => currentPull ?? makePull()) + }) + ) + /** * Repeats this channel forever. * @@ -3252,7 +3265,7 @@ export const repeat: { */ export const forever = ( self: Channel -): Channel => concatWith(self, () => forever(self)) +): Channel => repeatLoop(self, () => Effect.void) /** * Runs a schedule step for each output element while preserving the emitted @@ -8862,8 +8875,9 @@ export const runIntoPubSubArray: { * **Details** * * Emitted non-empty arrays are published as output `Take` values. When the - * channel ends, its final `Exit` is published so subscribers can observe - * completion or failure. + * channel ends, the `PubSub` is ended with its final `Exit`, so every + * subscriber, including one that subscribes later, observes completion or + * failure once it has consumed its buffered values. * * @category destructors * @since 4.0.0 @@ -8915,7 +8929,7 @@ export const toPubSubTake: { ) { const pubsub = yield* makePubSub>(options) yield* runForEach(self, (value) => PubSub.publish(pubsub, value)).pipe( - Effect.onExit((exit) => PubSub.publish(pubsub, exit)), + Effect.onExit((exit) => PubSub.end(pubsub, exit)), Effect.forkScoped ) return pubsub diff --git a/repos/effect/packages/effect/src/ChannelSchema.ts b/repos/effect/packages/effect/src/ChannelSchema.ts index 6f91859ecc..b2a84f438c 100644 --- a/repos/effect/packages/effect/src/ChannelSchema.ts +++ b/repos/effect/packages/effect/src/ChannelSchema.ts @@ -12,6 +12,7 @@ import * as Channel from "./Channel.ts" import * as Effect from "./Effect.ts" import { dual } from "./Function.ts" import * as Schema from "./Schema.ts" +import type * as SchemaAST from "./SchemaAST.ts" /** * Creates a channel that encodes non-empty chunks of schema values into the @@ -96,7 +97,8 @@ export const encodeUnknown: ( * @since 4.0.0 */ export const decode = ( - schema: S + schema: S, + options?: SchemaAST.ParseOptions ) => (): Channel.Channel< Arr.NonEmptyReadonlyArray, @@ -107,7 +109,7 @@ export const decode = ( Done, S["DecodingServices"] > => { - const decode = Schema.decodeEffect(Schema.NonEmptyArray(schema)) + const decode = Schema.decodeEffect(Schema.NonEmptyArray(schema), options) return Channel.fromTransform((upstream, _scope) => Effect.succeed(Effect.flatMap(upstream, (chunk) => decode(chunk)))) } @@ -131,7 +133,8 @@ export const decode = ( * @since 4.0.0 */ export const decodeUnknown: ( - schema: S + schema: S, + options?: SchemaAST.ParseOptions ) => () => Channel.Channel< Arr.NonEmptyReadonlyArray, IE | Schema.SchemaError, diff --git a/repos/effect/packages/effect/src/Chunk.ts b/repos/effect/packages/effect/src/Chunk.ts index 1e6a6d94a8..8555f32539 100644 --- a/repos/effect/packages/effect/src/Chunk.ts +++ b/repos/effect/packages/effect/src/Chunk.ts @@ -169,9 +169,19 @@ const emptyArray: ReadonlyArray = [] * @since 4.0.0 */ export const makeEquivalence = (isEquivalent: Equivalence.Equivalence): Equivalence.Equivalence> => - Equivalence.make((self, that) => - self.length === that.length && toReadonlyArray(self).every((value, i) => isEquivalent(value, getUnsafe(that, i))) - ) + Equivalence.make((self, that) => { + if (self.length !== that.length) { + return false + } + const as = toReadonlyArray(self) + const bs = toReadonlyArray(that) + for (let i = 0; i < as.length; i++) { + if (!isEquivalent(as[i], bs[i])) { + return false + } + } + return true + }) const _equivalence = makeEquivalence(Equal.equals) @@ -366,13 +376,7 @@ const copyToArray = (self: Chunk, array: Array, initial: number): voi break } case "ISlice": { - let i = 0 - let j = initial - while (i < self.length) { - array[j] = getUnsafe(self, i) - i += 1 - j += 1 - } + copy(toReadonlyArray(self.backing.chunk), self.backing.offset, array, initial, self.length) break } } @@ -1011,7 +1015,7 @@ export const filterMap: { } = dual( 2, (self: Chunk, f: (input: A, i: number) => Result): Chunk => { - const as = RA.fromIterable(self) + const as = toReadonlyArray(self) const out: Array = [] for (let i = 0; i < as.length; i++) { const result = f(as[i], i) @@ -1051,7 +1055,7 @@ export const filter: { (self: Chunk, predicate: Predicate): Chunk } = dual( 2, - (self: Chunk, predicate: Predicate): Chunk => fromArrayUnsafe(RA.filter(self, predicate)) + (self: Chunk, predicate: Predicate): Chunk => fromArrayUnsafe(RA.filter(toReadonlyArray(self), predicate)) ) /** @@ -1156,12 +1160,17 @@ export const flatMap: { if (self.backing._tag === "ISingleton") { return f(self.backing.a, 0) } - let out: Chunk = _empty - let i = 0 - for (const k of self) { - out = appendAll(out, f(k, i++)) + const as = toReadonlyArray(self) + const chunks = new Array>(as.length) + for (let i = 0; i < as.length; i++) { + chunks[i] = f(as[i], i) } - return out + for (let step = 1; step < chunks.length; step *= 2) { + for (let i = 0; i + step < chunks.length; i += 2 * step) { + chunks[i] = appendAll(chunks[i], chunks[i + step]) + } + } + return chunks.length === 0 ? _empty : chunks[0] }) /** @@ -1722,23 +1731,23 @@ export const mapAccum: { }) /** - * Splits a chunk using a `Filter` into failures and successes. + * Splits a chunk using a `Filter` into successes and failures. * * **Details** * - * Returns `[excluded, satisfying]`. The filter receives `(element, index)`. + * Returns `[passes, fails]`. The filter receives `(element, index)`. * * **Example** (Partitioning with a Result) * * ```ts import.meta.vitest * import { Chunk, Result } from "effect" * - * const [excluded, satisfying] = Chunk.partition(Chunk.make(1, -2, 3), (n, i) => + * const [passes, fails] = Chunk.partition(Chunk.make(1, -2, 3), (n, i) => * n > 0 ? Result.succeed(n + i) : Result.fail(`negative:${n}`) * ) * - * Chunk.toArray(excluded) // => ["negative:-2"] - * Chunk.toArray(satisfying) // => [1, 5] + * Chunk.toArray(passes) // => [1, 5] + * Chunk.toArray(fails) // => ["negative:-2"] * ``` * * @category filtering @@ -1747,32 +1756,32 @@ export const mapAccum: { export const partition: { ( f: (input: NoInfer, i: number) => Result - ): (self: Chunk) => [excluded: Chunk, satisfying: Chunk] + ): (self: Chunk) => [passes: Chunk, fails: Chunk] ( self: Chunk, f: (input: A, i: number) => Result - ): [excluded: Chunk, satisfying: Chunk] + ): [passes: Chunk, fails: Chunk] } = dual( 2, ( self: Chunk, f: (input: A, i: number) => Result - ): [excluded: Chunk, satisfying: Chunk] => { - const [excluded, satisfying] = RA.partition(self, f) - return [fromArrayUnsafe(excluded), fromArrayUnsafe(satisfying)] + ): [passes: Chunk, fails: Chunk] => { + const [passes, fails] = RA.partition(self, f) + return [fromArrayUnsafe(passes), fromArrayUnsafe(fails)] } ) /** - * Separates a chunk of `Result` values into a chunk of failures and a chunk of - * successes. + * Separates a chunk of `Result` values into a chunk of successes and a chunk of + * failures. * * **Details** * - * The returned tuple is `[failures, successes]`, preserving the original order + * The returned tuple is `[successes, failures]`, preserving the original order * within each side. * - * **Example** (Separating failures and successes) + * **Example** (Separating successes and failures) * * ```ts import.meta.vitest * import { Chunk, Result } from "effect" @@ -1785,21 +1794,21 @@ export const partition: { * Result.succeed(3) * ) * - * const [errors, values] = Chunk.separate(chunk) - * Chunk.toArray(errors) // => ["error1", "error2"] + * const [values, errors] = Chunk.separate(chunk) * Chunk.toArray(values) // => [1, 2, 3] + * Chunk.toArray(errors) // => ["error1", "error2"] * * // All successes * const allSuccesses = Chunk.make(Result.succeed(1), Result.succeed(2)) - * const [noErrors, allValues] = Chunk.separate(allSuccesses) - * Chunk.toArray(noErrors) // => [] + * const [allValues, noErrors] = Chunk.separate(allSuccesses) * Chunk.toArray(allValues) // => [1, 2] + * Chunk.toArray(noErrors) // => [] * ``` * * @category filtering * @since 2.0.0 */ -export const separate = (self: Chunk>): [Chunk, Chunk] => +export const separate = (self: Chunk>): [Chunk, Chunk] => pipe( RA.separate(toReadonlyArray(self)), ([l, r]) => [fromArrayUnsafe(l), fromArrayUnsafe(r)] @@ -2264,7 +2273,7 @@ export const dedupeAdjacent = (self: Chunk): Chunk => fromArrayUnsafe(R * @since 2.0.0 */ export const unzip = (self: Chunk): [Chunk, Chunk] => { - const [left, right] = RA.unzip(self) + const [left, right] = RA.unzip(toReadonlyArray(self)) return [fromArrayUnsafe(left), fromArrayUnsafe(right)] } @@ -2295,7 +2304,7 @@ export const zipWith: { } = dual( 3, (self: Chunk, that: Chunk, f: (a: A, b: B) => C): Chunk => - fromArrayUnsafe(RA.zipWith(self, that, f)) + fromArrayUnsafe(RA.zipWith(toReadonlyArray(self), toReadonlyArray(that), f)) ) /** @@ -2619,7 +2628,7 @@ export const findLast: { (predicate: Predicate>): (self: Chunk) => Option (self: Chunk, refinement: Refinement): Option (self: Chunk, predicate: Predicate): Option -} = RA.findLast +} = dual(2, (self: Chunk, predicate: Predicate): Option => RA.findLast(toReadonlyArray(self), predicate)) /** * Returns the last index for which a predicate holds. @@ -2647,7 +2656,7 @@ export const findLastIndex: { (self: Chunk, predicate: Predicate): O.Option } = dual( 2, - (self: Chunk, predicate: Predicate): O.Option => RA.findLastIndex(self, predicate) + (self: Chunk, predicate: Predicate): O.Option => RA.findLastIndex(toReadonlyArray(self), predicate) ) /** @@ -2682,8 +2691,15 @@ export const every: { (self: Chunk, predicate: Predicate): boolean } = dual( 2, - (self: Chunk, refinement: Refinement): self is Chunk => - RA.fromIterable(self).every(refinement) + (self: Chunk, refinement: Refinement): self is Chunk => { + const as = toReadonlyArray(self) + for (let i = 0; i < as.length; i++) { + if (!refinement(as[i])) { + return false + } + } + return true + } ) /** @@ -2714,7 +2730,15 @@ export const some: { (self: Chunk, predicate: Predicate): self is NonEmptyChunk } = dual( 2, - (self: Chunk, predicate: Predicate): self is NonEmptyChunk => RA.fromIterable(self).some(predicate) + (self: Chunk, predicate: Predicate): self is NonEmptyChunk => { + const as = toReadonlyArray(self) + for (let i = 0; i < as.length; i++) { + if (predicate(as[i])) { + return true + } + } + return false + } ) /** @@ -2744,7 +2768,7 @@ export const some: { export const join: { (sep: string): (self: Chunk) => string (self: Chunk, sep: string): string -} = RA.join +} = dual(2, (self: Chunk, sep: string): string => toReadonlyArray(self).join(sep)) /** * Reduces the elements of a chunk from left to right. @@ -2874,5 +2898,5 @@ export const difference: { (self: Chunk, that: Chunk): Chunk } = dual( 2, - (self: Chunk, that: Chunk): Chunk => fromArrayUnsafe(RA.difference(self, that)) + (self: Chunk, that: Chunk): Chunk => fromArrayUnsafe(RA.difference(self, toReadonlyArray(that))) ) diff --git a/repos/effect/packages/effect/src/Config.ts b/repos/effect/packages/effect/src/Config.ts index c8a764b94e..5690a6398e 100644 --- a/repos/effect/packages/effect/src/Config.ts +++ b/repos/effect/packages/effect/src/Config.ts @@ -110,33 +110,14 @@ export interface Config extends Effect.Effect { readonly parse: (provider: ConfigProvider.ConfigProvider) => Effect.Effect } -// Config composition needs to distinguish an absent recipe from a hard failure -// before the public Effect error channel is finalized. `hasInput` records -// provider evidence separately from the value, because successful values such -// as `undefined` and values supplied by defaults are not evidence of input. -// Hard failures carry the same evidence so recovery cannot erase it. -interface Resolved { - readonly _tag: "Resolved" - readonly value: T - readonly hasInput: boolean -} - -interface Absent { - readonly _tag: "Absent" - readonly error: ConfigError -} - -type Resolution = Resolved | Absent - -interface EvaluationFailure { - readonly error: ConfigError - readonly hasInput: boolean -} +// Result failure represents recoverable absence; Effect failure represents an +// invalid value or a source error. Keep them separate until parsing completes. +type Resolution = Result.Result type Evaluator = ( provider: ConfigProvider.ConfigProvider, pathPrefix: Path -) => Effect.Effect, EvaluationFailure> +) => Effect.Effect, ConfigError> interface ConfigImpl extends Config { readonly evaluator: Evaluator @@ -163,12 +144,7 @@ function make( const self = Object.create(Proto) self.evaluator = evaluator self.parse = (provider: ConfigProvider.ConfigProvider) => - evaluator(provider, []).pipe( - Effect.mapErrorEager((failure) => failure.error), - Effect.flatMapEager((resolution) => - resolution._tag === "Resolved" ? Effect.succeed(resolution.value) : Effect.fail(resolution.error) - ) - ) + Effect.flatMapEager(evaluator(provider, []), Effect.fromResult) return self } @@ -176,52 +152,7 @@ const evaluateAt = ( self: Config, provider: ConfigProvider.ConfigProvider, pathPrefix: Path -): Effect.Effect, EvaluationFailure> => (self as ConfigImpl).evaluator(provider, pathPrefix) - -const resolved = (value: T, hasInput: boolean): Resolution => ({ - _tag: "Resolved", - value, - hasInput -}) - -const absent = (error: ConfigError): Absent => ({ - _tag: "Absent", - error -}) - -const evaluationFailure = (error: ConfigError, hasInput: boolean): EvaluationFailure => ({ - error, - hasInput -}) - -const isSourceError = (u: unknown): u is ConfigProvider.SourceError => Predicate.isTagged(u, "SourceError") - -const catchSourceError = ( - self: Effect.Effect, - hasInput: boolean -): Effect.Effect => - self.pipe( - Effect.catchDefect((defect) => - isSourceError(defect) - ? Effect.fail(evaluationFailure(new ConfigError(defect), hasInput)) - : Effect.die(defect) - ) - ) - -const preserveInputEvidence = ( - self: Effect.Effect, EvaluationFailure>, - hasInput: boolean -): Effect.Effect, EvaluationFailure> => { - if (!hasInput) return self - return self.pipe( - Effect.mapErrorEager((failure) => evaluationFailure(failure.error, true)), - Effect.flatMapEager((resolution) => - resolution._tag === "Resolved" - ? Effect.succeed(resolved(resolution.value, true)) - : Effect.fail(evaluationFailure(resolution.error, true)) - ) - ) -} +): Effect.Effect, ConfigError> => (self as ConfigImpl).evaluator(provider, pathPrefix) /** * Transforms the parsed value of a config with a pure function. @@ -253,11 +184,81 @@ export const map: { (f: (a: A) => B): (self: Config) => Config (self: Config, f: (a: A) => B): Config } = dual(2, (self: Config, f: (a: A) => B): Config => { + return make((provider, pathPrefix) => Effect.map(evaluateAt(self, provider, pathPrefix), Result.map(f))) +}) + +/** + * Lets you sequence multiple configs that depend on each other or branch to + * multiple different configs depending on the parent. + * + * **When to use** + * + * Use when you want to maintain 2 sets of env vars with different names, and switch + * between whole sets based on a single dedicated env var. Or when you want to + * provide a variety of environment variable names (`PORT`, `BACKEND_PORT`, + * `API_PORT`) in order of preference for a specific configuration option, such + * as the listening port. + * + * **Example** (fallback configs and branching) + * + * ```ts import.meta.vitest + * import { Config, Option } from "effect" + * + * const EnvVar = (name: string) => + * Config.Literals([ + * ...["prod", "production", "PROD", "PRODUCTION"] as const, + * ...["dev", "development", "DEV", "DEVELOPMENT"] as const + * ], name) + * + * const withAbsenceFallback = (fallback: Config.Config) => (self: Config.Config): Config.Config => + * Config.flatMap( + * Config.option(self), + * Option.match({ + * onNone: () => fallback, + * onSome: Config.succeed + * }) + * ) + * + * // const ENV: Config.Config<"dev" | "prod"> + * const ENV = EnvVar("ENV").pipe( + * withAbsenceFallback(EnvVar("NODE_ENV")), + * Config.map((fuzzyEnv) => + * fuzzyEnv.toLowerCase().startsWith("dev") ? "dev" : "prod"), + * Config.withDefault('dev') + * ) + * + * // const hostConfig: Config.Config + * const hostConfig = ENV.pipe( + * Config.flatMap((env) => + * env === "dev" + * // dev is very forgiving + * ? Config.NonEmptyString("DEV_HOST").pipe( + * Config.orElse(() => Config.NonEmptyString("HOST")), + * Config.orElse(() => Config.succeed("localhost")) + * ) + * // prod is much stricter + * : Config.NonEmptyString("PROD_HOST").pipe( + * withAbsenceFallback(Config.NonEmptyString("HOST")) + * ) + * ) + * ) + * ``` + * + * @category mapping + * @since 4.0.0 + */ +export const flatMap: { + (f: (a: A) => Config): (self: Config) => Config + (self: Config, f: (a: A) => Config): Config +} = dual(2, (self: Config, f: (a: A) => Config): Config => { return make((provider, pathPrefix) => - Effect.map(evaluateAt(self, provider, pathPrefix), (resolution) => - resolution._tag === "Resolved" - ? resolved(f(resolution.value), resolution.hasInput) - : resolution) + Effect.flatMap( + evaluateAt(self, provider, pathPrefix), + Result.match({ + onSuccess: (success) => evaluateAt(f(success), provider, pathPrefix), + onFailure: (error) => Effect.succeed(Result.fail(error)) + }) + ) ) }) @@ -291,13 +292,13 @@ export const mapEffect: { (self: Config, f: (a: A) => Effect.Effect): Config } = dual(2, (self: Config, f: (a: A) => Effect.Effect): Config => { return make((provider, pathPrefix) => - Effect.flatMap(evaluateAt(self, provider, pathPrefix), (resolution) => - resolution._tag === "Resolved" - ? f(resolution.value).pipe( - Effect.mapEager((value) => resolved(value, resolution.hasInput)), - Effect.mapErrorEager((error) => evaluationFailure(error, resolution.hasInput)) - ) - : Effect.succeed(resolution)) + Effect.flatMap( + evaluateAt(self, provider, pathPrefix), + (resolution): Effect.Effect, ConfigError> => + Result.isSuccess(resolution) + ? Effect.mapEager(f(resolution.success), Result.succeed) + : Effect.succeed(Result.fail(resolution.failure)) + ) ) }) @@ -317,21 +318,24 @@ export const mapEffect: { * * **Gotchas** * - * Recovery preserves whether the primary config read provider input. When the - * recovered config is composed with {@link all}, invalid input in the primary - * branch still makes the enclosing group partially supplied, so an outer - * {@link withDefault} or {@link option} does not replace the whole group. + * The fallback's result replaces the original failure. If the fallback is + * absent, an outer {@link withDefault} or {@link option} can recover it. If the + * fallback fails, only its error propagates. * - * **Example** (Falling back to a literal) + * **Example** (Trying another port before using a default) * * ```ts import.meta.vitest * import { Config, ConfigProvider, Effect } from "effect" * - * const hostConfig = Config.String("HOST").pipe( - * Config.orElse(() => Config.succeed("localhost")) + * const port = Config.Int("PORT").pipe( + * Config.orElse(() => Config.Int("BACKUP_PORT")), + * Config.withDefault(3000) * ) - * const provider = ConfigProvider.fromUnknown({}) - * Effect.runSync(hostConfig.parse(provider)) // => "localhost" + * const provider = ConfigProvider.fromUnknown({ PORT: "invalid", BACKUP_PORT: "8080" }) + * await Effect.runPromise(port.parse(provider)) // => 8080 + * + * const missingBackup = ConfigProvider.fromUnknown({ PORT: "invalid" }) + * await Effect.runPromise(port.parse(missingBackup)) // => 3000 * ``` * * @see {@link withDefault} – fallback only on semantic absence @@ -345,14 +349,10 @@ export const orElse: { } = dual(2, (self: Config, that: (error: ConfigError) => Config): Config => { return make((provider, pathPrefix) => Effect.matchEffect(evaluateAt(self, provider, pathPrefix), { - onFailure: (failure) => - preserveInputEvidence( - evaluateAt(that(failure.error), provider, pathPrefix), - failure.hasInput - ), - onSuccess: (resolution): Effect.Effect, EvaluationFailure> => - resolution._tag === "Absent" - ? evaluateAt(that(resolution.error), provider, pathPrefix) + onFailure: (error) => evaluateAt(that(error), provider, pathPrefix), + onSuccess: (resolution): Effect.Effect, ConfigError> => + Result.isFailure(resolution) + ? evaluateAt(that(resolution.failure), provider, pathPrefix) : Effect.succeed(resolution) }) ) @@ -370,17 +370,17 @@ export const orElse: { * Accepts a tuple (preserves positions), an iterable, or a record of configs. * Returns a config whose parsed value mirrors the input shape. * - * A combined config is absent when at least one child cannot resolve and none - * of the other children read provider input. This lets {@link withDefault} and - * {@link option} handle a wholly absent group. Once any child reads input, a - * missing sibling makes the group incomplete and parsing fails. Values supplied - * by child defaults do not count as provider input. + * A combined config is absent when any child is absent and no child fails. + * Validation and source errors propagate even when another child is absent. + * An outer {@link withDefault} replaces the entire absent group, while + * {@link option} returns `None`. Apply defaults to individual children to + * preserve the values of other children. * - * Unlike a `Schema.Struct` passed to {@link schema}, `all` only considers input - * read by its children. An explicitly present but empty parent container does - * not by itself make the group present. + * Unlike a `Schema.Struct` passed to {@link schema}, `all` combines independent + * lookups. A struct validates an existing object and fails when required fields + * are missing; `all` can recover missing children through a group default. * - * **Example** (Combining configs as a struct) + * **Example** (Defaulting an incomplete config group) * * ```ts import.meta.vitest * import { Config, ConfigProvider, Effect } from "effect" @@ -388,10 +388,13 @@ export const orElse: { * const dbConfig = Config.all({ * host: Config.String("host"), * port: Config.Number("port") - * }) + * }).pipe(Config.withDefault({ host: "localhost", port: 5432 })) * - * const provider = ConfigProvider.fromUnknown({ host: "localhost", port: 5432 }) - * Effect.runSync(dbConfig.parse(provider)) // => { host: "localhost", port: 5432 } + * const provider = ConfigProvider.fromUnknown({ host: "db.internal", port: 6000 }) + * await Effect.runPromise(dbConfig.parse(provider)) // => { host: "db.internal", port: 6000 } + * + * const missingPort = ConfigProvider.fromUnknown({ host: "db.internal" }) + * await Effect.runPromise(dbConfig.parse(missingPort)) // => { host: "localhost", port: 5432 } * ``` * * @category combinators @@ -414,87 +417,21 @@ export function all> | Record - Effect.flatMapEager( - Effect.all(configs.map((config) => Effect.result(evaluateAt(config, provider, pathPrefix)))), - resolveArray + return make((provider, pathPrefix) => + globalThis.Array.isArray(configs) + ? Effect.mapEager( + Effect.all(configs.map((config) => evaluateAt(config, provider, pathPrefix))), + Result.all ) - ) as any - } else { - return make((provider, pathPrefix) => - Effect.flatMapEager( - Effect.all(Rec.map(configs, (config) => Effect.result(evaluateAt(config, provider, pathPrefix)))), - resolveRecord + : Effect.mapEager( + Effect.all(Rec.map(configs, (config) => evaluateAt(config, provider, pathPrefix))), + Result.all ) - ) as any - } -} - -const resolveArray = ( - results: ReadonlyArray, EvaluationFailure>> -): Effect.Effect>, EvaluationFailure> => { - const values: Array = [] - let firstFailure: EvaluationFailure | undefined - let firstAbsent: Absent | undefined - let hasInput = false - for (const result of results) { - if (Result.isFailure(result)) { - firstFailure ??= result.failure - hasInput = hasInput || result.failure.hasInput - continue - } - const resolution = result.success - if (resolution._tag === "Absent") { - firstAbsent ??= resolution - } else { - values.push(resolution.value) - hasInput = hasInput || resolution.hasInput - } - } - if (firstFailure !== undefined) { - return Effect.fail(evaluationFailure(firstFailure.error, hasInput)) - } - if (firstAbsent !== undefined) { - return hasInput ? Effect.fail(evaluationFailure(firstAbsent.error, true)) : Effect.succeed(firstAbsent) - } - return Effect.succeed(resolved(values, hasInput)) -} - -const resolveRecord = ( - results: Record, EvaluationFailure>> -): Effect.Effect>, EvaluationFailure> => { - const values: Record = {} - let firstFailure: EvaluationFailure | undefined - let firstAbsent: Absent | undefined - let hasInput = false - for (const key in results) { - const result = results[key] - if (Result.isFailure(result)) { - firstFailure ??= result.failure - hasInput = hasInput || result.failure.hasInput - continue - } - const resolution = result.success - if (resolution._tag === "Absent") { - firstAbsent ??= resolution - } else { - InternalRecord.assignProperty(values, key, resolution.value) - hasInput = hasInput || resolution.hasInput - } - } - if (firstFailure !== undefined) { - return Effect.fail(evaluationFailure(firstFailure.error, hasInput)) - } - if (firstAbsent !== undefined) { - return hasInput ? Effect.fail(evaluationFailure(firstAbsent.error, true)) : Effect.succeed(firstAbsent) - } - return Effect.succeed(resolved(values, hasInput)) + ) as any } /** - * Provides a fallback value when the config cannot resolve because none of its - * relevant input is present. + * Provides a fallback value when the config is absent. * * **When to use** * @@ -502,9 +439,12 @@ const resolveRecord = ( * * **Gotchas** * - * Validation errors and partially supplied groups still propagate. A schema - * that successfully decodes absent input also keeps its decoded value instead - * of using the default. Schema configs first represent a missing or + * Validation and source errors still propagate. For an {@link all} group, any + * absent child causes the default to replace the entire group unless another + * child fails. Apply defaults to individual children to preserve other values. + * + * A schema that successfully decodes absent input keeps its decoded value + * instead of using the default. Schema configs first represent a missing or * incompatible provider shape as `undefined`; the default is used only when * the schema rejects that value and no relevant input was found. * @@ -532,14 +472,14 @@ export const withDefault: { return make((provider, pathPrefix) => Effect.mapEager( evaluateAt(self, provider, pathPrefix), - (resolution) => resolution._tag === "Absent" ? resolved(defaultValue, false) : resolution + (resolution) => Result.isFailure(resolution) ? Result.succeed(defaultValue) : resolution ) ) }) /** * Makes a config optional: returns `Some(value)` on success and `None` when the - * config cannot resolve because none of its relevant input is present. + * config is absent. * * **When to use** * @@ -547,8 +487,9 @@ export const withDefault: { * * **Gotchas** * - * Validation errors and partially supplied groups still propagate. Successful - * values are always wrapped in `Some`, including `undefined` when the schema + * Validation and source errors still propagate. For an {@link all} group, any + * absent child produces `None` unless another child fails. Successful values + * are always wrapped in `Some`, including `undefined` when the schema * explicitly accepts it. Schema configs first represent a missing or * incompatible provider shape as `undefined`; `None` is returned only when the * schema rejects that value and no relevant input was found. @@ -667,6 +608,8 @@ interface ConfigCursor { readonly toString: () => string } +const isSourceError = (u: unknown): u is ConfigProvider.SourceError => Predicate.isTagged(u, "SourceError") + const cursorToString = (): string => "" const loadCursor: ( @@ -681,8 +624,6 @@ const loadCursor: ( const loadChildCursor = (cursor: ConfigCursor, segment: string | number): Effect.Effect => loadCursor(cursor.provider, [...cursor.path, segment]) -const getScalar = (node: ConfigProvider.Node | undefined): string | undefined => node?.value - const decodeFromCursor = ( ast: SchemaAST.AST, decode: (cursor: ConfigCursor) => Effect.Effect @@ -723,7 +664,7 @@ const hasProviderInput = ( case "Suspend": return hasProviderInput(ast.thunk(), node) default: - return getScalar(node) !== undefined + return node?.value !== undefined } } @@ -774,9 +715,8 @@ const toConfigCursorAST = memoize((root: SchemaAST.AST): SchemaAST.AST => { for (const member of ast.types) { recur(member) } - return isScalarInput(ast) - ? decodeFromCursor(ast, (cursor) => Effect.succeed(getScalar(cursor.node))) - : ast.recur(recur) + if (!isScalarInput(ast)) return ast.recur(recur) + break case "Suspend": { const target = ast.thunk() // Force new branches so opaque encodings fail when the Config is constructed. @@ -786,9 +726,8 @@ const toConfigCursorAST = memoize((root: SchemaAST.AST): SchemaAST.AST => { case "Declaration": case "Any": throw new globalThis.Error("Config.schema does not support opaque StringTree encodings", { cause: ast }) - default: - return decodeFromCursor(ast, (cursor) => Effect.succeed(getScalar(cursor.node))) } + return decodeFromCursor(ast, (cursor) => Effect.succeed(cursor.node?.value)) }) return recur(root) }) @@ -834,9 +773,9 @@ const toConfigCursorAST = memoize((root: SchemaAST.AST): SchemaAST.AST => { * also be accepted. * * `Schema.Struct` and {@link all} describe different lookup models. An - * explicitly present empty object is relevant input for a struct and required - * fields are validated. The same empty parent container does not make an - * `all` group present when all of its child configs are absent. + * existing object, even an empty one, is validated as a whole by a struct; + * missing required fields cause a validation failure. An `all` group combines + * independent lookups and is absent if any child is absent and none fail. * * The canonical `StringTree` encoding must expose a concrete scalar, object, * array, or union shape. Opaque encodings such as `Schema.Any`, @@ -883,24 +822,21 @@ export function schema(codec: Schema.ConstraintCodec, path?: stri const localPath = typeof path === "string" ? [path] : path ?? [] return make((provider, pathPrefix) => { const fullPath = [...pathPrefix, ...localPath] - return catchSourceError(loadCursor(provider, fullPath), false).pipe( - Effect.flatMapEager((cursor) => { - const hasInput = hasProviderInput(encodedAst, cursor.node) - return catchSourceError( - decodeCursor(cursor).pipe( - Effect.mapEager((value) => resolved(value, hasInput)), - Effect.catchEager((issue) => { - const error = new ConfigError( - new Schema.SchemaError(fullPath.length > 0 ? new SchemaIssue.Pointer(fullPath, issue) : issue) - ) - return hasInput - ? Effect.fail(evaluationFailure(error, true)) - : Effect.succeed(absent(error)) - }) - ), - hasInput + return loadCursor(provider, fullPath).pipe( + Effect.flatMapEager((cursor) => + decodeCursor(cursor).pipe( + Effect.mapEager(Result.succeed), + Effect.catchEager((issue) => { + const error = new ConfigError( + new Schema.SchemaError(fullPath.length > 0 ? new SchemaIssue.Pointer(fullPath, issue) : issue) + ) + return hasProviderInput(encodedAst, cursor.node) + ? Effect.fail(error) + : Effect.succeed(Result.fail(error)) + }) ) - }) + ), + Effect.catchDefect((defect) => isSourceError(defect) ? Effect.fail(new ConfigError(defect)) : Effect.die(defect)) ) }) } @@ -935,8 +871,8 @@ const isPath = (u: unknown): u is string | Path => Predicate.isString(u) || glob * @category constructors * @since 2.0.0 */ -export function fail(err: SourceError | Schema.SchemaError) { - return make(() => Effect.fail(evaluationFailure(new ConfigError(err), false))) +export function fail(err: SourceError | Schema.SchemaError): Config { + return make(() => Effect.fail(new ConfigError(err))) } /** @@ -964,7 +900,7 @@ export function fail(err: SourceError | Schema.SchemaError) { * @since 2.0.0 */ export function succeed(value: T) { - return make(() => Effect.succeed(resolved(value, false))) + return make(() => Effect.succeed(Result.succeed(value))) } /** @@ -1193,7 +1129,8 @@ export function Array>( const arrayString = Schema.String.pipe( Schema.decodeTo(Schema.toCodecStringTree(array), { decode: SchemaGetter.split(resolvedOptions), - encode: SchemaGetter.passthrough, Schema.StringTree>({ strict: false }).compose( + encode: SchemaGetter.compose( + SchemaGetter.passthrough, Schema.StringTree>({ strict: false }), SchemaGetter.transform((input) => input.join(separator)) ) }) @@ -1270,7 +1207,8 @@ export function Record< const recordString = Schema.String.pipe( Schema.decodeTo(Schema.toCodecStringTree(record), { decode: split.decode, - encode: SchemaGetter.passthrough, Schema.StringTree>({ strict: false }).compose( + encode: SchemaGetter.compose( + SchemaGetter.passthrough, Schema.StringTree>({ strict: false }), split.encode ) }) diff --git a/repos/effect/packages/effect/src/ConfigProvider.ts b/repos/effect/packages/effect/src/ConfigProvider.ts index 8dd6622c39..59d4e6f876 100644 --- a/repos/effect/packages/effect/src/ConfigProvider.ts +++ b/repos/effect/packages/effect/src/ConfigProvider.ts @@ -15,7 +15,7 @@ import * as Data from "./Data.ts" import * as Effect from "./Effect.ts" import * as FileSystem from "./FileSystem.ts" import { format } from "./Formatter.ts" -import { dual, flow } from "./Function.ts" +import { dual } from "./Function.ts" import { PipeInspectableProto } from "./internal/core.ts" import * as Layer from "./Layer.ts" import * as Path_ from "./Path.ts" @@ -371,7 +371,7 @@ function makeSource( ): ConfigProvider { return makeProvider( (path) => get(transform(path)), - (f) => makeSource(get, flow(transform, f)) + (f) => makeSource(get, (path) => f(transform(path))) ) } diff --git a/repos/effect/packages/effect/src/Context.ts b/repos/effect/packages/effect/src/Context.ts index 52ee4d14c1..a3d918ff8d 100644 --- a/repos/effect/packages/effect/src/Context.ts +++ b/repos/effect/packages/effect/src/Context.ts @@ -487,6 +487,8 @@ interface Overlay { } const MaxDepth = 8 +// Keep small bases cheap to read; larger bases are worth copying only after +// enough fall-throughs to amortize the copy. const FlattenAfterBaseHits = 8 const makeImpl = ( @@ -538,7 +540,7 @@ const lookup = (self: Context, key: string): unknown => { // base on every fiber cache refresh, which would flatten every short-lived // request context and reintroduce the O(services) per-request cost if (value === undefined && !impl.base.has(key)) return notFound - if (impl.overlay && ++impl.baseHits >= FlattenAfterBaseHits) { + if (impl.overlay && ++impl.baseHits >= impl.base.size && impl.baseHits >= FlattenAfterBaseHits) { impl.base = flatten(impl) impl.overlay = undefined impl.depth = 0 diff --git a/repos/effect/packages/effect/src/Crypto.ts b/repos/effect/packages/effect/src/Crypto.ts index 8a332c44b8..fa16326e09 100644 --- a/repos/effect/packages/effect/src/Crypto.ts +++ b/repos/effect/packages/effect/src/Crypto.ts @@ -12,6 +12,7 @@ import * as Context from "./Context.ts" import * as Effect from "./Effect.ts" import * as random from "./internal/random.ts" +import * as Ulid from "./internal/ulid.ts" import * as Uuid from "./internal/uuid.ts" import * as PlatformError from "./PlatformError.ts" @@ -152,6 +153,21 @@ export interface Crypto { * Generates a cryptographically secure UUIDv7 string. */ readonly randomUUIDv7: Effect.Effect + + /** + * Generates a cryptographically secure ULID string. + * + * **Details** + * + * ULIDs contain 26 uppercase Crockford base32 characters. The first 10 encode + * the `Clock` timestamp in milliseconds; the remaining 16 encode 80 random + * bits. ULIDs sort by timestamp, with no ordering guarantee within the same + * millisecond. + * + * Timestamp normalization matches UUIDv7: fractions are truncated, values are + * clamped to the 48-bit range, and `NaN` encodes as zero. + */ + readonly randomULID: Effect.Effect } /** @@ -275,6 +291,9 @@ export const make = ( randomUUIDv4: Effect.sync(() => Uuid.v4String(randomBytesUnsafe(16))), randomUUIDv7: Effect.clockWith((clock) => Effect.succeed(Uuid.v7String(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(16))) + ), + randomULID: Effect.clockWith((clock) => + Effect.succeed(Ulid.ulidString(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(10))) ) }) } diff --git a/repos/effect/packages/effect/src/Effect.ts b/repos/effect/packages/effect/src/Effect.ts index 5e5b558d95..e7e8041b68 100644 --- a/repos/effect/packages/effect/src/Effect.ts +++ b/repos/effect/packages/effect/src/Effect.ts @@ -72,7 +72,6 @@ import type { unassigned } from "./Types.ts" import type * as Unify from "./Unify.ts" -import { internalCall } from "./Utils.ts" /** * Type-level identifier for `Effect` values. @@ -227,7 +226,7 @@ export type Services = T extends Effect ? _R * @category guards * @since 2.0.0 */ -export const isEffect: (u: unknown) => u is Effect = core.isEffect +export const isEffect: (u: unknown) => u is Effect = core.isEffect /** * Iterator interface for Effect generators, enabling Effect values to work with generator functions. @@ -256,6 +255,7 @@ export interface EffectIterator> { * Namespace containing type utilities for the `Effect.all` function, which handles * collecting multiple effects into various output structures. * + * @category collecting * @since 2.0.0 */ export declare namespace All { @@ -485,7 +485,7 @@ export declare namespace All { * ``` * * @see {@link forEach} for iterating over elements and applying an effect. - * @category combining + * @category collecting * @since 2.0.0 */ export const all: < @@ -503,13 +503,13 @@ export const all: < ) => All.Return = internal.all /** - * Applies an effectful function to each element and partitions failures and - * successes. + * Applies an effectful function to each element and partitions successes and + * failures. * * **Details** * - * The returned tuple is `[excluded, satisfying]`, where `excluded` contains - * all failures and `satisfying` contains all successes. + * The returned tuple is `[passes, fails]`, where `passes` contains all + * successes and `fails` contains all failures. * * This function runs every effect and never fails. Use `concurrency` to control * parallelism. @@ -523,22 +523,22 @@ export const all: < * n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n) * ) * - * await Effect.runPromise(program) // => [['0 is even', '2 is even'], [1, 3]] + * await Effect.runPromise(program) // => [[1, 3], ['0 is even', '2 is even']] * ``` * - * @category filtering + * @category collecting * @since 2.0.0 */ export const partition: { ( f: (a: A, i: number) => Effect, options?: { readonly concurrency?: Concurrency | undefined } - ): (elements: Iterable) => Effect<[excluded: Array, satisfying: Array], never, R> + ): (elements: Iterable) => Effect<[passes: Array, fails: Array], never, R> ( elements: Iterable, f: (a: A, i: number) => Effect, options?: { readonly concurrency?: Concurrency | undefined } - ): Effect<[excluded: Array, satisfying: Array], never, R> + ): Effect<[passes: Array, fails: Array], never, R> } = internal.partition /** @@ -575,7 +575,7 @@ export const partition: { * output // => ["Adding 1 at index 0", "Adding 2 at index 1", "Adding 3 at index 2", 6] * ``` * - * @category folding + * @category collecting * @since 2.0.0 */ export const reduce: { @@ -614,7 +614,7 @@ export const reduce: { * await Effect.runPromiseExit(program) // => Exit.fail(["0 is even", "2 is even"]) * ``` * - * @category validation + * @category collecting * @since 2.0.0 */ export const validate: { @@ -668,7 +668,7 @@ export const validate: { * await Effect.runPromise(program) // => Option.some(3) * ``` * - * @category searching + * @category collecting * @since 2.0.0 */ export const findFirst: { @@ -696,7 +696,7 @@ export const findFirst: { * * @see {@link findFirst} for the simpler effectful predicate-based variant * - * @category searching + * @category collecting * @since 4.0.0 */ export const findFirstFilter: { @@ -770,7 +770,7 @@ export const findFirstFilter: { * ``` * * @see {@link all} for combining multiple effects into one. - * @category sequencing + * @category collecting * @since 2.0.0 */ export const forEach: { @@ -808,7 +808,7 @@ export const forEach: { * await Effect.runPromise(empty) // => Option.none() * ``` * - * @category getters + * @category collecting * @since 2.0.0 */ export const head: ( @@ -1270,7 +1270,7 @@ export const never: Effect = internal.never * Effect.runSync(program) // => { x: 2, y: 3, sum: 5 } * ``` * - * @category constructors + * @category do notation * @since 2.0.0 */ export const Do: Effect<{}> = internal.Do @@ -1287,7 +1287,7 @@ export const Do: Effect<{}> = internal.Do * @see {@link Do} for starting from an empty accumulated record * @see {@link bind} for adding fields produced by effects * - * @category mapping + * @category do notation * @since 2.0.0 */ export const bindTo: { @@ -1329,7 +1329,7 @@ export { * @see {@link Do} for starting from an empty accumulated record * @see {@link gen} for sequencing without accumulating a record * - * @category mapping + * @category do notation * @since 2.0.0 */ let_ as let @@ -1359,7 +1359,7 @@ export { * @see {@link bindTo} for naming the success value of an existing effect * @see {@link gen} for generator-based sequencing without accumulating a record * - * @category sequencing + * @category do notation * @since 2.0.0 */ export const bind: { @@ -1459,6 +1459,7 @@ export const gen: { /** * Type helpers for `Effect.gen` generator return signatures. * + * @category constructors * @since 2.0.0 */ export declare namespace gen { @@ -3714,7 +3715,7 @@ export const orDie: (self: Effect) => Effect = in * output // => ["expected error: NetworkError", Exit.fail("NetworkError")] * ``` * - * @category sequencing + * @category error handling * @since 2.0.0 */ export const tapError: { @@ -3761,7 +3762,7 @@ export const tapError: { * output // => ["expected error: 504", Exit.fail(new NetworkError({ statusCode: 504 }))] * ``` * - * @category sequencing + * @category error handling * @since 2.0.0 */ export const tapErrorTag: { @@ -3816,7 +3817,7 @@ export const tapErrorTag: { * output // => ["Logging cause: Something went wrong", Exit.fail("Something went wrong")] * ``` * - * @category sequencing + * @category error handling * @since 4.0.0 */ export const tapCause: { @@ -3857,7 +3858,7 @@ export const tapCause: { * output // => ["Logging failure cause: Network timeout", Exit.fail("Network timeout")] * ``` * - * @category sequencing + * @category error handling * @since 4.0.0 */ export const tapCauseIf: { @@ -3891,7 +3892,7 @@ export const tapCauseIf: { * @see {@link tapCause} for observing every failure cause * @see {@link catchCauseFilter} for recovering from selected causes instead of only observing them * - * @category sequencing + * @category error handling * @since 4.0.0 */ export const tapCauseFilter: { @@ -3937,7 +3938,7 @@ export const tapCauseFilter: { * output // => ["defect: Something went wrong", Exit.die("Something went wrong")] * ``` * - * @category sequencing + * @category error handling * @since 2.0.0 */ export const tapDefect: { @@ -3987,6 +3988,7 @@ export const eventually: (self: Effect) => Effect /** * Type helpers for retrying effects. * + * @category error handling * @since 2.0.0 */ export declare namespace Retry { @@ -4431,8 +4433,9 @@ export const withErrorReporting: < * **Details** * * If the source effect succeeds, its value is preserved. If it fails in the - * error channel, `orElseSucceed` evaluates the fallback and succeeds with that - * value, removing the typed error from the returned effect. + * error channel, `orElseSucceed` evaluates the fallback with that error and + * succeeds with the returned value, removing the typed error from the returned + * effect. * * Defects and interruptions are not recovered by this operator. * @@ -4451,21 +4454,21 @@ export const withErrorReporting: < * } * } * - * const program = Effect.orElseSucceed(validate(-1), () => 18) + * const program = Effect.orElseSucceed(validate(-1), (error) => error === "IllegalAgeError" ? 18 : 0) * - * Effect.runSyncExit(program) // => Exit.succeed(18) + * Effect.runSyncExit(program) // => Exit.succeed(0) * ``` * * @category error handling * @since 2.0.0 */ export const orElseSucceed: { - ( - evaluate: LazyArg - ): (self: Effect) => Effect + ( + f: (error: NoInfer) => A2 + ): (self: Effect) => Effect ( self: Effect, - evaluate: LazyArg + f: (error: E) => A2 ): Effect } = internal.orElseSucceed @@ -4942,7 +4945,7 @@ export const raceFirst: { * output // => [[2, 4], [2, 3]] * ``` * - * @category filtering + * @category collecting * @since 2.0.0 */ export const filter: { @@ -4987,7 +4990,7 @@ export const filter: { * @see {@link filter} for keeping original elements with a boolean predicate, refinement, or effectful predicate * @see {@link filterMapEffect} for using an effectful `Filter` * - * @category filtering + * @category collecting * @since 2.0.0 */ export const filterMap: { @@ -5021,7 +5024,7 @@ export const filterMap: { * @see {@link filterMap} for using a synchronous `Filter` * @see {@link filter} for keeping original elements with a predicate * - * @category filtering + * @category collecting * @since 4.0.0 */ export const filterMapEffect: { @@ -7491,6 +7494,7 @@ export const abortSignal: Effect = internal.abortSign /** * Type helpers for repeating effects. * + * @category repetition * @since 2.0.0 */ export declare namespace Repeat { @@ -7501,9 +7505,12 @@ export declare namespace Repeat { * @since 2.0.0 */ export type Return> = Effect< - O extends { until: Predicate.Refinement } ? B + O extends unknown ? "schedule" extends keyof O ? A + : "times" extends keyof O ? A + : O extends { until: Predicate.Refinement } ? B : O extends { while: Predicate.Refinement } ? Exclude - : A, + : A + : never, | E | (O extends { schedule: Schedule } ? E : never) @@ -8727,12 +8734,16 @@ export const forkDetach: < * Child fibers that already exist before the wrapped effect starts are not * awaited. * + * If interrupted while awaiting child fibers after the wrapped effect fails, + * both the original failure and the interruption are retained in the cause. + * An enclosing uninterruptible region keeps the child wait uninterruptible. + * * @see {@link forkChild} for forking child fibers that are awaited by this operator * @see {@link forkDetach} for forking fibers outside the child scope * @see {@link forkIn} for forking into an explicit scope * @see {@link forkScoped} for forking fibers tied to the current scope * - * @category sequencing + * @category forking * @since 2.0.0 */ export const awaitAllChildren: (self: Effect) => Effect = internal.awaitAllChildren @@ -9350,6 +9361,7 @@ export const runSyncExitWith: ( * * Use these to describe generator-based signatures and traced or untraced variants. * + * @category constructors * @since 3.11.0 */ export declare namespace fn { @@ -13939,15 +13951,18 @@ export const logTrace: (...message: ReadonlyArray) => Effect = intern * @category logging * @since 4.0.0 */ -export const withLogger = dual< +export const withLogger: { ( logger: Logger - ) => (effect: Effect) => Effect, + ): (effect: Effect) => Effect ( effect: Effect, logger: Logger - ) => Effect ->(2, (effect, logger) => + ): Effect +} = dual(2, ( + effect: Effect, + logger: Logger +): Effect => internal.updateService( effect, internal.CurrentLoggers, @@ -13989,28 +14004,24 @@ export const withLogger = dual< * @category logging * @since 2.0.0 */ -export const annotateLogs = dual< - { - ( - key: string, - value: unknown - ): (effect: Effect) => Effect - ( - values: Record - ): (effect: Effect) => Effect - }, - { - ( - effect: Effect, - key: string, - value: unknown - ): Effect - ( - effect: Effect, - values: Record - ): Effect - } ->( +export const annotateLogs: { + ( + key: string, + value: unknown + ): (effect: Effect) => Effect + ( + values: Record + ): (effect: Effect) => Effect + ( + effect: Effect, + key: string, + value: unknown + ): Effect + ( + effect: Effect, + values: Record + ): Effect +} = dual( (args) => isEffect(args[0]), ( effect: Effect, @@ -14106,12 +14117,12 @@ export const annotateLogsScoped: { * @category logging * @since 2.0.0 */ -export const withLogSpan = dual< - (label: string) => (effect: Effect) => Effect, - (effect: Effect, label: string) => Effect ->( +export const withLogSpan: { + (label: string): (effect: Effect) => Effect + (effect: Effect, label: string): Effect +} = dual( 2, - (effect, label) => + (effect: Effect, label: string): Effect => internal.flatMap(internal.currentTimeMillis, (now) => internal.updateService(effect, CurrentLogSpans, (spans) => { const span: [label: string, timestamp: number] = [label, now] @@ -14194,7 +14205,7 @@ export const track: { f: (exit: Exit.Exit) => Input ): Effect => onExit(self, (exit) => { - const input = f === undefined ? exit : internalCall(() => f(exit)) + const input = f === undefined ? exit : f(exit) return Metric.update(metric, input as any) }) ) @@ -14346,7 +14357,7 @@ export const trackErrors: { f: ((error: E) => Input) | undefined ): Effect => tapError(self, (error) => { - const input = f === undefined ? error : internalCall(() => f(error)) + const input = f === undefined ? error : f(error) return Metric.update(metric, input as any) }) ) @@ -14420,7 +14431,7 @@ export const trackDefects: { (args) => isEffect(args[0]), (self, metric, f) => tapDefect(self, (defect) => { - const input = f === undefined ? defect : internalCall(() => f(defect)) + const input = f === undefined ? defect : f(defect) return Metric.update(metric, input) }) ) @@ -14501,7 +14512,7 @@ export const trackDuration: { Duration.fromInputUnsafe(endTime), Duration.fromInputUnsafe(startTime) ) - const input = f === undefined ? duration : internalCall(() => f(duration)) + const input = f === undefined ? duration : f(duration) return Metric.update(metric, input as any) }) }) @@ -14538,7 +14549,7 @@ export const trackDuration: { * Effect.runSync(runnable) // => "Transaction complete" * ``` * - * @category services + * @category transactions * @since 4.0.0 */ export class Transaction extends Context.Service< @@ -14550,6 +14561,7 @@ export class Transaction extends Context.Service< { readonly version: number value: any + written: boolean } > } @@ -14653,7 +14665,13 @@ const isTransactionConsistent = (state: Transaction["Service"]) => { } const awaitPendingTransaction = (state: Transaction["Service"]) => - suspend(() => { + callback((resume) => { + // Validate the read set and register the waiter in one synchronous step. + // A commit that landed after the reads has already signalled its waiters + // and will not signal this one, so a stale read set reruns immediately. + if (!isTransactionConsistent(state)) { + return resume(void_) + } const key = {} const refs = Array.from(state.journal.keys()) const clearPending = () => { @@ -14661,21 +14679,20 @@ const awaitPendingTransaction = (state: Transaction["Service"]) => clear.pending.delete(key) } } - return callback((resume) => { - const onCall = () => { - clearPending() - resume(void_) - } - for (const ref of refs) { - ref.pending.set(key, onCall) - } - return sync(clearPending) - }) + const onCall = () => { + clearPending() + resume(void_) + } + for (const ref of refs) { + ref.pending.set(key, onCall) + } + return sync(clearPending) }) function commitTransaction(fiber: Fiber, state: Transaction["Service"]) { - for (const [ref, { value }] of state.journal) { - if (value !== ref.value) { + for (const [ref, { value, written }] of state.journal) { + if (!written) continue + if (!Object.is(value, ref.value)) { ref.version = ref.version + 1 ref.value = value } @@ -14737,6 +14754,7 @@ export const txRetry: Effect = flatMap( /** * Type helpers for converting callback-based functions into `Effect` functions. * + * @category converting * @since 4.0.0 */ export declare namespace Effectify { diff --git a/repos/effect/packages/effect/src/Encoding.ts b/repos/effect/packages/effect/src/Encoding.ts deleted file mode 100644 index 25dcc13127..0000000000 --- a/repos/effect/packages/effect/src/Encoding.ts +++ /dev/null @@ -1,889 +0,0 @@ -/** - * Encoding and decoding helpers for Base64, Base64Url, and hexadecimal text. - * The functions convert between strings, UTF-8 text, and `Uint8Array` bytes. - * Encode functions return strings directly, while decode functions return - * `Result.Result` so invalid input is reported as an `EncodingError` instead of - * being thrown. - * - * @since 4.0.0 - */ -import * as Data from "./Data.ts" -import { hasProperty } from "./Predicate.ts" -import * as Result from "./Result.ts" - -// ------------------------------------------------------------------------------------- -// EncodingError -// ------------------------------------------------------------------------------------- - -/** - * Type identifier stored on `EncodingError` values and used by - * `isEncodingError`. - * - * **When to use** - * - * Use when implementing low-level `EncodingError`-compatible values that need - * to carry the runtime marker. - * - * **Details** - * - * This marker is part of the runtime representation of `EncodingError`. Prefer - * `isEncodingError` when narrowing unknown values. - * - * @see {@link isEncodingError} for the public guard that checks this marker - * - * @category type IDs - * @since 4.0.0 - */ -export const EncodingErrorTypeId = "~effect/Encoding/EncodingError" as const - -/** - * Literal type of the `EncodingErrorTypeId` marker. - * - * **When to use** - * - * Use to type the marker carried by `EncodingError` values. - * - * @category type IDs - * @since 4.0.0 - */ -export type EncodingErrorTypeId = typeof EncodingErrorTypeId - -/** - * Error returned when an encoding or decoding operation cannot process its - * input. - * - * **When to use** - * - * Use when you need to handle or inspect failures from encoding or decoding - * operations. - * - * **Details** - * - * The error records whether the failure happened during encoding or decoding, - * which encoding module reported it, the original input, and a human-readable - * message. - * - * @see {@link isEncodingError} for checking whether a value is an EncodingError - * @category errors - * @since 4.0.0 - */ -export class EncodingError extends Data.TaggedError("EncodingError")<{ - kind: "Decode" | "Encode" - module: string - input: unknown - message: string -}> { - /** - * Marks this value as an encoding or decoding error for runtime guards. - * - * **When to use** - * - * Use to identify `EncodingError` instances through `isEncodingError`. - * - * @since 4.0.0 - */ - readonly [EncodingErrorTypeId]: EncodingErrorTypeId = EncodingErrorTypeId -} - -/** - * Checks whether a value is an `EncodingError`. - * - * **When to use** - * - * Use to narrow an unknown value before handling it as an `EncodingError` from - * encoding or decoding code. - * - * **Details** - * - * Returns `true` when the value carries the `EncodingErrorTypeId` marker and - * narrows the value to `EncodingError`. - * - * @see {@link EncodingError} for the structured error produced by failed - * encoding and decoding operations - * - * @category guards - * @since 4.0.0 - */ -export const isEncodingError = (u: unknown): u is EncodingError => hasProperty(u, EncodingErrorTypeId) - -// ------------------------------------------------------------------------------------- -// Base64 -// ------------------------------------------------------------------------------------- - -/** - * Encodes the given value into a base64 (RFC4648) `string`. - * - * **When to use** - * - * Use to encode text or bytes as a standard padded Base64 string for storage or - * transport. - * - * **Details** - * - * String inputs are encoded as UTF-8 bytes before Base64 encoding. - * `Uint8Array` inputs are encoded directly. The output uses the standard - * RFC4648 alphabet with `=` padding. - * - * **Example** (Encoding Base64 strings and bytes) - * - * ```ts import.meta.vitest - * import { Encoding } from "effect" - * - * // Encode a string - * Encoding.encodeBase64("hello") // => "aGVsbG8=" - * - * // Encode binary data - * const bytes = new Uint8Array([72, 101, 108, 108, 111]) - * Encoding.encodeBase64(bytes) // => "SGVsbG8=" - * ``` - * - * @see {@link decodeBase64} for decoding standard Base64 to bytes - * @see {@link decodeBase64String} for decoding standard Base64 to UTF-8 text - * @see {@link encodeBase64Url} for URL-safe unpadded Base64 output - * - * @category encoding - * @since 2.0.0 - */ -export const encodeBase64: (input: Uint8Array | string) => string = (input) => - typeof input === "string" ? base64EncodeUint8Array(encoder.encode(input)) : base64EncodeUint8Array(input) - -/** - * Decodes a base64 (RFC4648) string into bytes safely. - * - * **When to use** - * - * Use to decode a standard padded Base64 string into bytes without throwing on - * invalid input. - * - * **Details** - * - * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input is not valid base64. - * - * **Example** (Decoding Base64 bytes) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeBase64("SGVsbG8=") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeBase64 = (str: string): Result.Result => { - const stripped = stripCrlf(str) - const length = stripped.length - if (length % 4 !== 0) { - return Result.fail( - new EncodingError({ - kind: "Decode", - module: "Base64", - input: stripped, - message: `Length must be a multiple of 4, but is ${length}` - }) - ) - } - - const index = stripped.indexOf("=") - if (index !== -1 && ((index < length - 2) || (index === length - 2 && stripped[length - 1] !== "="))) { - return Result.fail( - new EncodingError({ - kind: "Decode", - module: "Base64", - input: stripped, - message: `Found a '=' character, but it is not at the end` - }) - ) - } - - try { - const missingOctets = stripped.endsWith("==") ? 2 : stripped.endsWith("=") ? 1 : 0 - const result = new Uint8Array(3 * (length / 4) - missingOctets) - for (let i = 0, j = 0; i < length; i += 4, j += 3) { - const buffer = getBase64Code(stripped.charCodeAt(i)) << 18 | - getBase64Code(stripped.charCodeAt(i + 1)) << 12 | - getBase64Code(stripped.charCodeAt(i + 2)) << 6 | - getBase64Code(stripped.charCodeAt(i + 3)) - - result[j] = buffer >> 16 - result[j + 1] = (buffer >> 8) & 0xff - result[j + 2] = buffer & 0xff - } - - return Result.succeed(result) - } catch (e) { - return Result.fail( - new EncodingError({ - kind: "Decode", - module: "Base64", - input: stripped, - message: e instanceof Error ? e.message : "Invalid input" - }) - ) - } -} - -/** - * Decodes a base64 (RFC4648) string into a UTF-8 string safely. - * - * **When to use** - * - * Use to decode a standard padded Base64 string into UTF-8 text without - * throwing on invalid input. - * - * **Details** - * - * Returns `Result.succeed` with the decoded text when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input is not valid base64. - * - * **Example** (Decoding Base64 strings) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeBase64String("aGVsbG8=") // => Result.succeed("hello") - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeBase64String = (str: string) => Result.map(decodeBase64(str), (_) => decoder.decode(_)) - -// ------------------------------------------------------------------------------------- -// Base64Url -// ------------------------------------------------------------------------------------- - -/** - * Encodes the given value into a base64 (URL) `string`. - * - * **When to use** - * - * Use to encode text or bytes as an unpadded Base64Url string for contexts that - * require the URL-safe alphabet. - * - * **Details** - * - * String inputs are encoded as UTF-8 bytes before Base64Url encoding. - * `Uint8Array` inputs are encoded directly. The output removes `=` padding and - * replaces `+` with `-` and `/` with `_`. - * - * **Example** (Encoding URL-safe Base64) - * - * ```ts import.meta.vitest - * import { Encoding } from "effect" - * - * // URL-safe base64 encoding (uses - and _ instead of + and /) - * Encoding.encodeBase64Url("hello?") // => "aGVsbG8_" - * - * const bytes = new Uint8Array([72, 101, 108, 108, 111, 63]) - * Encoding.encodeBase64Url(bytes) // => "SGVsbG8_" - * ``` - * - * @see {@link decodeBase64Url} for decoding URL-safe Base64 to bytes - * @see {@link decodeBase64UrlString} for decoding URL-safe Base64 to UTF-8 text - * @see {@link encodeBase64} for standard padded Base64 output - * - * @category encoding - * @since 2.0.0 - */ -export const encodeBase64Url: (input: Uint8Array | string) => string = (input) => - typeof input === "string" ? base64UrlEncodeUint8Array(encoder.encode(input)) : base64UrlEncodeUint8Array(input) - -/** - * Decodes a URL-safe base64 string into bytes safely. - * - * **When to use** - * - * Use to decode padded or unpadded Base64Url text into bytes without throwing - * on invalid input. - * - * **Details** - * - * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input is not valid URL-safe - * base64. Both padded and unpadded URL-safe base64 forms are accepted when - * otherwise valid. - * - * **Example** (Decoding URL-safe Base64 bytes) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeBase64Url("SGVsbG8_") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111, 63])) - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeBase64Url = (str: string): Result.Result => { - const stripped = stripCrlf(str) - const length = stripped.length - if (length % 4 === 1) { - return Result.fail( - new EncodingError({ - module: "Base64Url", - kind: "Decode", - input: stripped, - message: `Length should be a multiple of 4, but is ${length}` - }) - ) - } - - if (!/^[-_A-Z0-9]*?={0,2}$/i.test(stripped)) { - return Result.fail( - new EncodingError({ - module: "Base64Url", - kind: "Decode", - input: stripped, - message: "Invalid input" - }) - ) - } - - // Some variants allow or require omitting the padding '=' signs - let sanitized = length % 4 === 2 ? `${stripped}==` : length % 4 === 3 ? `${stripped}=` : stripped - sanitized = sanitized.replace(/-/g, "+").replace(/_/g, "/") - - return decodeBase64(sanitized) -} - -/** - * Decodes a URL-safe base64 string into a UTF-8 string safely. - * - * **When to use** - * - * Use to decode padded or unpadded Base64Url text into UTF-8 text without - * throwing on invalid input. - * - * **Details** - * - * Returns `Result.succeed` with the decoded text when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input is not valid URL-safe - * base64. - * - * **Example** (Decoding URL-safe Base64 strings) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeBase64UrlString("aGVsbG8_") // => Result.succeed("hello?") - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeBase64UrlString = (str: string) => Result.map(decodeBase64Url(str), (_) => decoder.decode(_)) - -// ------------------------------------------------------------------------------------- -// Hex -// ------------------------------------------------------------------------------------- - -/** - * Encodes the given value into a hex `string`. - * - * **When to use** - * - * Use to encode text or bytes as lowercase hexadecimal text. - * - * **Example** (Encoding hex strings and bytes) - * - * ```ts import.meta.vitest - * import { Encoding } from "effect" - * - * // Encode a string to hex - * Encoding.encodeHex("hello") // => "68656c6c6f" - * - * // Encode binary data to hex - * const bytes = new Uint8Array([72, 101, 108, 108, 111]) - * Encoding.encodeHex(bytes) // => "48656c6c6f" - * ``` - * - * @category encoding - * @since 2.0.0 - */ -export const encodeHex: (input: Uint8Array | string) => string = (input) => - typeof input === "string" ? hexEncodeUint8Array(encoder.encode(input)) : hexEncodeUint8Array(input) - -/** - * Generates a random lowercase hexadecimal string, optimized for lengths that - * are multiples of 8. - * - * **Details** - * - * `length` is not validated. The function generates `length >>> 3` random - * 8-character words, so non-negative lengths below `2 ** 32` are rounded down - * to a multiple of 8 and other values follow JavaScript's unsigned 32-bit - * coercion rules. - * - * This function uses `Math.random()` and is not cryptographically secure. For - * security-sensitive values, use the `Crypto.Crypto` service's `randomBytes` - * method and encode the result with {@link encodeHex}. - * - * @category encoding - * @since 4.0.0 - */ -export const randomHex = (length: number): string => { - switch (length) { - case 16: - return randomHex16() - case 32: - return randomHex32() - default: { - let result = "" - for (let i = length >>> 3; i > 0; i--) { - result += randomHex8() - } - return result - } - } -} - -const hexCharCodes = Uint8Array.from("0123456789abcdef", (c) => c.charCodeAt(0)) - -const randomWord = (): number => (Math.random() * 0x100000000) >>> 0 - -// Trace and span identifiers are the common lengths. A single -// String.fromCharCode call produces a flat string, which avoids rope -// flattening when the identifier is later serialized. -const randomHex8 = (): string => { - const a = randomWord() - return String.fromCharCode( - hexCharCodes[a >>> 28], - hexCharCodes[(a >>> 24) & 15], - hexCharCodes[(a >>> 20) & 15], - hexCharCodes[(a >>> 16) & 15], - hexCharCodes[(a >>> 12) & 15], - hexCharCodes[(a >>> 8) & 15], - hexCharCodes[(a >>> 4) & 15], - hexCharCodes[a & 15] - ) -} - -const randomHex16 = (): string => { - const a = randomWord() - const b = randomWord() - return String.fromCharCode( - hexCharCodes[a >>> 28], - hexCharCodes[(a >>> 24) & 15], - hexCharCodes[(a >>> 20) & 15], - hexCharCodes[(a >>> 16) & 15], - hexCharCodes[(a >>> 12) & 15], - hexCharCodes[(a >>> 8) & 15], - hexCharCodes[(a >>> 4) & 15], - hexCharCodes[a & 15], - hexCharCodes[b >>> 28], - hexCharCodes[(b >>> 24) & 15], - hexCharCodes[(b >>> 20) & 15], - hexCharCodes[(b >>> 16) & 15], - hexCharCodes[(b >>> 12) & 15], - hexCharCodes[(b >>> 8) & 15], - hexCharCodes[(b >>> 4) & 15], - hexCharCodes[b & 15] - ) -} - -const randomHex32 = (): string => { - const a = randomWord() - const b = randomWord() - const c = randomWord() - const d = randomWord() - return String.fromCharCode( - hexCharCodes[a >>> 28], - hexCharCodes[(a >>> 24) & 15], - hexCharCodes[(a >>> 20) & 15], - hexCharCodes[(a >>> 16) & 15], - hexCharCodes[(a >>> 12) & 15], - hexCharCodes[(a >>> 8) & 15], - hexCharCodes[(a >>> 4) & 15], - hexCharCodes[a & 15], - hexCharCodes[b >>> 28], - hexCharCodes[(b >>> 24) & 15], - hexCharCodes[(b >>> 20) & 15], - hexCharCodes[(b >>> 16) & 15], - hexCharCodes[(b >>> 12) & 15], - hexCharCodes[(b >>> 8) & 15], - hexCharCodes[(b >>> 4) & 15], - hexCharCodes[b & 15], - hexCharCodes[c >>> 28], - hexCharCodes[(c >>> 24) & 15], - hexCharCodes[(c >>> 20) & 15], - hexCharCodes[(c >>> 16) & 15], - hexCharCodes[(c >>> 12) & 15], - hexCharCodes[(c >>> 8) & 15], - hexCharCodes[(c >>> 4) & 15], - hexCharCodes[c & 15], - hexCharCodes[d >>> 28], - hexCharCodes[(d >>> 24) & 15], - hexCharCodes[(d >>> 20) & 15], - hexCharCodes[(d >>> 16) & 15], - hexCharCodes[(d >>> 12) & 15], - hexCharCodes[(d >>> 8) & 15], - hexCharCodes[(d >>> 4) & 15], - hexCharCodes[d & 15] - ) -} - -/** - * Decodes a hexadecimal string into bytes safely. - * - * **When to use** - * - * Use to decode hexadecimal text into bytes without throwing on invalid input. - * - * **Details** - * - * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input has an odd length or - * contains invalid hex characters. - * - * **Example** (Decoding hex bytes) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeHex("48656c6c6f") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeHex = (str: string): Result.Result => { - const bytes = new TextEncoder().encode(str) - if (bytes.length % 2 !== 0) { - return Result.fail( - new EncodingError({ - module: "Hex", - kind: "Decode", - input: str, - message: `Length must be a multiple of 2, but is ${bytes.length}` - }) - ) - } - - try { - const length = bytes.length / 2 - const result = new Uint8Array(length) - for (let i = 0; i < length; i++) { - const a = fromHexChar(bytes[i * 2]) - const b = fromHexChar(bytes[i * 2 + 1]) - result[i] = (a << 4) | b - } - - return Result.succeed(result) - } catch (e) { - return Result.fail( - new EncodingError({ - module: "Hex", - kind: "Decode", - input: str, - message: e instanceof Error ? e.message : "Invalid input" - }) - ) - } -} - -/** - * Decodes a hexadecimal string into a UTF-8 string safely. - * - * **When to use** - * - * Use to decode hexadecimal text into UTF-8 text without throwing on invalid - * input. - * - * **Details** - * - * Returns `Result.succeed` with the decoded text when decoding succeeds, or - * `Result.fail` with an `EncodingError` when the input is not valid hex. - * - * **Example** (Decoding hex strings) - * - * ```ts import.meta.vitest - * import { Encoding, Result } from "effect" - * - * Encoding.decodeHexString("68656c6c6f") // => Result.succeed("hello") - * ``` - * - * @category decoding - * @since 2.0.0 - */ -export const decodeHexString = (str: string) => Result.map(decodeHex(str), (_) => decoder.decode(_)) - -// ------------------------------------------------------------------------------------- -// internals -// ------------------------------------------------------------------------------------- - -const encoder = new TextEncoder() -const decoder = new TextDecoder() - -const stripCrlf = (str: string) => str.replace(/[\n\r]/g, "") - -// Base64 internals - -const base64EncodeUint8Array = (bytes: Uint8Array) => { - const length = bytes.length - - let result = "" - let i: number - - for (i = 2; i < length; i += 3) { - result += base64abc[bytes[i - 2] >> 2] - result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] - result += base64abc[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)] - result += base64abc[bytes[i] & 0x3f] - } - - if (i === length + 1) { - result += base64abc[bytes[i - 2] >> 2] - result += base64abc[(bytes[i - 2] & 0x03) << 4] - result += "==" - } - - if (i === length) { - result += base64abc[bytes[i - 2] >> 2] - result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] - result += base64abc[(bytes[i - 1] & 0x0f) << 2] - result += "=" - } - - return result -} - -function getBase64Code(charCode: number) { - if (charCode >= base64codes.length) { - throw new TypeError(`Invalid character ${String.fromCharCode(charCode)}`) - } - - const code = base64codes[charCode] - if (code === 255) { - throw new TypeError(`Invalid character ${String.fromCharCode(charCode)}`) - } - - return code -} - -const base64abc = [ - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "+", - "/" -] - -const base64codes = [ - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 62, - 255, - 255, - 255, - 63, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 255, - 255, - 255, - 0, - 255, - 255, - 255, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 255, - 255, - 255, - 255, - 255, - 255, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51 -] - -// Base64Url internals - -const base64UrlEncodeUint8Array = (data: Uint8Array) => - base64EncodeUint8Array(data).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") - -// Hex internals - -const byteToHex: Array = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")) - -const hexEncodeUint8Array = (bytes: Uint8Array): string => { - let result = "" - for (let i = 0; i < bytes.length; i++) { - result += byteToHex[bytes[i]] - } - return result -} - -const fromHexChar = (byte: number) => { - if (48 <= byte && byte <= 57) { - return byte - 48 - } - - if (97 <= byte && byte <= 102) { - return byte - 97 + 10 - } - - if (65 <= byte && byte <= 70) { - return byte - 65 + 10 - } - - throw new TypeError("Invalid input") -} diff --git a/repos/effect/packages/effect/src/Equal.ts b/repos/effect/packages/effect/src/Equal.ts index f696fe444f..5ea7eb54ee 100644 --- a/repos/effect/packages/effect/src/Equal.ts +++ b/repos/effect/packages/effect/src/Equal.ts @@ -11,7 +11,7 @@ */ import type { Equivalence } from "./Equivalence.ts" import * as Hash from "./Hash.ts" -import { byReferenceInstances, getAllObjectKeys } from "./internal/equal.ts" +import { byReferenceInstances, getAllObjectKeys, viewBytes } from "./internal/equal.ts" import { hasProperty } from "./Predicate.ts" /** @@ -128,7 +128,7 @@ export interface Equal extends Hash.Hash { * arrays compare element-by-element, Maps and Sets compare entries * order-independently, and plain objects compare enumerable keys recursively. * Functions without an `Equal` implementation compare by reference. Circular - * references are handled when both structures are circular at the same depth. + * structures use coinductive equality. * * Hash values are checked first as a fast-path rejection. The function also * supports dual data-last usage: call it with one argument to get a curried @@ -136,9 +136,10 @@ export interface Equal extends Hash.Hash { * * **Gotchas** * - * - Results are cached per object pair in a WeakMap. **Objects must not be - * mutated after their first comparison.** - * - Map and Set comparisons are O(n²) in size. + * - Object-pair results are cached in a WeakMap. **Objects must not be + * be mutated after their first comparison.** + * - Map and Set entries are matched within groups of equal hashes, so they + * are O(n) for well-distributed hashes and O(n²) when every hash collides. * * **Example** (Comparing values) * @@ -197,38 +198,41 @@ function compareBoth(self: unknown, that: unknown): boolean { return false } - // For objects and functions, use cached comparison - return withCache(self, that, compareObjects) + return compareObjects(self, that) } -/** Helper to run comparison with proper visited tracking */ -function withVisitedTracking( - self: object, - that: object, - fn: () => boolean -): boolean { - const hasLeft = visitedLeft.has(self) - const hasRight = visitedRight.has(that) - // Check for circular references before adding - if (hasLeft && hasRight) { - return true // Both are circular at the same level - } - if (hasLeft || hasRight) { - return false // Only one is circular +function compareObjects(self: object, that: object): boolean { + const depth = pathLeft.length + // A repeated pair closes a cycle under coinductive equality. + for (let i = depth; i-- > 0;) { + if (pathLeft[i] === self && pathRight[i] === that) return true } - visitedLeft.add(self) - visitedRight.add(that) - const result = fn() - visitedLeft.delete(self) - visitedRight.delete(that) + if (depth) return compareOnPath(self, that) + // Only outermost results are independent of path assumptions. + let known = results.get(self) + if (!known) results.set(self, known = new WeakMap()) + let result = known.get(that) + if (result === undefined) known.set(that, result = compareOnPath(self, that)) return result } -const visitedLeft = new WeakSet() -const visitedRight = new WeakSet() +function compareOnPath(self: object, that: object): boolean { + pathLeft.push(self) + pathRight.push(that) + try { + return compareStructure(self, that) + } finally { + pathLeft.pop() + pathRight.pop() + } +} -/** Helper to perform cached object comparison */ -function compareObjects(self: object, that: object): boolean { +// The pairs on the current comparison's path (the coinductive assumptions). +const pathLeft: Array = [] +const pathRight: Array = [] +const results = new WeakMap>() + +function compareStructure(self: object, that: object): boolean { if (Hash.hash(self) !== Hash.hash(that)) { return false } else if (self instanceof Date) { @@ -237,84 +241,45 @@ function compareObjects(self: object, that: object): boolean { const thatTime = that.getTime() return selfTime === thatTime || (Number.isNaN(selfTime) && Number.isNaN(thatTime)) } else if (self instanceof RegExp) { - if (!(that instanceof RegExp)) return false - return self.toString() === that.toString() + return that instanceof RegExp && self.toString() === that.toString() } - const selfIsEqual = isEqual(self) - const thatIsEqual = isEqual(that) - if (selfIsEqual !== thatIsEqual) return false - const bothEquals = selfIsEqual && thatIsEqual - if (typeof self === "function" && !bothEquals) { + const bothEquals = isEqual(self) + if (bothEquals !== isEqual(that) || (typeof self === "function" && !bothEquals)) { return false - } - return withVisitedTracking(self, that, () => { - if (bothEquals) { - return (self as any)[symbol](that) - } else if (Array.isArray(self)) { - if (!Array.isArray(that) || self.length !== that.length) { - return false - } - return compareArrays(self, that) - } else if (ArrayBuffer.isView(self)) { - const selfIsDataView = self instanceof DataView - if ( - !ArrayBuffer.isView(that) || - self.byteLength !== that.byteLength || - selfIsDataView !== (that instanceof DataView) - ) { - return false - } - if (selfIsDataView) { - const thatDataView = that as DataView - return compareTypedArrays( - new Uint8Array(self.buffer, self.byteOffset, self.byteLength), - new Uint8Array(thatDataView.buffer, thatDataView.byteOffset, thatDataView.byteLength) - ) - } - return compareTypedArrays(self as Uint8Array, that as Uint8Array) - } else if (self instanceof Map) { - if (!(that instanceof Map) || self.size !== that.size) { - return false - } - return compareMaps(self, that) - } else if (self instanceof Set) { - if (!(that instanceof Set) || self.size !== that.size) { - return false - } - return compareSets(self, that) + } else if (bothEquals) { + return (self as Equal)[symbol](that as Equal) + } else if (Array.isArray(self)) { + if (!Array.isArray(that) || self.length !== that.length) { + return false } - return compareRecords(self as any, that as any) - }) -} - -function withCache(self: object, that: object, f: (a: any, b: any) => boolean): boolean { - // Check cache first - let selfMap = equalityCache.get(self) - if (!selfMap) { - selfMap = new WeakMap() - equalityCache.set(self, selfMap) - } else if (selfMap.has(that)) { - return selfMap.get(that)! - } - - // Perform the comparison - const result = f(self, that) - - // Cache the result bidirectionally - selfMap.set(that, result) - - let thatMap = equalityCache.get(that) - if (!thatMap) { - thatMap = new WeakMap() - equalityCache.set(that, thatMap) + return compareArrays(self, that) + } else if (ArrayBuffer.isView(self)) { + const selfIsDataView = self instanceof DataView + if ( + !ArrayBuffer.isView(that) || + self.byteLength !== that.byteLength || + selfIsDataView !== (that instanceof DataView) + ) { + return false + } + if (selfIsDataView) { + return compareTypedArrays(viewBytes(self), viewBytes(that as DataView)) + } + return compareTypedArrays(self as Uint8Array, that as Uint8Array) + } else if (self instanceof Map) { + if (!(that instanceof Map) || self.size !== that.size) { + return false + } + return compareHashed(self, that, entryHash, equalEntries) + } else if (self instanceof Set) { + if (!(that instanceof Set) || self.size !== that.size) { + return false + } + return compareHashed(self, that, Hash.hash, compareBoth) } - thatMap.set(self, result) - - return result + return compareRecords(self as any, that as any) } -const equalityCache = new WeakMap>() - function compareArrays(self: Array, that: Array): boolean { for (let i = 0; i < self.length; i++) { if (!compareBoth(self[i], that[i])) { @@ -357,58 +322,57 @@ function compareRecords( return true } -/** @internal */ -export function makeCompareMap(keyEquivalence: Equivalence, valueEquivalence: Equivalence) { - return function compareMaps(self: Iterable<[K, V]>, that: Iterable<[K, V]>): boolean { - const thatEntries = Array.from(that) - for (const [selfKey, selfValue] of self) { - let found = false - for (let i = 0; i < thatEntries.length; i++) { - const [thatKey, thatValue] = thatEntries[i] - if (keyEquivalence(selfKey, thatKey) && valueEquivalence(selfValue, thatValue)) { - thatEntries[i] = thatEntries[thatEntries.length - 1] - thatEntries.pop() - found = true - break +// Match items one-to-one within equal-hash groups. +function compareHashed( + self: Iterable, + that: Iterable, + hashOf: (item: A) => number, + equivalent: (self: A, that: A) => boolean +): boolean { + const groups = new Map>() + for (const item of that) { + const h = hashOf(item) + const group = groups.get(h) + if (group) group.push(item) + else groups.set(h, [item]) + } + outer: for (const item of self) { + const group = groups.get(hashOf(item)) + if (group) { + for (let i = 0; i < group.length; i++) { + if (equivalent(item, group[i])) { + group[i] = group[group.length - 1] + group.pop() + continue outer } } - if (!found) { - return false - } } - - return true + return false } + return true } -const compareMaps = makeCompareMap(compareBoth, compareBoth) +const entryHash = (entry: readonly [unknown, unknown]): number => Hash.hash(entry[0]) + +const equalEntries = (self: readonly [K, V], that: readonly [K, V]): boolean => + compareBoth(self[0], that[0]) && compareBoth(self[1], that[1]) + +const sameGroup = (): number => 0 + +/** @internal */ +export function makeCompareMap(keyEquivalence: Equivalence, valueEquivalence: Equivalence) { + return makeCompareSet((self, that) => + keyEquivalence(self[0], that[0]) && valueEquivalence(self[1], that[1]) + ) +} /** @internal */ export function makeCompareSet(equivalence: Equivalence) { return function compareSets(self: Iterable, that: Iterable): boolean { - const thatValues = Array.from(that) - for (const selfValue of self) { - let found = false - for (let i = 0; i < thatValues.length; i++) { - const thatValue = thatValues[i] - if (equivalence(selfValue, thatValue)) { - thatValues[i] = thatValues[thatValues.length - 1] - thatValues.pop() - found = true - break - } - } - if (!found) { - return false - } - } - - return true + return compareHashed(self, that, sameGroup, equivalence) } } -const compareSets = makeCompareSet(compareBoth) - /** * Checks whether a value implements the {@link Equal} interface. * diff --git a/repos/effect/packages/effect/src/FiberHandle.ts b/repos/effect/packages/effect/src/FiberHandle.ts index 2317b7a424..3f78b45555 100644 --- a/repos/effect/packages/effect/src/FiberHandle.ts +++ b/repos/effect/packages/effect/src/FiberHandle.ts @@ -11,7 +11,6 @@ * @since 2.0.0 */ import * as Cause from "./Cause.ts" -import type { Context } from "./Context.ts" import * as Deferred from "./Deferred.ts" import * as Effect from "./Effect.ts" import * as Exit from "./Exit.ts" @@ -20,6 +19,7 @@ import * as Filter from "./Filter.ts" import { dual } from "./Function.ts" import type * as Inspectable from "./Inspectable.ts" import { PipeInspectableProto } from "./internal/core.ts" +import * as internalEffect from "./internal/effect.ts" import * as Option from "./Option.ts" import type { Pipeable } from "./Pipeable.ts" import * as Predicate from "./Predicate.ts" @@ -552,6 +552,8 @@ const constInterruptedFiber = (function() { * The handle manages only one fiber: running a new effect interrupts the * previous fiber unless `onlyIfMissing` is set. When the managed fiber * completes, it is removed from the handle. + * Set `startImmediately: false` to defer startup. By default, the effect starts + * immediately. * * **Example** (Running an effect in a fiber handle) * @@ -611,6 +613,7 @@ const runImpl = ( self: FiberHandle, effect: Effect.Effect, options?: { + readonly startImmediately?: boolean | undefined readonly onlyIfMissing?: boolean | undefined } ): Effect.Effect, never, R> => @@ -620,7 +623,12 @@ const runImpl = ( } else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) { return Effect.sync(constInterruptedFiber) } - const fiber = Effect.runForkWith(parent.context as Context)(effect) + const fiber: Fiber.Fiber = internalEffect.forkUnsafe( + parent, + effect, + options?.startImmediately ?? true, + true + ) setUnsafe(self, fiber, options) return Effect.succeed(fiber) }) diff --git a/repos/effect/packages/effect/src/FiberMap.ts b/repos/effect/packages/effect/src/FiberMap.ts index a8b7f1fbe1..cdda71b254 100644 --- a/repos/effect/packages/effect/src/FiberMap.ts +++ b/repos/effect/packages/effect/src/FiberMap.ts @@ -10,7 +10,6 @@ * @since 2.0.0 */ import * as Cause from "./Cause.ts" -import type { Context } from "./Context.ts" import * as Deferred from "./Deferred.ts" import * as Effect from "./Effect.ts" import * as Exit from "./Exit.ts" @@ -19,6 +18,7 @@ import * as Filter from "./Filter.ts" import { constVoid, dual } from "./Function.ts" import type * as Inspectable from "./Inspectable.ts" import { PipeInspectableProto } from "./internal/core.ts" +import * as internalEffect from "./internal/effect.ts" import * as Iterable from "./Iterable.ts" import * as MutableHashMap from "./MutableHashMap.ts" import * as Option from "./Option.ts" @@ -733,6 +733,8 @@ const constInterruptedFiber = (function() { * * When the fiber completes, it is removed from the map. If the key already has * a fiber, the previous fiber is interrupted unless `onlyIfMissing` is set. + * Set `startImmediately: false` to defer startup. By default, the effect starts + * immediately. * * **Example** (Forking effects into a map) * @@ -796,17 +798,23 @@ const runImpl = ( key: K, effect: Effect.Effect, options?: { + readonly startImmediately?: boolean | undefined readonly onlyIfMissing?: boolean readonly propagateInterruption?: boolean | undefined } -) => +): Effect.Effect, never, R> => Effect.withFiber((parent) => { if (self.state._tag === "Closed") { return Effect.interrupt } else if (options?.onlyIfMissing === true && hasUnsafe(self, key)) { return Effect.sync(constInterruptedFiber) } - const fiber = Effect.runForkWith(parent.context as Context)(effect) + const fiber: Fiber.Fiber = internalEffect.forkUnsafe( + parent, + effect, + options?.startImmediately ?? true, + true + ) setUnsafe(self, key, fiber, options) return Effect.succeed(fiber) }) diff --git a/repos/effect/packages/effect/src/FiberSet.ts b/repos/effect/packages/effect/src/FiberSet.ts index f99040716f..f7cfd58db3 100644 --- a/repos/effect/packages/effect/src/FiberSet.ts +++ b/repos/effect/packages/effect/src/FiberSet.ts @@ -9,7 +9,6 @@ * @since 2.0.0 */ import * as Cause from "./Cause.ts" -import type { Context } from "./Context.ts" import * as Deferred from "./Deferred.ts" import * as Effect from "./Effect.ts" import * as Exit from "./Exit.ts" @@ -18,6 +17,7 @@ import * as Filter from "./Filter.ts" import { constVoid, dual } from "./Function.ts" import type * as Inspectable from "./Inspectable.ts" import { PipeInspectableProto } from "./internal/core.ts" +import * as internalEffect from "./internal/effect.ts" import * as Iterable from "./Iterable.ts" import type { Pipeable } from "./Pipeable.ts" import * as Predicate from "./Predicate.ts" @@ -449,8 +449,9 @@ const constInterruptedFiber = (function() { })() /** - * Forks an Effect and add the forked fiber to the FiberSet. - * When the fiber completes, it will be removed from the FiberSet. + * Forks an Effect and adds the fiber to the FiberSet. The fiber is removed + * when it completes. Set `startImmediately: false` to defer startup. By default, + * the effect starts immediately. * * **Example** (Forking effects into a set) * @@ -506,6 +507,7 @@ const runImpl = ( self: FiberSet, effect: Effect.Effect, options?: { + readonly startImmediately?: boolean | undefined readonly propagateInterruption?: boolean | undefined } ): Effect.Effect, never, R> => @@ -513,7 +515,12 @@ const runImpl = ( if (self.state._tag === "Closed") { return Effect.sync(constInterruptedFiber) } - const fiber = Effect.runForkWith(parent.context as Context)(effect) + const fiber: Fiber.Fiber = internalEffect.forkUnsafe( + parent, + effect, + options?.startImmediately ?? true, + true + ) addUnsafe(self, fiber, options) return Effect.succeed(fiber) }) diff --git a/repos/effect/packages/effect/src/FileSystem.ts b/repos/effect/packages/effect/src/FileSystem.ts index 9ea4cf3506..c737dc17f0 100644 --- a/repos/effect/packages/effect/src/FileSystem.ts +++ b/repos/effect/packages/effect/src/FileSystem.ts @@ -813,6 +813,11 @@ export const isFile = (u: unknown): u is File => hasProperty(u, FileTypeId) * and retrieving file information. File handles are automatically managed * within scoped operations to ensure proper cleanup. * + * Each handle tracks its own cursor, which can differ from the POSIX file + * offset. In append mode, writes go to the end of the file without moving the + * cursor. Outside append mode, `truncate` clamps a cursor past the new length + * to that length. + * * **Example** (Working with file handles) * * ```ts import.meta.vitest diff --git a/repos/effect/packages/effect/src/Formatter.ts b/repos/effect/packages/effect/src/Formatter.ts index 9121c252de..b184d5c458 100644 --- a/repos/effect/packages/effect/src/Formatter.ts +++ b/repos/effect/packages/effect/src/Formatter.ts @@ -258,8 +258,10 @@ function safeGet(input: object, key: PropertyKey): unknown { * object ancestry. Circular references are replaced with `undefined`, which * omits them from object output. `Redactable` values are automatically redacted * before serialization. `BigInt` values are stringified with an `n` suffix. - * Values not supported by JSON otherwise follow standard `JSON.stringify` - * behavior. The `space` parameter controls indentation and defaults to `0`. + * `Error` instances without a `toJSON` property include their enumerable + * properties plus `name` and `message`. Errors with `toJSON` keep their custom + * representation. Other values follow standard `JSON.stringify` behavior. The + * `space` parameter controls indentation and defaults to `0`. * * **Gotchas** * @@ -316,6 +318,9 @@ export function formatJson(input: unknown, options?: { if (typeof redacted !== "object" || redacted === null) { return redacted } + const current = redacted instanceof Error && !Predicate.hasProperty(redacted, "toJSON") + ? { ...redacted, name: redacted.name, message: redacted.message } + : redacted while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) { ancestors.pop() } @@ -323,7 +328,10 @@ export function formatJson(input: unknown, options?: { return undefined // circular reference } ancestors.push(redacted) - return redacted + if (current !== redacted) { + ancestors.push(current) + } + return current }, options?.space ) ?? "null" diff --git a/repos/effect/packages/effect/src/Function.ts b/repos/effect/packages/effect/src/Function.ts index f12e50a746..edb9002856 100644 --- a/repos/effect/packages/effect/src/Function.ts +++ b/repos/effect/packages/effect/src/Function.ts @@ -1379,7 +1379,9 @@ export function memoizeIdempotent(f: (a: A) => A): (a: A) => A if (cached !== undefined) return cached const result = f(a) cache.set(a, result) - cache.set(result, result) + if (result !== a) { + cache.set(result, result) + } return result } } diff --git a/repos/effect/packages/effect/src/Graph.ts b/repos/effect/packages/effect/src/Graph.ts index 7f9ae698be..f0dbb6730e 100644 --- a/repos/effect/packages/effect/src/Graph.ts +++ b/repos/effect/packages/effect/src/Graph.ts @@ -860,9 +860,10 @@ class EdgeIdentity implements Equal.Equal { [Hash.symbol](): number { const hash = Hash.hash(this.identity) - return this.type === "directed" - ? Hash.combine(Hash.hash(this.target))(Hash.combine(Hash.hash(this.source))(hash)) - : Hash.optimize(hash ^ (Hash.hash(this.source) + Hash.hash(this.target))) + if (this.type === "directed") { + return Hash.optimize(Hash.combine(Hash.combine(hash, Hash.hash(this.source)), Hash.hash(this.target))) + } + return Hash.optimize(Hash.combine(hash, internal.endpointsHash(this.source, this.target))) } } @@ -3336,7 +3337,24 @@ const getUniqueDirectedNeighbors = ( graph: Graph | MutableGraph, nodeIndex: NodeIndex, direction: Direction -): Array => Array.from(new Set(getDirectedNeighbors(graph, nodeIndex, direction))) +): Array => { + const neighbors = getDirectedNeighbors(graph, nodeIndex, direction) + if (neighbors.length > ScanDegreeLimit) { + return Array.from(new Set(neighbors)) + } + // getDirectedNeighbors returns a fresh array, so duplicates are compacted in place + let length = 0 + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i] + let j = 0 + while (j < length && neighbors[j] !== neighbor) j++ + if (j === length) neighbors[length++] = neighbor + } + neighbors.length = length + return neighbors +} + +const ScanDegreeLimit = 32 /** * Returns the neighboring node indices for a node. @@ -4662,20 +4680,15 @@ export interface ReachabilityConfig { const getUnweightedDistances = ( graph: Graph | MutableGraph, source: NodeIndex, - direction: TraversalDirection, - target?: NodeIndex + direction: TraversalDirection ): Map => { const impl = internal.toImpl(graph) if (!impl.nodes.has(source)) { throw missingNode(source) } - if (target !== undefined && !impl.nodes.has(target)) { - throw missingNode(target) - } const cache = csr.get(graph) const sourceNode = csr.getNodeIndex(cache, source)! - const targetNode = target === undefined ? undefined : csr.getNodeIndex(cache, target)! const adjacencies = csr.getAdjacencies(cache, graph.type === "undirected" ? "outgoing" : direction) const compactDistances = new Int32Array(cache.nodeIds.length) compactDistances.fill(-1) @@ -4685,23 +4698,20 @@ const getUnweightedDistances = ( let tail = 0 queue[tail++] = sourceNode - while (head < tail) { - const current = queue[head++] - if (current === targetNode) { - break - } - const visit = (adjacency: csr.Adjacency) => { - for (let i = adjacency.rowOffsets[current]; i < adjacency.rowOffsets[current + 1]; i++) { - const neighbor = adjacency.columnIndices[i] - if (compactDistances[neighbor] === -1) { - compactDistances[neighbor] = compactDistances[current] + 1 - queue[tail++] = neighbor - } + const visit = (adjacency: csr.Adjacency, current: number) => { + for (let i = adjacency.rowOffsets[current]; i < adjacency.rowOffsets[current + 1]; i++) { + const neighbor = adjacency.columnIndices[i] + if (compactDistances[neighbor] === -1) { + compactDistances[neighbor] = compactDistances[current] + 1 + queue[tail++] = neighbor } } - visit(adjacencies.primary) + } + while (head < tail) { + const current = queue[head++] + visit(adjacencies.primary, current) if (adjacencies.secondary !== undefined) { - visit(adjacencies.secondary) + visit(adjacencies.secondary, current) } } @@ -5905,8 +5915,7 @@ export const minimumSpanningForest: { compactByNode.set(index, nodes.length) nodes.push({ index, data }) } - const weightedEdges: Array<{ readonly index: EdgeIndex; readonly weight: number; readonly order: number }> = [] - let order = 0 + const weightedEdges: Array<{ readonly index: EdgeIndex; readonly weight: number }> = [] withMutationGuard(graph, () => { for (const [index, edge] of impl.edges) { const weight = cost(edge.data) @@ -5914,12 +5923,11 @@ export const minimumSpanningForest: { throw new GraphError({ message: "Minimum spanning forest does not support NaN or -Infinity edge weights" }) } if (weight !== Infinity) { - weightedEdges.push({ index, weight, order }) + weightedEdges.push({ index, weight }) } - order++ } }) - weightedEdges.sort((self, that) => self.weight - that.weight || self.order - that.order) + weightedEdges.sort((self, that) => self.weight - that.weight) const parents = new Uint32Array(nodes.length) const ranks = new Uint8Array(nodes.length) @@ -6754,7 +6762,7 @@ export const astar: { } const getHeuristic = (nodeData: N): number => { - const value = withMutationGuard(graph, () => config.heuristic(nodeData, targetNodeData)) + const value = config.heuristic(nodeData, targetNodeData) if (!Number.isFinite(value)) { throw new GraphError({ message: "A* algorithm requires finite heuristic values" }) } @@ -6772,42 +6780,44 @@ export const astar: { const visited = new Uint8Array(cache.nodeIds.length) const openSet = denseMinHeapMake(cache.nodeIds.length) let sequence = 0 - denseMinHeapPush(openSet, source, getHeuristic(sourceNodeData), sequence++) - - while (openSet.size > 0) { - denseMinHeapPop(openSet) - const current = openSet.poppedNode - if (visited[current] !== 0) { - continue - } - visited[current] = 1 - if (current === target) { - break - } + withMutationGuard(graph, () => { + denseMinHeapPush(openSet, source, getHeuristic(sourceNodeData), sequence++) - const currentScore = scores[current] - for (let i: number = outgoing.rowOffsets[current]; i < outgoing.rowOffsets[current + 1]; i++) { - const neighbor = outgoing.columnIndices[i] - if (visited[neighbor] !== 0) { + while (openSet.size > 0) { + denseMinHeapPop(openSet) + const current = openSet.poppedNode + if (visited[current] !== 0) { continue } - const edge = outgoing.edgeIndices[i] - const tentativeScore = currentScore + edgeWeights[edge] - if (edgeWeights[edge] !== Infinity && !Number.isFinite(tentativeScore)) { - throw new GraphError({ message: "A* distance calculation exceeded the finite number range" }) + visited[current] = 1 + if (current === target) { + break } - if (tentativeScore < scores[neighbor]) { - scores[neighbor] = tentativeScore - previousNode[neighbor] = current - previousEdge[neighbor] = edge - const priority = tentativeScore + getHeuristic(cache.nodeData[neighbor] as N) - if (!Number.isFinite(priority)) { - throw new GraphError({ message: "A* priority calculation exceeded the finite number range" }) + + const currentScore = scores[current] + for (let i: number = outgoing.rowOffsets[current]; i < outgoing.rowOffsets[current + 1]; i++) { + const neighbor = outgoing.columnIndices[i] + if (visited[neighbor] !== 0) { + continue + } + const edge = outgoing.edgeIndices[i] + const tentativeScore = currentScore + edgeWeights[edge] + if (edgeWeights[edge] !== Infinity && !Number.isFinite(tentativeScore)) { + throw new GraphError({ message: "A* distance calculation exceeded the finite number range" }) + } + if (tentativeScore < scores[neighbor]) { + scores[neighbor] = tentativeScore + previousNode[neighbor] = current + previousEdge[neighbor] = edge + const priority = tentativeScore + getHeuristic(cache.nodeData[neighbor] as N) + if (!Number.isFinite(priority)) { + throw new GraphError({ message: "A* priority calculation exceeded the finite number range" }) + } + denseMinHeapPush(openSet, neighbor, priority, sequence++) } - denseMinHeapPush(openSet, neighbor, priority, sequence++) } } - } + }) if (scores[target] === Infinity) { return Option.none() @@ -7341,18 +7351,12 @@ export const allShortestPaths: { throw new GraphError({ message: "All shortest paths distance calculation exceeded the finite number range" }) } const known = distances[neighbor] - const predecessor = { node: currentNode, edge } if (nextDistance < known) { distances[neighbor] = nextDistance - previous[neighbor] = [predecessor] + previous[neighbor] = [{ node: currentNode, edge }] denseMinHeapPush(queue, neighbor, nextDistance, sequence++) } else if (nextDistance === known && nextDistance !== Infinity) { - const predecessors = previous[neighbor] - if (predecessors === undefined) { - previous[neighbor] = [predecessor] - } else { - predecessors.push(predecessor) - } + ;(previous[neighbor] ??= []).push({ node: currentNode, edge }) } } } diff --git a/repos/effect/packages/effect/src/Hash.ts b/repos/effect/packages/effect/src/Hash.ts index 99f226526e..a66c55a0e0 100644 --- a/repos/effect/packages/effect/src/Hash.ts +++ b/repos/effect/packages/effect/src/Hash.ts @@ -10,7 +10,8 @@ * @since 2.0.0 */ import { dual } from "./Function.ts" -import { byReferenceInstances, getAllObjectKeys } from "./internal/equal.ts" +import { byReferenceInstances, getAllObjectKeys, viewBytes } from "./internal/equal.ts" +import { addBackEdge, backEdges } from "./internal/hash.ts" import { hasProperty } from "./Predicate.ts" /** @@ -108,12 +109,10 @@ export const hash: (self: A) => number = (self: A) => { return string(self.toString(10)) case "string": return string(self) - case "undefined": - return string("undefined") case "function": case "object": { if (self === null) { - return string("null") + break } else if (self instanceof Date) { if (Number.isNaN(self.getTime())) { return string("Invalid Date") @@ -125,33 +124,45 @@ export const hash: (self: A) => number = (self: A) => { if (byReferenceInstances.has(self)) { return random(self) } - if (hashCache.has(self)) { - return hashCache.get(self)! + const cached = hashCache.get(self) + if (cached !== undefined) { + return cached } - const h = withVisitedTracking(self, () => { - if (isHash(self)) { - return self[symbol]() + if (visitedObjects.has(self)) { + addBackEdge() + return string("[Circular]") + } + visitedObjects.add(self) + const seen = backEdges + let h: number + try { + if (symbol in self) { + h = (self as Hash)[symbol]() } else if (typeof self === "function") { - return random(self) + h = random(self) } else if (self instanceof DataView) { - return array(new Uint8Array(self.buffer, self.byteOffset, self.byteLength)) + h = array(viewBytes(self)) } else if (Array.isArray(self) || ArrayBuffer.isView(self)) { - return array(self as any) + h = array(self as any) } else if (self instanceof Map) { - return hashMap(self) + h = hashMap(self) } else if (self instanceof Set) { - return hashSet(self) + h = hashSet(self) + } else { + h = structure(self) } - return structure(self) - }) - hashCache.set(self, h) + } finally { + visitedObjects.delete(self) + } + // Hashes containing a back-edge depend on the entry point. + if (seen === backEdges) { + hashCache.set(self, h) + } return h } } - default: - // The remaining primitive types are boolean and symbol. - return string(String(self)) } + return optimize(mix(string(String(self)))) } /** @@ -185,11 +196,20 @@ export const hash: (self: A) => number = (self: A) => { */ export const random: (self: A) => number = (self) => { if (!randomHashCache.has(self)) { - randomHashCache.set(self, number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))) + randomHashCache.set(self, optimize((Math.random() * 0x100000000) | 0)) } return randomHashCache.get(self)! } +// 32-bit MurmurHash3 finalizer. +const mix = (h: number): number => { + h ^= h >>> 16 + h = Math.imul(h, 0x85ebca6b) + h ^= h >>> 13 + h = Math.imul(h, 0xc2b2ae35) + return h ^ (h >>> 16) +} + /** * Combines two hash values into a single hash value. * @@ -200,8 +220,7 @@ export const random: (self: A) => number = (self) => { * * **Details** * - * Supports both direct and pipeable usage. The implementation combines two - * hash values with `(self * 53) ^ b`. + * Supports direct and pipeable usage. Argument order affects the result. * * **Example** (Combining hash values) * @@ -224,7 +243,7 @@ export const random: (self: A) => number = (self) => { export const combine: { (b: number): (self: number) => number (self: number, b: number): number -} = dual(2, (self: number, b: number): number => (self * 53) ^ b) +} = dual(2, (self: number, b: number): number => mix(Math.imul(self, 0x9e3779b1) + Math.imul(b, 0x85ebca6b))) /** * Applies bit manipulation techniques to optimize a hash value. @@ -284,6 +303,8 @@ export const optimize = (n: number): number => (n & 0xbfffffff) | ((n >>> 1) & 0 */ export const isHash = (u: unknown): u is Hash => hasProperty(u, symbol) +const float64 = new DataView(new ArrayBuffer(8)) + /** * Computes a hash value for a number. * @@ -293,9 +314,8 @@ export const isHash = (u: unknown): u is Hash => hasProperty(u, symbol) * * **Details** * - * This function creates a hash value for numeric inputs, handling special cases - * like NaN, Infinity, and -Infinity with distinct hash values. It uses bitwise operations to ensure good distribution - * of hash values across different numeric inputs. + * Int32 values hash to themselves. Other numbers hash from their IEEE-754 bits, + * with a canonical representation for `NaN`. * * **Example** (Hashing numbers) * @@ -313,17 +333,12 @@ export const isHash = (u: unknown): u is Hash => hasProperty(u, symbol) * @since 2.0.0 */ export const number = (n: number) => { - if (n !== n || n === Infinity || n === -Infinity) { - return string(String(n)) - } - let h = n | 0 - if (h !== n) { - h ^= n * 0xffffffff - } - while (n > 0xffffffff) { - h ^= n /= 0xffffffff + const h = n | 0 + if (h === n) { + return optimize(h) } - return optimize(h) + float64.setFloat64(0, n !== n ? NaN : n) + return optimize(combine(float64.getInt32(0), float64.getInt32(4))) } /** @@ -385,8 +400,8 @@ export const string = (str: string) => { * const hash1 = Hash.structureKeys(person, ["name", "age"]) * const hash2 = Hash.structureKeys(person, ["name", "city"]) * - * hash1 // => -590673747 - * hash2 // => 284850673 + * hash1 // => -731887653 + * hash2 // => 148523102 * * const person2 = { name: "John", age: 30, city: "Boston" } * const hash3 = Hash.structureKeys(person2, ["name", "age"]) @@ -425,9 +440,9 @@ export const structureKeys = (o: object, keys: Iterable) => { * const obj2 = { name: "Jane", age: 25 } * const obj3 = { name: "John", age: 30 } * - * Hash.structure(obj1) // => -590673747 - * Hash.structure(obj2) // => -590160631 - * Hash.structure(obj3) // => -590673747 + * Hash.structure(obj1) // => -731887653 + * Hash.structure(obj2) // => -222100417 + * Hash.structure(obj3) // => -731887653 * Hash.structure(obj1) === Hash.structure(obj3) // => true * ``` * @@ -436,7 +451,7 @@ export const structureKeys = (o: object, keys: Iterable) => { */ export const structure = (o: A) => structureKeys(o, getAllObjectKeys(o)) -const iterableWith = (seed: number, f: (el: any) => number) => (iter: Iterable) => { +const unordered = (seed: number, f: (el: any) => number) => (iter: Iterable) => { let h = seed for (const element of iter) { h ^= f(element) @@ -453,13 +468,12 @@ const iterableWith = (seed: number, f: (el: any) => number) => (iter: Iterable number) => (iter: Iterable 6151 - * Hash.array(arr2) // => 6151 - * Hash.array(arr3) // => 6151 * Hash.array(arr1) === Hash.array(arr2) // => true - * Hash.array(arr1) === Hash.array(arr3) // => true + * Hash.array(arr1) === Hash.array(arr3) // => false * ``` * * @see {@link hash} for the general-purpose hash dispatcher @@ -482,24 +493,21 @@ const iterableWith = (seed: number, f: (el: any) => number) => (iter: Iterable(arr: Iterable) => number = iterableWith(6151, hash) +export const array = (arr: Iterable): number => { + let h = 6151 + for (const element of arr) { + h = combine(h, hash(element)) + } + return optimize(h) +} -const hashMap: (map: Iterable) => number = iterableWith( +const hashMap: (map: Iterable) => number = unordered( string("Map"), ([k, v]) => combine(hash(k), hash(v)) ) -const hashSet: (set: Iterable) => number = iterableWith(string("Set"), hash) +const setSeed = string("Set") +const hashSet: (set: Iterable) => number = unordered(setSeed, (element) => combine(setSeed, hash(element))) const randomHashCache = new WeakMap() -const hashCache = new WeakMap() +const hashCache = new WeakMap() const visitedObjects = new WeakSet() - -function withVisitedTracking(obj: object, fn: () => T): T { - if (visitedObjects.has(obj)) { - return string("[Circular]") as T - } - visitedObjects.add(obj) - const result = fn() - visitedObjects.delete(obj) - return result -} diff --git a/repos/effect/packages/effect/src/Layer.ts b/repos/effect/packages/effect/src/Layer.ts index c5bd8f6292..aed2f10670 100644 --- a/repos/effect/packages/effect/src/Layer.ts +++ b/repos/effect/packages/effect/src/Layer.ts @@ -234,19 +234,34 @@ export interface MemoMap { type MemoMapEntry = { observers: number - effect: Effect, any> + readonly deferred: Deferred.Deferred, any> + readonly scope: Scope.Closeable readonly finalizer: (exit: Exit.Exit) => Effect } -const memoMapReuse = ( - entry: MemoMapEntry, - scope: Scope.Scope -): Effect, E, RIn> => { +// The finalizer must not retain the caller of `getOrElseMemoize`. +const makeMemoMapEntry = (memoMap: MemoMapImpl, layer: Layer): MemoMapEntry => { + const entry: MemoMapEntry = { + observers: 0, + deferred: Deferred.makeUnsafe(), + scope: Scope.makeUnsafe(), + finalizer: (exit) => + internalEffect.suspend(() => { + if (--entry.observers > 0) return internalEffect.void + memoMap.map.delete(layer) + return Scope.close(entry.scope, exit) + }) + } + return entry +} + +// Count and register synchronously so interruption cannot strand an observer. +// A closed scope cannot own an entry. +const memoMapObserve = (entry: MemoMapEntry, scope: Scope.Scope): boolean => { + if (scope.state._tag === "Closed") return false entry.observers++ - return internalEffect.andThen( - internalEffect.scopeAddFinalizerExit(scope, (exit) => entry.finalizer(exit)), - entry.effect - ) + internalEffect.scopeAddFinalizerUnsafe(scope, {}, entry.finalizer) + return true } /** @@ -387,37 +402,6 @@ export const fromBuildMemo = ( return self } -const memoMapBuild = ( - memoMap: MemoMapImpl, - layer: Layer, - scope: Scope.Scope, - build: (memoMap: MemoMap, scope: Scope.Scope) => Effect, E, RIn> -): Effect, E, RIn> => { - const layerScope = Scope.makeUnsafe() - const deferred = Deferred.makeUnsafe, E>() - const entry: MemoMapEntry = { - observers: 1, - effect: Deferred.await(deferred), - finalizer: (exit: Exit.Exit) => - internalEffect.suspend(() => { - entry.observers-- - if (entry.observers === 0) { - memoMap.map.delete(layer) - return Scope.close(layerScope, exit) - } - return internalEffect.void - }) - } - memoMap.map.set(layer, entry) - return internalEffect.scopeAddFinalizerExit(scope, entry.finalizer).pipe( - internalEffect.flatMap(() => build(memoMap, layerScope)), - internalEffect.onExit((exit) => { - entry.effect = exit - return Deferred.done(deferred, exit) - }) - ) -} - class MemoMapImpl implements MemoMap { get [MemoMapTypeId](): typeof MemoMapTypeId { return MemoMapTypeId @@ -437,7 +421,8 @@ class MemoMapImpl implements MemoMap { ): Effect, E, RIn> | undefined { const local = this.map.get(layer) if (local) { - return memoMapReuse(local, scope) + memoMapObserve(local, scope) + return local.deferred.effect ?? Deferred.await(local.deferred) } return this.parent?.get(layer, scope) } @@ -448,11 +433,24 @@ class MemoMapImpl implements MemoMap { build: (memoMap: MemoMap, scope: Scope.Scope) => Effect, E, RIn> ): Effect, E, RIn> { return internalEffect.suspend(() => { - const existing = this.get(layer, scope) - if (existing) { - return existing - } - return memoMapBuild(this, layer, scope, build) + // Install the exit handler before publishing an entry: it must complete + // the Deferred even if the first requester is interrupted. + let deferred: Deferred.Deferred, E> | undefined + return internalEffect.onExitPrimitive( + internalEffect.suspend(() => { + const existing = this.get(layer, scope) + if (existing) return existing + const entry = makeMemoMapEntry(this, layer) + // A closed scope cannot own a shared entry; build in that scope instead. + if (!memoMapObserve(entry, scope)) return build(this, scope) + deferred = entry.deferred + this.map.set(layer, entry) + return build(this, entry.scope) + }), + (exit) => { + if (deferred) Deferred.doneUnsafe(deferred, exit) + } + ) }) } } diff --git a/repos/effect/packages/effect/src/LayerMap.ts b/repos/effect/packages/effect/src/LayerMap.ts index d9bc31fe03..014766e373 100644 --- a/repos/effect/packages/effect/src/LayerMap.ts +++ b/repos/effect/packages/effect/src/LayerMap.ts @@ -10,7 +10,7 @@ * @since 3.14.0 */ import * as Context from "./Context.ts" -import type * as Duration from "./Duration.ts" +import * as Duration from "./Duration.ts" import * as Effect from "./Effect.ts" import { identity } from "./Function.ts" import { getStackTraceLimit, setStackTraceLimit } from "./internal/stackTraceLimit.ts" @@ -161,6 +161,10 @@ export const make: < lookup: (key: K) => L, options?: { readonly idleTimeToLive?: IdleTimeToLiveInput | undefined + /** + * Preloaded entries are retained only for their idle TTL. Keys whose idle + * TTL is zero are not preloaded (including when no TTL is specified). + */ readonly preloadKeys?: PreloadKeys } | undefined ) => Effect.Effect< @@ -187,7 +191,9 @@ export const make: < if (options?.preloadKeys) { for (const key of options.preloadKeys) { - yield* Effect.scoped(RcMap.get(rcMap, key)) + if (!Duration.isZero(rcMap.idleTimeToLive(key))) { + yield* Effect.scoped(RcMap.get(rcMap, key)) + } } } @@ -260,6 +266,10 @@ export const fromRecord = < layers: Layers, options?: { readonly idleTimeToLive?: IdleTimeToLiveInput | undefined + /** + * Preloaded entries are retained only for their idle TTL. Keys whose idle + * TTL is zero are not preloaded (including when no TTL is specified). + */ readonly preload?: Preload | undefined } | undefined ): Effect.Effect< @@ -403,6 +413,10 @@ export const Service = () => readonly lookup: (key: any) => Layer.Layer readonly dependencies?: ReadonlyArray> | undefined readonly idleTimeToLive?: IdleTimeToLiveInput | undefined + /** + * Preloaded entries are retained only for their idle TTL. Keys whose idle + * TTL is zero are not preloaded (including when no TTL is specified). + */ readonly preloadKeys?: | Iterable any } ? K : never> | undefined @@ -411,6 +425,10 @@ export const Service = () => readonly layers: Record> readonly dependencies?: ReadonlyArray> | undefined readonly idleTimeToLive?: IdleTimeToLiveInput | undefined + /** + * Preloaded entries are retained only for their idle TTL. Keys whose idle + * TTL is zero are not preloaded (including when no TTL is specified). + */ readonly preload?: boolean | undefined }, Options> >( diff --git a/repos/effect/packages/effect/src/ManagedRuntime.ts b/repos/effect/packages/effect/src/ManagedRuntime.ts index e0a99849da..877935c683 100644 --- a/repos/effect/packages/effect/src/ManagedRuntime.ts +++ b/repos/effect/packages/effect/src/ManagedRuntime.ts @@ -289,10 +289,11 @@ export const make = ( } | undefined ): ManagedRuntime => { const memoMap = options?.memoMap ?? Layer.makeMemoMapUnsafe() - const scope = Scope.makeUnsafe("parallel") + const scope = Scope.makeUnsafe("sequential") const layerScope = Scope.forkUnsafe(scope, "sequential") + const fiberScope = Scope.forkUnsafe(scope, "parallel") const defaultRunOptions: Effect.RunOptions = { - onFiberStart: Fiber.runIn(scope) + onFiberStart: Fiber.runIn(fiberScope) } const mergeRunOptions = (options?: O): O => options diff --git a/repos/effect/packages/effect/src/Metric.ts b/repos/effect/packages/effect/src/Metric.ts index 14eb02edc8..c4a2cb31a5 100644 --- a/repos/effect/packages/effect/src/Metric.ts +++ b/repos/effect/packages/effect/src/Metric.ts @@ -1660,7 +1660,8 @@ abstract class Metric$ implements Metric { declare readonly Input: Contravariant declare readonly State: Covariant - readonly #metadata = new WeakMap>() + #extraAttributes: Metric.AttributeSet | undefined + #key = "" readonly id: string readonly description: string | undefined @@ -1692,39 +1693,28 @@ abstract class Metric$ implements Metric { hook(context: Context.Context): Metric.Hooks { const extraAttributes = Context.get(context, CurrentMetricAttributes) - if (Object.keys(extraAttributes).length > 0) { - return this.getOrCreate(context, mergeAttributes(this.attributes, extraAttributes)).hooks + if (extraAttributes !== this.#extraAttributes) { + this.#extraAttributes = extraAttributes + this.#key = makeKey(this, mergeAttributes(this.attributes, extraAttributes)) } + // The registry owns the series, so it is looked up on every update. const registry = Context.get(context, MetricRegistry) - let metadata = this.#metadata.get(registry) + let metadata = registry.get(this.#key) if (Predicate.isUndefined(metadata)) { - metadata = this.getOrCreate(context, this.attributes) - this.#metadata.set(registry, metadata) + metadata = { + id: this.id, + type: this.type, + description: this.description, + attributes: Object.keys(extraAttributes).length > 0 + ? mergeAttributes(this.attributes, extraAttributes) + : this.attributes, + hooks: this.createHooks() + } + registry.set(this.#key, metadata) } return metadata.hooks } - getOrCreate( - context: Context.Context, - attributes: Metric.Attributes | undefined - ): Metric.Metadata { - const key = makeKey(this, attributes) - const registry = Context.get(context, MetricRegistry) - if (registry.has(key)) { - return registry.get(key)! - } - const hooks = this.createHooks() - const meta: Metric.Metadata = { - id: this.id, - type: this.type, - description: this.description, - attributes: attributesToRecord(attributes), - hooks - } - registry.set(key, meta) - return meta - } - pipe() { return pipeArguments(this, arguments) } @@ -2883,13 +2873,15 @@ export const withAttributes: { >(2, ( self: Metric, attributes: Metric.Attributes -): Metric => - new MetricTransform( +): Metric => { + const attributed = addAttributesToContext(attributes) + return new MetricTransform( self, - (context) => self.valueUnsafe(addAttributesToContext(context, attributes)), - (input, context) => self.updateUnsafe(input, addAttributesToContext(context, attributes)), - (input, context) => self.modifyUnsafe(input, addAttributesToContext(context, attributes)) - )) + (context) => self.valueUnsafe(attributed(context)), + (input, context) => self.updateUnsafe(input, attributed(context)), + (input, context) => self.modifyUnsafe(input, attributed(context)) + ) +}) // Metric Snapshots @@ -3476,16 +3468,11 @@ export const disableRuntimeMetrics: (self: Effect) => Effect( metric: Metric, - attributes: Metric.Attributes | undefined -) { - let key = `${metric.type}:${metric.id}` - if (Predicate.isNotUndefined(metric.description)) { - key += `:${metric.description}` - } - if (Predicate.isNotUndefined(attributes)) { - key += `:${serializeAttributes(attributes)}` - } - return key + attributes: Metric.AttributeSet +): string { + // Tuple encoding keeps ids, descriptions and attributes unambiguous. + const entries = Object.entries(attributes).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0) + return JSON.stringify([metric.type, metric.id, metric.description, entries]) } function makeHooks( @@ -3496,12 +3483,6 @@ function makeHooks( return { get, update, modify: modify ?? update } } -function serializeAttributes(attributes: Metric.Attributes): string { - const entries = Array.isArray(attributes) ? [...attributes] : Object.entries(attributes) - entries.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0) - return JSON.stringify(entries) -} - function mergeAttributes( self: Metric.Attributes | undefined, other: Metric.Attributes | undefined @@ -3520,10 +3501,17 @@ function attributesToRecord(attributes?: Metric.Attributes): Metric.AttributeSet } function addAttributesToContext( - context: Context.Context, attributes: Metric.Attributes -): Context.Context { - const current = Context.get(context, CurrentMetricAttributes) - const updated = mergeAttributes(current, attributes) - return Context.add(context, CurrentMetricAttributes, updated) +): (context: Context.Context) => Context.Context { + // Merge once per contextual attribute set, so the wrapped metric can reuse its cached series key. + let current: Metric.AttributeSet | undefined + let merged: Metric.AttributeSet = {} + return (context) => { + const extraAttributes = Context.get(context, CurrentMetricAttributes) + if (extraAttributes !== current) { + current = extraAttributes + merged = mergeAttributes(extraAttributes, attributes) + } + return Context.add(context, CurrentMetricAttributes, merged) + } } diff --git a/repos/effect/packages/effect/src/MutableHashMap.ts b/repos/effect/packages/effect/src/MutableHashMap.ts index a9d97d7478..2d788f18b3 100644 --- a/repos/effect/packages/effect/src/MutableHashMap.ts +++ b/repos/effect/packages/effect/src/MutableHashMap.ts @@ -269,10 +269,6 @@ export const get: { } else if (isSimpleKey(key)) { return Option.none() } - const refKey = referentialKeysCache.get(self) - if (refKey !== undefined) { - return self.backing.has(refKey) ? Option.some(self.backing.get(refKey)!) : Option.none() - } const hash = Hash.hash(key) const bucket = self.buckets.get(hash) if (bucket === undefined) { @@ -281,7 +277,6 @@ export const get: { return getFromBucket(self, bucket, key) }) -const referentialKeysCache = new WeakMap() const isSimpleKey = (u: unknown): boolean => typeof u !== "object" && typeof u !== "function" /** @@ -355,7 +350,6 @@ const getFromBucket = ( for (let i = 0, len = bucket.length; i < len; i++) { if (Equal.equals(key, bucket[i])) { const refKey = bucket[i] - referentialKeysCache.set(key, refKey) return Option.some(self.backing.get(refKey)!) } } @@ -447,12 +441,6 @@ export const set: { self.backing.set(key, value) return self } - let refKey = referentialKeysCache.get(self) - if (refKey !== undefined && self.backing.has(refKey)) { - self.backing.set(refKey, value) - return self - } - const hash = Hash.hash(key) const bucket = self.buckets.get(hash) if (bucket === undefined) { @@ -461,7 +449,7 @@ export const set: { return self } - refKey = getRefKey(bucket, key) + let refKey = getRefKey(bucket, key) if (refKey === undefined) { bucket.push(key) refKey = key @@ -476,7 +464,6 @@ const getRefKey = ( ) => { for (let i = 0, len = bucket.length; i < len; i++) { if (Equal.equals(key, bucket[i])) { - referentialKeysCache.set(key, bucket[i]) return bucket[i] } } @@ -535,19 +522,13 @@ export const modify: { } return self } - let refKey = referentialKeysCache.get(self) - if (refKey !== undefined && self.backing.has(refKey)) { - self.backing.set(refKey, f(self.backing.get(refKey)!)) - return self - } - const hash = Hash.hash(key) const bucket = self.buckets.get(hash) if (bucket === undefined) { return self } - refKey = getRefKey(bucket, key) + const refKey = getRefKey(bucket, key) if (refKey === undefined) { return self } @@ -686,15 +667,14 @@ export const remove: { return self } - const key = referentialKeysCache.get(self) ?? key_ - const hash = Hash.hash(key) + const hash = Hash.hash(key_) const bucket = self.buckets.get(hash) if (bucket === undefined) { return self } for (let i = 0, len = bucket.length; i < len; i++) { const bkey = bucket[i] - if (bkey === key || Equal.equals(key, bkey)) { + if (bkey === key_ || Equal.equals(key_, bkey)) { self.backing.delete(bkey) bucket.splice(i, 1) break diff --git a/repos/effect/packages/effect/src/MutableList.ts b/repos/effect/packages/effect/src/MutableList.ts index 3506009e81..061e9d6a89 100644 --- a/repos/effect/packages/effect/src/MutableList.ts +++ b/repos/effect/packages/effect/src/MutableList.ts @@ -159,6 +159,24 @@ export const make = (): MutableList => ({ length: 0 }) +// A mutable tail bucket grows with every append while takes only advance its +// offset, so a list that never drains keeps every slot it ever used. Copy the +// live values into a fresh bucket once consumed slots dominate. +const compactHead = (self: MutableList): void => { + const head = self.head! + if ( + head.offset >= 1024 && head === self.tail && head.mutable && + (head.array.length - head.offset) * 8 <= head.offset + ) { + self.head = self.tail = { + array: head.array.slice(head.offset), + mutable: true, + offset: 0, + next: undefined + } + } +} + const emptyBucket = (): MutableList.Bucket => ({ array: [], mutable: true, @@ -285,13 +303,16 @@ export const prependAll = (self: MutableList, messages: Iterable): void * @since 4.0.0 */ export const prependAllUnsafe = (self: MutableList, messages: ReadonlyArray, mutable = false): void => { + if (messages.length === 0) { + return + } self.head = { array: messages as Array, mutable, offset: 0, next: self.head } - if (!self.tail && messages.length > 0) self.tail = self.head + if (!self.tail) self.tail = self.head self.length += self.head.array.length } @@ -440,9 +461,10 @@ export const takeN = (self: MutableList, n: number): Array => { if (chunk.mutable) chunk.array[chunk.offset] = undefined as any chunk.offset++ if (index === n) { - self.head = chunk + self.head = chunk.offset === chunk.array.length && chunk.next ? chunk.next : chunk self.length -= n if (self.length === 0) clear(self) + else compactHead(self) return array } } @@ -490,6 +512,7 @@ export const takeNVoid = (self: MutableList, n: number): void => { chunk.offset += n - count self.head = chunk self.length -= n + compactHead(self) return } count += size @@ -553,6 +576,8 @@ export const take = (self: MutableList): Empty | A => { } else { clear(self) } + } else { + compactHead(self) } return message } @@ -612,6 +637,7 @@ export const toArray = (self: MutableList): Array => toArrayN(self, sel /** * Filters the MutableList in place, keeping only elements that satisfy the predicate. * This operation modifies the list and rebuilds its internal structure for efficiency. + * The predicate receives each element's current index. * * **Example** (Filtering in place) * @@ -632,9 +658,10 @@ export const toArray = (self: MutableList): Array => toArrayN(self, sel export const filter = (self: MutableList, f: (value: A, i: number) => boolean): void => { const array: Array = [] let chunk: MutableList.Bucket | undefined = self.head + let index = 0 while (chunk) { for (let i = chunk.offset; i < chunk.array.length; i++) { - if (f(chunk.array[i], i)) { + if (f(chunk.array[i], index++)) { array.push(chunk.array[i]) } } diff --git a/repos/effect/packages/effect/src/Number.ts b/repos/effect/packages/effect/src/Number.ts index b42e1c07ec..b7d8f3d61f 100644 --- a/repos/effect/packages/effect/src/Number.ts +++ b/repos/effect/packages/effect/src/Number.ts @@ -632,23 +632,13 @@ export const remainder: { (divisor: number): (self: number) => number (self: number, divisor: number): number } = dual(2, (self: number, divisor: number): number => { - const selfString = self.toString() - const divisorString = divisor.toString() - if (selfString.includes("e") || divisorString.includes("e")) { - if (!globalThis.Number.isFinite(self) || !globalThis.Number.isFinite(divisor) || divisor === 0) { - return NaN - } - return remainderWithScientificNotation(self, divisor) + if (!globalThis.Number.isFinite(self) || !globalThis.Number.isFinite(divisor) || divisor === 0) { + return NaN + } + if (globalThis.Number.isInteger(self) && globalThis.Number.isInteger(divisor)) { + return self % divisor } - const selfDecCount = (selfString.split(".")[1] || "").length - const divisorDecCount = (divisorString.split(".")[1] || "").length - const decCount = selfDecCount > divisorDecCount ? selfDecCount : divisorDecCount - const selfInt = self === 0 ? self : parseInt(self.toFixed(decCount).replace(".", "")) - const divisorInt = parseInt(divisor.toFixed(decCount).replace(".", "")) - return (selfInt % divisorInt) / Math.pow(10, decCount) -}) -function remainderWithScientificNotation(self: number, divisor: number): number { const [selfCoefficient, selfExponent] = toScientificInteger(self) const [divisorCoefficient, divisorExponent] = toScientificInteger(divisor) const exponent = Math.min(selfExponent, divisorExponent) @@ -660,9 +650,12 @@ function remainderWithScientificNotation(self: number, divisor: number): number } const remainder = globalThis.Number(`${out}e${exponent}`) return remainder === 0 ? Math.sign(self) * globalThis.Number.MIN_VALUE : remainder -} +}) function toScientificInteger(n: number): readonly [coefficient: bigint, exponent: number] { + if (globalThis.Number.isInteger(n)) { + return [BigInt(n), 0] + } const scientific = Math.abs(n).toExponential() const eIndex = scientific.indexOf("e") const digits = scientific.slice(0, eIndex).replace(".", "") diff --git a/repos/effect/packages/effect/src/Option.ts b/repos/effect/packages/effect/src/Option.ts index 3e497eb10d..6feed438b1 100644 --- a/repos/effect/packages/effect/src/Option.ts +++ b/repos/effect/packages/effect/src/Option.ts @@ -1528,8 +1528,14 @@ export const tap: { * @category combining * @since 2.0.0 */ -export const product = (self: Option, that: Option): Option<[A, B]> => - isSome(self) && isSome(that) ? some([self.value, that.value]) : none() +export const product: { + (that: Option): (self: Option) => Option<[A, B]> + (self: Option, that: Option): Option<[A, B]> +} = dual( + 2, + (self: Option, that: Option): Option<[A, B]> => + isSome(self) && isSome(that) ? some([self.value, that.value]) : none() +) /** * Combines a primary `Option` with an iterable of `Option`s into a tuple if @@ -1563,10 +1569,10 @@ export const product = (self: Option, that: Option): Option<[A, B]> * @category combining * @since 2.0.0 */ -export const productMany = ( - self: Option, - collection: Iterable> -): Option<[A, ...Array]> => { +export const productMany: { + (collection: Iterable>): (self: Option) => Option<[A, ...Array]> + (self: Option, collection: Iterable>): Option<[A, ...Array]> +} = dual(2, (self: Option, collection: Iterable>): Option<[A, ...Array]> => { if (isNone(self)) { return none() } @@ -1578,7 +1584,7 @@ export const productMany = ( out.push(o.value) } return some(out) -} +}) /** * Combines a structure of `Option`s (tuple, struct, or iterable) into a single @@ -1768,14 +1774,14 @@ export const toArray = (self: Option): Array => isNone(self) ? [] : [se * * **When to use** * - * Use when you need to split an optional value into "left" and "right" + * Use when you need to split an optional value into success and failure * channels using a `Result`-returning function. * * **Details** * * - `None` → `[None, None]` - * - `Some` where `f` returns `Err` → `[Some(error), None]` - * - `Some` where `f` returns `Ok` → `[None, Some(value)]` + * - `Some` where `f` returns `Ok` → `[Some(value), None]` + * - `Some` where `f` returns `Err` → `[None, Some(error)]` * * **Example** (Partitioning by Result) * @@ -1787,8 +1793,8 @@ export const toArray = (self: Option): Array => isNone(self) ? [] : [se * return isNaN(n) ? Result.fail("Not a number") : Result.succeed(n) * } * - * Option.partitionMap(Option.some("42"), parseNumber) // => [Option.none(), Option.some(42)] - * Option.partitionMap(Option.some("abc"), parseNumber) // => [Option.some("Not a number"), Option.none()] + * Option.partitionMap(Option.some("42"), parseNumber) // => [Option.some(42), Option.none()] + * Option.partitionMap(Option.some("abc"), parseNumber) // => [Option.none(), Option.some("Not a number")] * Option.partitionMap(Option.none(), parseNumber) // => [Option.none(), Option.none()] * ``` * @@ -1798,17 +1804,17 @@ export const toArray = (self: Option): Array => isNone(self) ? [] : [se * @since 2.0.0 */ export const partitionMap: { - (f: (a: A) => Result): (self: Option) => [left: Option, right: Option] - (self: Option, f: (a: A) => Result): [left: Option, right: Option] + (f: (a: A) => Result): (self: Option) => [passes: Option, fails: Option] + (self: Option, f: (a: A) => Result): [passes: Option, fails: Option] } = dual(2, ( self: Option, f: (a: A) => Result -): [excluded: Option, satisfying: Option] => { +): [passes: Option, fails: Option] => { if (isNone(self)) { return [none(), none()] } const e = f(self.value) - return result.isFailure(e) ? [some(e.failure), none()] : [none(), some(e.success)] + return result.isFailure(e) ? [none(), some(e.failure)] : [some(e.success), none()] }) /** diff --git a/repos/effect/packages/effect/src/PartitionedSemaphore.ts b/repos/effect/packages/effect/src/PartitionedSemaphore.ts index 07aaabfb6f..0f8a80628f 100644 --- a/repos/effect/packages/effect/src/PartitionedSemaphore.ts +++ b/repos/effect/packages/effect/src/PartitionedSemaphore.ts @@ -8,7 +8,9 @@ * @since 4.0.0 */ import * as Effect from "./Effect.ts" +import * as Exit from "./Exit.ts" import { dual } from "./Function.ts" +import * as internalEffect from "./internal/effect.ts" import * as MutableHashMap from "./MutableHashMap.ts" import * as Option from "./Option.ts" @@ -180,53 +182,64 @@ export const makeUnsafe = (options: { return Effect.void } - return Effect.callback((resume) => { - if (maxPermits < permits) { - resume(Effect.never) - return - } + if (maxPermits < permits) { + return Effect.never + } + return Effect.withFiber((fiber) => { if (totalPermits >= permits) { + // Keep the capacity check, deduction, and interruption cleanup in one evaluation step. totalPermits -= permits - resume(Effect.void) - return + internalEffect.onExitUnsafe(fiber, (exit) => { + if (Exit.isFailure(exit)) { + releaseUnsafe(permits) + } + return undefined + }) + return Effect.void } - const needed = permits - totalPermits - if (totalPermits > 0) { - totalPermits = 0 - } - waitingPermits += needed - - const waiters = Option.getOrElse( - MutableHashMap.get(partitions, key), - () => { - const set = new Set() - MutableHashMap.set(partitions, key, set) - return set + return Effect.callback((resume) => { + if (totalPermits >= permits) { + resume(take(key, permits)) + return } - ) + const needed = permits - totalPermits + if (totalPermits > 0) { + totalPermits = 0 + } + waitingPermits += needed + + const waiters = Option.getOrElse( + MutableHashMap.get(partitions, key), + () => { + const set = new Set() + MutableHashMap.set(partitions, key, set) + return set + } + ) - const entry: Waiter = { - permits: needed, - resume: () => { - cleanup() - resume(Effect.void) + const entry: Waiter = { + permits: needed, + resume: () => { + cleanup() + resume(Effect.void) + } } - } - const cleanup = () => { - if (waiters.delete(entry) && waiters.size === 0) { - MutableHashMap.remove(partitions, key) + const cleanup = () => { + if (waiters.delete(entry) && waiters.size === 0) { + MutableHashMap.remove(partitions, key) + } } - } - waiters.add(entry) + waiters.add(entry) - return Effect.sync(() => { - cleanup() - waitingPermits -= entry.permits - releaseUnsafe(permits - entry.permits) + return Effect.sync(() => { + cleanup() + waitingPermits -= entry.permits + releaseUnsafe(permits - entry.permits) + }) }) }) } @@ -279,17 +292,17 @@ export const makeUnsafe = (options: { return Effect.asSome(effect) } - return Effect.suspend(() => { + return Effect.withFiber((fiber) => { if (!tryTake(permits)) { return Effect.succeed(Option.none()) } - return Effect.ensuring( - Effect.asSome(effect), - Effect.sync(() => { - releaseUnsafe(permits) - }) - ) + // Register cleanup before the fiber can yield to the user effect. + internalEffect.onExitUnsafe(fiber, () => { + releaseUnsafe(permits) + return undefined + }) + return Effect.asSome(effect) }) } } diff --git a/repos/effect/packages/effect/src/PlatformError.ts b/repos/effect/packages/effect/src/PlatformError.ts index b2fca10c2b..4a5f1681a2 100644 --- a/repos/effect/packages/effect/src/PlatformError.ts +++ b/repos/effect/packages/effect/src/PlatformError.ts @@ -10,6 +10,7 @@ * @since 4.0.0 */ import * as Data from "./Data.ts" +import * as Predicate from "./Predicate.ts" const TypeId = "~effect/PlatformError" @@ -181,6 +182,33 @@ export class PlatformError extends Data.TaggedError("PlatformError")<{ } } +/** + * Returns `true` if a value is a `PlatformError`. + * + * **When to use** + * + * Use to narrow a caught defect, or an error channel typed more loosely than + * the platform APIs, to `PlatformError`. + * + * **Example** (Checking a caught value) + * + * ```ts import.meta.vitest + * import { PlatformError } from "effect" + * + * const error = PlatformError.badArgument({ + * module: "FileSystem", + * method: "readFile" + * }) + * + * PlatformError.isPlatformError(error) // => true + * PlatformError.isPlatformError(new Error("boom")) // => false + * ``` + * + * @category guards + * @since 4.0.0 + */ +export const isPlatformError = (u: unknown): u is PlatformError => Predicate.hasProperty(u, TypeId) + /** * Creates a `PlatformError` whose reason is a `SystemError`. * diff --git a/repos/effect/packages/effect/src/Pool.ts b/repos/effect/packages/effect/src/Pool.ts index 104c0224f9..39152daa69 100644 --- a/repos/effect/packages/effect/src/Pool.ts +++ b/repos/effect/packages/effect/src/Pool.ts @@ -119,11 +119,12 @@ export interface State { isShuttingDown: boolean usage: number readonly resizeSemaphore: Semaphore.Semaphore + // Insertion order determines usage-TTL retirement order; reclaimed items move to the back. readonly items: Set> availableHead: PoolItem | undefined availableTail: PoolItem | undefined readonly invalidated: Set> - readonly waiters: Set<() => void> + readonly waiters: Set<(failure?: Exit.Failure) => void> } /** @@ -447,7 +448,7 @@ export const get = (self: Pool): Effect.Effect => core.withFiber((fiber) => { const state = self.state if (state.isShuttingDown) return internal.interrupt - if (state.availableHead !== undefined) { + if (state.availableHead?.exit._tag === "Success") { state.usage++ if (self.config.isFixed || targetSize(self) <= activeSize(self)) { return leaseItem(self, state.availableHead, fiber) @@ -497,35 +498,36 @@ export const use: { self: Pool, f: (item: A) => Effect.Effect ): Effect.Effect => - internal.suspend(() => { + core.withFiber((fiber) => { const state = self.state if (state.isShuttingDown) return internal.interrupt - if (state.availableHead !== undefined) { + if (state.availableHead?.exit._tag === "Success") { state.usage++ if (self.config.isFixed || targetSize(self) <= activeSize(self)) { - return useItem(self, state.availableHead, f) + return useItem(self, state.availableHead, f, fiber) } state.usage-- } - return getSlowWith(self, (self, item, _fiber, restore) => useItem(self, item, f, restore)) + return getSlowWith(self, (self, item, fiber, restore) => useItem(self, item, f, fiber, restore)) })) +// Count the lease and install its release in the same step. const useItem = ( self: Pool, item: PoolItem, f: (item: A) => Effect.Effect, + fiber: Fiber.Fiber, restore?: (effect: Effect.Effect) => Effect.Effect ): Effect.Effect => { - if (!leaseItemBookkeeping(self, item)) { - return item.exit as Exit.Exit - } + leaseItemBookkeeping(self, item) + internal.onExitUnsafe(fiber, item.release) let body: Effect.Effect try { body = f((item.exit as Exit.Success).value) } catch (defect) { - return internal.flatMap(item.release(item.exit), () => core.exitDie(defect)) + return core.exitDie(defect) } - return internal.onExitPrimitive(restore !== undefined ? restore(body) : body, item.release) + return restore !== undefined ? restore(body) : body } const getSlowWith = ( @@ -541,7 +543,7 @@ const getSlowWith = ( const state = self.state state.usage++ const wait: Effect.Effect = internal.flatMap( - internal.onInterrupt( + internal.onError( restore(waitForItem(self)), () => internal.sync(() => { @@ -555,10 +557,17 @@ const getSlowWith = ( state.usage-- return internal.interrupt } - if (state.availableHead !== undefined) { - return lease(self, state.availableHead, fiber, restore) + // Replace failed slots only when the pool cannot grow. + const item = firstHealthyAvailable(self) ?? + (targetSize(self) <= activeSize(self) ? state.availableHead : undefined) + if (item === undefined) { + return wait + } + if (item.exit._tag === "Failure") { + removeFailedItem(self, item) + return loop } - return wait + return lease(self, item, fiber, restore) }) const loop: Effect.Effect = internal.suspend(() => { if (state.isShuttingDown) { @@ -577,20 +586,17 @@ const getSlowWith = ( return loop }) -const leaseItemBookkeeping = (self: Pool, item: PoolItem): boolean => { - const state = self.state - if (item.exit._tag === "Failure") { - state.usage-- - state.items.delete(item) - state.invalidated.delete(item) - removeAvailable(self, item) - return false - } +const leaseItemBookkeeping = (self: Pool, item: PoolItem): void => { item.refCount++ if (item.refCount >= self.config.concurrency) { removeAvailable(self, item) } - return true +} + +const removeFailedItem = (self: Pool, item: PoolItem): void => { + self.state.items.delete(item) + self.state.invalidated.delete(item) + removeAvailable(self, item) } const leaseItem = ( @@ -598,14 +604,13 @@ const leaseItem = ( item: PoolItem, fiber: Fiber.Fiber ): Effect.Effect => { - if (!leaseItemBookkeeping(self, item)) { - return item.exit - } + leaseItemBookkeeping(self, item) const scope = Context.getUnsafe(fiber.context, Scope.Scope) if (scope.state._tag === "Closed") { - return internal.flatMap(item.release(item.exit), () => item.exit) + internal.onExitUnsafe(fiber, item.release) + } else { + internal.scopeAddFinalizerUnsafe(scope, {}, item.release) } - internal.scopeAddFinalizerUnsafe(scope, {}, item.release) return item.exit } @@ -635,15 +640,21 @@ const releaseItem = (self: Pool, item: PoolItem): Effect.Effec return internal.void }) -const waitForItem = (self: Pool): Effect.Effect => +const firstHealthyAvailable = (self: Pool): PoolItem | undefined => { + let item = self.state.availableHead + while (item !== undefined && item.exit._tag === "Failure") item = item.availableNext + return item +} + +const waitForItem = (self: Pool): Effect.Effect => internal.callback((resume) => { const state = self.state - if (state.availableHead !== undefined || state.isShuttingDown) { + if (firstHealthyAvailable(self) !== undefined || state.isShuttingDown) { return resume(internal.void) } - const observer = () => { + const observer = (failure?: Exit.Failure) => { state.waiters.delete(observer) - resume(internal.void) + resume(failure ?? internal.void) } state.waiters.add(observer) return internal.sync(() => { @@ -656,7 +667,7 @@ const wakeWaiters = (self: Pool, fiber: Fiber.Fiber { let remaining = count - const toWake: Array<() => void> = [] + const toWake: Array<(failure?: Exit.Failure) => void> = [] for (const notify of waiters) { if (remaining-- <= 0) break toWake.push(notify) @@ -899,15 +910,26 @@ const allocate = (self: Pool): Effect.Effect> => release: undefined as any } item.release = constant(releaseItem(self, item)) - self.state.items.add(item) - addAvailable(self, item) - if (self.config.strategy === strategyNoop) { - return exit._tag === "Success" ? Effect.succeed(item) : Effect.as(item.finalizer, item) + const onAcquire = Effect.suspend(() => + // A borrower may have removed the item before the callback runs. + self.state.items.has(item) ? self.config.strategy.onAcquire(item) : Effect.void + ) + if (exit._tag === "Success") { + self.state.items.add(item) + addAvailable(self, item) + return self.config.strategy === strategyNoop ? Effect.succeed(item) : Effect.as(onAcquire, item) } + // Deliver a waiting borrower its own failure; otherwise retain the slot. + const waiter = firstHealthyAvailable(self) === undefined ? self.state.waiters.values().next().value : undefined + if (waiter !== undefined) { + waiter(exit) + } else { + self.state.items.add(item) + addAvailable(self, item) + } + // Do not let cleanup delay a replacement acquisition. return Effect.as( - exit._tag === "Success" - ? self.config.strategy.onAcquire(item) - : Effect.flatMap(item.finalizer, () => self.config.strategy.onAcquire(item)), + Effect.forkIn(Effect.andThen(item.finalizer, onAcquire), self.state.scope, { startImmediately: true }), item ) }) @@ -977,24 +999,26 @@ const strategyCreationTTL = Effect.fnUntraced(function*(ttl: Duration.Inpu }) }) -const strategyUsageTTL = Effect.fnUntraced(function*(ttl: Duration.Input) { - const queue = yield* Queue.unbounded>() - return identity>({ +const strategyUsageTTL = (ttl: Duration.Input): Effect.Effect> => + Effect.succeed>({ run: (pool) => { + // `state.items` iterates in insertion order, so its oldest live entry is + // the next to retire. Using it directly means an item stops being + // referenced by this strategy as soon as it leaves the pool. const process: Effect.Effect = Effect.suspend(() => { - const excess = activeSize(pool) - targetSize(pool) - if (excess <= 0) return Effect.void - return Queue.take(queue).pipe( - Effect.tap((item) => invalidatePoolItem(pool, item)), - Effect.flatMap(() => process) - ) + if (activeSize(pool) <= targetSize(pool)) return Effect.void + for (const item of pool.state.items) { + if (pool.state.invalidated.has(item)) continue + return Effect.flatMap(invalidatePoolItem(pool, item), () => process) + } + return Effect.void }) return process.pipe( Effect.delay(ttl), Effect.forever({ disableYield: true }) ) }, - onAcquire: (item) => Queue.offer(queue, item), + onAcquire: (_) => Effect.void, reclaim(pool) { return Effect.suspend((): Effect.Effect | undefined> => { if (pool.state.invalidated.size === 0) { @@ -1007,14 +1031,16 @@ const strategyUsageTTL = Effect.fnUntraced(function*(ttl: Duration.Input) return Effect.undefined } pool.state.invalidated.delete(item.value) + // Re-adding moves the reclaimed item to the back of the retirement order. + pool.state.items.delete(item.value) + pool.state.items.add(item.value) if (item.value.refCount < pool.config.concurrency) { addAvailable(pool, item.value) } - return Effect.as(Queue.offer(queue, item.value), item.value) + return Effect.succeed(item.value) }) } }) -}) const reportUnhandledError = (cause: Cause.Cause) => Effect.withFiber((fiber) => { diff --git a/repos/effect/packages/effect/src/PubSub.ts b/repos/effect/packages/effect/src/PubSub.ts index 6861798fd6..e8a8e5a83c 100644 --- a/repos/effect/packages/effect/src/PubSub.ts +++ b/repos/effect/packages/effect/src/PubSub.ts @@ -5,8 +5,8 @@ * `Subscription` receives its own copy of every accepted message. Unlike a * queue, subscribers do not compete for messages. This module includes bounded, * dropping, sliding, and unbounded hubs, optional replay buffers for late - * subscribers, message-taking helpers, capacity and shutdown operations, and - * low-level types for custom hub strategies. + * subscribers, message-taking helpers, capacity and shutdown operations, a + * type guard, and low-level types for custom hub strategies. * * @since 2.0.0 */ @@ -24,6 +24,7 @@ import * as MutableRef from "./MutableRef.ts" import { nextPow2 } from "./Number.ts" import * as Option from "./Option.ts" import { type Pipeable, pipeArguments } from "./Pipeable.ts" +import { hasProperty } from "./Predicate.ts" import * as Scope from "./Scope.ts" import type { Covariant, Invariant } from "./Types.ts" @@ -72,6 +73,7 @@ export interface PubSub extends Pipeable { readonly shutdownHook: Latch.Latch readonly shutdownFlag: MutableRef.MutableRef readonly strategy: PubSub.Strategy + readonly ended: MutableRef.MutableRef> } /** @@ -200,6 +202,32 @@ export declare namespace PubSub { } } +/** + * Returns `true` if a value is a `PubSub`. + * + * **Details** + * + * This is a type guard that checks for the `PubSub` runtime marker. + * + * **Example** (Checking if a value is a PubSub) + * + * ```ts import.meta.vitest + * import { Effect, PubSub } from "effect" + * + * const program = Effect.gen(function*() { + * const pubsub = yield* PubSub.bounded(10) + * return [PubSub.isPubSub(pubsub), PubSub.isPubSub({}), PubSub.isPubSub(null)] + * }) + * + * const actual = await Effect.runPromise(program) + * actual // => [true, false, false] + * ``` + * + * @category guards + * @since 4.0.0 + */ +export const isPubSub = (u: unknown): u is PubSub => hasProperty(u, TypeId) + const SubscriptionTypeId = "~effect/PubSub/Subscription" /** @@ -246,6 +274,7 @@ export interface Subscription extends Pipeable { readonly shutdownFlag: MutableRef.MutableRef readonly strategy: PubSub.Strategy readonly replayWindow: PubSub.ReplayWindow + readonly ended: MutableRef.MutableRef> } /** @@ -289,7 +318,8 @@ export const make = ( Scope.makeUnsafe(), Latch.makeUnsafe(false), MutableRef.make(false), - options.strategy() + options.strategy(), + MutableRef.make(Option.none()) ) ) @@ -509,6 +539,9 @@ export const makeAtomicBounded = ( ): PubSub.Atomic => { const options = typeof capacity === "number" ? { capacity } : capacity ensureCapacity(options.capacity) + if (options.capacity === Infinity) { + return makeAtomicUnbounded(options) + } const replayBuffer = options.replay && options.replay > 0 ? new ReplayBuffer(Math.ceil(options.replay)) : undefined if (options.capacity === 1) { return new BoundedPubSubSingle(replayBuffer) @@ -765,6 +798,107 @@ export const shutdown = (self: PubSub): Effect.Effect => ) })) +/** + * Ends the `PubSub` with a final message. + * + * **When to use** + * + * Use to tell every subscriber that no more messages will follow, without + * losing the messages they have not consumed yet. + * + * **Details** + * + * Later publishes return `false`, as do publishers waiting for capacity when + * `end` is called. Each subscriber receives the messages already buffered + * for it, then the final message. Subscribers that arrive + * after the end receive the replayed messages, if any, and then the final + * message. The final message never occupies capacity, so a bounded `PubSub` + * cannot drop it. Returns `false` if the `PubSub` was already ended or shut + * down. + * + * `take`, `takeAll`, and `takeBetween` deliver the final message; + * non-suspending `takeUpTo` does not. + * + * **Gotchas** + * + * The final message is sticky: subsequent takes return it again. Consumers + * must treat it as terminal rather than continuing to take messages. + * + * **Example** (Ending a PubSub) + * + * ```ts import.meta.vitest + * import { Effect, PubSub } from "effect" + * + * const program = Effect.scoped(Effect.gen(function*() { + * const pubsub = yield* PubSub.bounded(2) + * const subscription = yield* PubSub.subscribe(pubsub) + * + * yield* PubSub.publish(pubsub, "Hello") + * const ended = yield* PubSub.end(pubsub, "Bye") + * + * // Later publishes are rejected + * const published = yield* PubSub.publish(pubsub, "World") + * + * // Buffered messages are delivered before the final message + * const first = yield* PubSub.take(subscription) + * const last = yield* PubSub.take(subscription) + * + * // Late subscribers receive the final message too + * const late = yield* PubSub.subscribe(pubsub) + * const lateMessage = yield* PubSub.take(late) + * return [ended, published, first, last, lateMessage] + * })) + * + * const actual = await Effect.runPromise(program) + * actual // => [true, false, "Hello", "Bye", "Bye"] + * ``` + * + * @see {@link shutdown} for interrupting subscribers instead of delivering a final message + * + * @category lifecycle + * @since 4.0.0 + */ +export const end: { + (value: A): (self: PubSub) => Effect.Effect + (self: PubSub, value: A): Effect.Effect +} = dual(2, (self: PubSub, value: A): Effect.Effect => Effect.sync(() => endUnsafe(self, value))) + +/** + * Ends the `PubSub` with a final message synchronously. + * + * **Details** + * + * See {@link end} for the semantics. + * + * @category lifecycle + * @since 4.0.0 + */ +export const endUnsafe: { + (value: A): (self: PubSub) => boolean + (self: PubSub, value: A): boolean +} = dual(2, (self: PubSub, value: A): boolean => { + if (self.shutdownFlag.current || Option.isSome(self.ended.current)) return false + MutableRef.set(self.ended, Option.some(value)) + if (self.strategy instanceof BackPressureStrategy) { + for (const [_, deferred, last] of MutableList.takeAll(self.strategy.publishers)) { + if (last) Deferred.doneUnsafe(deferred, Exit.succeed(false)) + } + } + // A waiting subscriber has nothing buffered, so it receives the final + // message right away. + const exit = Exit.succeed(value) + for (const pollersSet of self.subscribers.values()) { + for (const pollers of pollersSet) { + let poller: Deferred.Deferred | MutableList.Empty + while ((poller = MutableList.take(pollers)) !== MutableList.Empty) { + Deferred.doneUnsafe(poller, exit) + } + } + } + self.subscribers.clear() + return true +}) + /** * Checks effectfully whether `shutdown` has been called, returning `true` * after shutdown and `false` otherwise. @@ -910,7 +1044,7 @@ export const publish: { (self: PubSub, value: A): Effect.Effect } = dual(2, (self: PubSub, value: A): Effect.Effect => Effect.suspend(() => { - if (self.shutdownFlag.current) { + if (self.shutdownFlag.current || Option.isSome(self.ended.current)) { return Effect.succeed(false) } @@ -965,7 +1099,7 @@ export const publishUnsafe: { (value: A): (self: PubSub) => boolean (self: PubSub, value: A): boolean } = dual(2, (self: PubSub, value: A): boolean => { - if (self.shutdownFlag.current) return false + if (self.shutdownFlag.current || Option.isSome(self.ended.current)) return false if (self.pubsub.publish(value)) { self.strategy.completeSubscribersUnsafe(self.pubsub, self.subscribers) return true @@ -1013,7 +1147,7 @@ export const publishAll: { (self: PubSub, elements: Iterable): Effect.Effect } = dual(2, (self: PubSub, elements: Iterable): Effect.Effect => Effect.suspend(() => { - if (self.shutdownFlag.current) { + if (self.shutdownFlag.current || Option.isSome(self.ended.current)) { return Effect.succeed(false) } const surplus = self.pubsub.publishAll(elements) @@ -1085,7 +1219,7 @@ export const subscribe = (self: PubSub): Effect.Effect, ne Effect.contextWith((services) => { const localScope = Context.get(services, Scope.Scope) const scope = Scope.forkUnsafe(self.scope) - const subscription = makeSubscriptionUnsafe(self.pubsub, self.subscribers, self.strategy) + const subscription = makeSubscriptionUnsafe(self.pubsub, self.subscribers, self.strategy, self.ended) return Scope.addFinalizer(scope, unsubscribe(subscription)).pipe( Effect.andThen(Scope.addFinalizerExit(localScope, (exit) => Scope.close(scope, exit))), Effect.as(subscription) @@ -1215,29 +1349,28 @@ export const takeAll = (self: Subscription): Effect.Effect(self: Subscription) => { - const deferred = Deferred.makeUnsafe() - let set = self.subscribers.get(self.subscription) - if (!set) { - set = new Set() - self.subscribers.set(self.subscription, set) - } - set.add(self.pollers) - MutableList.append(self.pollers, deferred) - self.strategy.completePollersUnsafe( - self.pubsub, - self.subscribers, - self.subscription, - self.pollers - ) - return Effect.onInterrupt( - Deferred.await(deferred), - () => { - MutableList.remove(self.pollers, deferred) - return Effect.void - } - ) -} +const pollForItem = (self: Subscription) => + Effect.callback((resume) => { + if (self.shutdownFlag.current) return resume(Effect.interrupt) + if (Option.isSome(self.ended.current)) return resume(Effect.succeed(self.ended.current.value)) + const deferred = Deferred.makeUnsafe() + let set = self.subscribers.get(self.subscription) + if (!set) { + set = new Set() + self.subscribers.set(self.subscription, set) + } + set.add(self.pollers) + MutableList.append(self.pollers, deferred) + self.strategy.completePollersUnsafe( + self.pubsub, + self.subscribers, + self.subscription, + self.pollers + ) + if (deferred.effect) return resume(deferred.effect) + deferred.resumes = [resume] + return Effect.sync(() => MutableList.remove(self.pollers, deferred)) + }) /** * Takes up to the specified number of messages from the subscription without @@ -1497,7 +1630,8 @@ const removeSubscribers = ( const makeSubscriptionUnsafe = ( pubsub: PubSub.Atomic, subscribers: PubSub.Subscribers, - strategy: PubSub.Strategy + strategy: PubSub.Strategy, + ended: MutableRef.MutableRef> ): Subscription => new SubscriptionImpl( pubsub, @@ -1507,7 +1641,8 @@ const makeSubscriptionUnsafe = ( Latch.makeUnsafe(false), MutableRef.make(false), strategy, - pubsub.replayWindow() + pubsub.replayWindow(), + ended ) class BoundedPubSubArb implements PubSub.Atomic { @@ -2259,6 +2394,7 @@ class SubscriptionImpl implements Subscription { readonly shutdownFlag: MutableRef.MutableRef readonly strategy: PubSub.Strategy readonly replayWindow: PubSub.ReplayWindow + readonly ended: MutableRef.MutableRef> constructor( pubsub: PubSub.Atomic, @@ -2268,7 +2404,8 @@ class SubscriptionImpl implements Subscription { shutdownHook: Latch.Latch, shutdownFlag: MutableRef.MutableRef, strategy: PubSub.Strategy, - replayWindow: PubSub.ReplayWindow + replayWindow: PubSub.ReplayWindow, + ended: MutableRef.MutableRef> ) { this.pubsub = pubsub this.subscribers = subscribers @@ -2278,6 +2415,7 @@ class SubscriptionImpl implements Subscription { this.shutdownFlag = shutdownFlag this.strategy = strategy this.replayWindow = replayWindow + this.ended = ended } pipe() { @@ -2296,6 +2434,7 @@ class PubSubImpl implements PubSub { readonly shutdownHook: Latch.Latch readonly shutdownFlag: MutableRef.MutableRef readonly strategy: PubSub.Strategy + readonly ended: MutableRef.MutableRef> constructor( pubsub: PubSub.Atomic, @@ -2303,7 +2442,8 @@ class PubSubImpl implements PubSub { scope: Scope.Closeable, shutdownHook: Latch.Latch, shutdownFlag: MutableRef.MutableRef, - strategy: PubSub.Strategy + strategy: PubSub.Strategy, + ended: MutableRef.MutableRef> ) { this.pubsub = pubsub this.subscribers = subscribers @@ -2311,6 +2451,7 @@ class PubSubImpl implements PubSub { this.shutdownHook = shutdownHook this.shutdownFlag = shutdownFlag this.strategy = strategy + this.ended = ended } pipe() { @@ -2324,8 +2465,9 @@ const makePubSubUnsafe = ( scope: Scope.Closeable, shutdownHook: Latch.Latch, shutdownFlag: MutableRef.MutableRef, - strategy: PubSub.Strategy -): PubSub => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy) + strategy: PubSub.Strategy, + ended: MutableRef.MutableRef> +): PubSub => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy, ended) const ensureCapacity = (capacity: number): void => { if (capacity <= 0) { @@ -2383,17 +2525,18 @@ export class BackPressureStrategy implements PubSub.Strategy { elements: Iterable, isShutdown: MutableRef.MutableRef ): Effect.Effect { - return Effect.suspend(() => { + return Effect.callback((resume) => { const deferred = Deferred.makeUnsafe() this.offerUnsafe(elements, deferred) this.onPubSubEmptySpaceUnsafe(pubsub, subscribers) this.completeSubscribersUnsafe(pubsub, subscribers) - return (MutableRef.get(isShutdown) ? Effect.interrupt : Deferred.await(deferred)).pipe( - Effect.onInterrupt(() => { - this.removeUnsafe(deferred) - return Effect.void - }) - ) + if (MutableRef.get(isShutdown)) { + this.removeUnsafe(deferred) + return resume(Effect.interrupt) + } + if (deferred.effect) return resume(deferred.effect) + deferred.resumes = [resume] + return Effect.sync(() => this.removeUnsafe(deferred)) }) } diff --git a/repos/effect/packages/effect/src/Queue.ts b/repos/effect/packages/effect/src/Queue.ts index 91a8004ee3..5a1f93afc0 100644 --- a/repos/effect/packages/effect/src/Queue.ts +++ b/repos/effect/packages/effect/src/Queue.ts @@ -344,13 +344,13 @@ export declare namespace Queue { export type State = | { readonly _tag: "Open" - readonly takers: Set<(_: Effect) => void> + readonly takers: Set> readonly offers: Set> readonly awaiters: Set<(_: Effect) => void> } | { readonly _tag: "Closing" - readonly takers: Set<(_: Effect) => void> + readonly takers: Set> readonly offers: Set> readonly awaiters: Set<(_: Effect) => void> readonly exit: Failure @@ -384,6 +384,24 @@ export declare namespace Queue { readonly message: A readonly resume: (_: Effect) => void } + + /** + * Represents a suspended take waiting for the queue to become readable. + * + * **Details** + * + * `ready` reports whether the take can now complete, so a batch take is + * only resumed once its minimum is available. `resume` completes the + * suspended take, with `void` to retry or with a failure exit when the queue + * is done. + * + * @category models + * @since 4.0.0 + */ + export interface Taker { + readonly ready: () => boolean + readonly resume: (_: Effect) => void + } } const variance = { @@ -643,31 +661,20 @@ export const unbounded = (): Effect> => make() * @category offering * @since 2.0.0 */ -export const offer = (self: Enqueue, message: Types.NoInfer): Effect => - internalEffect.suspend(() => { - if (self.state._tag !== "Open") { - return exitFalse - } else if (self.messages.length >= self.capacity) { - switch (self.strategy) { - case "dropping": - return exitFalse - case "suspend": - if (self.capacity <= 0 && self.state.takers.size > 0) { - MutableList.append(self.messages, message) - releaseTakers(self as Queue) - return exitTrue - } - return offerRemainingSingle(self as Queue, message) - case "sliding": - MutableList.take(self.messages) - MutableList.append(self.messages, message) - return exitTrue - } - } - MutableList.append(self.messages, message) - scheduleReleaseTaker(self as Queue) - return exitTrue - }) +export const offer: { + (message: A): (self: Enqueue) => Effect + (self: Enqueue, message: Types.NoInfer): Effect +} = dual( + 2, + (self: Enqueue, message: Types.NoInfer): Effect => + internalEffect.suspend(() => + offerUnsafe(self, message) + ? exitTrue + : self.state._tag === "Open" && self.strategy === "suspend" + ? offerOrWait(self, message) + : exitFalse + ) +) /** * Adds a message to the queue synchronously. Returns `false` if the queue is done. @@ -760,19 +767,19 @@ export const offerUnsafe = (self: Enqueue, message: Types.NoInfer * @category offering * @since 2.0.0 */ -export const offerAll = (self: Enqueue, messages: Iterable): Effect> => - internalEffect.suspend(() => { - if (self.state._tag !== "Open") { - return internalEffect.succeed(Arr.fromIterable(messages)) - } - const remaining = offerAllUnsafe(self as Queue, messages) - if (remaining.length === 0) { - return core.exitSucceed([]) - } else if (self.strategy === "dropping") { - return internalEffect.succeed(remaining) +export const offerAll: { + (messages: Iterable): (self: Enqueue) => Effect> + (self: Enqueue, messages: Iterable): Effect> +} = dual(2, (self: Enqueue, messages: Iterable): Effect> => + // Admitting what fits and registering the rest are one step, so no take can + // free capacity between them + internalEffect.callback>((resume) => { + const remaining = offerAllUnsafe(self, messages) + if (remaining.length === 0 || self.strategy === "dropping") { + return resume(core.exitSucceed(remaining)) } - return offerRemainingArray(self as Queue, remaining) - }) + return waitToOffer(self, { _tag: "Array", remaining, offset: 0, resume }) + })) /** * Adds multiple messages to the queue synchronously. Returns the remaining messages that @@ -870,7 +877,10 @@ export const offerAllUnsafe = (self: Enqueue, messages: Iterable) * @category completion * @since 4.0.0 */ -export const fail = (self: Enqueue, error: E) => failCause(self, core.causeFail(error)) +export const fail: { + (error: E): (self: Enqueue) => Effect + (self: Enqueue, error: E): Effect +} = dual(2, (self: Enqueue, error: E): Effect => failCause(self, core.causeFail(error))) /** * Fails the queue with a cause. If the queue is already done, `false` is @@ -958,6 +968,8 @@ export const failCauseUnsafe = (self: Enqueue, cause: Cause): boo return true } self.state = { ...self.state, _tag: "Closing", exit: fail } + // Batch takers waiting on a minimum can now drain the remainder. + scheduleReleaseTaker(self) return true } @@ -1106,8 +1118,8 @@ export const interrupt = (self: Enqueue): Effect => * * **Details** * - * The operation is idempotent and returns `true`, including when the queue has - * already been shut down or completed. + * Returns `true` when the queue is shut down by this call, or `false` when it + * has already been shut down or completed. * * **Example** (Shutting down queues) * @@ -1132,29 +1144,45 @@ export const interrupt = (self: Enqueue): Effect => * await Effect.runPromise(program) // => { wasShutdown: true, size: 0 } * ``` * + * @see {@link shutdownUnsafe} for synchronous shutdown * @category completion * @since 2.0.0 */ -export const shutdown = (self: Enqueue): Effect => - internalEffect.sync(() => { - if (self.state._tag === "Done") { - return true - } - MutableList.clear(self.messages) - const offers = self.state.offers - finalize(self, self.state._tag === "Open" ? exitInterrupt : self.state.exit) - if (offers.size > 0) { - for (const entry of offers) { - if (entry._tag === "Single") { - entry.resume(exitFalse) - } else { - entry.resume(core.exitSucceed(entry.remaining.slice(entry.offset))) - } - } - offers.clear() - } - return true - }) +export const shutdown = (self: Enqueue): Effect => internalEffect.sync(() => shutdownUnsafe(self)) + +/** + * Shuts down the queue synchronously, discarding buffered messages and resuming + * pending operations. + * + * **When to use** + * + * Use when a synchronous callback must discard buffered messages and settle + * pending queue operations before returning. + * + * **Details** + * + * An open queue completes with an interruption. A queue already closing retains + * its completion cause. Call `failCauseUnsafe` first to shut down with a specific + * failure. Returns `true` when the queue is shut down by this call, or `false` + * when it has already been shut down or completed. + * + * @see {@link shutdown} for the effectful variant + * @see {@link failCauseUnsafe} to set a failure before discarding buffered messages + * @category completion + * @since 4.0.0 + */ +export const shutdownUnsafe = (self: Enqueue): boolean => { + if (self.state._tag === "Done") { + return false + } + MutableList.clear(self.messages) + const offers = self.state.offers + finalize(self, self.state._tag === "Open" ? exitInterrupt : self.state.exit) + for (const entry of offers) { + resumeUnoffered(entry) + } + return true +} /** * Takes and returns all currently buffered messages without waiting for more. @@ -1297,8 +1325,9 @@ export const collect = (self: Dequeue): Effect, Pull * The operation may wait until enough messages are available to satisfy the * queue's batching rules. Finite fractional values of `n` are rounded down. * If `n` is `NaN` or non-positive, it succeeds with an empty array. If the - * queue completes or fails before messages can be taken, the effect fails with - * the queue's terminal error. + * queue is closing, drains the currently available messages even when fewer + * than `n` are available. Once the queue is done, the effect fails with the + * queue's terminal error. * * **Example** (Taking a fixed number of values) * @@ -1328,10 +1357,10 @@ export const collect = (self: Dequeue): Effect, Pull * @category taking * @since 2.0.0 */ -export const takeN = ( - self: Dequeue, - n: number -): Effect, E> => takeBetween(self, n, n) +export const takeN: { + (n: number): (self: Dequeue) => Effect, E> + (self: Dequeue, n: number): Effect, E> +} = dual(2, (self: Dequeue, n: number): Effect, E> => takeBetween(self, n, n)) /** * Takes between `min` and `max` messages from the queue. @@ -1341,8 +1370,9 @@ export const takeN = ( * The operation waits when fewer than the required minimum messages are * available. It returns at most `max` messages. Finite fractional bounds are * rounded down, while `NaN` and non-positive bounds are treated as `0`. If the - * queue completes or fails before the minimum can be satisfied, the effect - * fails with the queue's terminal error. + * queue is closing, drains the currently available messages even when fewer + * than `min` are available. Once the queue is done, the effect fails with the + * queue's terminal error. * * **Example** (Taking a bounded batch of values) * @@ -1372,17 +1402,17 @@ export const takeN = ( * @category taking * @since 2.0.0 */ -export const takeBetween = ( - self: Dequeue, - min: number, - max: number -): Effect, E> => { +export const takeBetween: { + (min: number, max: number): (self: Dequeue) => Effect, E> + (self: Dequeue, min: number, max: number): Effect, E> +} = dual(3, (self: Dequeue, min: number, max: number): Effect, E> => { min = Count.normalize(min) max = Count.normalize(max) return internalEffect.suspend(() => - takeBetweenUnsafe(self, min, max) ?? internalEffect.andThen(awaitTake(self), takeBetween(self, 1, max)) + takeBetweenUnsafe(self, min, max) ?? + internalEffect.andThen(awaitTake(self, () => canTake(self, min)), takeBetween(self, min, max)) ) -} +}) /** * Takes a single message from the queue, or wait for a message to be @@ -1424,8 +1454,8 @@ export const takeBetween = ( * @since 2.0.0 */ export const take = (self: Dequeue): Effect => - internalEffect.suspend( - () => takeUnsafe(self) ?? internalEffect.andThen(awaitTake(self), take(self)) + internalEffect.suspend(() => + takeUnsafe(self) ?? internalEffect.andThen(awaitTake(self, () => canTake(self, 1)), take(self)) ) /** @@ -1509,7 +1539,8 @@ export const peek = (self: Dequeue): Effect => if (self.messages.length > 0 && self.messages.head) { return internalEffect.succeed(self.messages.head.array[self.messages.head.offset]) } - return internalEffect.andThen(awaitTake(self), peek(self)) + // Pending offers on a rendezvous queue are not visible to peek. + return internalEffect.andThen(awaitTake(self, () => self.messages.length > 0), peek(self)) }) /** @@ -1919,8 +1950,9 @@ const releaseTakers = (self: Enqueue) => { return } for (const taker of self.state.takers) { + if (!taker.ready()) continue self.state.takers.delete(taker) - taker(internalEffect.exitVoid) + taker.resume(internalEffect.exitVoid) if (self.messages.length === 0) { break } @@ -1947,54 +1979,59 @@ const takeBetweenUnsafe = ( return self.state.exit } else if (max <= 0 || min <= 0) { return core.exitSucceed([]) - } else if (self.capacity <= 0 && self.messages.length === 0 && self.state.offers.size > 0) { - const messages = [takeOfferUnsafe(self.state.offers)] - releaseCapacity(self) - return core.exitSucceed(messages) - } - min = Math.min(min, self.capacity || 1) - if (min <= self.messages.length) { - const messages = MutableList.takeN(self.messages, max) - releaseCapacity(self) - return core.exitSucceed(messages) + } else if (!canTake(self, min)) { + return undefined } + const messages = self.messages.length > 0 + ? MutableList.takeN(self.messages, max) + : [takeOfferUnsafe(self.state.offers)] + releaseCapacity(self) + return core.exitSucceed(messages) } -const offerRemainingSingle = (self: Enqueue, message: A) => { - return internalEffect.callback((resume) => { - if (self.state._tag !== "Open") { - return resume(exitFalse) - } - const entry: Queue.OfferEntry = { _tag: "Single", message, resume } - self.state.offers.add(entry) +// Whether a take of at least `min` messages can complete without waiting. +// A closing queue receives no more messages, so any remainder satisfies `min`. +const canTake = (self: Dequeue, min: number): boolean => + self.messages.length >= (self.state._tag === "Closing" ? 1 : Math.min(min, self.capacity || 1)) || + (self.capacity <= 0 && self.state._tag !== "Done" && self.state.offers.size > 0) + +// The readiness check and the taker registration run in one step, so no +// message can arrive between them. Wake-ups resume with void and the caller +// retries, which keeps the fiber stack flat across spurious wake-ups. +const awaitTake = (self: Dequeue, ready: () => boolean) => + internalEffect.callback((resume) => { + if (self.state._tag === "Done") return resume(self.state.exit) + if (ready()) return resume(internalEffect.exitVoid) + const taker = { ready, resume } + self.state.takers.add(taker) return internalEffect.sync(() => { - if (self.state._tag === "Open") { - self.state.offers.delete(entry) - } + if (self.state._tag !== "Done") self.state.takers.delete(taker) }) }) -} -const offerRemainingArray = (self: Enqueue, remaining: Array) => { - return internalEffect.callback>((resume) => { - if (self.state._tag !== "Open") { - return resume(core.exitSucceed(remaining)) - } - const entry: Queue.OfferEntry = { - _tag: "Array", - remaining, - offset: 0, - resume +const offerOrWait = (self: Enqueue, message: A) => + internalEffect.callback((resume) => + offerUnsafe(self, message) ? resume(exitTrue) : waitToOffer(self, { _tag: "Single", message, resume }) + ) + +const waitToOffer = (self: Enqueue, entry: Queue.OfferEntry) => { + if (self.state._tag !== "Open") return resumeUnoffered(entry) + const offers = self.state.offers + offers.add(entry) + return internalEffect.sync(() => { + if (self.state._tag === "Done") return + offers.delete(entry) + if (self.state._tag === "Closing" && offers.size === 0 && self.messages.length === 0) { + finalize(self, self.state.exit) } - self.state.offers.add(entry) - return internalEffect.sync(() => { - if (self.state._tag === "Open") { - self.state.offers.delete(entry) - } - }) }) } +const resumeUnoffered = (entry: Queue.OfferEntry) => + entry._tag === "Single" + ? entry.resume(exitFalse) + : entry.resume(core.exitSucceed(entry.remaining.slice(entry.offset))) + // Reserve a pending message for the consumer before the producer can reenter. const takeOfferUnsafe = (offers: Set>): A => { const entry = offers.values().next().value! @@ -2045,19 +2082,6 @@ const releaseCapacity = (self: Dequeue): boolean => { return false } -const awaitTake = (self: Dequeue) => - internalEffect.callback((resume) => { - if (self.state._tag === "Done") { - return resume(self.state.exit) - } - self.state.takers.add(resume) - return internalEffect.sync(() => { - if (self.state._tag !== "Done") { - self.state.takers.delete(resume) - } - }) - }) - const takeAllUnsafe = (self: Dequeue) => { if (self.messages.length > 0) { const messages = MutableList.takeAll(self.messages) @@ -2078,7 +2102,7 @@ const finalize = (self: Enqueue | Dequeue, exit: Failure(self: RcMap, key: K, entry: State.Entry entry.expiresAt = clock.currentTimeMillisUnsafe() + Duration.toMillis(entry.idleTimeToLive) if (entry.fiber) return Effect.void - entry.fiber = Effect.interruptibleMask(function loop(restore): Effect.Effect { + entry.fiber = Effect.uninterruptibleMask(function loop(restore): Effect.Effect { const now = clock.currentTimeMillisUnsafe() const remaining = entry.expiresAt - now if (remaining <= 0) { @@ -485,9 +485,9 @@ const release = (self: RcMap, key: K, entry: State.Entry const o = MutableHashMap.get(self.state.map, key) if (o._tag === "None" || o.value !== entry) return Effect.void MutableHashMap.remove(self.state.map, key) - return restore(Scope.close(entry.scope, Exit.void)) + return Scope.close(entry.scope, Exit.void) } - return Effect.flatMap(clock.sleep(Duration.millis(remaining)), () => loop(restore)) + return Effect.flatMap(restore(clock.sleep(Duration.millis(remaining))), () => loop(restore)) }).pipe( Effect.ensuring(Effect.sync(() => { entry.fiber = undefined diff --git a/repos/effect/packages/effect/src/RcRef.ts b/repos/effect/packages/effect/src/RcRef.ts index 3d64d58900..8c81fce749 100644 --- a/repos/effect/packages/effect/src/RcRef.ts +++ b/repos/effect/packages/effect/src/RcRef.ts @@ -158,8 +158,16 @@ export const make: ( options: { readonly acquire: Effect.Effect /** - * When the reference count reaches zero, the resource will be released - * after this duration. + * How long to keep an idle resource after its last reference is released. + * + * If the resource has not been invalidated, finite durations, including `0`, + * schedule release in a forked fiber that the scope releasing the last + * reference does not await. An infinite duration keeps the idle resource + * until invalidation or the RcRef's scope closes. + * + * If this option is omitted or the resource has been invalidated with + * `RcRef.invalidate`, the scope releasing the last reference releases the + * resource and awaits completion. */ readonly idleTimeToLive?: Duration.Input | undefined } diff --git a/repos/effect/packages/effect/src/Record.ts b/repos/effect/packages/effect/src/Record.ts index ec47fe3031..198c3fb5eb 100644 --- a/repos/effect/packages/effect/src/Record.ts +++ b/repos/effect/packages/effect/src/Record.ts @@ -907,8 +907,8 @@ export const getSuccesses = ( * * **Details** * - * Failure values are collected in the left record, and success values are - * collected in the right record, preserving the original keys. + * Success values are collected in the first record, and failure values are + * collected in the second record, preserving the original keys. * * **Example** (Partitioning with Result) * @@ -917,7 +917,7 @@ export const getSuccesses = ( * * const x = { a: 1, b: 2, c: 3 } * const f = (n: number) => (n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) - * Record.partition(x, f) // => [{ a: 1, c: 3 }, { b: 2 }] + * Record.partition(x, f) // => [{ b: 2 }, { a: 1, c: 3 }] * ``` * * @category filtering @@ -928,41 +928,41 @@ export const partition: { f: (input: A, key: K) => Result ): ( self: ReadonlyRecord - ) => [left: Record, B>, right: Record, C>] + ) => [passes: Record, C>, fails: Record, B>] ( self: ReadonlyRecord, f: (input: A, key: K) => Result - ): [left: Record, B>, right: Record, C>] + ): [passes: Record, C>, fails: Record, B>] } = dual( 2, ( self: ReadonlyRecord, f: (input: A, key: K) => Result - ): [left: Record, B>, right: Record, C>] => { - const left: Record = empty() - const right: Record = empty() + ): [passes: Record, C>, fails: Record, B>] => { + const passes: Record = empty() + const fails: Record = empty() for (const key of keys(self)) { const e = f(self[key], key) if (R.isFailure(e)) { - InternalRecord.assignProperty(left, key, e.failure) + InternalRecord.assignProperty(fails, key, e.failure) } else { - InternalRecord.assignProperty(right, key, e.success) + InternalRecord.assignProperty(passes, key, e.success) } } - return [left, right] + return [passes, fails] } ) /** * Partitions a record of `Result` values into two separate records, - * one with the `Err` values and one with the `Ok` values. + * one with the success values and one with the failure values. * * **Example** (Separating Result values) * * ```ts import.meta.vitest * import { Record, Result } from "effect" * - * Record.separate({ a: Result.fail("e"), b: Result.succeed(1) }) // => [{ a: "e" }, { b: 1 }] + * Record.separate({ a: Result.fail("e"), b: Result.succeed(1) }) // => [{ b: 1 }, { a: "e" }] * ``` * * @category filtering @@ -970,7 +970,7 @@ export const partition: { */ export const separate: ( self: ReadonlyRecord> -) => [Record, A>, Record, B>] = partition(identity) +) => [Record, B>, Record, A>] = partition(identity) /** * Retrieves the keys of a given record as an array. diff --git a/repos/effect/packages/effect/src/RequestResolver.ts b/repos/effect/packages/effect/src/RequestResolver.ts index 951c5f7151..02239e6755 100644 --- a/repos/effect/packages/effect/src/RequestResolver.ts +++ b/repos/effect/packages/effect/src/RequestResolver.ts @@ -23,6 +23,8 @@ import * as effect from "./internal/effect.ts" import * as internal from "./internal/request.ts" import * as Iterable from "./Iterable.ts" import * as MutableHashMap from "./MutableHashMap.ts" +import type * as Persistable from "./persistence/Persistable.ts" +import * as Persistence from "./persistence/Persistence.ts" import { type Pipeable, pipeArguments } from "./Pipeable.ts" import { hasProperty } from "./Predicate.ts" import type * as Request from "./Request.ts" @@ -30,8 +32,6 @@ import type * as Schema from "./Schema.ts" import type { Scope } from "./Scope.ts" import * as Tracer from "./Tracer.ts" import type * as Types from "./Types.ts" -import type * as Persistable from "./unstable/persistence/Persistable.ts" -import * as Persistence from "./unstable/persistence/Persistence.ts" const TypeId = "~effect/RequestResolver" diff --git a/repos/effect/packages/effect/src/Schedule.ts b/repos/effect/packages/effect/src/Schedule.ts index a9d4816dac..144692a10f 100644 --- a/repos/effect/packages/effect/src/Schedule.ts +++ b/repos/effect/packages/effect/src/Schedule.ts @@ -1459,6 +1459,20 @@ export const windowed = (interval: Duration.Input): Schedule => { */ export const forever: Schedule = spaced(Duration.zero) +/** + * Returns a schedule that recurs immediately once, then completes. + * + * **Details** + * + * The output of both the recurrence and completion is `void`. When used with + * `Effect.repeat`, the effect runs twice in total: once initially and once + * after the schedule recurs. + * + * @category constructors + * @since 4.0.0 + */ +export const once: Schedule = map(recurs(1), () => void 0) + const constIdentity = fromStep( effect.succeed((_now, input: unknown) => effect.succeed([input, Duration.zero] as [unknown, Duration.Duration])) ) diff --git a/repos/effect/packages/effect/src/Schema.ts b/repos/effect/packages/effect/src/Schema.ts index c6897d77b3..de3428da89 100644 --- a/repos/effect/packages/effect/src/Schema.ts +++ b/repos/effect/packages/effect/src/Schema.ts @@ -23,7 +23,7 @@ import * as DateTime from "./DateTime.ts" import type { Differ } from "./Differ.ts" import * as Duration_ from "./Duration.ts" import * as Effect from "./Effect.ts" -import * as Encoding from "./Encoding.ts" +import * as Base64 from "./encoding/Base64.ts" import * as Equal from "./Equal.ts" import type * as Equivalence from "./Equivalence.ts" import * as Exit_ from "./Exit.ts" @@ -33,6 +33,9 @@ import { identity } from "./Function.ts" import type * as Graph_ from "./Graph.ts" import * as HashMap_ from "./HashMap.ts" import * as HashSet_ from "./HashSet.ts" +import * as Cookies_ from "./http/Cookies.ts" +import * as Headers_ from "./http/Headers.ts" +import * as UrlParams_ from "./http/UrlParams.ts" import * as core from "./internal/core.ts" import { effectIsExit } from "./internal/effect.ts" import * as InternalGraph from "./internal/graph.ts" @@ -52,6 +55,9 @@ import { isSchemaError as isSchemaErrorInternal, SchemaErrorTypeId } from "./int import { getStackTraceLimit, setStackTraceLimit } from "./internal/stackTraceLimit.ts" import type * as JsonPatch from "./JsonPatch.ts" import type * as JsonSchema from "./JsonSchema.ts" +import * as IpInterface_ from "./net/IpInterface.ts" +import * as IpNetwork_ from "./net/IpNetwork.ts" +import * as NetAddress_ from "./net/NetAddress.ts" import { remainder } from "./Number.ts" import type * as Optic_ from "./Optic.ts" import * as Option_ from "./Option.ts" @@ -71,14 +77,8 @@ import * as SchemaTransformation from "./SchemaTransformation.ts" import type { StandardJSONSchemaV1, StandardSchemaV1 } from "./StandardSchema.ts" import type { Assign, Lambda, Mutable, Simplify } from "./Struct.ts" import * as Struct_ from "./Struct.ts" -import type { RequiredKeys, UnionToIntersection } from "./Types.ts" +import type { IsUnion, RequiredKeys } from "./Types.ts" import type { Unify } from "./Unify.ts" -import * as Cookies_ from "./unstable/http/Cookies.ts" -import * as Headers_ from "./unstable/http/Headers.ts" -import * as UrlParams_ from "./unstable/http/UrlParams.ts" -import * as IpInterface_ from "./unstable/net/IpInterface.ts" -import * as IpNetwork_ from "./unstable/net/IpNetwork.ts" -import * as NetAddress_ from "./unstable/net/NetAddress.ts" const TypeId = InternalMake.TypeId /** @@ -2988,6 +2988,58 @@ export interface String extends Bottom +{} +/** + * Schema for `string & {}`. Validates that the input is `typeof` `"string"`. + * + * **When to use** + * + * Use with {@link Union} and {@link Literals} when a value may be any string + * and known literals should stay available for editor autocomplete. + * + * **Details** + * + * `string & {}` stays alongside string literals in a union, so editors keep + * suggesting those literals. Any string still passes validation. Read the + * literals from the {@link Literals} member. + * + * **Example** (Suggesting known HTTP methods) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * + * const Method = Schema.Union([ + * Schema.StringForLiteralAutocomplete, + * Schema.Literals(["GET", "POST"]) + * ]) + * + * // Type: "GET" | "POST" | (string & {}) + * Method.make("PATCH") // => "PATCH" + * Method.members[1].literals // => ["GET", "POST"] + * ``` + * + * @see {@link String} for a schema whose type is plain `string`. + * @see {@link Literals} for the known literals to include in the union. + * @category schemas + * @since 4.0.0 + */ +export const StringForLiteralAutocomplete: StringForLiteralAutocomplete = String /** * Type-level representation of {@link Number}. * @@ -4477,7 +4529,7 @@ export function withArrayLengthConstraints( maximum: number | undefined ): $Array { if (minimum !== undefined && maximum !== undefined) { - return self.check(isLengthBetween(minimum, maximum)) + return self.check(isBetweenLength(minimum, maximum)) } if (minimum !== undefined) return self.check(isMinLength(minimum)) if (maximum !== undefined) return self.check(isMaxLength(maximum)) @@ -5005,14 +5057,19 @@ export function refine( return (schema: S): refine => make(SchemaAST.appendChecks(schema.ast, [SchemaAST.makeFilterByGuard(refinement, annotations)]), { schema }) } -type DistributeBrands = UnionToIntersection : never> +// A concrete key requires a property; broad and open template keys do not. +type EnsureSingleBrandKey = IsUnion extends false + ? {} extends Record ? never : unknown + : never +// Collect all keys, including those not shared by every union member. +type FromBrandKeys> = A extends unknown ? Brand.Brand.Keys : never /** * Type-level representation returned by {@link brand}. * * @category branding * @since 3.10.0 */ -export interface brand extends +export interface brand extends BottomLazy< S["ast"], brand, @@ -5024,19 +5081,19 @@ export interface brand extends S["~encoded.optionality"] > { - readonly "Type": S["Type"] & DistributeBrands + readonly "Type": S["Type"] & Brand.Brand readonly "Encoded": S["Encoded"] readonly "DecodingServices": S["DecodingServices"] readonly "EncodingServices": S["EncodingServices"] readonly "~type.make.in": S["~type.make.in"] - readonly "~type.make": S["Type"] & DistributeBrands - readonly "Iso": S["Type"] & DistributeBrands + readonly "~type.make": S["Type"] & Brand.Brand + readonly "Iso": S["Type"] & Brand.Brand readonly schema: S readonly identifier: string } /** - * Adds a nominal brand to a schema, intersecting the output type with - * `Brand.Brand` to prevent accidental mixing of structurally identical types. + * Intersects a schema's output type with `Brand.Brand` to prevent accidental + * mixing of structurally identical types. * * **When to use** * @@ -5045,31 +5102,52 @@ export interface brand extends * * **Gotchas** * - * `brand` adds brand metadata and narrows the TypeScript output type, but it - * does not add runtime checks. + * - `identifier` must be a single concrete string literal. Widened strings, + * unions, and open template literal types are rejected. + * - `brand` only narrows the TypeScript output type. It does not change the + * schema's runtime AST or add runtime checks. + * - Schema representations and generated schema code omit the brand. Reapply + * `brand` after rebuilding or generating a schema when the nominal type is + * still required. * - * @see {@link fromBrand} for applying a Brand constructor's checks along with the brand tag + * @see {@link fromBrand} for applying a Brand constructor's checks along with its branded type * * @category branding * @since 3.10.0 */ -export function brand(identifier: B) { +export function brand(identifier: B & EnsureSingleBrandKey) { return (schema: S): brand => - make(SchemaAST.brand(schema.ast, identifier), { schema, identifier }) + make(schema.ast, { schema, identifier }) } /** * Creates a branded schema from a {@link Brand.Constructor}, applying the - * constructor's checks and brand tag to the underlying schema. + * constructor's checks and branded type to the underlying schema. + * + * **When to use** + * + * Use to reuse the checks from a constructor with one concrete brand key. + * + * **Gotchas** + * + * `identifier` must match the constructor's only brand key. Apply `fromBrand` + * repeatedly to compose distinct brands, and use {@link Union} to represent + * alternatives. + * + * @see {@link brand} for adding a brand without constructor checks * * @category branding * @since 3.10.0 */ -export function fromBrand>(identifier: string, ctor: Brand.Constructor) { +export function fromBrand>( + identifier: Brand.Brand.Keys & string, + ctor: Brand.Constructor & EnsureSingleBrandKey> +) { + type B = Brand.Brand.Keys & string return }>( self: S - ): brand> => { - return (ctor.checks ? self.check(...ctor.checks) : self).pipe(brand(identifier)) - } + ): brand => + // The constructor already guarantees a single concrete brand key. + (ctor.checks ? self.check(...ctor.checks) : self).pipe(brand(identifier as B & EnsureSingleBrandKey)) } /** * Type-level representation returned by {@link middlewareDecoding}. @@ -5404,7 +5482,7 @@ export function decodeTo() { readonly encode: SchemaGetter.Getter, T> } ): SchemaAST.Link => { - return new SchemaAST.Link(encodeTo.ast, SchemaTransformation.make(transformation)) + return new SchemaAST.Link(encodeTo.ast, SchemaTransformation.makeTransformation(transformation)) } } /** @@ -6563,9 +6641,9 @@ export function isTrimmed(annotations?: Annotations.Filter) { * * JSON Schema: * - * JSON Schema receives the RegExp source as a `pattern`. JavaScript flags are - * not represented, so validation can differ when the RegExp uses flags or - * relies on JavaScript's non-Unicode behavior. + * Unless annotations override `toJsonSchema`, JSON Schema receives a `pattern` + * only when the JavaScript RegExp uses the Unicode flag and its other flags are + * `d`, `g`, or `y`. Sticky patterns are anchored at the start of the string. * * Arbitrary: * @@ -6581,10 +6659,12 @@ export function isPattern( ): SchemaAST.Filter { const source = regExp.source const flags = regExp.flags + const canExport = /^[dg]*uy?$/.test(flags) const runtimeRegExp = flags === "" ? `new RegExp(${format(source)})` : `new RegExp(${format(source)}, ${format(flags)})` return SchemaAST.isPattern(regExp, { + toJsonSchema: () => canExport ? { pattern: flags.endsWith("y") ? `^(?:${source})` : source } : [{}, true], toCode: () => ({ runtime: `Schema.isPattern(${runtimeRegExp})` }), ...annotations }) @@ -6853,30 +6933,43 @@ export function isBase64Url(annotations?: Annotations.Filter) { } ) } + +function literalToJsonSchema( + literal: string, + original: string, + pattern: string +): SchemaRepresentation.ToJsonSchema.CheckOutput { + if (literal.length === 0) return original.length === 0 ? {} : [{}, true] + const constraint = { pattern: new globalThis.RegExp(pattern).source } + return literal === original ? constraint : [constraint, true] +} + /** * Validates at runtime that a string starts with the specified literal prefix. * * **Details** * * RegExp metacharacters in the prefix are escaped in JSON Schema and arbitrary - * metadata so that the generated patterns retain literal `startsWith` semantics. + * metadata. If the prefix ends with a high surrogate, the JSON Schema pattern + * omits that code unit so Unicode matching cannot reject a valid string. * * @category validation * @since 4.0.0 */ -export function isStartsWith(startsWith: string, annotations?: Annotations.Filter) { +export function isStartingWith(startsWith: string, annotations?: Annotations.Filter) { const formatted = JSON.stringify(startsWith) const regExp = new globalThis.RegExp(`^${RegExp_.escape(startsWith)}`) + const jsonSchemaLiteral = startsWith.replace(/[\uD800-\uDBFF]$/, "") return makeFilter( (s: string) => s.startsWith(startsWith), { expected: `a string starting with ${formatted}`, representation: { - id: "effect/schema/isStartsWith", + id: "effect/schema/isStartingWith", payload: { startsWith } }, - toJsonSchema: () => ({ pattern: regExp.source }), - toCode: () => ({ runtime: `Schema.isStartsWith(${format(startsWith)})` }), + toJsonSchema: () => literalToJsonSchema(jsonSchemaLiteral, startsWith, `^${RegExp_.escape(jsonSchemaLiteral)}`), + toCode: () => ({ runtime: `Schema.isStartingWith(${format(startsWith)})` }), arbitraryConstraint: { patterns: [{ source: regExp.source, flags: regExp.flags }] }, @@ -6890,24 +6983,26 @@ export function isStartsWith(startsWith: string, annotations?: Annotations.Filte * **Details** * * RegExp metacharacters in the suffix are escaped in JSON Schema and arbitrary - * metadata so that the generated patterns retain literal `endsWith` semantics. + * metadata. If the suffix begins with a low surrogate, the JSON Schema pattern + * omits that code unit so Unicode matching cannot reject a valid string. * * @category validation * @since 4.0.0 */ -export function isEndsWith(endsWith: string, annotations?: Annotations.Filter) { +export function isEndingWith(endsWith: string, annotations?: Annotations.Filter) { const formatted = JSON.stringify(endsWith) const regExp = new globalThis.RegExp(`${RegExp_.escape(endsWith)}$`) + const jsonSchemaLiteral = endsWith.replace(/^[\uDC00-\uDFFF]/, "") return makeFilter( (s: string) => s.endsWith(endsWith), { expected: `a string ending with ${formatted}`, representation: { - id: "effect/schema/isEndsWith", + id: "effect/schema/isEndingWith", payload: { endsWith } }, - toJsonSchema: () => ({ pattern: regExp.source }), - toCode: () => ({ runtime: `Schema.isEndsWith(${format(endsWith)})` }), + toJsonSchema: () => literalToJsonSchema(jsonSchemaLiteral, endsWith, `${RegExp_.escape(jsonSchemaLiteral)}$`), + toCode: () => ({ runtime: `Schema.isEndingWith(${format(endsWith)})` }), arbitraryConstraint: { patterns: [{ source: regExp.source, flags: regExp.flags }] }, @@ -6921,25 +7016,27 @@ export function isEndsWith(endsWith: string, annotations?: Annotations.Filter) { * **Details** * * RegExp metacharacters in the substring are escaped in JSON Schema and - * arbitrary metadata so that the generated patterns retain literal `includes` - * semantics. + * arbitrary metadata. A leading low surrogate or trailing high surrogate is + * omitted from the JSON Schema pattern so Unicode matching cannot reject a + * valid string. * * @category validation * @since 4.0.0 */ -export function isIncludes(includes: string, annotations?: Annotations.Filter) { +export function isIncluding(includes: string, annotations?: Annotations.Filter) { const formatted = JSON.stringify(includes) const regExp = new globalThis.RegExp(RegExp_.escape(includes)) + const jsonSchemaLiteral = includes.replace(/^[\uDC00-\uDFFF]|[\uD800-\uDBFF]$/g, "") return makeFilter( (s: string) => s.includes(includes), { expected: `a string including ${formatted}`, representation: { - id: "effect/schema/isIncludes", + id: "effect/schema/isIncluding", payload: { includes } }, - toJsonSchema: () => ({ pattern: regExp.source }), - toCode: () => ({ runtime: `Schema.isIncludes(${format(includes)})` }), + toJsonSchema: () => literalToJsonSchema(jsonSchemaLiteral, includes, RegExp_.escape(jsonSchemaLiteral)), + toCode: () => ({ runtime: `Schema.isIncluding(${format(includes)})` }), arbitraryConstraint: { patterns: [{ source: regExp.source, flags: regExp.flags }] }, @@ -6971,7 +7068,7 @@ export function isUppercased(annotations?: Annotations.Filter) { id: "effect/schema/isUppercased", payload: null }, - toJsonSchema: () => ({ pattern: regExp.source }), + toJsonSchema: () => [{ pattern: regExp.source }, true], toCode: () => ({ runtime: "Schema.isUppercased()" }), arbitraryConstraint: { patterns: [{ source: UPPERCASED_PATTERN, flags: "" }] @@ -7004,7 +7101,7 @@ export function isLowercased(annotations?: Annotations.Filter) { id: "effect/schema/isLowercased", payload: null }, - toJsonSchema: () => ({ pattern: regExp.source }), + toJsonSchema: () => [{ pattern: regExp.source }, true], toCode: () => ({ runtime: "Schema.isLowercased()" }), arbitraryConstraint: { patterns: [{ source: LOWERCASED_PATTERN, flags: "" }] @@ -7037,7 +7134,7 @@ export function isCapitalized(annotations?: Annotations.Filter) { id: "effect/schema/isCapitalized", payload: null }, - toJsonSchema: () => ({ pattern: regExp.source }), + toJsonSchema: () => [{ pattern: regExp.source }, true], toCode: () => ({ runtime: "Schema.isCapitalized()" }), arbitraryConstraint: { patterns: [{ source: CAPITALIZED_PATTERN, flags: "" }] @@ -7070,7 +7167,7 @@ export function isUncapitalized(annotations?: Annotations.Filter) { id: "effect/schema/isUncapitalized", payload: null }, - toJsonSchema: () => ({ pattern: regExp.source }), + toJsonSchema: () => [{ pattern: regExp.source }, true], toCode: () => ({ runtime: "Schema.isUncapitalized()" }), arbitraryConstraint: { patterns: [{ source: UNCAPITALIZED_PATTERN, flags: "" }] @@ -7554,7 +7651,7 @@ export function isInt(annotations?: Annotations.Filter) { id: "effect/schema/isInt", payload: null }, - toJsonSchema: () => ({ type: "integer" }), + toJsonSchema: () => [{ type: "integer" }, true], toCode: () => ({ runtime: "Schema.isInt()" }), arbitraryConstraint: { number: "integer" @@ -7698,7 +7795,7 @@ export const isGreaterThanDate: ( id: "effect/schema/isGreaterThanDate", payload: { exclusiveMinimum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isGreaterThanDate(${formatDateRuntime(exclusiveMinimum)})` }) } } @@ -7733,7 +7830,7 @@ export const isGreaterThanOrEqualToDate: ( id: "effect/schema/isGreaterThanOrEqualToDate", payload: { minimum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isGreaterThanOrEqualToDate(${formatDateRuntime(minimum)})` }) } } @@ -7762,7 +7859,7 @@ export const isLessThanDate: ( id: "effect/schema/isLessThanDate", payload: { exclusiveMaximum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isLessThanDate(${formatDateRuntime(exclusiveMaximum)})` }) } } @@ -7797,7 +7894,7 @@ export const isLessThanOrEqualToDate: ( id: "effect/schema/isLessThanOrEqualToDate", payload: { maximum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isLessThanOrEqualToDate(${formatDateRuntime(maximum)})` }) } } @@ -7841,7 +7938,7 @@ export const isBetweenDate: (options: { id: "effect/schema/isBetweenDate", payload }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isBetweenDate({ minimum: ${formatDateRuntime(options.minimum)}, maximum: ${ formatDateRuntime(options.maximum) @@ -7874,7 +7971,7 @@ export const isGreaterThanBigInt: ( id: "effect/schema/isGreaterThanBigInt", payload: { exclusiveMinimum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isGreaterThanBigInt(${format(exclusiveMinimum)})` }) } } @@ -7904,7 +8001,7 @@ export const isGreaterThanOrEqualToBigInt: ( id: "effect/schema/isGreaterThanOrEqualToBigInt", payload: { minimum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isGreaterThanOrEqualToBigInt(${format(minimum)})` }) } } @@ -7933,7 +8030,7 @@ export const isLessThanBigInt: ( id: "effect/schema/isLessThanBigInt", payload: { exclusiveMaximum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isLessThanBigInt(${format(exclusiveMaximum)})` }) } } @@ -7963,7 +8060,7 @@ export const isLessThanOrEqualToBigInt: ( id: "effect/schema/isLessThanOrEqualToBigInt", payload: { maximum: encoded } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isLessThanOrEqualToBigInt(${format(maximum)})` }) } } @@ -8002,7 +8099,7 @@ export const isBetweenBigInt: (options: { id: "effect/schema/isBetweenBigInt", payload }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isBetweenBigInt({ minimum: ${format(options.minimum)}, maximum: ${ format(options.maximum) @@ -8011,6 +8108,7 @@ export const isBetweenBigInt: (options: { } } }) + /** * Validates that a value has at least the specified length. Works with strings * and arrays. @@ -8019,9 +8117,11 @@ export const isBetweenBigInt: (options: { * * JSON Schema: * - * For arrays, this check corresponds to `minItems`. For strings, it corresponds - * to `minLength`. JavaScript counts UTF-16 code units while JSON Schema counts - * Unicode code points, so the two validations can differ for some strings. + * The bound must be finite; it is rounded down and clamped to zero. For arrays, + * this check corresponds to `minItems`. JavaScript counts UTF-16 code units + * while JSON Schema counts Unicode code points, so strings use + * `minLength: Math.ceil(minLength / 2)`, the tightest lower bound that cannot + * reject a string accepted by this check. * * Arbitrary: * @@ -8044,7 +8144,11 @@ export const isBetweenBigInt: (options: { * @since 4.0.0 */ export function isMinLength(minLength: number, annotations?: Annotations.Filter) { - minLength = Math.max(0, Math.floor(minLength)) + minLength = normalizeCardinality(minLength) + return makeIsMinLength(minLength, Math.ceil(minLength / 2), annotations) +} + +function makeIsMinLength(minLength: number, minCodePoints: number, annotations?: Annotations.Filter) { return makeFilter<{ readonly length: number }>( (input) => input.length >= minLength, { @@ -8053,7 +8157,16 @@ export function isMinLength(minLength: number, annotations?: Annotations.Filter) id: "effect/schema/isMinLength", payload: { minLength } }, - toJsonSchema: ({ type }) => type === "array" ? { minItems: minLength } : { minLength }, + toJsonSchema: ({ type }) => + type === "string" + ? minLength <= 1 + ? { minLength: minCodePoints } + : [{ minLength: minCodePoints }, true] + : type === "array" + ? { minItems: minLength } + : type === undefined + ? [{ minLength: minCodePoints, minItems: minLength }, true] + : [{}, true], toCode: () => ({ runtime: `Schema.isMinLength(${minLength})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8083,7 +8196,7 @@ export function isMinLength(minLength: number, annotations?: Annotations.Filter) * @since 4.0.0 */ export function isNonEmpty(annotations?: Annotations.Filter) { - return isMinLength(1, annotations) + return makeIsMinLength(1, 1, annotations) } /** * Validates that a value has at most the specified length. Works with strings @@ -8093,8 +8206,10 @@ export function isNonEmpty(annotations?: Annotations.Filter) { * * JSON Schema: * - * This check corresponds to the `maxLength` constraint for strings or the - * `maxItems` constraint for arrays in JSON Schema. + * The bound must be finite; it is rounded down and clamped to zero. This check + * corresponds to `maxItems` for arrays. Strings use the same bound for + * `maxLength`, which cannot reject a string accepted by this check because a + * string has no more code points than UTF-16 code units. * * Arbitrary: * @@ -8106,7 +8221,7 @@ export function isNonEmpty(annotations?: Annotations.Filter) { * @since 4.0.0 */ export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) { - maxLength = Math.max(0, Math.floor(maxLength)) + maxLength = normalizeCardinality(maxLength) return makeFilter<{ readonly length: number }>( (input) => input.length <= maxLength, { @@ -8115,7 +8230,14 @@ export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) id: "effect/schema/isMaxLength", payload: { maxLength } }, - toJsonSchema: ({ type }) => type === "array" ? { maxItems: maxLength } : { maxLength }, + toJsonSchema: ({ type }) => + type === "string" + ? maxLength === 0 ? { maxLength } : [{ maxLength }, true] + : type === "array" + ? { maxItems: maxLength } + : type === undefined + ? [{ maxLength, maxItems: maxLength }, true] + : [{}, true], toCode: () => ({ runtime: `Schema.isMaxLength(${maxLength})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8133,10 +8255,11 @@ export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) * * JSON Schema: * - * For arrays, this check corresponds to `minItems` and `maxItems`. For strings, - * it corresponds to `minLength` and `maxLength`. JavaScript counts UTF-16 code - * units while JSON Schema counts Unicode code points, so the two validations - * can differ for some strings. + * Both bounds must be finite; they are rounded down and clamped to zero. Arrays + * use the same bounds for `minItems` and `maxItems`. JavaScript counts UTF-16 + * code units while JSON Schema counts Unicode code points, so strings use + * `Math.ceil(minimum / 2)` for `minLength` and `maximum` for `maxLength`. These + * are the tightest bounds that cannot reject a string accepted by this check. * * Arbitrary: * @@ -8147,9 +8270,9 @@ export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) * @category validation * @since 4.0.0 */ -export function isLengthBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { - minimum = Math.max(0, Math.floor(minimum)) - maximum = Math.max(0, Math.floor(maximum)) +export function isBetweenLength(minimum: number, maximum: number, annotations?: Annotations.Filter) { + minimum = normalizeCardinality(minimum) + maximum = normalizeCardinality(maximum) return makeFilter<{ readonly length: number }>( (input) => input.length >= minimum && input.length <= maximum, { @@ -8158,14 +8281,25 @@ export function isLengthBetween(minimum: number, maximum: number, annotations?: : `a value with a length between ${minimum} and ${maximum}`, representation: { - id: "effect/schema/isLengthBetween", + id: "effect/schema/isBetweenLength", payload: { minimum, maximum } }, toJsonSchema: ({ type }) => - type === "array" - ? { allOf: [{ minItems: minimum }, { maxItems: maximum }] } - : { allOf: [{ minLength: minimum }, { maxLength: maximum }] }, - toCode: () => ({ runtime: `Schema.isLengthBetween(${minimum}, ${maximum})` }), + type === "string" + ? maximum === 0 || Math.ceil(minimum / 2) > maximum + ? { minLength: Math.ceil(minimum / 2), maxLength: maximum } + : [{ minLength: Math.ceil(minimum / 2), maxLength: maximum }, true] + : type === "array" + ? { minItems: minimum, maxItems: maximum } + : type === undefined + ? [{ + minLength: Math.ceil(minimum / 2), + maxLength: maximum, + minItems: minimum, + maxItems: maximum + }, true] + : [{}, true], + toCode: () => ({ runtime: `Schema.isBetweenLength(${minimum}, ${maximum})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { minLength: minimum, @@ -8175,12 +8309,151 @@ export function isLengthBetween(minimum: number, maximum: number, annotations?: } ) } +/** + * Validates that a string contains at least the specified number of Unicode code points. + * + * **Details** + * + * The bound must be finite; it is rounded down and clamped to zero. This check + * corresponds to `minLength` in JSON Schema and guides arbitrary generation by + * code point count. + * + * **Gotchas** + * + * Code points are not grapheme clusters: combining marks and joined emoji can + * contribute multiple code points to one visible character. Unpaired UTF-16 + * surrogates count as one code point each. This check does not normalize strings. + * + * @see {@link isMinLength} for counting UTF-16 code units + * @see {@link isMaxCodePoints} + * @see {@link isBetweenCodePoints} + * @category validation + * @since 4.0.0 + */ +export function isMinCodePoints(minCodePoints: number, annotations?: Annotations.Filter) { + minCodePoints = normalizeCardinality(minCodePoints) + return makeFilter( + (input) => countCodePointsUpTo(input, minCodePoints) >= minCodePoints, + { + expected: `a string with at least ${minCodePoints} code points`, + representation: { + id: "effect/schema/isMinCodePoints", + payload: { minCodePoints } + }, + toJsonSchema: () => ({ minLength: minCodePoints }), + toCode: () => ({ runtime: `Schema.isMinCodePoints(${minCodePoints})` }), + arbitraryConstraint: { minCodePoints }, + ...annotations + } + ) +} +/** + * Validates that a string contains at most the specified number of Unicode code points. + * + * **Details** + * + * The bound must be finite; it is rounded down and clamped to zero. This check + * corresponds to `maxLength` in JSON Schema and guides arbitrary generation by + * code point count. + * + * **Gotchas** + * + * Code points are not grapheme clusters: combining marks and joined emoji can + * contribute multiple code points to one visible character. Unpaired UTF-16 + * surrogates count as one code point each. This check does not normalize strings. + * + * @see {@link isMaxLength} for counting UTF-16 code units + * @see {@link isMinCodePoints} + * @see {@link isBetweenCodePoints} + * @category validation + * @since 4.0.0 + */ +export function isMaxCodePoints(maxCodePoints: number, annotations?: Annotations.Filter) { + maxCodePoints = normalizeCardinality(maxCodePoints) + return makeFilter( + (input) => countCodePointsUpTo(input, maxCodePoints + 1) <= maxCodePoints, + { + expected: `a string with at most ${maxCodePoints} code points`, + representation: { + id: "effect/schema/isMaxCodePoints", + payload: { maxCodePoints } + }, + toJsonSchema: () => ({ maxLength: maxCodePoints }), + toCode: () => ({ runtime: `Schema.isMaxCodePoints(${maxCodePoints})` }), + arbitraryConstraint: { maxCodePoints }, + ...annotations + } + ) +} +/** + * Validates that a string's Unicode code point count is within the specified inclusive range. + * + * **Details** + * + * Bounds must be finite; they are rounded down and clamped to zero. Equal bounds + * require an exact count. This check corresponds to `minLength` and `maxLength` + * in JSON Schema and guides arbitrary generation by code point count. + * + * **Gotchas** + * + * Code points are not grapheme clusters: combining marks and joined emoji can + * contribute multiple code points to one visible character. Unpaired UTF-16 + * surrogates count as one code point each. This check does not normalize strings. + * + * @see {@link isBetweenLength} for counting UTF-16 code units + * @see {@link isMinCodePoints} + * @see {@link isMaxCodePoints} + * @category validation + * @since 4.0.0 + */ +export function isBetweenCodePoints(minimum: number, maximum: number, annotations?: Annotations.Filter) { + minimum = normalizeCardinality(minimum) + maximum = normalizeCardinality(maximum) + return makeFilter( + (input) => { + const count = countCodePointsUpTo(input, maximum + 1) + return count >= minimum && count <= maximum + }, + { + expected: minimum === maximum + ? `a string with ${minimum} code points` + : `a string with between ${minimum} and ${maximum} code points`, + representation: { + id: "effect/schema/isBetweenCodePoints", + payload: { minimum, maximum } + }, + toJsonSchema: () => ({ allOf: [{ minLength: minimum }, { maxLength: maximum }] }), + toCode: () => ({ runtime: `Schema.isBetweenCodePoints(${minimum}, ${maximum})` }), + arbitraryConstraint: { minCodePoints: minimum, maxCodePoints: maximum }, + ...annotations + } + ) +} + +function countCodePointsUpTo(input: string, limit: number): number { + if (limit === 0) return 0 + let count = 0 + for (const _ of input) { + if (++count >= limit) break + } + return count +} + +function normalizeCardinality(value: number): number { + if (!globalThis.Number.isFinite(value)) { + throw new globalThis.RangeError(`Expected a finite number, got ${value}`) + } + return Math.max(0, Math.floor(value)) +} + /** * Validates that a value has at least the specified size. Works with values * that have a `size` property, such as `Set` or `Map`. * * **Details** * + * The bound must be finite; it is rounded down and clamped to zero. + * * JSON Schema: * * This check does not have a direct JSON Schema equivalent, as it applies to @@ -8194,7 +8467,7 @@ export function isLengthBetween(minimum: number, maximum: number, annotations?: * @since 4.0.0 */ export function isMinSize(minSize: number, annotations?: Annotations.Filter) { - minSize = Math.max(0, Math.floor(minSize)) + minSize = normalizeCardinality(minSize) return makeFilter<{ readonly size: number }>( (input) => input.size >= minSize, { @@ -8203,7 +8476,7 @@ export function isMinSize(minSize: number, annotations?: Annotations.Filter) { id: "effect/schema/isMinSize", payload: { minSize } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isMinSize(${minSize})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8219,6 +8492,8 @@ export function isMinSize(minSize: number, annotations?: Annotations.Filter) { * * **Details** * + * The bound must be finite; it is rounded down and clamped to zero. + * * JSON Schema: * * This check does not have a direct JSON Schema equivalent, as it applies to @@ -8232,7 +8507,7 @@ export function isMinSize(minSize: number, annotations?: Annotations.Filter) { * @since 4.0.0 */ export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { - maxSize = Math.max(0, Math.floor(maxSize)) + maxSize = normalizeCardinality(maxSize) return makeFilter<{ readonly size: number }>( (input) => input.size <= maxSize, { @@ -8241,7 +8516,7 @@ export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { id: "effect/schema/isMaxSize", payload: { maxSize } }, - toJsonSchema: () => ({}), + toJsonSchema: () => [{}, true], toCode: () => ({ runtime: `Schema.isMaxSize(${maxSize})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8257,6 +8532,8 @@ export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { * * **Details** * + * Both bounds must be finite; they are rounded down and clamped to zero. + * * JSON Schema: * * This check does not have a direct JSON Schema equivalent, as it applies to @@ -8269,9 +8546,9 @@ export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { * @category validation * @since 4.0.0 */ -export function isSizeBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { - minimum = Math.max(0, Math.floor(minimum)) - maximum = Math.max(0, Math.floor(maximum)) +export function isBetweenSize(minimum: number, maximum: number, annotations?: Annotations.Filter) { + minimum = normalizeCardinality(minimum) + maximum = normalizeCardinality(maximum) return makeFilter<{ readonly size: number }>( (input) => input.size >= minimum && input.size <= maximum, { @@ -8280,11 +8557,11 @@ export function isSizeBetween(minimum: number, maximum: number, annotations?: An : `a value with a size between ${minimum} and ${maximum}`, representation: { - id: "effect/schema/isSizeBetween", + id: "effect/schema/isBetweenSize", payload: { minimum, maximum } }, - toJsonSchema: () => ({}), - toCode: () => ({ runtime: `Schema.isSizeBetween(${minimum}, ${maximum})` }), + toJsonSchema: () => [{}, true], + toCode: () => ({ runtime: `Schema.isBetweenSize(${minimum}, ${maximum})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { minSize: minimum, @@ -8301,6 +8578,8 @@ export function isSizeBetween(minimum: number, maximum: number, annotations?: An * * **Details** * + * The bound must be finite; it is rounded down and clamped to zero. + * * JSON Schema: * * This check corresponds to `minProperties` in JSON Schema. Effect applies the @@ -8316,7 +8595,7 @@ export function isSizeBetween(minimum: number, maximum: number, annotations?: An * @since 4.0.0 */ export function isMinProperties(minProperties: number, annotations?: Annotations.Filter) { - minProperties = Math.max(0, Math.floor(minProperties)) + minProperties = normalizeCardinality(minProperties) return makeFilter( (input) => Reflect.ownKeys(input).length >= minProperties, { @@ -8325,7 +8604,7 @@ export function isMinProperties(minProperties: number, annotations?: Annotations id: "effect/schema/isMinProperties", payload: { minProperties } }, - toJsonSchema: () => ({ minProperties }), + toJsonSchema: ({ type }) => type === "object" ? { minProperties } : [{ minProperties }, true], toCode: () => ({ runtime: `Schema.isMinProperties(${minProperties})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8341,6 +8620,8 @@ export function isMinProperties(minProperties: number, annotations?: Annotations * * **Details** * + * The bound must be finite; it is rounded down and clamped to zero. + * * JSON Schema: * * This check corresponds to `maxProperties` in JSON Schema. Effect applies the @@ -8356,7 +8637,7 @@ export function isMinProperties(minProperties: number, annotations?: Annotations * @since 4.0.0 */ export function isMaxProperties(maxProperties: number, annotations?: Annotations.Filter) { - maxProperties = Math.max(0, Math.floor(maxProperties)) + maxProperties = normalizeCardinality(maxProperties) return makeFilter( (input) => Reflect.ownKeys(input).length <= maxProperties, { @@ -8365,7 +8646,7 @@ export function isMaxProperties(maxProperties: number, annotations?: Annotations id: "effect/schema/isMaxProperties", payload: { maxProperties } }, - toJsonSchema: () => ({ maxProperties }), + toJsonSchema: ({ type }) => type === "object" ? { maxProperties } : [{ maxProperties }, true], toCode: () => ({ runtime: `Schema.isMaxProperties(${maxProperties})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { @@ -8381,6 +8662,8 @@ export function isMaxProperties(maxProperties: number, annotations?: Annotations * * **Details** * + * Both bounds must be finite; they are rounded down and clamped to zero. + * * JSON Schema: * * This check corresponds to `minProperties` and `maxProperties` in JSON @@ -8395,9 +8678,9 @@ export function isMaxProperties(maxProperties: number, annotations?: Annotations * @category validation * @since 4.0.0 */ -export function isPropertiesLengthBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { - minimum = Math.max(0, Math.floor(minimum)) - maximum = Math.max(0, Math.floor(maximum)) +export function isBetweenProperties(minimum: number, maximum: number, annotations?: Annotations.Filter) { + minimum = normalizeCardinality(minimum) + maximum = normalizeCardinality(maximum) return makeFilter( (input) => Reflect.ownKeys(input).length >= minimum && Reflect.ownKeys(input).length <= maximum, { @@ -8406,11 +8689,14 @@ export function isPropertiesLengthBetween(minimum: number, maximum: number, anno : `a value with between ${minimum} and ${maximum} entries`, representation: { - id: "effect/schema/isPropertiesLengthBetween", + id: "effect/schema/isBetweenProperties", payload: { minimum, maximum } }, - toJsonSchema: () => ({ minProperties: minimum, maxProperties: maximum }), - toCode: () => ({ runtime: `Schema.isPropertiesLengthBetween(${minimum}, ${maximum})` }), + toJsonSchema: ({ type }) => + type === "object" + ? { minProperties: minimum, maxProperties: maximum } + : [{ minProperties: minimum, maxProperties: maximum }, true], + toCode: () => ({ runtime: `Schema.isBetweenProperties(${minimum}, ${maximum})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, arbitraryConstraint: { minProperties: minimum, @@ -8464,7 +8750,8 @@ export function isPropertyNames(keySchema: Constraint, annotations?: Annotations payload: null, schemas: [propertyNames.ast] }, - toJsonSchema: ({ schemas }) => ({ propertyNames: schemas[0] }), + toJsonSchema: ({ schemas, type }) => + type === "object" ? { propertyNames: schemas[0] } : [{ propertyNames: schemas[0] }, true], toCode: ({ schemas }) => ({ runtime: `Schema.isPropertyNames(${schemas[0].runtime})` }), [InternalAnnotations.STRUCTURAL_ANNOTATION_KEY]: true, ...annotations @@ -8576,12 +8863,12 @@ export interface Char extends String { * * @see {@link String} for unconstrained string values * @see {@link NonEmptyString} for strings with length greater than zero - * @see {@link isLengthBetween} for the underlying length check + * @see {@link isBetweenLength} for the underlying length check * * @category schemas * @since 3.10.0 */ -export const Char: Char = String.check(isLengthBetween(1, 1)) +export const Char: Char = String.check(isBetweenLength(1, 1)) /** * Type-level representation of {@link ErrorInstance}. * @@ -9247,7 +9534,7 @@ export const File: File = instanceOf(globalThis.File, { }), SchemaTransformation.transformEffect({ decode: (e, options) => - Result_.match(Encoding.decodeBase64(e.data), { + Result_.match(Base64.decode(e.data), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -9268,7 +9555,7 @@ export const File: File = instanceOf(globalThis.File, { try: async () => { const bytes = new globalThis.Uint8Array(await file.arrayBuffer()) return { - data: Encoding.encodeBase64(bytes), + data: Base64.encode(bytes), type: file.type, name: file.name, lastModified: file.lastModified @@ -10155,50 +10442,6 @@ export interface BigDecimal extends declare { readonly "Rebuild": BigDecimal } const BigDecimalString = String.annotate({ expected: "a string that will be decoded as a BigDecimal" }) -const arbitraryBigDecimalMaxScale = 20 -function bigIntArbitrarySchema(minimum: bigint | undefined, maximum: bigint | undefined): Codec { - if (minimum !== undefined && maximum !== undefined) { - return BigInt.check(isBetweenBigInt({ minimum, maximum })) - } - if (minimum !== undefined) return BigInt.check(isGreaterThanOrEqualToBigInt(minimum)) - if (maximum !== undefined) return BigInt.check(isLessThanOrEqualToBigInt(maximum)) - return BigInt -} -function bigDecimalValueAtScale(value: BigDecimal_.BigDecimal, scale: number): bigint { - return value.value * globalThis.BigInt(10) ** globalThis.BigInt(scale - value.scale) -} -function bigDecimalArbitrarySchema( - constraint: Annotations.ToArbitrary.GenerationConstraint | undefined -): Codec<{ readonly value: bigint; readonly scale: number }> { - if (constraint?.minimum === undefined && constraint?.maximum === undefined) { - return Struct({ - value: BigInt, - scale: Int.check(isBetween({ minimum: 0, maximum: arbitraryBigDecimalMaxScale })) - }) - } - const scale = Math.max( - arbitraryBigDecimalMaxScale, - constraint.minimum?.scale ?? 0, - constraint.maximum?.scale ?? 0, - constraint.exclusiveMinimum === true && constraint.minimum !== undefined ? constraint.minimum.scale + 1 : 0, - constraint.exclusiveMaximum === true && constraint.maximum !== undefined ? constraint.maximum.scale + 1 : 0 - ) - const minimum = constraint.minimum === undefined - ? undefined - : bigDecimalValueAtScale(constraint.minimum, scale) + - (constraint.exclusiveMinimum === true ? globalThis.BigInt(1) : globalThis.BigInt(0)) - const maximum = constraint.maximum === undefined - ? undefined - : bigDecimalValueAtScale(constraint.maximum, scale) - - (constraint.exclusiveMaximum === true ? globalThis.BigInt(1) : globalThis.BigInt(0)) - if (minimum !== undefined && maximum !== undefined && minimum > maximum) { - return Struct({ - value: BigInt, - scale: Int.check(isBetween({ minimum: 0, maximum: arbitraryBigDecimalMaxScale })) - }) - } - return Struct({ value: bigIntArbitrarySchema(minimum, maximum), scale: Literal(scale) }) -} /** * Schema for `BigDecimal` values. * @@ -10231,11 +10474,6 @@ export const BigDecimal: BigDecimal = declare( importDeclarations: [`import * as BigDecimal from "effect/BigDecimal"`] }), expected: "BigDecimal", - toCodecArbitrary: ({ constraint }) => - linkDecoding()( - bigDecimalArbitrarySchema(constraint), - SchemaGetter.transform(({ scale, value }) => BigDecimal_.make(value, scale)) - ), toCodecJson: () => link()( BigDecimalString, @@ -10550,7 +10788,16 @@ export function CauseReason( return Cause_.makeInterruptReason(e.fiberId) } }, - encode: identity + encode: (reason) => { + switch (reason._tag) { + case "Fail": + return { _tag: "Fail" as const, error: reason.error } + case "Die": + return { _tag: "Die" as const, defect: reason.defect } + case "Interrupt": + return { _tag: "Interrupt" as const, fiberId: reason.fiberId } + } + } }) ) } @@ -11359,7 +11606,7 @@ const netAddressFromString = , E extends { readonly messa /** * Type-level representation of {@link MacAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11370,7 +11617,7 @@ export interface MacAddress extends declare { /** * Schema for already-constructed MAC address values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11381,7 +11628,7 @@ export const MacAddress: MacAddress = declare(NetAddress_.isMacAddress, { /** * Type-level representation of {@link MacAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11392,7 +11639,7 @@ export interface MacAddressFromString extends decodeTo { /** * Schema for MAC addresses encoded as canonical colon-separated hexadecimal strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11406,7 +11653,7 @@ export const MacAddressFromString: MacAddressFromString = netAddressFromString( /** * Type-level representation of {@link Ipv4Address}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11417,7 +11664,7 @@ export interface Ipv4Address extends declare { /** * Schema for already-constructed IPv4 address values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11428,7 +11675,7 @@ export const Ipv4Address: Ipv4Address = declare(NetAddress_.isIpv4Address, { /** * Type-level representation of {@link Ipv4AddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11439,7 +11686,7 @@ export interface Ipv4AddressFromString extends decodeTo { /** * Schema for IPv4 addresses encoded as canonical dotted-decimal strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11453,7 +11700,7 @@ export const Ipv4AddressFromString: Ipv4AddressFromString = netAddressFromString /** * Type-level representation of {@link Ipv6Address}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11464,7 +11711,7 @@ export interface Ipv6Address extends declare { /** * Schema for already-constructed IPv6 address values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11475,7 +11722,7 @@ export const Ipv6Address: Ipv6Address = declare(NetAddress_.isIpv6Address, { /** * Type-level representation of {@link Ipv6AddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11486,7 +11733,7 @@ export interface Ipv6AddressFromString extends decodeTo { /** * Schema for IPv6 addresses encoded as canonical strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11500,7 +11747,7 @@ export const Ipv6AddressFromString: Ipv6AddressFromString = netAddressFromString /** * Type-level representation of {@link IpAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11511,7 +11758,7 @@ export interface IpAddress extends declare { /** * Schema for already-constructed IPv4 or IPv6 address values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11522,7 +11769,7 @@ export const IpAddress: IpAddress = declare(NetAddress_.isIpAddress, { /** * Type-level representation of {@link IpAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11533,7 +11780,7 @@ export interface IpAddressFromString extends decodeTo { /** * Schema for IP addresses encoded as canonical numeric strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11544,278 +11791,981 @@ export const IpAddressFromString: IpAddressFromString = netAddressFromString( "IpAddressFromString" ) +const netAddressRefinement = ( + schema: S, + guard: (value: S["Type"]) => value is T, + expected: string, + identifier: string +): refine => refine(guard, { expected })(schema).annotate({ identifier }) + /** - * Type-level representation of {@link Ipv4Interface}. + * Type-level representation of {@link IpMulticastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv4Interface extends declare { - readonly "Rebuild": Ipv4Interface +export interface IpMulticastAddress extends refine, IpAddress> { + readonly "Rebuild": IpMulticastAddress } /** - * Schema for already-constructed IPv4 interface address values. + * Schema for already-constructed multicast IP addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv4Interface: Ipv4Interface = declare(IpInterface_.isIpv4Interface, { - identifier: "Ipv4Interface" -}) +export const IpMulticastAddress: IpMulticastAddress = netAddressRefinement( + IpAddress, + NetAddress_.isMulticast, + "a multicast address", + "IpMulticastAddress" +) /** - * Type-level representation of {@link Ipv4InterfaceFromString}. + * Type-level representation of {@link IpMulticastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv4InterfaceFromString extends decodeTo { - readonly "Rebuild": Ipv4InterfaceFromString +export interface IpMulticastAddressFromString + extends refine, IpAddressFromString> +{ + readonly "Rebuild": IpMulticastAddressFromString } /** - * Schema for IPv4 interface addresses encoded as an address and prefix length. + * Schema for multicast IP addresses encoded as numeric strings. + * + * **Example** (Decoding a validated multicast address) + * + * ```ts import.meta.vitest + * import { assert } from "@effect/vitest" + * import { Schema } from "effect" + * import { NetAddress } from "effect/net" + * + * const address = Schema.decodeUnknownSync(Schema.IpMulticastAddressFromString)("239.255.0.1") + * assert.isTrue(NetAddress.isMulticast(address)) + * ``` * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv4InterfaceFromString: Ipv4InterfaceFromString = netAddressFromString( - Ipv4Interface, - IpInterface_.ipv4FromString, - IpInterface_.format, - "Ipv4InterfaceFromString" +export const IpMulticastAddressFromString: IpMulticastAddressFromString = netAddressRefinement( + IpAddressFromString, + NetAddress_.isMulticast, + "a multicast address", + "IpMulticastAddressFromString" ) /** - * Type-level representation of {@link Ipv6Interface}. + * Type-level representation of {@link MacMulticastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv6Interface extends declare { - readonly "Rebuild": Ipv6Interface +export interface MacMulticastAddress extends refine, MacAddress> { + readonly "Rebuild": MacMulticastAddress } /** - * Schema for already-constructed IPv6 interface address values. + * Schema for already-constructed multicast MAC addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv6Interface: Ipv6Interface = declare(IpInterface_.isIpv6Interface, { - identifier: "Ipv6Interface" -}) +export const MacMulticastAddress: MacMulticastAddress = netAddressRefinement( + MacAddress, + NetAddress_.isMulticast, + "a multicast address", + "MacMulticastAddress" +) /** - * Type-level representation of {@link Ipv6InterfaceFromString}. + * Type-level representation of {@link MacMulticastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv6InterfaceFromString extends decodeTo { - readonly "Rebuild": Ipv6InterfaceFromString +export interface MacMulticastAddressFromString + extends refine, MacAddressFromString> +{ + readonly "Rebuild": MacMulticastAddressFromString } /** - * Schema for IPv6 interface addresses encoded as an address and prefix length. + * Schema for multicast MAC addresses encoded as canonical strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv6InterfaceFromString: Ipv6InterfaceFromString = netAddressFromString( - Ipv6Interface, - IpInterface_.ipv6FromString, - IpInterface_.format, - "Ipv6InterfaceFromString" +export const MacMulticastAddressFromString: MacMulticastAddressFromString = netAddressRefinement( + MacAddressFromString, + NetAddress_.isMulticast, + "a multicast address", + "MacMulticastAddressFromString" ) /** - * Type-level representation of {@link IpInterface}. + * Type-level representation of {@link IpUnicastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface IpInterface extends declare { - readonly "Rebuild": IpInterface +export interface IpUnicastAddress extends refine, IpAddress> { + readonly "Rebuild": IpUnicastAddress } /** - * Schema for already-constructed IPv4 or IPv6 interface address values. + * Schema for already-constructed syntactic unicast IP addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const IpInterface: IpInterface = declare(IpInterface_.isIpInterface, { - identifier: "IpInterface" -}) +export const IpUnicastAddress: IpUnicastAddress = netAddressRefinement( + IpAddress, + NetAddress_.isUnicast, + "a unicast address", + "IpUnicastAddress" +) /** - * Type-level representation of {@link IpInterfaceFromString}. + * Type-level representation of {@link IpUnicastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface IpInterfaceFromString extends decodeTo { - readonly "Rebuild": IpInterfaceFromString +export interface IpUnicastAddressFromString + extends refine, IpAddressFromString> +{ + readonly "Rebuild": IpUnicastAddressFromString } /** - * Schema for IPv4 or IPv6 interface addresses encoded as an address and prefix length. + * Schema for syntactic unicast IP addresses encoded as numeric strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const IpInterfaceFromString: IpInterfaceFromString = netAddressFromString( - IpInterface, - IpInterface_.fromString, - IpInterface_.format, - "IpInterfaceFromString" +export const IpUnicastAddressFromString: IpUnicastAddressFromString = netAddressRefinement( + IpAddressFromString, + NetAddress_.isUnicast, + "a unicast address", + "IpUnicastAddressFromString" ) /** - * Type-level representation of {@link Ipv4Network}. + * Type-level representation of {@link MacUnicastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv4Network extends declare { - readonly "Rebuild": Ipv4Network +export interface MacUnicastAddress extends refine, MacAddress> { + readonly "Rebuild": MacUnicastAddress } /** - * Schema for already-constructed canonical IPv4 network prefixes. + * Schema for already-constructed unicast MAC addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv4Network: Ipv4Network = declare(IpNetwork_.isIpv4Network, { - identifier: "Ipv4Network" -}) +export const MacUnicastAddress: MacUnicastAddress = netAddressRefinement( + MacAddress, + NetAddress_.isMacUnicast, + "a unicast address", + "MacUnicastAddress" +) /** - * Type-level representation of {@link Ipv4NetworkFromString}. + * Type-level representation of {@link MacUnicastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv4NetworkFromString extends decodeTo { - readonly "Rebuild": Ipv4NetworkFromString +export interface MacUnicastAddressFromString + extends refine, MacAddressFromString> +{ + readonly "Rebuild": MacUnicastAddressFromString } /** - * Schema for canonical IPv4 network prefixes encoded in CIDR notation. + * Schema for unicast MAC addresses encoded as canonical strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv4NetworkFromString: Ipv4NetworkFromString = netAddressFromString( - Ipv4Network, - IpNetwork_.ipv4FromString, - IpNetwork_.format, - "Ipv4NetworkFromString" +export const MacUnicastAddressFromString: MacUnicastAddressFromString = netAddressRefinement( + MacAddressFromString, + NetAddress_.isMacUnicast, + "a unicast address", + "MacUnicastAddressFromString" ) /** - * Type-level representation of {@link Ipv6Network}. + * Type-level representation of {@link Ipv4BroadcastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv6Network extends declare { - readonly "Rebuild": Ipv6Network +export interface Ipv4BroadcastAddress + extends refine, Ipv4Address> +{ + readonly "Rebuild": Ipv4BroadcastAddress } /** - * Schema for already-constructed canonical IPv6 network prefixes. + * Schema for the already-constructed IPv4 limited broadcast address. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv6Network: Ipv6Network = declare(IpNetwork_.isIpv6Network, { - identifier: "Ipv6Network" -}) +export const Ipv4BroadcastAddress: Ipv4BroadcastAddress = netAddressRefinement( + Ipv4Address, + NetAddress_.isBroadcast, + "a broadcast address", + "Ipv4BroadcastAddress" +) /** - * Type-level representation of {@link Ipv6NetworkFromString}. + * Type-level representation of {@link Ipv4BroadcastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface Ipv6NetworkFromString extends decodeTo { - readonly "Rebuild": Ipv6NetworkFromString +export interface Ipv4BroadcastAddressFromString + extends refine, Ipv4AddressFromString> +{ + readonly "Rebuild": Ipv4BroadcastAddressFromString } /** - * Schema for canonical IPv6 network prefixes encoded in CIDR notation. + * Schema for the IPv4 limited broadcast address encoded as a string. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const Ipv6NetworkFromString: Ipv6NetworkFromString = netAddressFromString( - Ipv6Network, - IpNetwork_.ipv6FromString, - IpNetwork_.format, - "Ipv6NetworkFromString" +export const Ipv4BroadcastAddressFromString: Ipv4BroadcastAddressFromString = netAddressRefinement( + Ipv4AddressFromString, + NetAddress_.isBroadcast, + "a broadcast address", + "Ipv4BroadcastAddressFromString" ) /** - * Type-level representation of {@link IpNetwork}. + * Type-level representation of {@link MacBroadcastAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface IpNetwork extends declare { - readonly "Rebuild": IpNetwork +export interface MacBroadcastAddress extends refine, MacAddress> { + readonly "Rebuild": MacBroadcastAddress } /** - * Schema for already-constructed canonical IPv4 or IPv6 network prefixes. + * Schema for the already-constructed MAC all-ones broadcast address. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ -export const IpNetwork: IpNetwork = declare(IpNetwork_.isIpNetwork, { - identifier: "IpNetwork" -}) +export const MacBroadcastAddress: MacBroadcastAddress = netAddressRefinement( + MacAddress, + NetAddress_.isMacBroadcast, + "a broadcast address", + "MacBroadcastAddress" +) /** - * Type-level representation of {@link IpNetworkFromString}. + * Type-level representation of {@link MacBroadcastAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ -export interface IpNetworkFromString extends decodeTo { - readonly "Rebuild": IpNetworkFromString +export interface MacBroadcastAddressFromString + extends refine, MacAddressFromString> +{ + readonly "Rebuild": MacBroadcastAddressFromString +} + +/** + * Schema for the MAC all-ones broadcast address encoded as a string. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const MacBroadcastAddressFromString: MacBroadcastAddressFromString = netAddressRefinement( + MacAddressFromString, + NetAddress_.isMacBroadcast, + "a broadcast address", + "MacBroadcastAddressFromString" +) + +/** + * Type-level representation of {@link IpLoopbackAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpLoopbackAddress extends refine, IpAddress> { + readonly "Rebuild": IpLoopbackAddress +} + +/** + * Schema for already-constructed loopback IP addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpLoopbackAddress: IpLoopbackAddress = netAddressRefinement( + IpAddress, + NetAddress_.isLoopback, + "a loopback address", + "IpLoopbackAddress" +) + +/** + * Type-level representation of {@link IpLoopbackAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpLoopbackAddressFromString + extends refine, IpAddressFromString> +{ + readonly "Rebuild": IpLoopbackAddressFromString +} + +/** + * Schema for loopback IP addresses encoded as numeric strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpLoopbackAddressFromString: IpLoopbackAddressFromString = netAddressRefinement( + IpAddressFromString, + NetAddress_.isLoopback, + "a loopback address", + "IpLoopbackAddressFromString" +) + +/** + * Type-level representation of {@link IpLinkLocalAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpLinkLocalAddress extends refine, IpAddress> { + readonly "Rebuild": IpLinkLocalAddress +} + +/** + * Schema for already-constructed link-local IP addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpLinkLocalAddress: IpLinkLocalAddress = netAddressRefinement( + IpAddress, + NetAddress_.isLinkLocal, + "a link-local address", + "IpLinkLocalAddress" +) + +/** + * Type-level representation of {@link IpLinkLocalAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpLinkLocalAddressFromString + extends refine, IpAddressFromString> +{ + readonly "Rebuild": IpLinkLocalAddressFromString +} + +/** + * Schema for link-local IP addresses encoded as numeric strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpLinkLocalAddressFromString: IpLinkLocalAddressFromString = netAddressRefinement( + IpAddressFromString, + NetAddress_.isLinkLocal, + "a link-local address", + "IpLinkLocalAddressFromString" +) + +/** + * Type-level representation of {@link IpUnspecifiedAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpUnspecifiedAddress extends refine, IpAddress> { + readonly "Rebuild": IpUnspecifiedAddress +} + +/** + * Schema for already-constructed unspecified IP addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpUnspecifiedAddress: IpUnspecifiedAddress = netAddressRefinement( + IpAddress, + NetAddress_.isUnspecified, + "an unspecified address", + "IpUnspecifiedAddress" +) + +/** + * Type-level representation of {@link IpUnspecifiedAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpUnspecifiedAddressFromString + extends refine, IpAddressFromString> +{ + readonly "Rebuild": IpUnspecifiedAddressFromString +} + +/** + * Schema for unspecified IP addresses encoded as numeric strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpUnspecifiedAddressFromString: IpUnspecifiedAddressFromString = netAddressRefinement( + IpAddressFromString, + NetAddress_.isUnspecified, + "an unspecified address", + "IpUnspecifiedAddressFromString" +) + +/** + * Type-level representation of {@link Ipv4PrivateAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4PrivateAddress extends refine, Ipv4Address> { + readonly "Rebuild": Ipv4PrivateAddress +} + +/** + * Schema for already-constructed RFC 1918 private-use addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4PrivateAddress: Ipv4PrivateAddress = netAddressRefinement( + Ipv4Address, + NetAddress_.isPrivate, + "a private address", + "Ipv4PrivateAddress" +) + +/** + * Type-level representation of {@link Ipv4PrivateAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4PrivateAddressFromString + extends refine, Ipv4AddressFromString> +{ + readonly "Rebuild": Ipv4PrivateAddressFromString +} + +/** + * Schema for RFC 1918 private-use addresses encoded as strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4PrivateAddressFromString: Ipv4PrivateAddressFromString = netAddressRefinement( + Ipv4AddressFromString, + NetAddress_.isPrivate, + "a private address", + "Ipv4PrivateAddressFromString" +) + +/** + * Type-level representation of {@link Ipv6UniqueLocalAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6UniqueLocalAddress + extends refine, Ipv6Address> +{ + readonly "Rebuild": Ipv6UniqueLocalAddress +} + +/** + * Schema for already-constructed IPv6 unique-local addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6UniqueLocalAddress: Ipv6UniqueLocalAddress = netAddressRefinement( + Ipv6Address, + NetAddress_.isUniqueLocal, + "a unique-local address", + "Ipv6UniqueLocalAddress" +) + +/** + * Type-level representation of {@link Ipv6UniqueLocalAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6UniqueLocalAddressFromString + extends refine, Ipv6AddressFromString> +{ + readonly "Rebuild": Ipv6UniqueLocalAddressFromString +} + +/** + * Schema for IPv6 unique-local addresses encoded as strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6UniqueLocalAddressFromString: Ipv6UniqueLocalAddressFromString = netAddressRefinement( + Ipv6AddressFromString, + NetAddress_.isUniqueLocal, + "a unique-local address", + "Ipv6UniqueLocalAddressFromString" +) + +/** + * Type-level representation of {@link MacLocallyAdministeredAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface MacLocallyAdministeredAddress + extends refine, MacAddress> +{ + readonly "Rebuild": MacLocallyAdministeredAddress +} + +/** + * Schema for already-constructed locally administered MAC addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const MacLocallyAdministeredAddress: MacLocallyAdministeredAddress = netAddressRefinement( + MacAddress, + NetAddress_.isMacLocallyAdministered, + "a locally administered address", + "MacLocallyAdministeredAddress" +) + +/** + * Type-level representation of {@link MacLocallyAdministeredAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface MacLocallyAdministeredAddressFromString + extends refine, MacAddressFromString> +{ + readonly "Rebuild": MacLocallyAdministeredAddressFromString +} + +/** + * Schema for locally administered MAC addresses encoded as strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const MacLocallyAdministeredAddressFromString: MacLocallyAdministeredAddressFromString = netAddressRefinement( + MacAddressFromString, + NetAddress_.isMacLocallyAdministered, + "a locally administered address", + "MacLocallyAdministeredAddressFromString" +) + +/** + * Type-level representation of {@link MacUniversallyAdministeredAddress}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface MacUniversallyAdministeredAddress + extends refine, MacAddress> +{ + readonly "Rebuild": MacUniversallyAdministeredAddress +} + +/** + * Schema for already-constructed universally administered MAC addresses. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const MacUniversallyAdministeredAddress: MacUniversallyAdministeredAddress = netAddressRefinement( + MacAddress, + NetAddress_.isMacUniversallyAdministered, + "a universally administered address", + "MacUniversallyAdministeredAddress" +) + +/** + * Type-level representation of {@link MacUniversallyAdministeredAddressFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface MacUniversallyAdministeredAddressFromString + extends refine, MacAddressFromString> +{ + readonly "Rebuild": MacUniversallyAdministeredAddressFromString +} + +/** + * Schema for universally administered MAC addresses encoded as strings. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const MacUniversallyAdministeredAddressFromString: MacUniversallyAdministeredAddressFromString = + netAddressRefinement( + MacAddressFromString, + NetAddress_.isMacUniversallyAdministered, + "a universally administered address", + "MacUniversallyAdministeredAddressFromString" + ) + +/** + * Type-level representation of {@link Ipv4Interface}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4Interface extends declare { + readonly "Rebuild": Ipv4Interface +} + +/** + * Schema for already-constructed IPv4 interface address values. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4Interface: Ipv4Interface = declare(IpInterface_.isIpv4Interface, { + identifier: "Ipv4Interface" +}) + +/** + * Type-level representation of {@link Ipv4InterfaceFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4InterfaceFromString extends decodeTo { + readonly "Rebuild": Ipv4InterfaceFromString +} + +/** + * Schema for IPv4 interface addresses encoded as an address and prefix length. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4InterfaceFromString: Ipv4InterfaceFromString = netAddressFromString( + Ipv4Interface, + IpInterface_.ipv4FromString, + IpInterface_.format, + "Ipv4InterfaceFromString" +) + +/** + * Type-level representation of {@link Ipv6Interface}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6Interface extends declare { + readonly "Rebuild": Ipv6Interface +} + +/** + * Schema for already-constructed IPv6 interface address values. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6Interface: Ipv6Interface = declare(IpInterface_.isIpv6Interface, { + identifier: "Ipv6Interface" +}) + +/** + * Type-level representation of {@link Ipv6InterfaceFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6InterfaceFromString extends decodeTo { + readonly "Rebuild": Ipv6InterfaceFromString +} + +/** + * Schema for IPv6 interface addresses encoded as an address and prefix length. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6InterfaceFromString: Ipv6InterfaceFromString = netAddressFromString( + Ipv6Interface, + IpInterface_.ipv6FromString, + IpInterface_.format, + "Ipv6InterfaceFromString" +) + +/** + * Type-level representation of {@link IpInterface}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpInterface extends declare { + readonly "Rebuild": IpInterface +} + +/** + * Schema for already-constructed IPv4 or IPv6 interface address values. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpInterface: IpInterface = declare(IpInterface_.isIpInterface, { + identifier: "IpInterface" +}) + +/** + * Type-level representation of {@link IpInterfaceFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpInterfaceFromString extends decodeTo { + readonly "Rebuild": IpInterfaceFromString +} + +/** + * Schema for IPv4 or IPv6 interface addresses encoded as an address and prefix length. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpInterfaceFromString: IpInterfaceFromString = netAddressFromString( + IpInterface, + IpInterface_.fromString, + IpInterface_.format, + "IpInterfaceFromString" +) + +/** + * Type-level representation of {@link Ipv4Network}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4Network extends declare { + readonly "Rebuild": Ipv4Network +} + +/** + * Schema for already-constructed canonical IPv4 network prefixes. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4Network: Ipv4Network = declare(IpNetwork_.isIpv4Network, { + identifier: "Ipv4Network" +}) + +/** + * Type-level representation of {@link Ipv4NetworkFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv4NetworkFromString extends decodeTo { + readonly "Rebuild": Ipv4NetworkFromString +} + +/** + * Schema for canonical IPv4 network prefixes encoded in CIDR notation. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv4NetworkFromString: Ipv4NetworkFromString = netAddressFromString( + Ipv4Network, + IpNetwork_.ipv4FromString, + IpNetwork_.format, + "Ipv4NetworkFromString" +) + +/** + * Type-level representation of {@link Ipv6Network}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6Network extends declare { + readonly "Rebuild": Ipv6Network +} + +/** + * Schema for already-constructed canonical IPv6 network prefixes. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6Network: Ipv6Network = declare(IpNetwork_.isIpv6Network, { + identifier: "Ipv6Network" +}) + +/** + * Type-level representation of {@link Ipv6NetworkFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Ipv6NetworkFromString extends decodeTo { + readonly "Rebuild": Ipv6NetworkFromString +} + +/** + * Schema for canonical IPv6 network prefixes encoded in CIDR notation. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Ipv6NetworkFromString: Ipv6NetworkFromString = netAddressFromString( + Ipv6Network, + IpNetwork_.ipv6FromString, + IpNetwork_.format, + "Ipv6NetworkFromString" +) + +/** + * Type-level representation of {@link IpNetwork}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpNetwork extends declare { + readonly "Rebuild": IpNetwork +} + +/** + * Schema for already-constructed canonical IPv4 or IPv6 network prefixes. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const IpNetwork: IpNetwork = declare(IpNetwork_.isIpNetwork, { + identifier: "IpNetwork" +}) + +/** + * Type-level representation of {@link IpNetworkFromString}. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpNetworkFromString extends decodeTo { + readonly "Rebuild": IpNetworkFromString } /** * Schema for canonical IPv4 or IPv6 network prefixes encoded in CIDR notation. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11829,7 +12779,7 @@ export const IpNetworkFromString: IpNetworkFromString = netAddressFromString( /** * Type-level representation of {@link InetAddressV4}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11840,7 +12790,7 @@ export interface InetAddressV4 extends declare { /** * Schema for already-constructed resolved IPv4 internet addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11851,7 +12801,7 @@ export const InetAddressV4: InetAddressV4 = declare(NetAddress_.isInetAddressV4, /** * Type-level representation of {@link InetAddressV6}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11862,7 +12812,7 @@ export interface InetAddressV6 extends declare { /** * Schema for already-constructed resolved IPv6 internet addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11873,7 +12823,7 @@ export const InetAddressV6: InetAddressV6 = declare(NetAddress_.isInetAddressV6, /** * Type-level representation of {@link InetAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11884,7 +12834,7 @@ export interface InetAddress extends declare { /** * Schema for already-constructed resolved internet addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11895,7 +12845,7 @@ export const InetAddress: InetAddress = declare(NetAddress_.isInetAddress, { /** * Type-level representation of {@link InetAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11906,7 +12856,7 @@ export interface InetAddressFromString extends decodeTo { /** * Schema for resolved internet addresses encoded as numeric socket strings. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11920,7 +12870,7 @@ export const InetAddressFromString: InetAddressFromString = netAddressFromString /** * Type-level representation of {@link UnixPathAddress}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11931,7 +12881,7 @@ export interface UnixPathAddress extends declare { /** * Schema for already-constructed Unix-domain filesystem addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -11943,7 +12893,7 @@ export const UnixPathAddress: UnixPathAddress = declare( /** * Type-level representation of {@link UnixPathAddressFromString}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -11954,7 +12904,7 @@ export interface UnixPathAddressFromString extends decodeTo { /** * Schema for already-constructed portable concrete socket addresses. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -12965,7 +13915,7 @@ export function OptionFromNullishOr( /** * Type-level representation of {@link Cookie}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -12976,7 +13926,7 @@ export interface Cookie extends declare { /** * Schema for HTTP cookie values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -12990,7 +13940,7 @@ export const Cookie: Cookie = declare( toCode: () => ({ runtime: "Schema.Cookie", Type: "Cookies.Cookie", - importDeclarations: [`import * as Cookies from "effect/unstable/http/Cookies"`] + importDeclarations: [`import * as Cookies from "effect/http/Cookies"`] }), expected: "Cookie" } @@ -12999,7 +13949,7 @@ export const Cookie: Cookie = declare( /** * Type-level representation of {@link Cookies}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13020,7 +13970,7 @@ export interface Cookies extends * JSON encoding uses `Set-Cookie` header strings, while isomorphic encoding uses * a readonly record of cookie values. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13034,7 +13984,7 @@ export const Cookies: Cookies = declare( toCode: () => ({ runtime: "Schema.Cookies", Type: "Cookies.Cookies", - importDeclarations: [`import * as Cookies from "effect/unstable/http/Cookies"`] + importDeclarations: [`import * as Cookies from "effect/http/Cookies"`] }), expected: "Cookies", toCodecJson: () => @@ -13059,7 +14009,7 @@ export const Cookies: Cookies = declare( /** * Type-level representation of {@link RecordFromCookies}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13071,7 +14021,7 @@ export interface RecordFromCookies extends decodeTo<$Record, Coo * Schema that decodes HTTP cookies into a record of decoded string values keyed * by cookie name. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13091,7 +14041,7 @@ export const RecordFromCookies: RecordFromCookies = Cookies.pipe( /** * Type-level representation of {@link Headers}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13106,7 +14056,7 @@ export interface Headers extends declare ({ runtime: "Schema.Headers", Type: "Headers.Headers", - importDeclarations: [`import * as Headers from "effect/unstable/http/Headers"`] + importDeclarations: [`import * as Headers from "effect/http/Headers"`] }), expected: "Headers", toEquivalence: () => Headers_.Equivalence, @@ -13138,7 +14088,7 @@ export const Headers: Headers = declare( /** * Type-level representation of {@link UrlParams}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13158,7 +14108,7 @@ export interface UrlParams extends * * The encoded representation is an array of string key-value tuples. * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13172,7 +14122,7 @@ export const UrlParams: UrlParams = declare( toCode: () => ({ runtime: "Schema.UrlParams", Type: "UrlParams.UrlParams", - importDeclarations: [`import * as UrlParams from "effect/unstable/http/UrlParams"`] + importDeclarations: [`import * as UrlParams from "effect/http/UrlParams"`] }), expected: "UrlParams", toEquivalence: () => UrlParams_.Equivalence, @@ -13190,7 +14140,7 @@ export const UrlParams: UrlParams = declare( /** * Type-level representation of {@link JsonFromUrlParamsField}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13206,7 +14156,7 @@ export interface JsonFromUrlParamsField extends decodeTo * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { UrlParams } from "effect/unstable/http" + * import { UrlParams } from "effect/http" * * const extractFoo = Schema.JsonFromUrlParamsField("foo").pipe( * Schema.decodeTo(Schema.Struct({ @@ -13222,7 +14172,7 @@ export interface JsonFromUrlParamsField extends decodeTo * const result = [decoded.some, decoded.number] // => ["bar", 42] * ``` * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13247,7 +14197,7 @@ export const JsonFromUrlParamsField = ( /** * Type-level representation of {@link RecordFromUrlParams}. * - * @unstable + * @stability unstable * @category models * @since 4.0.0 */ @@ -13274,7 +14224,7 @@ export interface RecordFromUrlParams extends * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { UrlParams } from "effect/unstable/http" + * import { UrlParams } from "effect/http" * * const toStruct = Schema.RecordFromUrlParams.pipe( * Schema.decodeTo(Schema.Struct({ @@ -13290,7 +14240,7 @@ export interface RecordFromUrlParams extends * const result = [decoded.some, decoded.number] // => ["value", 42] * ``` * - * @unstable + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -13481,7 +14431,7 @@ export function Redacted(value: S, options?: { Effect.mapBothEager( SchemaParser.decodeUnknownEffect(value)(Redacted_.value(input), poptions), { - onSuccess: () => input, + onSuccess: (value) => Redacted_.make(value, { label: input.label }), onFailure: () => { return new SchemaIssue.Composite( ast, @@ -14318,6 +15268,8 @@ export function toEquivalence(schema: Schema): Equivalence.Equivalence * Use {@link toType} before this function to represent the type side instead. * The optional reference policy controls which candidates are extracted into the document's reference table. By * default, only candidates with a resolved identifier become references; recursive candidates always require one. + * TypeScript-only distinctions such as those added by {@link brand} are not + * part of the schema AST and are therefore omitted. * * @see {@link SchemaRepresentation.toRepresentation} for converting a `SchemaAST.AST` directly * @@ -14424,6 +15376,11 @@ export interface ToJsonSchemaOptions extends SchemaRepresentation.ToRepresentati * * **Details** * + * The document describes the encoded side of `Schema.toCodecJson(schema)`. + * Use that codec to decode JSON inputs. For example, it decodes JSON `null` + * to JavaScript `undefined` for a field defined with `Schema.optional(Schema.String)`. + * Decoding the same input with the original schema rejects `null`. + * * The `options` parameter controls reference extraction and generation details * such as excess properties and synthesized check descriptions; it does not * change the draft target. The reference policy receives canonical JSON @@ -14442,19 +15399,40 @@ export interface ToJsonSchemaOptions extends SchemaRepresentation.ToRepresentati * JSON Schema generation is best-effort. String length uses Unicode code points * in JSON Schema and UTF-16 code units in Effect. A generated `pattern` cannot * retain JavaScript RegExp flags. Object property checks apply to the original - * input in JSON Schema but to the decoded object in Effect. `oneOf` can also - * reject values accepted by overlapping Effect union members. Custom - * `toJsonSchema` annotations are the annotation author's responsibility. When + * input in JSON Schema but to the decoded object in Effect. When a `oneOf` + * branch contains a known approximation, the compiler emits `anyOf` so that + * the approximation cannot create a false rejection. Unions with only exact + * branches retain `oneOf`. Custom `toJsonSchema` callbacks return `[schema, true]` for safe, + * looser approximations and are responsible for the semantics they declare. When * canonical JSON derivation adds an artificial transformation, checks and * annotations on its source node are not copied to the JSON target, so they do * not appear in the emitted document. Opaque declarations without a structural * codec are represented by an unconstrained JSON Schema. The default * `onExcessProperty: "ignore"` matches the decoder default and leaves - * unrepresentable index-signature keys open. In `"error"` mode, the compiler + * index-signature keys without exact selectors open. In `"error"` mode, the compiler * constrains those keys with `propertyNames` and applies a permissive choice of * candidate index value schemas. The Effect decoder enforces the exact * key-value association. * + * **Example** (Decoding JSON with the matching codec) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * + * const schema = Schema.Struct({ + * name: Schema.optional(Schema.String) + * }) + * + * const document = Schema.toJsonSchemaDocument(schema) + * const jsonCodec = Schema.toCodecJson(schema) + * + * Schema.decodeUnknownResult(schema)({ name: null })._tag // => "Failure" + * Schema.decodeUnknownSync(jsonCodec)({ name: null }) // => { name: undefined } + * Schema.decodeUnknownSync(jsonCodec)({}) // => {} + * Schema.encodeSync(jsonCodec)({ name: undefined }) // => { name: null } + * ``` + * + * @see {@link toCodecJson} for decoding and encoding the canonical JSON representation * @see {@link SchemaRepresentation.toJsonSchemaDocument} for compiling an existing live representation document * * @category converting @@ -14774,7 +15752,7 @@ export function overrideToCodecIso( return (schema: S): overrideToCodecIso => { return make( SchemaAST.annotate(schema.ast, { - toCodecIso: () => new SchemaAST.Link(to.ast, SchemaTransformation.make(transformation)) + toCodecIso: () => new SchemaAST.Link(to.ast, SchemaTransformation.makeTransformation(transformation)) }), { schema } ) @@ -15095,8 +16073,8 @@ export declare namespace Annotations { } /** * Base annotations shared by all composite schema nodes. Extends - * {@link Documentation} with error messages, branding, and arbitrary - * generation hooks. {@link Declaration} and other annotation + * {@link Documentation} with error messages and arbitrary generation hooks. + * {@link Declaration} and other annotation * interfaces build on top of this. * * @category models @@ -15134,10 +16112,6 @@ export declare namespace Annotations { * filter/refinement instead. */ readonly identifier?: string | undefined - /** - * Accumulated brands when multiple brands are added with `Schema.brand`. - */ - readonly brands?: ReadonlyArray | undefined } /** * Helpers for projecting declaration type-parameter schemas into decoded or @@ -15260,11 +16234,21 @@ export declare namespace Annotations { /** * Compiles this filter to a JSON Schema fragment. * + * **Details** + * + * Return the fragment directly for an exact translation or `[fragment, true]` + * for a safe, looser approximation. Use `[{}, true]` to omit the constraint. + * Approximation propagates from `representation.schemas` automatically. It + * makes enclosing `oneOf` unions export as `anyOf` and prevents approximate + * record-key patterns from selecting `patternProperties` values. + * * **Gotchas** * - * Treat the input schemas as immutable. The returned value must be a valid JSON Schema object graph and must not be - * mutated after this function returns. Return a new object graph to produce different output during a later - * compilation. + * The compiler trusts the declared semantics. Treat the input schemas as immutable. The returned fragment must be + * a valid JSON Schema object graph and must not be mutated after this function returns. Return a new object graph + * to produce different output during a later compilation. + * + * @see {@link SchemaRepresentation.ToJsonSchema.CheckOutput} for the result contract */ readonly toJsonSchema?: SchemaRepresentation.ToJsonSchema.Check | undefined readonly toCode?: SchemaRepresentation.Generation.Check | undefined @@ -15345,6 +16329,8 @@ export declare namespace Annotations { readonly exclusiveMaximum?: true | undefined readonly minLength?: number | undefined readonly maxLength?: number | undefined + readonly minCodePoints?: number | undefined + readonly maxCodePoints?: number | undefined readonly minSize?: number | undefined readonly maxSize?: number | undefined readonly minProperties?: number | undefined diff --git a/repos/effect/packages/effect/src/SchemaAST.ts b/repos/effect/packages/effect/src/SchemaAST.ts index 0b6a455c16..e9eaae8506 100644 --- a/repos/effect/packages/effect/src/SchemaAST.ts +++ b/repos/effect/packages/effect/src/SchemaAST.ts @@ -501,6 +501,11 @@ export interface ParseOptions { * to structs, records, and structs with rest. Values must satisfy every * applicable index signature. Empty structs keep their non-nullish behavior. * + * An excess property is an enumerable own property that is not covered by a + * declared property or an index signature. Non-enumerable own properties, + * such as `Error#stack`, are ignored unless explicitly declared by the + * schema. Index signatures likewise select only enumerable own properties. + * * @default "ignore" */ readonly onExcessProperty?: "ignore" | "error" | undefined @@ -579,8 +584,8 @@ export const defaultParseOptions: ParseOptions = {} * * - `isOptional` — the property key may be absent from the input. * - `isMutable` — the property is `readonly` when `false`. - * - `constructorDefault` — a {@link Link} applied during construction to - * supply missing values. + * - `constructorDefault` — an effect evaluated during construction to supply + * missing values. * - `annotations` — key-level annotations (e.g. description of the key * itself). * @@ -593,7 +598,7 @@ export interface Context { readonly isOptional: boolean readonly isMutable: boolean /** Used for constructor default values (e.g. `withConstructorDefault` API) */ - readonly constructorDefault: Link | undefined + readonly constructorDefault: Effect.Effect | undefined readonly annotations: Schema.Annotations.Key | undefined } @@ -606,20 +611,20 @@ export interface Context { export const Context: new( isOptional: boolean, isMutable: boolean, /** Used for constructor default values (e.g. `withConstructorDefault` API) */ - constructorDefault?: Link | undefined, + constructorDefault?: Effect.Effect | undefined, annotations?: Schema.Annotations.Key | undefined ) => Context = class { readonly isOptional: boolean readonly isMutable: boolean /** Used for constructor default values (e.g. `withConstructorDefault` API) */ - readonly constructorDefault: Link | undefined + readonly constructorDefault: Effect.Effect | undefined readonly annotations: Schema.Annotations.Key | undefined constructor( isOptional: boolean, isMutable: boolean, /** Used for constructor default values (e.g. `withConstructorDefault` API) */ - constructorDefault: Link | undefined = undefined, + constructorDefault: Effect.Effect | undefined = undefined, annotations: Schema.Annotations.Key | undefined = undefined ) { this.isOptional = isOptional @@ -2222,7 +2227,10 @@ export interface Arrays extends ASTNode { readonly encodingChecks: Checks | undefined /** @internal */ - getParser(compile: SchemaParser.Compiler, compileConstructorDefault?: SchemaParser.Compiler): SchemaParser.Parser + getParser( + compile: SchemaParser.Compiler, + compileField?: SchemaParser.Compiler + ): SchemaParser.Parser /** @internal */ recur(recur: (ast: AST) => AST): Arrays @@ -2294,7 +2302,7 @@ export const Arrays: new( /** @internal */ getParser( compile: SchemaParser.Compiler, - compileConstructorDefault: SchemaParser.Compiler = compile + compileField: SchemaParser.Compiler = compile ): SchemaParser.Parser { // oxlint-disable-next-line @typescript-eslint/no-this-alias const ast = this @@ -2316,29 +2324,39 @@ export const Arrays: new( return rest![0] } - return Effect.fnUntracedEager(function*(input, options) { - if (input === InternalParser.missing) { - return InternalParser.missing - } - - // If the input is not an array, return early with an error - if (!Array.isArray(input)) { - return yield* Effect.fail(new SchemaIssue.InvalidType(ast, input, options)) + const finish = (state: ArrayParserState): Effect.Effect => { + const { input, len, options } = state + if (ast.rest.length === 0 && len > elementLen) { + for (let i = elementLen; i < len; i++) { + const unexpected = new SchemaIssue.UnexpectedKey(ast, input[i], options) + const issue = new SchemaIssue.Pointer([i], unexpected) + if (options.errors === "all") { + if (state.issues) state.issues.push(issue) + else state.issues = [issue] + } else { + return Effect.fail(new SchemaIssue.Composite(ast, [issue], input, options)) + } + } } - if (!elements) { - elements = ast.elements.map((ast) => ({ ast, parser: compileConstructorDefault(ast) })) - rest = ast.rest.map((ast) => ({ ast, parser: compileConstructorDefault(ast) })) + if (state.issues) { + return Effect.fail(new SchemaIssue.Composite(ast, state.issues, input, options)) } + return InternalParser.succeed(state.output) + } + const parse = ( + input: ReadonlyArray, + options: ParseOptions + ): Effect.Effect => { const len = input.length - const state = { + const state: ArrayParserState = { ast, getParser, input, len, tailThreshold: Math.max(elementLen, len - tailLen), output: new globalThis.Array(len), - issues: undefined as Arr.NonEmptyArray | undefined, + issues: undefined, options } const end = ast.rest.length === 0 ? elementLen : Math.max(len, elementLen + tailLen) @@ -2346,32 +2364,32 @@ export const Arrays: new( const eff = concurrency === 1 ? parseArray(state, input, 0, end) : parseArrayConcurrent(state, input, { concurrency, end }) - if (eff) yield* eff + if (!eff) return finish(state) + if (effectIsExit(eff)) return Effect.flatMapEager(eff, () => finish(state)) + // Reparse on later runs to avoid reusing mutable traversal state. + let first = true + return Effect.suspend(() => { + if (!first) return parse(input, options) + first = false + return Effect.flatMap(eff, () => finish(state)) + }) + } - // --------------------------------------------- - // handle excess indexes - // --------------------------------------------- - if (ast.rest.length === 0 && len > elementLen) { - for (let i = elementLen; i <= len - 1; i++) { - const unexpected = new SchemaIssue.UnexpectedKey(ast, input[i], options) - const issue = new SchemaIssue.Pointer([i], unexpected) - if (options.errors === "all") { - if (state.issues) state.issues.push(issue) - else state.issues = [issue] - } else { - return yield* Effect.fail( - new SchemaIssue.Composite(ast, [issue], input, options) - ) - } + return (input, options) => { + if (input === InternalParser.missing) return InternalParser.missingExit + try { + if (!Array.isArray(input)) { + return Effect.fail(new SchemaIssue.InvalidType(ast, input, options)) } + if (!elements) { + elements = ast.elements.map((ast) => ({ ast, parser: compileField(ast) })) + rest = ast.rest.map((ast) => ({ ast, parser: compileField(ast) })) + } + return parse(input, options) + } catch (error) { + return Effect.die(error) } - if (state.issues) { - return yield* Effect.fail( - new SchemaIssue.Composite(ast, state.issues, input, options) - ) - } - return state.output - }) + } } private _rebuild(recur: (ast: AST) => AST, checks: Checks | undefined, encodingChecks: Checks | undefined) { const elements = mapOrSame(this.elements, recur) @@ -2403,9 +2421,10 @@ export const Arrays: new( return "array" } } + type ArrayParserState = { readonly ast: AST - readonly input: unknown + readonly input: ReadonlyArray readonly len: number readonly getParser: ( tailThreshold: number, @@ -2414,7 +2433,37 @@ type ArrayParserState = { readonly tailThreshold: number readonly options: ParseOptions readonly output: Array - issues: Array | undefined + issues: Arr.NonEmptyArray | undefined +} + +/** @internal */ +export function stepArray( + s: ArrayParserState, + item: unknown, + exit: Exit.Exit, + i: number +) { + if (exit._tag === "Failure") { + return wrapPropertyKeyIssue(s, s.ast, i, exit) + } + const value = exit === InternalParser.sameExit + ? item + : (exit as InternalParser.Success)[InternalParser.args] + if (value !== InternalParser.missing) { + s.output[i] = value + } else { + const p = s.getParser(s.tailThreshold, i) + if (isOptional(p.ast)) return + const issue = new SchemaIssue.Pointer([i], new SchemaIssue.MissingKey(p.ast.context?.annotations)) + if (s.options.errors === "all") { + if (s.issues) s.issues.push(issue) + else s.issues = [issue] + } else { + return Exit.fail( + new SchemaIssue.Composite(s.ast, [issue], s.input, s.options) + ) + } + } } const parseArrayOptions = { @@ -2422,32 +2471,11 @@ const parseArrayOptions = { const value = i < s.len ? item : InternalParser.missing return s.getParser(s.tailThreshold, i).parser(value, s.options) }, - step(s: ArrayParserState, item: unknown, exit: Exit.Exit, i: number) { - if (exit._tag === "Failure") { - return wrapPropertyKeyIssue(s, s.ast, i, exit) - } - const value = exit === InternalParser.sameExit - ? item - : (exit as InternalParser.Success)[InternalParser.args] - if (value !== InternalParser.missing) { - s.output[i] = value - } else { - const p = s.getParser(s.tailThreshold, i) - if (isOptional(p.ast)) return - const issue = new SchemaIssue.Pointer([i], new SchemaIssue.MissingKey(p.ast.context?.annotations)) - if (s.options.errors === "all") { - if (s.issues) s.issues.push(issue) - else s.issues = [issue] - } else { - return Exit.fail( - new SchemaIssue.Composite(s.ast, [issue], s.input, s.options) - ) - } - } - } + step: stepArray } -const parseArray = iterateEager()(parseArrayOptions) +/** @internal */ +export const parseArray = iterateEager()(parseArrayOptions) const parseArrayConcurrent = iterateConcurrent()(parseArrayOptions) const wrapPropertyKeyIssue = ( @@ -2516,7 +2544,7 @@ export function getIndexSignatureKeys( return (stringKeys ??= Object.keys(input)).filter((k) => parameter.matchKey(k, options) !== undefined) case "Symbol": return (symbolKeys ??= Object.getOwnPropertySymbols(input)).filter((k) => - parameter.matchKey(k, options) !== undefined + Object.prototype.propertyIsEnumerable.call(input, k) && parameter.matchKey(k, options) !== undefined ) case "Union": return [...new Set(parameter.types.flatMap(go))] @@ -2707,7 +2735,10 @@ export interface Objects extends ASTNode { readonly encodingChecks: Checks | undefined /** @internal */ - getParser(compile: SchemaParser.Compiler, compileConstructorDefault?: SchemaParser.Compiler): SchemaParser.Parser + getParser( + compile: SchemaParser.Compiler, + compileField?: SchemaParser.Compiler + ): SchemaParser.Parser /** @internal */ flip(recur: (ast: AST) => AST): AST @@ -2752,36 +2783,16 @@ export const Objects: new( this.propertySignatures = propertySignatures this.indexSignatures = indexSignatures this.encodingChecks = encodingChecks - - // Duplicate property signatures - const seen = new Set() - const duplicates: Array = [] - for (const propertySignature of propertySignatures) { - const name = propertySignature.name - if (seen.has(name)) { - duplicates.push(name) - } else { - seen.add(name) - } - } - if (duplicates.length > 0) { - throw new Error(`Duplicate identifiers: ${JSON.stringify(duplicates)}. ts(2300)`) - } } /** @internal */ getParser( compile: SchemaParser.Compiler, - compileConstructorDefault: SchemaParser.Compiler = compile + compileField: SchemaParser.Compiler = compile ): SchemaParser.Parser { // oxlint-disable-next-line @typescript-eslint/no-this-alias const ast = this - const expectedKeys: Array = [] - for (const ps of ast.propertySignatures) { - expectedKeys.push(typeof ps.name === "number" ? globalThis.String(ps.name) : ps.name) - } - const hasProperties = expectedKeys.length + const hasProperties = ast.propertySignatures.length const indexCount = ast.indexSignatures.length - let expectedKeysSet = hasProperties && indexCount ? new Set(expectedKeys) : undefined // --------------------------------------------- // handle empty struct // --------------------------------------------- @@ -2798,75 +2809,10 @@ export const Objects: new( }> | undefined type Index = NonNullable[number] - const finishIndex = ( - s: ObjectParserState, - key: PropertyKey, - k2: PropertyKey | typeof InternalParser.missing, - inputValue: unknown, - exitValue: Exit.Exit - ): Effect.Effect => { - if (exitValue._tag === "Failure") { - return wrapPropertyKeyIssue(s, ast, key, exitValue) ?? Exit.void - } - const value = exitValue === InternalParser.sameExit - ? inputValue - : (exitValue as InternalParser.Success)[InternalParser.args] - if (k2 !== InternalParser.missing && value !== InternalParser.missing) { - if ( - hasProperties && - (expectedKeysSet!.has(key) || expectedKeysSet!.has(typeof k2 === "number" ? globalThis.String(k2) : k2)) - ) return Exit.void - InternalRecord.assignProperty(s.out, k2, value) - } - return Exit.void - } - const parseIndex = ( - s: ObjectParserState, - key: PropertyKey, - index: Index, - exitKey?: Exit.Exit - ): Effect.Effect => { - if (!exitKey) { - const eff = index.parserKey(key, s.options) - if (!effectIsExit(eff)) { - return Effect.flatMap(Effect.exit(eff), (exit) => parseIndex(s, key, index, exit)) - } - exitKey = eff - } - if (exitKey._tag === "Failure") { - return wrapPropertyKeyIssue(s, ast, key, exitKey) ?? Exit.void - } - const k2 = exitKey === InternalParser.sameExit - ? key - : (exitKey as InternalParser.Success)[InternalParser.args] - const inputValue = s.input[key] - const result = index.parserValue(inputValue, s.options) - return effectIsExit(result) - ? finishIndex(s, key, k2, inputValue, result) - : Effect.flatMap(Effect.exit(result), (exit) => finishIndex(s, key, k2, inputValue, exit)) - } - const parseStringIndex = ( - s: ObjectParserState, - key: PropertyKey, - index: Index - ): Effect.Effect => { - const inputValue = s.input[key] - const result = index.parserValue(inputValue, s.options) - return effectIsExit(result) - ? finishIndex(s, key, key, inputValue, result) - : Effect.flatMap(Effect.exit(result), (exit) => finishIndex(s, key, key, inputValue, exit)) - } - const parseIndexes = indexCount - ? iterateConcurrent()({ - onItem: (s, [key, index]) => - index.is.parameter === string ? parseStringIndex(s, key, index) : parseIndex(s, key, index), - step: (_s, _item, exit) => exit._tag === "Failure" ? exit : undefined - }) - : undefined const compileMembers = (): Array => { if (!properties) { properties = ast.propertySignatures.map((ps) => ({ - parser: compileConstructorDefault(ps.type), + parser: compileField(ps.type), name: ps.name, type: ps.type })) @@ -2874,124 +2820,197 @@ export const Objects: new( ? ast.indexSignatures.map((is) => ({ is, parserKey: compile(parameterFromPropertyKey(is.parameter)), - parserValue: compileConstructorDefault(is.type) + parserValue: compileField(is.type) })) : undefined } return properties } - const fallback: SchemaParser.Parser = Effect.fnUntracedEager(function*(input, options) { - if (input === InternalParser.missing) { - return InternalParser.missing + const makeFallback = (): SchemaParser.Parser => { + const expectedKeys = new Set( + ast.propertySignatures.map((ps) => typeof ps.name === "number" ? globalThis.String(ps.name) : ps.name) + ) + const finishIndex = ( + s: ObjectParserState, + key: PropertyKey, + k2: PropertyKey | typeof InternalParser.missing, + inputValue: unknown, + exitValue: Exit.Exit + ): Effect.Effect => { + if (exitValue._tag === "Failure") { + return wrapPropertyKeyIssue(s, ast, key, exitValue) ?? Exit.void + } + const value = exitValue === InternalParser.sameExit + ? inputValue + : (exitValue as InternalParser.Success)[InternalParser.args] + if (k2 !== InternalParser.missing && value !== InternalParser.missing) { + if ( + hasProperties && + (expectedKeys.has(key) || expectedKeys.has(typeof k2 === "number" ? globalThis.String(k2) : k2)) + ) return Exit.void + InternalRecord.assignProperty(s.out, k2, value) + } + return Exit.void } - - // If the input is not a record, return early with an error - if (!(typeof input === "object" && input !== null && !Array.isArray(input))) { - return yield* Effect.fail(new SchemaIssue.InvalidType(ast, input, options)) + const parseIndex = ( + s: ObjectParserState, + key: PropertyKey, + index: Index, + exitKey?: Exit.Exit + ): Effect.Effect => { + if (!exitKey) { + const eff = index.parserKey(key, s.options) + if (!effectIsExit(eff)) { + return Effect.flatMap(Effect.exit(eff), (exit) => parseIndex(s, key, index, exit)) + } + exitKey = eff + } + if (exitKey._tag === "Failure") { + return wrapPropertyKeyIssue(s, ast, key, exitKey) ?? Exit.void + } + const k2 = exitKey === InternalParser.sameExit + ? key + : (exitKey as InternalParser.Success)[InternalParser.args] + const inputValue = s.input[key] + const result = index.parserValue(inputValue, s.options) + return effectIsExit(result) + ? finishIndex(s, key, k2, inputValue, result) + : Effect.flatMap(Effect.exit(result), (exit) => finishIndex(s, key, k2, inputValue, exit)) } - compileMembers() - - const record = input as Record - const out: Record = {} - const state = { - ast, - input: record, - out, - issues: undefined as Arr.NonEmptyArray | undefined, - options + const parseStringIndex = ( + s: ObjectParserState, + key: PropertyKey, + index: Index + ): Effect.Effect => { + const inputValue = s.input[key] + const result = index.parserValue(inputValue, s.options) + return effectIsExit(result) + ? finishIndex(s, key, key, inputValue, result) + : Effect.flatMap(Effect.exit(result), (exit) => finishIndex(s, key, key, inputValue, exit)) } - const errorsAllOption = options.errors === "all" - const onExcessPropertyError = options.onExcessProperty === "error" - const concurrency = options.concurrency === undefined ? 1 : resolveConcurrency(options.concurrency) - - // --------------------------------------------- - // handle excess properties - // --------------------------------------------- - const indexKeys = indexCount && onExcessPropertyError - ? ast.indexSignatures.map((index) => getIndexSignatureKeys(record, index.parameter, options)) + const parseIndexes = indexCount + ? iterateConcurrent()({ + onItem: (s, [key, index]) => + index.is.parameter === string ? parseStringIndex(s, key, index) : parseIndex(s, key, index), + step: (_s, _item, exit) => exit._tag === "Failure" ? exit : undefined + }) : undefined - if (onExcessPropertyError) { - expectedKeysSet ??= new Set(expectedKeys) - const coveredKeys = indexKeys ? new Set(expectedKeysSet) : expectedKeysSet - if (indexKeys) { - for (const keys of indexKeys) { - for (const key of keys) coveredKeys.add(key) - } + return Effect.fnUntracedEager(function*(input, options) { + if (input === InternalParser.missing) { + return InternalParser.missing + } + + // If the input is not a record, return early with an error + if (!(typeof input === "object" && input !== null && !Array.isArray(input))) { + return yield* Effect.fail(new SchemaIssue.InvalidType(ast, input, options)) } - const inputKeys = Reflect.ownKeys(record) - for (let i = 0; i < inputKeys.length; i++) { - const key = inputKeys[i] - if (!coveredKeys.has(key)) { - // key is unexpected - const unexpected = new SchemaIssue.UnexpectedKey(ast, record[key], options) - const issue = new SchemaIssue.Pointer([key], unexpected) - if (errorsAllOption) { - if (state.issues) { - state.issues.push(issue) + compileMembers() + + const record = input as Record + const out: Record = {} + const state = { + ast, + input: record, + out, + issues: undefined as Arr.NonEmptyArray | undefined, + options + } + const errorsAllOption = options.errors === "all" + const onExcessPropertyError = options.onExcessProperty === "error" + const concurrency = options.concurrency === undefined ? 1 : resolveConcurrency(options.concurrency) + + // --------------------------------------------- + // handle excess properties + // --------------------------------------------- + const indexKeys = indexCount && onExcessPropertyError + ? ast.indexSignatures.map((index) => getIndexSignatureKeys(record, index.parameter, options)) + : undefined + if (onExcessPropertyError) { + const coveredKeys = indexKeys ? new Set(expectedKeys) : expectedKeys + if (indexKeys) { + for (const keys of indexKeys) { + for (const key of keys) coveredKeys.add(key) + } + } + // Only enumerable own properties can be excess. Declared fields are + // parsed separately, regardless of their enumerability. + const inputKeys = Reflect.ownKeys(record) + for (let i = 0; i < inputKeys.length; i++) { + const key = inputKeys[i] + if (!coveredKeys.has(key) && Object.prototype.propertyIsEnumerable.call(record, key)) { + // key is unexpected + const unexpected = new SchemaIssue.UnexpectedKey(ast, record[key], options) + const issue = new SchemaIssue.Pointer([key], unexpected) + if (errorsAllOption) { + if (state.issues) { + state.issues.push(issue) + } else { + state.issues = [issue] + } + continue } else { - state.issues = [issue] + return yield* Effect.fail( + new SchemaIssue.Composite(ast, [issue], input, options) + ) } - continue - } else { - return yield* Effect.fail( - new SchemaIssue.Composite(ast, [issue], input, options) - ) } } } - } - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - if (hasProperties) { - const eff = concurrency === 1 - ? parseProperties(state, properties!) - : parsePropertiesConcurrent(state, properties!, { concurrency }) - if (eff) yield* eff - } + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + if (hasProperties) { + const eff = concurrency === 1 + ? parseProperties(state, properties!) + : parsePropertiesConcurrent(state, properties!, { concurrency }) + if (eff) yield* eff + } - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - if (indexCount && concurrency === 1) { - for (let i = 0; i < indexCount; i++) { - const index = indexes![i] - const parse = index.is.parameter === string ? parseStringIndex : parseIndex - const keys = indexKeys?.[i] ?? (index.is.parameter === string - ? Object.keys(record) - : getIndexSignatureKeys(record, index.is.parameter, options)) - for (let j = 0; j < keys.length; j++) { - const eff = parse(state, keys[j], index) - if (!effectIsExit(eff)) yield* eff - else if (eff._tag === "Failure") return yield* eff as Exit.Exit + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + if (indexCount && concurrency === 1) { + for (let i = 0; i < indexCount; i++) { + const index = indexes![i] + const parse = index.is.parameter === string ? parseStringIndex : parseIndex + const keys = indexKeys?.[i] ?? (index.is.parameter === string + ? Object.keys(record) + : getIndexSignatureKeys(record, index.is.parameter, options)) + for (let j = 0; j < keys.length; j++) { + const eff = parse(state, keys[j], index) + if (!effectIsExit(eff)) yield* eff + else if (eff._tag === "Failure") return yield* eff as Exit.Exit + } } - } - } else if (parseIndexes) { - const keyPairs = Arr.empty() - for (let i = 0; i < indexCount; i++) { - const index = indexes![i] - const keys = indexKeys?.[i] ?? (index.is.parameter === string - ? Object.keys(record) - : getIndexSignatureKeys(record, index.is.parameter, options)) - for (let j = 0; j < keys.length; j++) { - keyPairs.push([keys[j], index]) + } else if (parseIndexes) { + const keyPairs = Arr.empty() + for (let i = 0; i < indexCount; i++) { + const index = indexes![i] + const keys = indexKeys?.[i] ?? (index.is.parameter === string + ? Object.keys(record) + : getIndexSignatureKeys(record, index.is.parameter, options)) + for (let j = 0; j < keys.length; j++) { + keyPairs.push([keys[j], index]) + } } + const eff = parseIndexes(state, keyPairs, { concurrency }) + if (eff) yield* eff } - const eff = parseIndexes(state, keyPairs, { concurrency }) - if (eff) yield* eff - } - if (state.issues) { - return yield* Effect.fail( - new SchemaIssue.Composite(ast, state.issues, input, options) - ) - } - return out - }) + if (state.issues) { + return yield* Effect.fail( + new SchemaIssue.Composite(ast, state.issues, input, options) + ) + } + return out + }) + } + + if (indexCount) return makeFallback() - if (indexCount) return fallback + let fallback: SchemaParser.Parser | undefined // Resumes at the property whose parser suspended, without replaying the // properties already parsed. @@ -3019,7 +3038,7 @@ export const Objects: new( options.onExcessProperty !== undefined || (options.concurrency !== undefined && resolveConcurrency(options.concurrency) !== 1) ) { - return fallback(input, options) + return (fallback ??= makeFallback())(input, options) } if (!(typeof input === "object" && input !== null && !Array.isArray(input))) { return Effect.fail(new SchemaIssue.InvalidType(ast, input, options)) @@ -3046,7 +3065,6 @@ export const Objects: new( if (terminal) return terminal } } catch (error) { - // `Effect.fnUntracedEager` turns a synchronous throw into a defect return Effect.die(error) } return InternalParser.succeed(out) @@ -3104,7 +3122,7 @@ type ObjectParserState = { readonly input: Record readonly options: ParseOptions readonly out: Record - issues: Array | undefined + issues: Arr.NonEmptyArray | undefined } type ParsedProperty = { @@ -3113,7 +3131,8 @@ type ParsedProperty = { readonly type: AST } -function stepProperty( +/** @internal */ +export function stepProperty( s: ObjectParserState, p: ParsedProperty, exit: Exit.Exit @@ -3154,7 +3173,8 @@ const parsePropertiesOptions = { step: stepProperty } -const parseProperties = iterateEager()(parsePropertiesOptions) +/** @internal */ +export const parseProperties = iterateEager()(parsePropertiesOptions) const parsePropertiesConcurrent = iterateConcurrent()(parsePropertiesOptions) function combineChecks(a: Checks | undefined, b: Checks | undefined): Checks | undefined { @@ -3210,6 +3230,11 @@ export function structWithRest(ast: Objects, records: ReadonlyArray): O let indexSignatures = ast.indexSignatures let checks = ast.checks for (const record of records) { + for (const propertySignature of record.propertySignatures) { + if (propertySignatures.some((ps) => ps.name === propertySignature.name)) { + throw new Error(`Duplicate identifier: ${JSON.stringify(propertySignature.name)}. ts(2300)`) + } + } propertySignatures = propertySignatures.concat(record.propertySignatures) indexSignatures = indexSignatures.concat(record.indexSignatures) checks = combineChecks(checks, record.checks) @@ -3369,7 +3394,8 @@ export function collectSentinels(ast: AST): ReadonlyArray { } } -type CandidateIndex = (input: any, isConstructor: boolean) => ReadonlyArray +/** @internal */ +export type CandidateIndex = (input: any, isConstructor: boolean) => ReadonlyArray type SentinelEntry = readonly [ byValue: Map>, all: Set @@ -3379,33 +3405,36 @@ type SentinelIndex = Map const candidateIndexCache = new WeakMap, CandidateIndex>() const emptyCandidates: ReadonlyArray = Object.freeze([]) +const getRuntimeType = (input: unknown): Type => input === null ? "null" : Array.isArray(input) ? "array" : typeof input + const hasPropertySignature = (input: object, key: PropertyKey): boolean => key === "__proto__" ? Object.hasOwn(input, key) : key in input -function getIndex(types: ReadonlyArray): CandidateIndex { +/** @internal */ +export function getCandidateIndex(types: ReadonlyArray): CandidateIndex { let index = candidateIndexCache.get(types) if (index) return index let bySentinel: SentinelIndex | undefined let sentinelCandidateCount = 0 let otherwise: { [K in Type]?: Array } | undefined - let literalCandidates: Map> | undefined + let literalCandidates: Map> | undefined let onlyLiterals = true + const literalOf: Array = [] for (let i = 0; i < types.length; i++) { const a = types[i] const encoded = toCandidate(a) if (isNever(encoded)) continue - if (onlyLiterals) { - if (isLiteral(encoded) || isUniqueSymbol(encoded)) { - literalCandidates ??= new Map() - const literal = isLiteral(encoded) ? encoded.literal : encoded.symbol - let arr = literalCandidates.get(literal) - if (!arr) literalCandidates.set(literal, arr = []) - arr.push(a) - } else { - onlyLiterals = false - } + if (isLiteral(encoded) || isUniqueSymbol(encoded)) { + literalCandidates ??= new Map() + const literal = isLiteral(encoded) ? encoded.literal : encoded.symbol + literalOf[i] = literal + let arr = literalCandidates.get(literal) + if (!arr) literalCandidates.set(literal, arr = []) + arr.push(i) + } else { + onlyLiterals = false } const sentinels = collectSentinels(encoded) @@ -3428,20 +3457,26 @@ function getIndex(types: ReadonlyArray): CandidateIndex { } } + // Non-discriminated members are grouped by runtime type once and reused by every decode. + const fallbacks: { [K in Type]?: ReadonlyArray } = {} + const getFallback = (type: Type): ReadonlyArray => + fallbacks[type] ??= Object.freeze(otherwise?.[type] ?? emptyCandidates) + if (onlyLiterals && literalCandidates) { literalCandidates.forEach(Object.freeze) index = (input) => literalCandidates.get(input) ?? emptyCandidates } else if (bySentinel?.size === 1 && !otherwise) { const [key, [byValue]] = bySentinel.entries().next().value! - const candidates = byValue as unknown as Map> + const candidates = new Map>() for (const [literal, indexes] of byValue) { - candidates.set(literal, Object.freeze(Array.from(indexes, (index) => types[index]))) + candidates.set(literal, Object.freeze(Array.from(indexes))) } + const all = Object.freeze(types.map((_, i) => i)) index = (input, isConstructor) => { if (Predicate.isObjectKeyword(input)) { const value = hasPropertySignature(input, key) ? (input as any)[key] : undefined if (value !== undefined) return candidates.get(value) ?? emptyCandidates - if (isConstructor) return types + if (isConstructor) return all } return emptyCandidates } @@ -3460,12 +3495,11 @@ function getIndex(types: ReadonlyArray): CandidateIndex { } index = (input, isConstructor) => { - const runtimeType: Type = input === null ? "null" : Array.isArray(input) ? "array" : typeof input - const base = otherwise?.[runtimeType] ?? emptyCandidates - if (!Predicate.isObjectKeyword(input)) return base.map((i) => types[i]) + const runtimeType = getRuntimeType(input) + if (!Predicate.isObjectKeyword(input)) return getFallback(runtimeType) // Non-discriminated candidates are runtime-type fallbacks and are never removed by sentinel checks. - const selected = new Set(base) + const selected = new Set(otherwise?.[runtimeType]) let directKey: PropertyKey | undefined // An observed common key can seed the selection directly; an unknown value rules out every // discriminated candidate. @@ -3475,7 +3509,7 @@ function getIndex(types: ReadonlyArray): CandidateIndex { const value = hasKey ? (input as any)[key] : undefined if (hasKey && (!isConstructor || value !== undefined)) { const match = byValue.get(value) - if (!match) return base.map((i) => types[i]) + if (!match) return getFallback(runtimeType) for (const i of match) selected.add(i) directKey = key } @@ -3509,12 +3543,12 @@ function getIndex(types: ReadonlyArray): CandidateIndex { } } } - return Array.from(selected).sort((a, b) => a - b).map((i) => types[i]) + return Array.from(selected).sort((a, b) => a - b) } } else { index = (input) => { - const runtimeType: Type = input === null ? "null" : Array.isArray(input) ? "array" : typeof input - return (otherwise?.[runtimeType] ?? emptyCandidates).map((i) => types[i]).filter(filterLiterals(input)) + const fallback = getFallback(getRuntimeType(input)) + return literalCandidates ? fallback.filter((i) => literalOf[i] === undefined || literalOf[i] === input) : fallback } } @@ -3522,31 +3556,6 @@ function getIndex(types: ReadonlyArray): CandidateIndex { return index } -function filterLiterals(input: any) { - return (ast: AST) => { - const encoded = toCandidate(ast) - return encoded._tag === "Literal" ? - encoded.literal === input - : encoded._tag === "UniqueSymbol" ? - encoded.symbol === input - : true - } -} - -/** - * The goal is to reduce the number of a union members that will be checked. - * This is useful to reduce the number of issues that will be returned. - * - * @internal - */ -export function getCandidates( - input: any, - types: ReadonlyArray, - isConstructor = false -): ReadonlyArray { - return getIndex(types)(input, isConstructor) -} - /** * AST node representing a union of schemas. * @@ -3584,7 +3593,7 @@ export interface Union extends ASTNode { readonly encodingChecks: Checks | undefined /** @internal */ - getParser(compile: SchemaParser.Compiler, compileConstructorDefault?: SchemaParser.Compiler): SchemaParser.Parser + getParser(compile: SchemaParser.Compiler, compileField?: SchemaParser.Compiler): SchemaParser.Parser /** @internal */ recur(recur: (ast: AST) => AST): Union @@ -3647,47 +3656,32 @@ export const Union: new( /** @internal */ getParser( compile: SchemaParser.Compiler, - compileConstructorDefault?: SchemaParser.Compiler + compileField?: SchemaParser.Compiler ): SchemaParser.Parser { // oxlint-disable-next-line @typescript-eslint/no-this-alias const ast = this + const isConstructor = compileField !== undefined + const parsers: Array = [] + const parser = (i: number): SchemaParser.Parser => parsers[i] ??= compile(ast.types[i]) + let index: CandidateIndex | undefined return (input, options) => { if (input === InternalParser.missing) { return InternalParser.missingExit } - const candidates = getCandidates(input, ast.types, compileConstructorDefault !== undefined) + const candidates = (index ??= getCandidateIndex(ast.types))(input, isConstructor) if (candidates.length === 0) { return Effect.fail(new SchemaIssue.AnyOf(ast, [], input, options)) } if (candidates.length === 1) { - const result = compile(candidates[0])(input, options) + const result = parser(candidates[0])(input, options) if ((result as Exit.Exit)._tag === "Success") return result return effectIsExit(result) ? failSingleUnionCandidate(ast, (result as Exit.Failure).cause, input, options) - : Effect.catchCause(result, (cause) => failSingleUnionCandidate(ast, cause, input, options)) - } - - const state = { - ast, - compile, - input, - out: undefined, - successes: ast.options?.mode === "oneOf" ? [] : undefined, - issues: undefined as Arr.NonEmptyArray | undefined, - options + : catchSingleUnionCandidate(ast, result, input, options) } - const eff = parseUnion(state, candidates) - if (!eff) { - if (state.out) return state.out - return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options)) - } - return Effect.flatMapEager(eff, (_) => { - if (state.out === InternalParser.sameExit) return Effect.succeed(input) - if (state.out) return state.out - return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options)) - }) + return parseUnionCandidates(ast, parser, candidates, input, options) } } private _rebuild( @@ -3766,20 +3760,65 @@ function failSingleUnionCandidate( return Exit.fail(new SchemaIssue.AnyOf(ast, [issue], input, options)) } -const parseUnion = iterateEager<{ - readonly compile: (ast: AST) => SchemaParser.Parser +function catchSingleUnionCandidate( + ast: Union, + result: Effect.Effect, + input: unknown, + options: ParseOptions +) { + return Effect.catchCause(result, (cause) => failSingleUnionCandidate(ast, cause, input, options)) +} + +type UnionParserState = { + readonly parser: (i: number) => SchemaParser.Parser readonly ast: Union readonly input: unknown readonly options: ParseOptions out: Exit.Success | undefined readonly successes: Array | undefined issues: Array | undefined -}, AST>()({ - onItem(s, ast) { - const parser = s.compile(ast) - return parser(s.input, s.options) +} + +function parseUnionCandidates( + ast: Union, + parser: (i: number) => SchemaParser.Parser, + candidates: ReadonlyArray, + input: unknown, + options: ParseOptions +): Effect.Effect { + const state: UnionParserState = { + ast, + parser, + input, + out: undefined, + successes: ast.options?.mode === "oneOf" ? [] : undefined, + issues: undefined, + options + } + const eff = parseUnion(state, candidates) + if (!eff) { + if (state.out) return state.out + return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options)) + } + return resumeUnion(eff, state) +} + +function resumeUnion( + eff: Effect.Effect, + state: UnionParserState +): Effect.Effect { + return Effect.flatMapEager(eff, (_) => { + if (state.out === InternalParser.sameExit) return Effect.succeed(state.input) + if (state.out) return state.out + return Effect.fail(new SchemaIssue.AnyOf(state.ast, state.issues ?? [], state.input, state.options)) + }) +} + +const parseUnion = iterateEager()({ + onItem(s, i) { + return s.parser(i)(s.input, s.options) }, - step(s, candidate, exit) { + step(s, i, exit) { if (exit._tag === "Failure") { const issue = InternalSchemaCause.getSchemaIssue(exit.cause) if (issue === undefined) { @@ -3789,12 +3828,12 @@ const parseUnion = iterateEager<{ else s.issues = [issue] } else { if (s.out && s.successes) { - s.successes.push(candidate) + s.successes.push(s.ast.types[i]) return Exit.fail(new SchemaIssue.OneOf(s.ast, s.successes, s.input, s.options)) } s.out = exit if (s.successes) { - s.successes.push(candidate) + s.successes.push(s.ast.types[i]) } else { return Exit.void } @@ -4148,9 +4187,9 @@ const numberToJson = new Link( * Arbitrary metadata preserves both `regExp.source` and `regExp.flags`. * Implementations that cannot consume all flags may still use the source as a * generation hint because the Schema filter validates every generated value. - * JSON Schema has no way to carry JavaScript regular-expression flags. The - * generated `pattern` contains the source only, so validation can differ when - * the RegExp uses flags or relies on JavaScript's non-Unicode behavior. + * JSON Schema has no way to carry JavaScript regular-expression flags. Unless + * annotations provide `toJsonSchema`, the RegExp constraint is omitted from + * JSON Schema export. * * **Example** (Validating an email pattern) * @@ -4167,54 +4206,57 @@ const numberToJson = new Link( * @since 4.0.0 */ export function isPattern(regExp: globalThis.RegExp, annotations?: Schema.Annotations.Filter) { - const source = regExp.source - const pattern = new globalThis.RegExp(source, regExp.flags) + const copy = new globalThis.RegExp(regExp) + const payload = { source: copy.source, flags: copy.flags } return makeFilter( (s: string) => { - pattern.lastIndex = 0 - return pattern.test(s) + copy.lastIndex = 0 + return copy.test(s) }, { - expected: `a string matching the RegExp ${source}`, + expected: `a string matching the RegExp ${payload.source}`, representation: { id: "effect/schema/isPattern", - payload: { source, flags: regExp.flags } + payload }, - toJsonSchema: () => ({ pattern: source }), + // Keep the filter exportable so its JSON Schema annotations are preserved + // when the RegExp constraint itself is omitted. + toJsonSchema: () => [{}, true], arbitraryConstraint: { - patterns: [{ source: regExp.source, flags: regExp.flags }] + patterns: [payload] }, ...annotations } ) } -function modifyOwnPropertyDescriptors( - ast: A, - f: ( - d: { [P in keyof A]: TypedPropertyDescriptor } - ) => void -): A { - const d = Object.getOwnPropertyDescriptors(ast) - f(d) - return Object.create(Object.getPrototypeOf(ast), d) -} +// Copies that update only context or encoding share a body. +// All other fields describe the body itself, including child contexts. +const bodyOwners = new WeakMap() -const contextOwners = new WeakMap() +function copy(ast: A, changes: Partial): A { + // AST copies preserve the prototype and enumerable values, not property descriptors. + const out = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, changes) as A + if (Reflect.ownKeys(changes).every((key) => key === "context" || key === "encoding")) { + bodyOwners.set(out, getContextOwner(ast)) + } + return out +} /** @internal */ -export function getContextOwner(ast: AST): AST { - return contextOwners.get(ast) ?? ast +export function getContextOwner(ast: A): A { + const existing = bodyOwners.get(ast) + if (existing !== undefined) return existing as A + if (ast.encoding === undefined) return ast + // A body representative must not expose or retain the node's own codec. + const owner = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, { encoding: undefined }) as A + bodyOwners.set(ast, owner) + return owner } /** @internal */ export function replaceEncoding(ast: A, encoding: Encoding | undefined): A { - if (ast.encoding === encoding) { - return ast - } - return modifyOwnPropertyDescriptors(ast, (d) => { - d.encoding.value = encoding - }) + return ast.encoding === encoding ? ast : copy(ast, { encoding }) } /** @internal */ @@ -4223,14 +4265,10 @@ export function replaceContext(ast: A, context: Context | undefin return ast } const owner = getContextOwner(ast) - if (owner.context === context) { - return owner as A + if (owner.context === context && owner.encoding === ast.encoding) { + return owner } - const out = modifyOwnPropertyDescriptors(ast, (d) => { - d.context.value = context - }) - contextOwners.set(out, owner) - return out + return copy(ast, { context }) } /** @internal */ @@ -4244,9 +4282,7 @@ export function annotate(ast: A, annotations: Schema.Annotations. const last = ast.checks[ast.checks.length - 1] return replaceChecks(ast, Arr.append(ast.checks.slice(0, -1), last.annotate(annotations))) } - return modifyOwnPropertyDescriptors(ast, (d) => { - d.annotations.value = { ...d.annotations.value, ...annotations } - }) + return copy(ast, { annotations: { ...ast.annotations, ...annotations } }) } /** @internal */ @@ -4257,9 +4293,7 @@ export function replaceChecks(ast: A, checks: Checks | undefined) if (ast.checks === checks) { return ast } - return modifyOwnPropertyDescriptors(ast, (d) => { - d.checks.value = checks - }) + return copy(ast, { checks }) } /** @internal */ @@ -4340,13 +4374,6 @@ function appendTransformation( return replaceEncoding(to, to.encoding ? [...to.encoding, link] : [link]) } -/** @internal */ -export function brand(ast: AST, brand: string): AST { - const existing = InternalAnnotations.resolveBrands(ast) - const brands = existing ? [...existing, brand] : [brand] - return annotate(ast, { brands }) -} - /** * Maps over the array but will return the original array if no changes occur. * @internal @@ -4354,17 +4381,21 @@ export function brand(ast: AST, brand: string): AST { export function mapOrSame(as: Arr.NonEmptyReadonlyArray, f: (a: A) => A): Arr.NonEmptyReadonlyArray export function mapOrSame(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray export function mapOrSame(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray { - let changed = false - const out: Array = new Array(as.length) + let out: Array | undefined for (let i = 0; i < as.length; i++) { const a = as[i] const fa = f(a) - if (fa !== a) { - changed = true + if (out) { + out[i] = fa + } else if (fa !== a) { + out = new Array(as.length) + for (let j = 0; j < i; j++) { + out[j] = as[j] + } + out[i] = fa } - out[i] = fa } - return changed ? out : as + return out ?? as } /** @internal */ @@ -4414,14 +4445,9 @@ export function withConstructorDefault( ast: A, defaultValue: Effect.Effect ): A { - const transformation = new SchemaTransformation.Transformation( - SchemaGetter.withDefault(defaultValue), - SchemaGetter.passthrough() - ) - const constructorDefault = new Link(unknown, transformation) const context = ast.context ? - new Context(ast.context.isOptional, ast.context.isMutable, constructorDefault, ast.context.annotations) : - new Context(false, false, constructorDefault) + new Context(ast.context.isOptional, ast.context.isMutable, defaultValue, ast.context.annotations) : + new Context(false, false, defaultValue) return replaceContext(ast, context) } @@ -4459,12 +4485,14 @@ function parseParameter(ast: AST): { function go(ast: AST) { switch (ast._tag) { case "Literal": - if (Predicate.isPropertyKey(ast.literal)) { + if (Predicate.isPropertyKey(ast.literal) && !literals.includes(ast.literal)) { literals.push(ast.literal) } return case "UniqueSymbol": - literals.push(ast.symbol) + if (!literals.includes(ast.symbol)) { + literals.push(ast.symbol) + } return case "Never": return @@ -4530,6 +4558,17 @@ function extractStructuralChecks(checks: Checks): Checks | undefined { return Arr.isArrayNonEmpty(out) ? out : undefined } +function canPreserveEncodingChecks(ast: AST): boolean { + let preserve = true + function visit(child: AST): AST { + preserve = preserve && !child.encoding && !isSuspend(child) + if (preserve && "recur" in child) child.recur(visit) + return child + } + if ("recur" in ast) ast.recur(visit) + return preserve +} + /** * Strips all encoding transformations from an AST, returning the decoded * (type-level) representation. @@ -4556,21 +4595,21 @@ function extractStructuralChecks(checks: Checks): Checks | undefined { * @since 4.0.0 */ export const toType = memoizeIdempotent((ast: A): A => { - if (ast.encoding) { - return toType(replaceEncoding(ast, undefined)) - } - const out: any = ast - const type = out.recur?.(toType) ?? out - const encodingChecks: Checks | undefined = type.encodingChecks - if (encodingChecks) { - const checks = type === ast - ? encodingChecks + const owner = getContextOwner(ast) + if (owner !== ast) { + const type = toType(owner) + return type === owner && ast.encoding === undefined ? ast : replaceContext(type, ast.context) + } + const type = ("recur" in ast ? ast.recur(toType) : ast) as A + if ("encodingChecks" in type && type.encodingChecks) { + const checks = canPreserveEncodingChecks(ast) + ? type.encodingChecks : isArrays(type) || isObjects(type) || isDeclaration(type) && type.typeParameters.length > 0 - ? extractStructuralChecks(encodingChecks) + ? extractStructuralChecks(type.encodingChecks) : undefined - return modifyOwnPropertyDescriptors(type, (d) => { - d.encodingChecks.value = undefined - d.checks.value = combineChecks(type.checks, checks) + return copy(type, { + encodingChecks: undefined, + checks: combineChecks(type.checks, checks) }) } return type @@ -4645,8 +4684,12 @@ export const flip = memoize((ast: AST): AST => { if (ast.encoding) { return flipEncoding(ast, ast.encoding) } - const out: any = ast - return out.flip?.(flip) ?? out.recur?.(flip) ?? out + const owner = getContextOwner(ast) + if (owner !== ast) { + const flipped = flip(owner) + return flipped === owner ? ast : replaceContext(flipped, ast.context) + } + return "flip" in ast ? ast.flip(flip) : "recur" in ast ? ast.recur(flip) : ast }) /** @internal */ @@ -4738,7 +4781,8 @@ function segmentTemplateLiteralParts( return go(0, 0) ? out : undefined } -const parameterFromPropertyKey = applyToSelfOrLastLinkEncodingIdempotent((ast) => { +/** @internal */ +export const parameterFromPropertyKey = applyToSelfOrLastLinkEncodingIdempotent((ast) => { switch (ast._tag) { default: return ast diff --git a/repos/effect/packages/effect/src/SchemaGetter.ts b/repos/effect/packages/effect/src/SchemaGetter.ts index 00bf6cdb62..2293489e91 100644 --- a/repos/effect/packages/effect/src/SchemaGetter.ts +++ b/repos/effect/packages/effect/src/SchemaGetter.ts @@ -14,7 +14,10 @@ import * as Arr from "./Array.ts" import * as DateTime from "./DateTime.ts" import * as Effect from "./Effect.ts" -import * as Encoding from "./Encoding.ts" +import * as Base64 from "./encoding/Base64.ts" +import * as Base64Url from "./encoding/Base64Url.ts" +import * as Hex from "./encoding/Hex.ts" +import { dual } from "./Function.ts" import * as InternalRecord from "./internal/record.ts" import * as Option from "./Option.ts" import * as Pipeable from "./Pipeable.ts" @@ -25,6 +28,66 @@ import type * as SchemaAST from "./SchemaAST.ts" import * as SchemaIssue from "./SchemaIssue.ts" import * as Str from "./String.ts" +/** + * A transformation that returns its input unchanged. + * + * @category models + * @since 4.0.0 + */ +export interface Passthrough extends Pipeable.Pipeable { + readonly _tag: "Passthrough" +} + +/** + * A synchronous transformation of present values. + * + * @category models + * @since 4.0.0 + */ +export interface Transform extends Pipeable.Pipeable { + readonly _tag: "Transform" + readonly transform: (input: E) => T +} + +/** + * A synchronous transformation of optional values. + * + * @category models + * @since 4.0.0 + */ +export interface TransformOptional extends Pipeable.Pipeable { + readonly _tag: "TransformOptional" + readonly transform: (input: Option.Option) => Option.Option +} + +/** + * An effectful transformation of present values. + * + * @category models + * @since 4.0.0 + */ +export interface TransformEffect extends Pipeable.Pipeable { + readonly _tag: "TransformEffect" + readonly transform: ( + input: E, + options: SchemaAST.ParseOptions + ) => Effect.Effect +} + +/** + * An effectful transformation of optional values. + * + * @category models + * @since 4.0.0 + */ +export interface TransformOptionalEffect extends Pipeable.Pipeable { + readonly _tag: "TransformOptionalEffect" + readonly transform: ( + input: Option.Option, + options: SchemaAST.ParseOptions + ) => Effect.Effect, SchemaIssue.Issue, R> +} + /** * Represents a composable transformation from an encoded type `E` to a decoded type `T`. * @@ -35,14 +98,14 @@ import * as Str from "./String.ts" * * **Details** * - * A getter wraps a function `Option -> Effect, Issue, R>`. It - * receives `Option.None` when the encoded key is absent, such as a missing - * struct field, and returns `Option.None` to omit the value from the decoded - * output. It fails with `Issue` on invalid input and may require Effect - * services via `R`. `.map(f)` applies `f` to the decoded value inside `Some` - * while leaving `None` unchanged. `.compose(other)` chains two getters by - * feeding the output of `this` into `other`; passthrough getters on either side - * are optimized away. + * A getter receives an `Option` and produces an `Option`. `Option.none()` + * represents a missing struct field and can also omit a field from the output. + * A getter may fail with a schema issue or require services through `R`. The + * tagged representation distinguishes synchronous transformations from + * transformations that return an `Effect`, allowing schema parsers to select + * the corresponding execution path when they are built. Getter values expose + * `pipe`; use the standalone {@link map}, {@link compose}, and {@link run} + * functions to operate on them. * * **Example** (Creating and composing getters) * @@ -51,65 +114,207 @@ import * as Str from "./String.ts" * * const parseNumber = SchemaGetter.transform((s) => Number(s)) * const double = SchemaGetter.transform((n) => n * 2) - * const composed = parseNumber.compose(double) - * await Effect.runPromise(composed.run(Option.some("21"), {})) // => Option.some(42) + * const composed = SchemaGetter.compose(parseNumber, double) + * Effect.runSync(SchemaGetter.run(composed, Option.some("21"), {})) // => Option.some(42) * ``` * - * @see {@link transform} to create a getter from a pure function * @see {@link passthrough} for the identity getter + * @see {@link transform} to create a getter from a pure function * @see {@link transformEffect} for effectful transformation * * @category models * @since 4.0.0 */ -export interface Getter extends Pipeable.Pipeable { - readonly run: ( - input: Option.Option, - options: SchemaAST.ParseOptions - ) => Effect.Effect, SchemaIssue.Issue, R> - map(f: (t: T) => T2): Getter - compose(other: Getter): Getter +export type Getter = + | Passthrough + | Transform + | TransformOptional + | TransformEffect + | TransformOptionalEffect + +const runGetter = ( + self: Getter, + input: Option.Option, + options: SchemaAST.ParseOptions +): Effect.Effect, SchemaIssue.Issue, R> => { + switch (self._tag) { + case "Passthrough": + return Effect.succeed(input as unknown as Option.Option) + case "Transform": + return Effect.succeed(Option.map(input, self.transform)) + case "TransformOptional": + return Effect.succeed(self.transform(input)) + case "TransformEffect": + return Option.isNone(input) + ? Effect.succeedNone + : Effect.mapEager(self.transform(input.value, options), Option.some) + case "TransformOptionalEffect": + return self.transform(input, options) + } } /** - * Constructs a composable schema getter. + * Runs a getter directly. * - * @category constructors + * **Details** + * + * This is a convenience API for executing a getter outside a schema. When the + * getter belongs to a schema, use the corresponding `SchemaParser` API. The + * result is an `Effect` for every getter variant, including synchronous ones. + * + * **Example** (Running a getter) + * + * ```ts import.meta.vitest + * import { Effect, Option, SchemaGetter } from "effect" + * + * const getter = SchemaGetter.transform(Number) + * + * const result = Effect.runSync( + * SchemaGetter.run(getter, Option.some("42"), {}) + * ) + * result // => Option.some(42) + * ``` + * + * @category converting * @since 4.0.0 */ -export const Getter: new( - run: ( +export const run: { + ( input: Option.Option, options: SchemaAST.ParseOptions - ) => Effect.Effect, SchemaIssue.Issue, R> -) => Getter = class extends Pipeable.Class { - readonly run: ( + ): (self: Getter) => Effect.Effect, SchemaIssue.Issue, R> + ( + self: Getter, input: Option.Option, options: SchemaAST.ParseOptions - ) => Effect.Effect, SchemaIssue.Issue, R> + ): Effect.Effect, SchemaIssue.Issue, R> +} = dual(3, runGetter) + +const makeGetter = (fields: A): A & Pipeable.Pipeable => + Object.assign(Object.create(Pipeable.Prototype), fields) + +const composeOptionalEffect = ( + first: Getter, + second: Getter +): Getter => + transformOptionalEffect((input, options) => + Effect.flatMapEager(runGetter(first, input, options), (output) => runGetter(second, output, options)) + ) - constructor( - run: ( - input: Option.Option, - options: SchemaAST.ParseOptions - ) => Effect.Effect, SchemaIssue.Issue, R> - ) { - super() - this.run = run - } - map(f: (t: T) => T2): Getter { - return new Getter((oe, options) => this.run(oe, options).pipe(Effect.mapEager(Option.map(f)))) - } - compose(other: Getter): Getter { - if (isPassthrough(this)) { - return other as any +/** + * Composes two getters by passing the output of the first to the second. + * + * **When to use** + * + * Use when a schema conversion requires multiple transformation steps. + * + * **Details** + * + * Composition forwards `Option.none()` to getters that handle optional values + * and skips getters that operate only on present values. Composing with + * {@link passthrough} returns the other getter unchanged. The function supports + * both `compose(first, second)` and `first.pipe(compose(second))`. + * + * **Example** (Parsing and normalizing a number) + * + * ```ts import.meta.vitest + * import { Effect, Option, SchemaGetter } from "effect" + * + * const getter = SchemaGetter.compose( + * SchemaGetter.transform(Number), + * SchemaGetter.transform((n) => Math.max(0, n)) + * ) + * + * Effect.runSync(SchemaGetter.run(getter, Option.some("-1"), {})) // => Option.some(0) + * ``` + * + * @category combining + * @since 4.0.0 + */ +export const compose: { + (other: Getter): (self: Getter) => Getter + (self: Getter, other: Getter): Getter +} = dual(2, ( + self: Getter, + other: Getter +): Getter => { + if (self._tag === "Passthrough") return other as Getter + if (other._tag === "Passthrough") return self as unknown as Getter + switch (self._tag) { + case "Transform": { + switch (other._tag) { + case "Transform": + return transform((input: E) => other.transform(self.transform(input))) + case "TransformOptional": + return transformOptional((input) => other.transform(Option.map(input, self.transform))) + case "TransformEffect": + return transformEffect((input: E, options) => other.transform(self.transform(input), options)) + case "TransformOptionalEffect": + return composeOptionalEffect(self, other) + } + } + case "TransformOptional": { + switch (other._tag) { + case "Transform": + return transformOptional((input) => Option.map(self.transform(input), other.transform)) + case "TransformOptional": + return transformOptional((input) => other.transform(self.transform(input))) + case "TransformEffect": + case "TransformOptionalEffect": + return composeOptionalEffect(self, other) + } } - if (isPassthrough(other)) { - return this as any + case "TransformEffect": { + switch (other._tag) { + case "Transform": + return transformEffect((input: E, options) => + Effect.mapEager(self.transform(input, options), other.transform) + ) + case "TransformOptional": + case "TransformOptionalEffect": + return composeOptionalEffect(self, other) + case "TransformEffect": + return transformEffect((input: E, options) => + Effect.flatMapEager(self.transform(input, options), (output) => other.transform(output, options)) + ) + } } - return new Getter((oe, options) => this.run(oe, options).pipe(Effect.flatMapEager((ot) => other.run(ot, options)))) + case "TransformOptionalEffect": + return composeOptionalEffect(self, other) } -} +}) + +/** + * Maps the output of a getter while preserving missing values. + * + * **When to use** + * + * Use to add a synchronous transformation after an existing getter. + * + * **Details** + * + * The mapping function runs only for `Option.some`. The function supports both + * `map(self, f)` and `self.pipe(map(f))`. + * + * **Example** (Mapping a getter result) + * + * ```ts import.meta.vitest + * import { Effect, Option, SchemaGetter } from "effect" + * + * const getter = SchemaGetter.transform(Number).pipe( + * SchemaGetter.map((n) => n * 2) + * ) + * + * Effect.runSync(SchemaGetter.run(getter, Option.some("21"), {})) // => Option.some(42) + * ``` + * + * @category mapping + * @since 4.0.0 + */ +export const map: { + (f: (value: T) => T2): (self: Getter) => Getter + (self: Getter, f: (value: T) => T2): Getter +} = dual(2, (self: Getter, f: (value: T) => T2): Getter => compose(self, transform(f))) /** * Creates a getter that always produces the given constant value, ignoring the input. @@ -130,7 +335,7 @@ export const Getter: new( * import { Effect, Option, SchemaGetter } from "effect" * * const alwaysZero = SchemaGetter.succeed(0) - * await Effect.runPromise(alwaysZero.run(Option.none(), {})) // => Option.some(0) + * Effect.runSync(SchemaGetter.run(alwaysZero, Option.none(), {})) // => Option.some(0) * ``` * * @see {@link transform} when you need to use the input value @@ -140,7 +345,7 @@ export const Getter: new( * @since 4.0.0 */ export function succeed(t: T): Getter { - return new Getter(() => Effect.succeedSome(t)) + return transformOptional(() => Option.some(t)) } /** @@ -165,7 +370,9 @@ export function succeed(t: T): Getter { * const rejectAll = SchemaGetter.fail( * () => new SchemaIssue.InvalidValue({ message: "not allowed" }) * ) - * const issue = await Effect.runPromise(Effect.flip(rejectAll.run(Option.some("x"), {}))) + * const issue = await Effect.runPromise( + * Effect.flip(SchemaGetter.run(rejectAll, Option.some("x"), {})) + * ) * issue._tag // => "InvalidValue" * ``` * @@ -178,7 +385,7 @@ export function succeed(t: T): Getter { export function fail( f: (oe: Option.Option, options: SchemaAST.ParseOptions) => SchemaIssue.Issue ): Getter { - return new Getter((oe, options) => Effect.fail(f(oe, options))) + return transformOptionalEffect((oe, options) => Effect.fail(f(oe, options))) } /** @@ -203,7 +410,9 @@ export function fail( * const noEncode = SchemaGetter.forbidden( * () => "encoding is not supported" * ) - * const issue = await Effect.runPromise(Effect.flip(noEncode.run(Option.some(1), {}))) + * const issue = await Effect.runPromise( + * Effect.flip(SchemaGetter.run(noEncode, Option.some(1), {})) + * ) * issue._tag // => "Forbidden" * ``` * @@ -239,7 +448,7 @@ export function forbidden(message: (oe: Option.Option) => string): Gett * import { Effect, Option, SchemaGetter } from "effect" * * const issue = await Effect.runPromise( - * Effect.flip(SchemaGetter.forbiddenEncoding.run(Option.some("value"), {})) + * Effect.flip(SchemaGetter.run(SchemaGetter.forbiddenEncoding, Option.some("value"), {})) * ) * issue._tag // => "Forbidden" * ``` @@ -251,11 +460,7 @@ export function forbidden(message: (oe: Option.Option) => string): Gett */ export const forbiddenEncoding: Getter = forbidden(() => "Encoding is not supported") -const passthrough_ = new Getter(Effect.succeed) - -function isPassthrough(getter: Getter): getter is typeof passthrough_ { - return getter.run === passthrough_.run -} +const passthrough_: Passthrough = makeGetter({ _tag: "Passthrough" }) /** * Returns the identity getter — passes the value through unchanged. @@ -268,7 +473,7 @@ function isPassthrough(getter: Getter): getter is typeof passt * **Details** * * - Pure, no allocation (singleton instance). - * - Optimized away during `.compose()` — composing with a passthrough is free. + * - Optimized away by {@link compose} — composing with a passthrough is free. * - The default overload requires `T === E`. Pass `{ strict: false }` to opt * out of the type constraint. * @@ -319,7 +524,7 @@ export function passthrough(): Getter { * * // string extends string, so this is valid * const g = SchemaGetter.passthroughSupertype() - * await Effect.runPromise(g.run(Option.some("hello"), {})) // => Option.some("hello") + * Effect.runSync(SchemaGetter.run(g, Option.some("hello"), {})) // => Option.some("hello") * ``` * * @see {@link passthrough} when types are identical @@ -352,7 +557,7 @@ export function passthroughSupertype(): Getter { * * // "hello" extends string, so E extends T * const g = SchemaGetter.passthroughSubtype() - * await Effect.runPromise(g.run(Option.some("hello"), {})) // => Option.some("hello") + * Effect.runSync(SchemaGetter.run(g, Option.some("hello"), {})) // => Option.some("hello") * ``` * * @see {@link passthrough} when types are identical @@ -366,45 +571,6 @@ export function passthroughSubtype(): Getter { return passthrough_ } -/** - * Creates a getter that handles the case when the input is absent (`Option.None`). - * - * **When to use** - * - * Use when you need a schema getter to provide a fallback or computed value for - * missing struct keys. - * - Building custom "default value" logic more complex than {@link withDefault}. - * - * **Details** - * - * - When input is `None`, calls `f` to produce the result. - * - When input is `Some`, passes it through unchanged. - * - `f` receives the parse options and may return `None` to keep the value absent. - * - * **Example** (Providing a default timestamp for a missing field) - * - * ```ts import.meta.vitest - * import { Effect, Option, SchemaGetter } from "effect" - * - * const withTimestamp = SchemaGetter.onNone(() => - * Effect.succeed(Option.some(0)) - * ) - * await Effect.runPromise(withTimestamp.run(Option.none(), {})) // => Option.some(0) - * ``` - * - * @see {@link required} when absent input should fail - * @see {@link withDefault} for a simpler default value for undefined inputs - * @see {@link onSome} to handle only present values - * - * @category transforming - * @since 4.0.0 - */ -export function onNone( - f: (options: SchemaAST.ParseOptions) => Effect.Effect, SchemaIssue.Issue, R> -): Getter { - return new Getter((ot, options) => Option.isNone(ot) ? f(options) : Effect.succeed(ot)) -} - /** * Creates a getter that fails with `MissingKey` if the input is absent (`Option.None`). * @@ -425,57 +591,21 @@ export function onNone( * import { Effect, Option, SchemaGetter } from "effect" * * const mustExist = SchemaGetter.required() - * const issue = await Effect.runPromise(Effect.flip(mustExist.run(Option.none(), {}))) + * const issue = await Effect.runPromise( + * Effect.flip(SchemaGetter.run(mustExist, Option.none(), {})) + * ) * issue._tag // => "MissingKey" * ``` * - * @see {@link onNone} to provide a fallback instead of failing * @see {@link withDefault} to substitute a default for undefined values * * @category validation * @since 4.0.0 */ export function required(annotations?: Schema.Annotations.Key): Getter { - return onNone(() => Effect.fail(new SchemaIssue.MissingKey(annotations))) -} - -/** - * Creates a getter that handles present values (`Option.Some`), passing `None` through. - * - * **When to use** - * - * Use when you need a schema getter to transform or validate only when a field - * value is present. - * - Missing keys should remain absent in the output. - * - * **Details** - * - * - When input is `None`, returns `None` (no-op). - * - When input is `Some(e)`, calls `f(e, options)` to produce the result. - * - `f` may return `None` to omit the value, or fail with an `Issue`. - * - * **Example** (Transforming only present values) - * - * ```ts import.meta.vitest - * import { Effect, Option, SchemaGetter } from "effect" - * - * const parseIfPresent = SchemaGetter.onSome( - * (s) => Effect.succeed(Option.some(Number(s))) - * ) - * await Effect.runPromise(parseIfPresent.run(Option.some("42"), {})) // => Option.some(42) - * ``` - * - * @see {@link onNone} to handle only absent values - * @see {@link transform} for a simpler pure transformation of present values - * @see {@link transformEffect} for effectful transformation of present values - * - * @category transforming - * @since 4.0.0 - */ -export function onSome( - f: (e: E, options: SchemaAST.ParseOptions) => Effect.Effect, SchemaIssue.Issue, R> -): Getter { - return new Getter((oe, options) => Option.isNone(oe) ? Effect.succeedNone : f(oe.value, options)) + return transformOptionalEffect((input) => + Option.isNone(input) ? Effect.fail(new SchemaIssue.MissingKey(annotations)) : Effect.succeed(input) + ) } /** @@ -506,7 +636,7 @@ export function onSome( * const nonNegative = SchemaGetter.checkEffect((n) => * Effect.succeed(n >= 0 ? undefined : "must be non-negative") * ) - * await Effect.runPromise(nonNegative.run(Option.some(1), {})) // => Option.some(1) + * await Effect.runPromise(SchemaGetter.run(nonNegative, Option.some(1), {})) // => Option.some(1) * ``` * * @see {@link transform} when you need to change the value, not just validate @@ -522,12 +652,12 @@ export function checkEffect( R > ): Getter { - return onSome((t, options) => { + return transformEffect((t, options) => { return f(t, options).pipe(Effect.flatMapEager((out) => { const issue = SchemaIssue.makeSingle(out, t, options) return issue ? Effect.fail(issue) : - Effect.succeed(Option.some(t)) + Effect.succeed(t) })) }) } @@ -570,7 +700,7 @@ export function checkEffect( * @since 4.0.0 */ export function transform(f: (e: E) => T): Getter { - return transformOptional(Option.map(f)) + return makeGetter({ _tag: "Transform", transform: f }) } /** @@ -600,11 +730,11 @@ export function transform(f: (e: E) => T): Getter { * : Effect.succeed(n) * } * ) - * await Effect.runPromise(safeParseInt.run(Option.some("42"), {})) // => Option.some(42) + * await Effect.runPromise(SchemaGetter.run(safeParseInt, Option.some("42"), {})) // => Option.some(42) * ``` * * @see {@link transform} when transformation cannot fail - * @see {@link onSome} when you need full `Option` control over the output + * @see {@link transformOptionalEffect} when you need full `Option` control over the output * * @category transforming * @since 4.0.0 @@ -612,7 +742,7 @@ export function transform(f: (e: E) => T): Getter { export function transformEffect( f: (e: E, options: SchemaAST.ParseOptions) => Effect.Effect ): Getter { - return onSome((e, options) => f(e, options).pipe(Effect.mapEager(Option.some))) + return makeGetter({ _tag: "TransformEffect", transform: f }) } /** @@ -636,7 +766,7 @@ export function transformEffect( * const skipEmpty = SchemaGetter.transformOptional((o) => * Option.filter(o, (s) => s.length > 0) * ) - * await Effect.runPromise(skipEmpty.run(Option.some(""), {})) // => Option.none() + * Effect.runSync(SchemaGetter.run(skipEmpty, Option.some(""), {})) // => Option.none() * ``` * * @see {@link transform} when you only need to transform present values @@ -646,7 +776,22 @@ export function transformEffect( * @since 4.0.0 */ export function transformOptional(f: (oe: Option.Option) => Option.Option): Getter { - return new Getter((oe) => Effect.succeed(f(oe))) + return makeGetter({ _tag: "TransformOptional", transform: f }) +} + +/** + * Creates a getter that effectfully transforms the full `Option`. + * + * @category transforming + * @since 4.0.0 + */ +export function transformOptionalEffect( + f: ( + input: Option.Option, + options: SchemaAST.ParseOptions + ) => Effect.Effect, SchemaIssue.Issue, R> +): Getter { + return makeGetter({ _tag: "TransformOptionalEffect", transform: f }) } /** @@ -668,7 +813,7 @@ export function transformOptional(f: (oe: Option.Option) => Option.Opti * import { Effect, Option, SchemaGetter } from "effect" * * const omitField = SchemaGetter.omit() - * await Effect.runPromise(omitField.run(Option.some("hidden"), {})) // => Option.none() + * Effect.runSync(SchemaGetter.run(omitField, Option.some("hidden"), {})) // => Option.none() * ``` * * @see {@link transformOptional} when you want conditional omission @@ -678,7 +823,7 @@ export function transformOptional(f: (oe: Option.Option) => Option.Opti * @since 4.0.0 */ export function omit(): Getter { - return new Getter(() => Effect.succeedNone) + return transformOptional(() => Option.none()) } /** @@ -701,10 +846,10 @@ export function omit(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const withZero = SchemaGetter.withDefault(Effect.succeed(0)) - * await Effect.runPromise(withZero.run(Option.some(undefined), {})) // => Option.some(0) + * await Effect.runPromise(SchemaGetter.run(withZero, Option.some(undefined), {})) // => Option.some(0) * ``` * - * @see {@link onNone} to handle only absent keys (not `undefined` values) + * @see {@link transformOptionalEffect} for custom effectful missing-key handling * @see {@link required} when absent input should fail instead of using a default * * @category transforming @@ -713,7 +858,7 @@ export function omit(): Getter { export function withDefault( defaultValue: Effect.Effect ): Getter { - return new Getter((o) => { + return transformOptionalEffect((o) => { const filtered = Option.filter(o, Predicate.isNotUndefined) return Option.isSome(filtered) ? Effect.succeed(filtered) : Effect.mapEager(defaultValue, Option.some) }) @@ -737,7 +882,7 @@ export function withDefault( * import { Effect, Option, SchemaGetter } from "effect" * * const toString = SchemaGetter.String() - * await Effect.runPromise(toString.run(Option.some(42), {})) // => Option.some("42") + * Effect.runSync(SchemaGetter.run(toString, Option.some(42), {})) // => Option.some("42") * ``` * * @see {@link transform} for custom string conversions @@ -768,7 +913,7 @@ export function String(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const toNumber = SchemaGetter.Number() - * await Effect.runPromise(toNumber.run(Option.some("42"), {})) // => Option.some(42) + * Effect.runSync(SchemaGetter.run(toNumber, Option.some("42"), {})) // => Option.some(42) * ``` * * @see {@link transformEffect} for effectful or validated number parsing @@ -798,7 +943,7 @@ export function Number(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const toBool = SchemaGetter.Boolean() - * await Effect.runPromise(toBool.run(Option.some("true"), {})) // => Option.some(true) + * Effect.runSync(SchemaGetter.run(toBool, Option.some("true"), {})) // => Option.some(true) * ``` * * @category converting @@ -827,7 +972,7 @@ export function Boolean(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const toBigInt = SchemaGetter.BigInt() - * await Effect.runPromise(toBigInt.run(Option.some("42"), {})) // => Option.some(42n) + * Effect.runSync(SchemaGetter.run(toBigInt, Option.some("42"), {})) // => Option.some(42n) * ``` * * @category converting @@ -856,7 +1001,7 @@ export function BigInt(): Getter() - * const result = await Effect.runPromise(toDate.run(Option.some("1970-01-01"), {})) + * const result = Effect.runSync(SchemaGetter.run(toDate, Option.some("1970-01-01"), {})) * Option.map(result, (date) => date.toISOString()) // => Option.some("1970-01-01T00:00:00.000Z") * ``` * @@ -882,7 +1027,7 @@ export function Date(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const trimmed = SchemaGetter.trim() - * await Effect.runPromise(trimmed.run(Option.some(" hello "), {})) // => Option.some("hello") + * Effect.runSync(SchemaGetter.run(trimmed, Option.some(" hello "), {})) // => Option.some("hello") * ``` * * @category transforming @@ -905,7 +1050,7 @@ export function trim(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const cap = SchemaGetter.capitalize() - * await Effect.runPromise(cap.run(Option.some("hello"), {})) // => Option.some("Hello") + * Effect.runSync(SchemaGetter.run(cap, Option.some("hello"), {})) // => Option.some("Hello") * ``` * * @category transforming @@ -928,7 +1073,7 @@ export function capitalize(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const uncap = SchemaGetter.uncapitalize() - * await Effect.runPromise(uncap.run(Option.some("Hello"), {})) // => Option.some("hello") + * Effect.runSync(SchemaGetter.run(uncap, Option.some("Hello"), {})) // => Option.some("hello") * ``` * * @category transforming @@ -951,7 +1096,7 @@ export function uncapitalize(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const toCamel = SchemaGetter.snakeToCamel() - * await Effect.runPromise(toCamel.run(Option.some("user_name"), {})) // => Option.some("userName") + * Effect.runSync(SchemaGetter.run(toCamel, Option.some("user_name"), {})) // => Option.some("userName") * ``` * * @see {@link camelToSnake} for the inverse operation @@ -976,7 +1121,7 @@ export function snakeToCamel(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const toSnake = SchemaGetter.camelToSnake() - * await Effect.runPromise(toSnake.run(Option.some("userName"), {})) // => Option.some("user_name") + * Effect.runSync(SchemaGetter.run(toSnake, Option.some("userName"), {})) // => Option.some("user_name") * ``` * * @see {@link snakeToCamel} for the inverse operation @@ -1001,7 +1146,7 @@ export function camelToSnake(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const lower = SchemaGetter.toLowerCase() - * await Effect.runPromise(lower.run(Option.some("HELLO"), {})) // => Option.some("hello") + * Effect.runSync(SchemaGetter.run(lower, Option.some("HELLO"), {})) // => Option.some("hello") * ``` * * @see {@link toUpperCase} for the inverse operation @@ -1026,7 +1171,7 @@ export function toLowerCase(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const upper = SchemaGetter.toUpperCase() - * await Effect.runPromise(upper.run(Option.some("hello"), {})) // => Option.some("HELLO") + * Effect.runSync(SchemaGetter.run(upper, Option.some("hello"), {})) // => Option.some("HELLO") * ``` * * @see {@link toLowerCase} for the inverse operation @@ -1065,7 +1210,8 @@ type ParseJsonOptions = { * import { Effect, Option, SchemaGetter } from "effect" * * const parse = SchemaGetter.parseJson() - * await Effect.runPromise(parse.run(Option.some("{\"a\":1}"), {})) // => Option.some({ a: 1 }) + * const result = await Effect.runPromise(SchemaGetter.run(parse, Option.some("{\"a\":1}"), {})) + * result // => Option.some({ a: 1 }) * ``` * * @see {@link stringifyJson} for the inverse operation @@ -1076,9 +1222,9 @@ type ParseJsonOptions = { export function parseJson(): Getter export function parseJson(options: ParseJsonOptions): Getter export function parseJson(options?: ParseJsonOptions | undefined): Getter { - return onSome((input, parseOptions) => + return transformEffect((input, parseOptions) => Effect.try({ - try: () => Option.some(JSON.parse(input, options?.reviver)), + try: () => JSON.parse(input, options?.reviver), catch: () => new SchemaIssue.InvalidValue( { expected: "a valid JSON string" }, @@ -1127,7 +1273,8 @@ type StringifyJsonOptions = { * import { Effect, Option, SchemaGetter } from "effect" * * const stringify = SchemaGetter.stringifyJson() - * await Effect.runPromise(stringify.run(Option.some({ a: 1 }), {})) // => Option.some("{\"a\":1}") + * const result = await Effect.runPromise(SchemaGetter.run(stringify, Option.some({ a: 1 }), {})) + * result // => Option.some("{\"a\":1}") * ``` * * @see {@link parseJson} for the inverse operation @@ -1136,14 +1283,14 @@ type StringifyJsonOptions = { * @since 4.0.0 */ export function stringifyJson(options?: StringifyJsonOptions): Getter { - return onSome((input, parseOptions) => + return transformEffect((input, parseOptions) => Effect.try({ try: () => { const output = JSON.stringify(input, options?.replacer as any, options?.space) if (output === undefined) { throw new TypeError("Value cannot be represented as JSON") } - return Option.some(output) + return output }, catch: () => new SchemaIssue.InvalidValue( @@ -1175,7 +1322,8 @@ export function stringifyJson(options?: StringifyJsonOptions): Getter() - * await Effect.runPromise(parse.run(Option.some("a=1,b=2"), {})) // => Option.some({ a: "1", b: "2" }) + * const result = Effect.runSync(SchemaGetter.run(parse, Option.some("a=1,b=2"), {})) + * result // => Option.some({ a: "1", b: "2" }) * ``` * * @see {@link joinKeyValue} for the inverse operation @@ -1221,7 +1369,8 @@ export function splitKeyValue(options?: { * import { Effect, Option, SchemaGetter } from "effect" * * const join = SchemaGetter.joinKeyValue() - * await Effect.runPromise(join.run(Option.some({ a: "1", b: "2" }), {})) // => Option.some("a=1,b=2") + * const result = Effect.runSync(SchemaGetter.run(join, Option.some({ a: "1", b: "2" }), {})) + * result // => Option.some("a=1,b=2") * ``` * * @see {@link splitKeyValue} for the inverse operation @@ -1259,7 +1408,8 @@ export function joinKeyValue>(options?: { * import { Effect, Option, SchemaGetter } from "effect" * * const splitComma = SchemaGetter.split() - * await Effect.runPromise(splitComma.run(Option.some("a,b,c"), {})) // => Option.some(["a", "b", "c"]) + * const result = Effect.runSync(SchemaGetter.run(splitComma, Option.some("a,b,c"), {})) + * result // => Option.some(["a", "b", "c"]) * ``` * * @see {@link splitKeyValue} when values are key-value pairs @@ -1287,7 +1437,8 @@ export function split(options?: { * import { Effect, Option, SchemaGetter } from "effect" * * const encode = SchemaGetter.encodeBase64() - * await Effect.runPromise(encode.run(Option.some(new Uint8Array([1, 2, 3])), {})) // => Option.some("AQID") + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some(new Uint8Array([1, 2, 3])), {})) + * result // => Option.some("AQID") * ``` * * @see {@link decodeBase64} for the inverse operation to `Uint8Array` @@ -1298,7 +1449,7 @@ export function split(options?: { * @since 4.0.0 */ export function encodeBase64(): Getter { - return transform(Encoding.encodeBase64) + return transform(Base64.encode) } /** @@ -1314,7 +1465,8 @@ export function encodeBase64(): Getter * import { Effect, Option, SchemaGetter } from "effect" * * const encode = SchemaGetter.encodeBase64Url() - * await Effect.runPromise(encode.run(Option.some(new Uint8Array([251, 255])), {})) // => Option.some("-_8") + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some(new Uint8Array([251, 255])), {})) + * result // => Option.some("-_8") * ``` * * @see {@link decodeBase64Url} for the inverse operation to `Uint8Array` @@ -1325,7 +1477,7 @@ export function encodeBase64(): Getter * @since 4.0.0 */ export function encodeBase64Url(): Getter { - return transform(Encoding.encodeBase64Url) + return transform(Base64Url.encode) } /** @@ -1341,7 +1493,8 @@ export function encodeBase64Url(): Getter() - * await Effect.runPromise(encode.run(Option.some(new Uint8Array([1, 2, 3])), {})) // => Option.some("010203") + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some(new Uint8Array([1, 2, 3])), {})) + * result // => Option.some("010203") * ``` * * @see {@link decodeHex} for the inverse operation to `Uint8Array` @@ -1351,7 +1504,7 @@ export function encodeBase64Url(): Getter(): Getter { - return transform(Encoding.encodeHex) + return transform(Hex.encode) } /** @@ -1367,7 +1520,7 @@ export function encodeHex(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeBase64() - * const result = await Effect.runPromise(decode.run(Option.some("AQID"), {})) + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("AQID"), {})) * Option.map(result, Array.from) // => Option.some([1, 2, 3]) * ``` * @@ -1380,7 +1533,7 @@ export function encodeHex(): Getter { export function decodeBase64(): Getter { return transformEffect((input, options) => Effect.mapErrorEager( - Effect.fromResult(Encoding.decodeBase64(input)), + Effect.fromResult(Base64.decode(input)), () => new SchemaIssue.InvalidValue( { expected: "a valid Base64 string" }, @@ -1404,7 +1557,8 @@ export function decodeBase64(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeBase64String() - * await Effect.runPromise(decode.run(Option.some("aGVsbG8="), {})) // => Option.some("hello") + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("aGVsbG8="), {})) + * result // => Option.some("hello") * ``` * * @see {@link decodeBase64} to decode to `Uint8Array` instead @@ -1415,7 +1569,7 @@ export function decodeBase64(): Getter { */ export function decodeBase64String(): Getter { return transformEffect((input, options) => - Result.match(Encoding.decodeBase64String(input), { + Result.match(Base64.decodeString(input), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -1442,7 +1596,7 @@ export function decodeBase64String(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeBase64Url() - * const result = await Effect.runPromise(decode.run(Option.some("-_8="), {})) + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("-_8="), {})) * Option.map(result, Array.from) // => Option.some([251, 255]) * ``` * @@ -1454,7 +1608,7 @@ export function decodeBase64String(): Getter { */ export function decodeBase64Url(): Getter { return transformEffect((input, options) => - Result.match(Encoding.decodeBase64Url(input), { + Result.match(Base64Url.decode(input), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -1481,7 +1635,8 @@ export function decodeBase64Url(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeBase64UrlString() - * await Effect.runPromise(decode.run(Option.some("aGVsbG8"), {})) // => Option.some("hello") + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("aGVsbG8"), {})) + * result // => Option.some("hello") * ``` * * @see {@link decodeBase64Url} to decode to `Uint8Array` instead @@ -1492,7 +1647,7 @@ export function decodeBase64Url(): Getter { */ export function decodeBase64UrlString(): Getter { return transformEffect((input, options) => - Result.match(Encoding.decodeBase64UrlString(input), { + Result.match(Base64Url.decodeString(input), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -1519,7 +1674,7 @@ export function decodeBase64UrlString(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeHex() - * const result = await Effect.runPromise(decode.run(Option.some("010203"), {})) + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("010203"), {})) * Option.map(result, Array.from) // => Option.some([1, 2, 3]) * ``` * @@ -1531,7 +1686,7 @@ export function decodeBase64UrlString(): Getter { */ export function decodeHex(): Getter { return transformEffect((input, options) => - Result.match(Encoding.decodeHex(input), { + Result.match(Hex.decode(input), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -1558,7 +1713,8 @@ export function decodeHex(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeHexString() - * await Effect.runPromise(decode.run(Option.some("68656c6c6f"), {})) // => Option.some("hello") + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("68656c6c6f"), {})) + * result // => Option.some("hello") * ``` * * @see {@link decodeHex} to decode to `Uint8Array` instead @@ -1569,7 +1725,7 @@ export function decodeHex(): Getter { */ export function decodeHexString(): Getter { return transformEffect((input, options) => - Result.match(Encoding.decodeHexString(input), { + Result.match(Hex.decodeString(input), { onFailure: () => Effect.fail( new SchemaIssue.InvalidValue( @@ -1598,7 +1754,8 @@ export function decodeHexString(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const encode = SchemaGetter.encodeUriComponent() - * await Effect.runPromise(encode.run(Option.some("hello world"), {})) // => Option.some("hello%20world") + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some("hello world"), {})) + * result // => Option.some("hello%20world") * ``` * * @see {@link decodeUriComponent} for the inverse operation @@ -1623,7 +1780,8 @@ export function encodeUriComponent(): Getter { * import { Effect, Option, SchemaGetter } from "effect" * * const decode = SchemaGetter.decodeUriComponent() - * await Effect.runPromise(decode.run(Option.some("hello%20world"), {})) // => Option.some("hello world") + * const result = await Effect.runPromise(SchemaGetter.run(decode, Option.some("hello%20world"), {})) + * result // => Option.some("hello world") * ``` * * @see {@link encodeUriComponent} for the inverse operation @@ -1670,7 +1828,9 @@ export function decodeUriComponent(): Getter { * import { DateTime, Effect, Option, SchemaGetter } from "effect" * * const parseDate = SchemaGetter.dateTimeUtcFromInput() - * const result = await Effect.runPromise(parseDate.run(Option.some("2024-01-01T00:00:00Z"), {})) + * const result = await Effect.runPromise( + * SchemaGetter.run(parseDate, Option.some("2024-01-01T00:00:00Z"), {}) + * ) * Option.map(result, DateTime.toEpochMillis) // => Option.some(1704067200000) * ``` * @@ -1713,7 +1873,8 @@ export function dateTimeUtcFromInput(): Gette * const decode = SchemaGetter.decodeFormData() * const formData = new FormData() * formData.append("user[name]", "Alice") - * await Effect.runPromise(decode.run(Option.some(formData), {})) // => Option.some({ user: { name: "Alice" } }) + * const result = Effect.runSync(SchemaGetter.run(decode, Option.some(formData), {})) + * result // => Option.some({ user: { name: "Alice" } }) * ``` * * @see {@link encodeFormData} for the corresponding encoder @@ -1751,7 +1912,7 @@ const collectFormDataEntries = collectBracketPathEntries((value): value is strin * import { Effect, Option, SchemaGetter } from "effect" * * const encode = SchemaGetter.encodeFormData() - * const result = await Effect.runPromise(encode.run(Option.some({ name: "Alice" }), {})) + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some({ name: "Alice" }), {})) * Option.map(result, (formData) => formData.get("name")) // => Option.some("Alice") * ``` * @@ -1796,7 +1957,8 @@ export function encodeFormData(): Getter { * * const decode = SchemaGetter.decodeURLSearchParams() * const params = new URLSearchParams("user[name]=Alice") - * await Effect.runPromise(decode.run(Option.some(params), {})) // => Option.some({ user: { name: "Alice" } }) + * const result = Effect.runSync(SchemaGetter.run(decode, Option.some(params), {})) + * result // => Option.some({ user: { name: "Alice" } }) * ``` * * @see {@link encodeURLSearchParams} for the corresponding encoder @@ -1831,7 +1993,7 @@ const collectURLSearchParamsEntries = collectBracketPathEntries(Predicate.isStri * import { Effect, Option, SchemaGetter } from "effect" * * const encode = SchemaGetter.encodeURLSearchParams() - * const result = await Effect.runPromise(encode.run(Option.some({ name: "Alice" }), {})) + * const result = Effect.runSync(SchemaGetter.run(encode, Option.some({ name: "Alice" }), {})) * Option.map(result, (params) => params.toString()) // => Option.some("name=Alice") * ``` * diff --git a/repos/effect/packages/effect/src/SchemaParser.ts b/repos/effect/packages/effect/src/SchemaParser.ts index 47b068c7c9..0ae10b550a 100644 --- a/repos/effect/packages/effect/src/SchemaParser.ts +++ b/repos/effect/packages/effect/src/SchemaParser.ts @@ -13,9 +13,9 @@ import * as Cause from "./Cause.ts" import * as Effect from "./Effect.ts" import * as Exit from "./Exit.ts" -import { memoize } from "./Function.ts" import { effectIsExit } from "./internal/effect.ts" import * as InternalSchemaCause from "./internal/schema/cause.ts" +import * as CompilerRegistry from "./internal/schema/compilerRegistry.ts" import * as InternalParser from "./internal/schema/parser.ts" import * as Option from "./Option.ts" import * as Result from "./Result.ts" @@ -109,18 +109,7 @@ export function makeOption(schema: S) { * @since 4.0.0 */ export function make(schema: S) { - const parser = makeEffect(schema) - return (input: S["~type.make.in"], options?: Schema.MakeOptions): S["Type"] => { - const exit = Effect.runSyncExit(parser(input, options)) - if (Exit.isSuccess(exit)) { - return exit.value - } - const issue = InternalSchemaCause.getSchemaIssueOrThrow( - exit.cause, - "Constructor adapter can only throw schema issues" - ) - throw new Error("Schema validation failed", { cause: issue }) - } + return makeConstructorSync(SchemaAST.toType(schema.ast)) } /** @@ -150,19 +139,55 @@ export function is(schema: S): (input: I) => inp return _is(schema.ast) } -/** @internal */ -export function _is(ast: SchemaAST.AST) { - const parser = asExit(run(SchemaAST.toType(ast))) - return (input: I): input is I & T => { - const exit = parser(input, SchemaAST.defaultParseOptions) - if (Exit.isSuccess(exit)) { - return true +function makeIs(ast: SchemaAST.AST): (input: I) => input is I & T { + if (!CompilerRegistry.compilerAdaptersEnabled) { + const parser = asExit(run(ast)) + return (input: I): input is I & T => { + const exit = parser(input, SchemaAST.defaultParseOptions) + if (Exit.isSuccess(exit)) return true + InternalSchemaCause.getSchemaIssueOrThrow( + exit.cause, + "Type guard adapter can only return false for schema issues" + ) + return false } + } + const entry = CompilerRegistry.resolve(ast) + const guard = entry.is + if (guard !== undefined) { + return (input: I): input is I & T => { + try { + return guard(input, SchemaAST.defaultParseOptions) + } catch (error) { + InternalSchemaCause.getSchemaIssueOrThrow( + Cause.die(error), + "Type guard adapter can only return false for schema issues" + ) + return false + } + } + } + const parser = entry.parser + return (input: I): input is I & T => { + const exit = Effect.runSyncExit(parserResult(parser(input, SchemaAST.defaultParseOptions), input)) + if (Exit.isSuccess(exit)) return true InternalSchemaCause.getSchemaIssueOrThrow(exit.cause, "Type guard adapter can only return false for schema issues") return false } } +/** @internal */ +export function _is(ast: SchemaAST.AST) { + const typeAST = SchemaAST.toType(ast) + let guard: (input: I) => input is I & T = (input: I): input is I & T => { + guard = makeIs(typeAST) + return guard(input) + } + return (input: I): input is I & T => { + return guard(input) + } +} + /** @internal */ export function _issue(ast: SchemaAST.AST) { const parser = run(ast) @@ -202,8 +227,9 @@ export function _issue(ast: SchemaAST.AST) { * @since 4.0.0 */ export function asserts(schema: S, input: I): asserts input is I & S["Type"] { - const parser = asExit(run(SchemaAST.toType(schema.ast))) - const exit = parser(input, SchemaAST.defaultParseOptions) + const ast = SchemaAST.toType(schema.ast) + const result = CompilerRegistry.resolve(ast).parser(input, SchemaAST.defaultParseOptions) + const exit = Effect.runSyncExit(parserResult(result, input)) if (Exit.isFailure(exit)) { const issue = InternalSchemaCause.getSchemaIssueOrThrow( exit.cause, @@ -526,7 +552,9 @@ export function decodeUnknownSync>( schema: S, options?: SchemaAST.ParseOptions ): (input: unknown, options?: SchemaAST.ParseOptions) => S["Type"] { - return asSync(decodeUnknownEffect(schema, options)) + return CompilerRegistry.compilerAdaptersEnabled + ? makeSync(schema.ast, options) + : asSync(decodeUnknownEffect(schema, options)) } /** @@ -871,7 +899,9 @@ export function encodeUnknownSync>( schema: S, options?: SchemaAST.ParseOptions ): (input: unknown, options?: SchemaAST.ParseOptions) => S["Encoded"] { - return asSync(encodeUnknownEffect(schema, options)) + return CompilerRegistry.compilerAdaptersEnabled + ? makeSync(SchemaAST.flip(schema.ast), options) + : asSync(encodeUnknownEffect(schema, options)) } /** @@ -926,6 +956,22 @@ export function run(ast: SchemaAST.AST) { return runWithCompiler(normalCompiler, ast) } +function parserResult( + result: Effect.Effect, + input: unknown +): Effect.Effect { + if (result === InternalParser.sameExit) { + return Effect.succeed(input) as Effect.Effect + } + if (!effectIsExit(result)) { + return Effect.flatMapEager(result, getValue) + } + return (result as InternalParser.Success)[InternalParser.args] === + InternalParser.missing + ? getValue(InternalParser.missing) + : result as Effect.Effect +} + function runWithCompiler(compiler: Compiler, ast: SchemaAST.AST) { let parser: Parser return (input: unknown, options?: SchemaAST.ParseOptions): Effect.Effect => { @@ -998,6 +1044,49 @@ function asResult( } } +function makeSync( + ast: SchemaAST.AST, + options?: SchemaAST.ParseOptions +): (input: unknown, options?: SchemaAST.ParseOptions) => T { + let run: ((input: unknown, options?: SchemaAST.ParseOptions) => T) | undefined + return (input, overrideOptions) => + (run ??= makeSyncEntry(CompilerRegistry.resolve(ast), options))(input, overrideOptions) +} + +function makeSyncEntry( + entry: CompilerRegistry.Entry, + options?: SchemaAST.ParseOptions +): (input: unknown, options?: SchemaAST.ParseOptions) => T { + const decode = entry.decode + let detailed: ((input: unknown, options?: SchemaAST.ParseOptions) => T) | undefined + const run = decode === undefined + ? (input: unknown, parseOptions = SchemaAST.defaultParseOptions): T => + (detailed ??= makeDetailedSync(entry))(input, parseOptions) + : (input: unknown, parseOptions = SchemaAST.defaultParseOptions): T => { + if (input === InternalParser.missing) { + return (detailed ??= makeDetailedSync(entry))(input, parseOptions) + } + let output: unknown + try { + output = decode(input, parseOptions) + } catch (error) { + InternalSchemaCause.getSchemaIssueOrThrow(Cause.die(error), "Sync adapter can only throw schema issues") + throw error + } + if (output !== CompilerRegistry.invalid) return output as T + return (detailed ??= makeDetailedSync(entry))(input, parseOptions) + } + return options === undefined + ? run + : (input, overrideOptions) => run(input, mergeParseOptions(options, overrideOptions)) +} + +function makeDetailedSync( + entry: CompilerRegistry.Entry +): (input: unknown, options?: SchemaAST.ParseOptions) => T { + return asSync(runWithCompiler(() => entry.decodeEffect, entry.ast)) +} + function asSync( parser: (input: E, options?: SchemaAST.ParseOptions) => Effect.Effect ): (input: E, options?: SchemaAST.ParseOptions) => T { @@ -1012,6 +1101,41 @@ function asSync( } } +function runSync(effect: Effect.Effect, message: string): T { + const exit = Effect.runSyncExit(effect) + if (Exit.isSuccess(exit)) { + return exit.value + } + const issue = InternalSchemaCause.getSchemaIssueOrThrow(exit.cause, message) + throw new Error("Schema validation failed", { cause: issue }) +} + +function makeConstructorSync( + ast: SchemaAST.AST +): (input: E, options?: Schema.MakeOptions) => T { + let entry: CompilerRegistry.Entry | undefined + let parser: Parser | undefined + return (input, options) => { + entry ??= CompilerRegistry.resolve(ast) + const parseOptions = options?.disableChecks + ? options.parseOptions ? { ...options.parseOptions, disableChecks: true } : { disableChecks: true } + : options?.parseOptions ?? SchemaAST.defaultParseOptions + const make = entry.make + if (make !== undefined && input !== InternalParser.missing) { + let output: unknown + try { + output = make(input, parseOptions) + } catch (error) { + InternalSchemaCause.getSchemaIssueOrThrow(Cause.die(error), "Constructor adapter can only throw schema issues") + throw error + } + if (output !== CompilerRegistry.invalid && output !== InternalParser.missing) return output as T + } + const result = (parser ??= entry.makeEffect)(input, parseOptions) + return runSync(parserResult(result, input), "Constructor adapter can only throw schema issues") + } +} + /** @internal */ export interface Parser { ( @@ -1025,186 +1149,5 @@ export interface Compiler { (ast: SchemaAST.AST): Parser } -const normalCompiler: Compiler = memoize((ast) => makeParser(ast, normalCompiler)) -const constructorCompiler: Compiler = memoize((ast) => makeParser(ast, constructorCompiler, compileConstructorDefault)) -const compileDefaulted = memoize((ast: SchemaAST.AST) => - makeParser(ast, constructorCompiler, compileConstructorDefault, ast.context?.constructorDefault) -) - -function compileConstructorDefault(ast: SchemaAST.AST): Parser { - return ast.context?.constructorDefault ? compileDefaulted(ast) : constructorCompiler(ast) -} - -function applyTransformation( - result: Effect.Effect, - current: unknown, - transformation: SchemaAST.Link["transformation"], - options: SchemaAST.ParseOptions -): Effect.Effect { - let transformed: Effect.Effect, SchemaIssue.Issue, unknown> - if (effectIsExit(result) && result._tag === "Success") { - const optional = InternalParser.toOption( - result === InternalParser.sameExit - ? current - : (result as InternalParser.Success)[InternalParser.args] - ) - transformed = transformation._tag === "Transformation" - ? transformation.decode.run(optional, options) - : transformation.decode(InternalParser.succeed(optional), options) - } else if (transformation._tag === "Transformation") { - transformed = Effect.flatMapEager( - result, - (value) => transformation.decode.run(InternalParser.toOption(value), options) - ) - } else { - transformed = transformation.decode( - Effect.mapEager(result, InternalParser.toOption), - options - ) - } - return effectIsExit(transformed) && transformed._tag === "Success" - ? InternalParser.fromOptionExit( - (transformed as InternalParser.Success, SchemaIssue.Issue>)[InternalParser.args] - ) - : Effect.flatMapEager(transformed, InternalParser.fromOptionExit) -} - -function makeConstructorParser(descriptor: SchemaAST.ConstructorDescriptor, compile: Compiler): Parser { - let sourceParser: Parser - return (input, options) => { - if (input === InternalParser.missing) return InternalParser.missingExit - if (descriptor.isConstructed(input)) return InternalParser.sameExit - const result = (sourceParser ??= compile(descriptor.link.to))(input, options) - return applyTransformation(result, input, descriptor.link.transformation, options) - } -} - -function makeParser( - ast: SchemaAST.AST, - compile: Compiler, - compileConstructorDefault?: Compiler, - constructorDefault?: SchemaAST.Link -): Parser { - const descriptor = compileConstructorDefault ? SchemaAST.getConstructorDescriptor(ast) : undefined - const parser = descriptor - ? makeConstructorParser(descriptor, compile) - : ast.getParser(compile, compileConstructorDefault) - const checks = ast.checks - const links = constructorDefault - ? ast.encoding ? [...ast.encoding, constructorDefault] : [constructorDefault] - : ast.encoding - const encodingChecks = (ast as any).encodingChecks - if (!links && !checks && !encodingChecks) { - return parser - } - let encodingParsers: ReadonlyArray | undefined - const parseLocal = ( - input: unknown, - options: SchemaAST.ParseOptions - ) => { - let result = parser(input, options) - if (encodingChecks && !options.disableChecks) { - if (effectIsExit(result)) { - if (result._tag === "Success") { - const output = result === InternalParser.sameExit - ? input - : (result as InternalParser.Success)[InternalParser.args] - if (input !== InternalParser.missing && output !== InternalParser.missing) { - const issues = SchemaAST.collectIssues(encodingChecks, input, undefined, ast, options) - if (issues) { - result = Effect.fail(new SchemaIssue.Composite(ast, issues, input, options)) - } - } - } - } else { - result = Effect.flatMap(result, (value) => { - if (input !== InternalParser.missing && value !== InternalParser.missing) { - const issues = SchemaAST.collectIssues(encodingChecks, input, undefined, ast, options) - if (issues) { - return Effect.fail(new SchemaIssue.Composite(ast, issues, input, options)) - } - } - return Effect.succeed(value) - }) - } - } - - if (checks && !options.disableChecks) { - if (effectIsExit(result)) { - if (result._tag === "Success") { - const value = result === InternalParser.sameExit - ? input - : (result as InternalParser.Success)[InternalParser.args] - if (value === InternalParser.missing) return result - const issues = SchemaAST.collectIssues(checks, value, undefined, ast, options) - if (issues) { - result = Effect.fail(new SchemaIssue.Composite(ast, issues, value, options)) - } - } - } else { - result = Effect.flatMap(result, (value) => { - if (value !== InternalParser.missing) { - const issues = SchemaAST.collectIssues(checks, value, undefined, ast, options) - if (issues) { - return Effect.fail(new SchemaIssue.Composite(ast, issues, value, options)) - } - } - return Effect.succeed(value) - }) - } - } - - return result - } - if (!links) { - return parseLocal - } - return ( - input: unknown, - options: SchemaAST.ParseOptions - ) => { - const parsers = encodingParsers ??= links.map((link) => compile(link.to)) - let current = input - let result = parsers[parsers.length - 1](input, options) - for (let i = links.length - 1; i >= 0; i--) { - result = applyTransformation(result, current, links[i].transformation, options) - if (i !== 0) { - const next = parsers[i - 1] - if ((result as Exit.Exit)._tag === "Success") { - current = (result as InternalParser.Success)[InternalParser.args] - result = next(current, options) - } else { - result = Effect.flatMapEager(result, (value) => { - const nextResult = next(value, options) - return nextResult === InternalParser.sameExit ? InternalParser.succeed(value) : nextResult - }) - } - } - } - if ((result as Exit.Exit)._tag === "Success") { - const value = (result as InternalParser.Success)[InternalParser.args] - const local = parseLocal(value, options) - return local === InternalParser.sameExit ? result : local - } - result = Effect.catchCause( - result, - (cause) => - Effect.failCauseSync(() => - Cause.map( - cause, - (issue) => - new SchemaIssue.Encoding( - ast, - issue, - input, - options - ) - ) - ) - ) - return Effect.flatMapEager(result, (value) => { - const local = parseLocal(value, options) - return local === InternalParser.sameExit ? InternalParser.succeed(value) : local - }) - } -} +const normalCompiler: Compiler = (ast) => CompilerRegistry.resolve(ast).parser +const constructorCompiler: Compiler = (ast) => CompilerRegistry.resolve(ast).makeEffect diff --git a/repos/effect/packages/effect/src/SchemaRepresentation.ts b/repos/effect/packages/effect/src/SchemaRepresentation.ts index cfcd31c484..42387557e6 100644 --- a/repos/effect/packages/effect/src/SchemaRepresentation.ts +++ b/repos/effect/packages/effect/src/SchemaRepresentation.ts @@ -37,7 +37,7 @@ export interface CheckRepresentationAnnotation extends RepresentationAnnotati } /** - * Input passed to JSON Schema compiler annotations. + * Input and output contracts for JSON Schema compiler annotations. * * @since 4.0.0 */ @@ -53,19 +53,71 @@ export declare namespace ToJsonSchema { readonly schemas: ReadonlyArray } + /** + * The result of compiling a check to JSON Schema. + * + * **Details** + * + * Return the JSON Schema fragment directly when it represents the check + * exactly. Return `[schema, true]` when the fragment is a safe, looser + * approximation: it must accept every value accepted by the check, but may + * accept additional values. Use `[{}, true]` when the constraint is omitted; + * a bare `{}` declares that the check imposes no constraint. + * + * Approximation propagates through enclosing schemas and dependencies listed + * in `representation.schemas`, including recursive references. A `oneOf` + * union with an approximate branch exports as `anyOf`. Approximate record-key + * patterns cannot be used as `patternProperties` selectors. + * + * **Gotchas** + * + * The compiler trusts the callback's declaration; it does not prove that the + * fragment is exact or safely looser. Returning a plain fragment does not + * override approximation inherited from a schema dependency. + * + * **Example** (Declaring a Unicode length approximation) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * + * const short = Schema.String.check(Schema.makeFilter( + * (value: string) => value.length <= 1, + * { toJsonSchema: () => [{ maxLength: 1 }, true] } + * )) + * const schema = Schema.Union([short, Schema.Literal("😀")], { mode: "oneOf" }) + * const document = Schema.toJsonSchemaDocument(schema) + * + * Schema.is(schema)("😀") // => true + * document.schema.oneOf // => undefined + * document.schema.anyOf // => [{ type: "string", maxLength: 1 }, { type: "string", enum: ["😀"] }] + * ``` + * + * @category models + * @since 4.0.0 + */ + export type CheckOutput = JsonSchema.JsonSchema | readonly [schema: JsonSchema.JsonSchema, approximate: true] + /** * Compiles a check to a JSON Schema fragment. * + * **Details** + * + * Return a fragment for an exact translation or `[fragment, true]` for a safe, + * looser approximation. Dependencies in `representation.schemas` are compiled + * into the input's `schemas` array; their approximation status propagates automatically. + * * **Gotchas** * - * Treat the input schemas as immutable. The returned value must be a valid JSON Schema object graph and must not be + * Treat the input schemas as immutable. The returned fragment must be a valid JSON Schema object graph and must not be * mutated after this function returns. Local `$defs` references must use valid JSON Pointer URI fragments. Return a * new object graph to produce different output during a later compilation. * + * @see {@link CheckOutput} for exact and approximate results + * * @category models * @since 4.0.0 */ - export type Check = (input: CheckInput) => JsonSchema.JsonSchema + export type Check = (input: CheckInput) => CheckOutput } /** @@ -853,63 +905,63 @@ export const isBase64UrlReviver: FilterReviver = makeReviverFilter( ) /** - * Reviver for persisted `isStartsWith` checks. + * Reviver for persisted `isStartingWith` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isStartsWith}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isStartingWith}. * - * @see {@link Schema.isStartsWith} for creating the corresponding check + * @see {@link Schema.isStartingWith} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isStartsWithReviver: FilterReviver<{ +export const isStartingWithReviver: FilterReviver<{ readonly startsWith: string }> = makeReviverFilter( - "effect/schema/isStartsWith", + "effect/schema/isStartingWith", Schema.Struct({ startsWith: Schema.String }), - ({ annotations, payload }) => Schema.isStartsWith(payload.startsWith, annotations) + ({ annotations, payload }) => Schema.isStartingWith(payload.startsWith, annotations) ) /** - * Reviver for persisted `isEndsWith` checks. + * Reviver for persisted `isEndingWith` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isEndsWith}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isEndingWith}. * - * @see {@link Schema.isEndsWith} for creating the corresponding check + * @see {@link Schema.isEndingWith} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isEndsWithReviver: FilterReviver<{ +export const isEndingWithReviver: FilterReviver<{ readonly endsWith: string }> = makeReviverFilter( - "effect/schema/isEndsWith", + "effect/schema/isEndingWith", Schema.Struct({ endsWith: Schema.String }), - ({ annotations, payload }) => Schema.isEndsWith(payload.endsWith, annotations) + ({ annotations, payload }) => Schema.isEndingWith(payload.endsWith, annotations) ) /** - * Reviver for persisted `isIncludes` checks. + * Reviver for persisted `isIncluding` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isIncludes}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isIncluding}. * - * @see {@link Schema.isIncludes} for creating the corresponding check + * @see {@link Schema.isIncluding} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isIncludesReviver: FilterReviver<{ +export const isIncludingReviver: FilterReviver<{ readonly includes: string }> = makeReviverFilter( - "effect/schema/isIncludes", + "effect/schema/isIncluding", Schema.Struct({ includes: Schema.String }), - ({ annotations, payload }) => Schema.isIncludes(payload.includes, annotations) + ({ annotations, payload }) => Schema.isIncluding(payload.includes, annotations) ) /** @@ -1189,24 +1241,70 @@ export const isMaxLengthReviver: FilterReviver<{ ) /** - * Reviver for persisted `isLengthBetween` checks. + * Reviver for persisted `isBetweenLength` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isLengthBetween}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isBetweenLength}. * - * @see {@link Schema.isLengthBetween} for creating the corresponding check + * @see {@link Schema.isBetweenLength} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isLengthBetweenReviver: FilterReviver<{ +export const isBetweenLengthReviver: FilterReviver<{ readonly minimum: number readonly maximum: number }> = makeReviverFilter( - "effect/schema/isLengthBetween", + "effect/schema/isBetweenLength", Schema.Struct({ minimum: Schema.Natural, maximum: Schema.Natural }), - ({ annotations, payload }) => Schema.isLengthBetween(payload.minimum, payload.maximum, annotations) + ({ annotations, payload }) => Schema.isBetweenLength(payload.minimum, payload.maximum, annotations) +) + +/** + * Reviver for persisted `isMinCodePoints` checks. + * + * @see {@link Schema.isMinCodePoints} for creating the corresponding check + * @category validation + * @since 4.0.0 + */ +export const isMinCodePointsReviver: FilterReviver<{ + readonly minCodePoints: number +}> = makeReviverFilter( + "effect/schema/isMinCodePoints", + Schema.Struct({ minCodePoints: Schema.Natural }), + ({ annotations, payload }) => Schema.isMinCodePoints(payload.minCodePoints, annotations) +) + +/** + * Reviver for persisted `isMaxCodePoints` checks. + * + * @see {@link Schema.isMaxCodePoints} for creating the corresponding check + * @category validation + * @since 4.0.0 + */ +export const isMaxCodePointsReviver: FilterReviver<{ + readonly maxCodePoints: number +}> = makeReviverFilter( + "effect/schema/isMaxCodePoints", + Schema.Struct({ maxCodePoints: Schema.Natural }), + ({ annotations, payload }) => Schema.isMaxCodePoints(payload.maxCodePoints, annotations) +) + +/** + * Reviver for persisted `isBetweenCodePoints` checks. + * + * @see {@link Schema.isBetweenCodePoints} for creating the corresponding check + * @category validation + * @since 4.0.0 + */ +export const isBetweenCodePointsReviver: FilterReviver<{ + readonly minimum: number + readonly maximum: number +}> = makeReviverFilter( + "effect/schema/isBetweenCodePoints", + Schema.Struct({ minimum: Schema.Natural, maximum: Schema.Natural }), + ({ annotations, payload }) => Schema.isBetweenCodePoints(payload.minimum, payload.maximum, annotations) ) /** @@ -1250,24 +1348,24 @@ export const isMaxSizeReviver: FilterReviver<{ ) /** - * Reviver for persisted `isSizeBetween` checks. + * Reviver for persisted `isBetweenSize` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isSizeBetween}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isBetweenSize}. * - * @see {@link Schema.isSizeBetween} for creating the corresponding check + * @see {@link Schema.isBetweenSize} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isSizeBetweenReviver: FilterReviver<{ +export const isBetweenSizeReviver: FilterReviver<{ readonly minimum: number readonly maximum: number }> = makeReviverFilter( - "effect/schema/isSizeBetween", + "effect/schema/isBetweenSize", Schema.Struct({ minimum: Schema.Natural, maximum: Schema.Natural }), - ({ annotations, payload }) => Schema.isSizeBetween(payload.minimum, payload.maximum, annotations) + ({ annotations, payload }) => Schema.isBetweenSize(payload.minimum, payload.maximum, annotations) ) /** @@ -1311,24 +1409,24 @@ export const isMaxPropertiesReviver: FilterReviver<{ ) /** - * Reviver for persisted `isPropertiesLengthBetween` checks. + * Reviver for persisted `isBetweenProperties` checks. * * **When to use** * - * Use when reconstructing documents that may contain checks created by {@link Schema.isPropertiesLengthBetween}. + * Use when reconstructing documents that may contain checks created by {@link Schema.isBetweenProperties}. * - * @see {@link Schema.isPropertiesLengthBetween} for creating the corresponding check + * @see {@link Schema.isBetweenProperties} for creating the corresponding check * * @category validation * @since 4.0.0 */ -export const isPropertiesLengthBetweenReviver: FilterReviver<{ +export const isBetweenPropertiesReviver: FilterReviver<{ readonly minimum: number readonly maximum: number }> = makeReviverFilter( - "effect/schema/isPropertiesLengthBetween", + "effect/schema/isBetweenProperties", Schema.Struct({ minimum: Schema.Natural, maximum: Schema.Natural }), - ({ annotations, payload }) => Schema.isPropertiesLengthBetween(payload.minimum, payload.maximum, annotations) + ({ annotations, payload }) => Schema.isBetweenProperties(payload.minimum, payload.maximum, annotations) ) /** @@ -2150,6 +2248,8 @@ const jsonSchemaRevivers: ReadonlyArray = [ isIntReviver, isMinLengthReviver, isMaxLengthReviver, + isMinCodePointsReviver, + isMaxCodePointsReviver, isMinPropertiesReviver, isMaxPropertiesReviver, isPropertyNamesReviver, @@ -2170,13 +2270,15 @@ const jsonSchemaRevivers: ReadonlyArray = [ * * - `"error"` rejects the document and is the default. * - `"ignore"` skips the constraint. - * - `"apply"` compiles and enforces the constraint with the runtime's native regular expression engine. + * - `"apply"` compiles and enforces the constraint with the runtime's native regular expression engine in Unicode + * mode. Patterns that cannot be compiled with the `u` flag are rejected with their source path. * * **Gotchas** * * Use `patterns: "apply"` only for trusted documents because regular expression evaluation may block for an unbounded * amount of time. `patterns: "ignore"` can admit values the source rejects, but can also reject previously valid * values inside `oneOf` when removing constraints makes multiple branches match. + * Input documents are assumed to be valid Draft 2020-12 schemas and are not validated against the meta-schema. * Ignoring `patternProperties` also skips its value constraints and `additionalProperties`, because matching keys cannot * be determined without evaluating the patterns. * `onEnter` must return a JSON Schema object. Its result is used directly, and exceptions raised by the callback pass @@ -2330,7 +2432,9 @@ export interface ToRepresentationOptions { * **Details** * * Apply `SchemaAST.toType` to the AST first to lower its type side instead. The optional reference policy controls which - * candidates are moved into the document's shared reference table. + * candidates are moved into the document's shared reference table. TypeScript-only + * distinctions such as those added by `Schema.brand` are absent from the AST + * and cannot be reconstructed from the resulting document. * * @see {@link toRepresentations} for multiple roots sharing one reference table * @@ -2396,19 +2500,29 @@ export function toMultiDocument(document: Document): MultiDocument { * decoder remains the final authority. Passing JSON Schema validation does not * guarantee that Effect decoding will succeed. * + * A `oneOf` union exports as `anyOf` when any branch contains a known + * approximation; otherwise it retains `oneOf`. Approximation propagates through + * checks, nested schemas, check dependencies, and recursive references. Filters + * without a `toJsonSchema` callback and opaque declarations are approximate. + * Custom callbacks declare their result using {@link ToJsonSchema.CheckOutput}. + * * **Gotchas** * * - Reference allocation is already fixed in the input `Document`. The inherited `referencePolicy` option has no effect * here; pass it to {@link toRepresentation} when creating the document. - * - String length, RegExp flags, decoded-object property checks, and `oneOf` can differ from Effect validation. + * - String length, RegExp flags, and decoded-object property checks can differ from Effect validation. * - Opaque declarations are represented by an unconstrained JSON Schema. * - Check callback results are used directly, and exceptions raised by a callback pass through unchanged. Callbacks - * must treat their input schemas as immutable. Each returned value must be a valid JSON Schema object graph and must + * must treat their input schemas as immutable. Each returned fragment must be a valid JSON Schema object graph and must * not be mutated after the callback returns. The callback author is responsible for the emitted semantics. * - Local definition references returned by callbacks are resolved together with compiler-generated references. * Invalid JSON Pointer URI fragments throw an `Error`. * - The default `onExcessProperty: "ignore"` matches the decoder default. Use `onExcessProperty: "error"` in both * places when a closed object contract is required. + * - Record keys require an exact translation to a pattern before they can select `patternProperties` values. + * Otherwise, `"ignore"` omits that index-signature constraint. In `"error"` mode, the compiler emits the generated + * key schemas under `propertyNames` and allows unmatched properties to satisfy any candidate index value schema. + * Exact selectors from other index signatures are retained, even when their value schemas are approximate. * * @see {@link toJsonSchemaMultiDocument} for multiple roots sharing definitions * @@ -2429,15 +2543,20 @@ export function toJsonSchemaDocument( * * Use when several representation roots must share the same JSON Schema definitions. * + * **Details** + * + * Uses the same approximation tracking and `oneOf` and record-key fallbacks as + * {@link toJsonSchemaDocument}, including across shared and recursive definitions. + * * **Gotchas** * * - Reference allocation is already fixed in the input `MultiDocument`. The inherited `referencePolicy` option has no * effect here; pass it to {@link toRepresentations} when creating the document. * - Every definition is compiled, including definitions that are not reachable from a root. Check callbacks must treat - * their input schemas as immutable. Each returned value must be a valid JSON Schema object graph and must not be + * their input schemas as immutable. Each returned fragment must be a valid JSON Schema object graph and must not be * mutated after the callback returns. Local definition references returned by callbacks are resolved together with * compiler-generated references. Invalid JSON Pointer URI fragments throw an `Error`. - * - String length, RegExp flags, decoded-object property checks, `oneOf`, and custom check callbacks can differ from + * - String length, RegExp flags, decoded-object property checks, and custom check callbacks can differ from * Effect validation, as described by {@link toJsonSchemaDocument}. * * @see {@link toJsonSchemaDocument} for a single root @@ -2839,6 +2958,8 @@ export function fromRepresentations( * Translates a Draft 2020-12 subset using Effect schemas and built-in checks. Validation follows those checks and the * decoder's parse options, so import and re-export do not guarantee identical accepted values or a lossless round trip. * Import errors explain the unsupported constraint or reference and include its source path. + * The input is assumed to be a valid Draft 2020-12 document; this function does not validate it against the meta-schema. + * Instance validation semantics assume JSON-compatible JavaScript values produced by `JSON.parse`. * * **Gotchas** * @@ -2850,8 +2971,10 @@ export function fromRepresentations( * Object keyword scopes still constrain declared properties in intersections. Combinations requiring index * signatures that exclude explicit properties or patterned keys are rejected with an explanation of the limitation. * - Property count and name checks run on the decoded object, after excess properties have been stripped. - * - String length checks count UTF-16 code units. `integer` uses `Schema.isInt`, which requires safe integers. - * Applied patterns use `Schema.isPattern` without adding a Unicode flag. + * - String `minLength` and `maxLength` checks count Unicode code points, except that `minLength: 1` uses the equivalent + * UTF-16 non-empty check. `integer` uses `Schema.isInt`, which requires safe integers. + * - Applied patterns use `Schema.isPattern` with the `u` flag. Patterns that cannot be compiled in Unicode mode are + * rejected as unsupported translations with their source path. * - `$dynamicRef`, `contains`, `dependentRequired`, `dependentSchemas`, active `if` / `then` / `else`, * `unevaluatedItems`, and `unevaluatedProperties` are rejected with an error identifying the unsupported keyword. Inactive * conditional keywords and `minContains` / `maxContains` without `contains` have no validation effect and are ignored. @@ -2894,6 +3017,7 @@ export function fromJsonSchemaDocument( * **Details** * * Uses the same best-effort translation, built-in checks, and excess-property behavior as {@link fromJsonSchemaDocument}. + * The input is assumed to be a valid Draft 2020-12 document; this function does not validate it against the meta-schema. * * **Gotchas** * diff --git a/repos/effect/packages/effect/src/SchemaTransformation.ts b/repos/effect/packages/effect/src/SchemaTransformation.ts index 1fa08ac9fd..d02d894a84 100644 --- a/repos/effect/packages/effect/src/SchemaTransformation.ts +++ b/repos/effect/packages/effect/src/SchemaTransformation.ts @@ -18,7 +18,9 @@ import * as DateTime from "./DateTime.ts" import * as Duration from "./Duration.ts" import * as Effect from "./Effect.ts" import { format, formatDate, formatJson } from "./Formatter.ts" +import { dual } from "./Function.ts" import * as Option from "./Option.ts" +import * as Pipeable from "./Pipeable.ts" import * as Predicate from "./Predicate.ts" import type { ErrorOptions, Json } from "./Schema.ts" import type * as SchemaAST from "./SchemaAST.ts" @@ -47,6 +49,7 @@ import * as SchemaIssue from "./SchemaIssue.ts" * `Effect, Issue, REE>`. * - `flip()` swaps the decode and encode functions, producing a * `Middleware`. + * - Middleware values implement `Pipeable`. * * Typically constructed indirectly via `Schema.middlewareDecoding` or * `Schema.middlewareEncoding` rather than by using `new Middleware` directly. @@ -69,7 +72,7 @@ import * as SchemaIssue from "./SchemaIssue.ts" * @category models * @since 4.0.0 */ -export interface Middleware { +export interface Middleware extends Pipeable.Pipeable { readonly _tag: "Middleware" readonly decode: ( effect: Effect.Effect, SchemaIssue.Issue, RDE>, @@ -97,7 +100,7 @@ export const Middleware: new( effect: Effect.Effect, SchemaIssue.Issue, RET>, options: SchemaAST.ParseOptions ) => Effect.Effect, SchemaIssue.Issue, REE> -) => Middleware = class { +) => Middleware = class extends Pipeable.Class { readonly _tag = "Middleware" readonly decode: ( effect: Effect.Effect, SchemaIssue.Issue, RDE>, @@ -118,6 +121,7 @@ export const Middleware: new( options: SchemaAST.ParseOptions ) => Effect.Effect, SchemaIssue.Issue, REE> ) { + super() this.decode = decode this.encode = encode } @@ -145,23 +149,25 @@ const TypeId = "~effect/SchemaTransformation/Transformation" * `Schema.decode`, `Schema.encode`, and `Schema.link`. Each direction is a * `SchemaGetter.Getter` that handles optionality, failure, and Effect services. * - * - Immutable — `flip()` and `compose()` return new instances. + * - Immutable — `flip()` and {@link composeTransformation} return new instances. + * - Transformation values implement `Pipeable`. * - `flip()` swaps the decode and encode getters. - * - `compose(other)` chains: `this.decode` then `other.decode` for decoding, - * `other.encode` then `this.encode` for encoding. + * - `composeTransformation(self, other)` chains: `self.decode` then `other.decode` for decoding, + * `other.encode` then `self.encode` for encoding. * * **Example** (Composing two transformations) * * ```ts import.meta.vitest * import { SchemaTransformation } from "effect" * - * const trimAndLower = SchemaTransformation.trim().compose( + * const trimAndLower = SchemaTransformation.composeTransformation( + * SchemaTransformation.trim(), * SchemaTransformation.toLowerCase() * ) * trimAndLower._tag // => "Transformation" * ``` * - * @see {@link make} — construct from `{ decode, encode }` getters + * @see {@link makeTransformation} — construct from `{ decode, encode }` getters * @see {@link transform} — construct from pure functions * @see {@link transformEffect} — construct from effectful functions * @see {@link Middleware} — effect-pipeline-level alternative @@ -169,13 +175,12 @@ const TypeId = "~effect/SchemaTransformation/Transformation" * @category models * @since 4.0.0 */ -export interface Transformation { +export interface Transformation extends Pipeable.Pipeable { readonly [TypeId]: typeof TypeId readonly _tag: "Transformation" readonly decode: SchemaGetter.Getter readonly encode: SchemaGetter.Getter flip(): Transformation - compose(other: Transformation): Transformation } /** @@ -187,7 +192,7 @@ export interface Transformation { export const Transformation: new( decode: SchemaGetter.Getter, encode: SchemaGetter.Getter -) => Transformation = class { +) => Transformation = class extends Pipeable.Class { readonly [TypeId] = TypeId readonly _tag = "Transformation" readonly decode: SchemaGetter.Getter @@ -197,20 +202,63 @@ export const Transformation: new( decode: SchemaGetter.Getter, encode: SchemaGetter.Getter ) { + super() this.decode = decode this.encode = encode } flip(): Transformation { return new Transformation(this.encode, this.decode) } - compose(other: Transformation): Transformation { - return new Transformation( - this.decode.compose(other.decode), - other.encode.compose(this.encode) - ) - } } +/** + * Composes two schema transformations into a single bidirectional conversion. + * + * **When to use** + * + * Use when decoding and encoding require the same sequence of conversion + * steps in opposite directions. + * + * **Details** + * + * Decoding applies `self.decode` followed by `other.decode`. Encoding applies + * `other.encode` followed by `self.encode`. The function supports both + * `composeTransformation(self, other)` and `composeTransformation(other)(self)`. + * + * **Example** (Trimming and lowercasing a string) + * + * ```ts import.meta.vitest + * import { Schema, SchemaTransformation } from "effect" + * + * const transformation = SchemaTransformation.composeTransformation( + * SchemaTransformation.trim(), + * SchemaTransformation.toLowerCase() + * ) + * const schema = Schema.String.pipe(Schema.decode(transformation)) + * + * Schema.decodeUnknownSync(schema)(" HELLO ") // => "hello" + * ``` + * + * @category combining + * @since 4.0.0 + */ +export const composeTransformation: { + ( + other: Transformation + ): (self: Transformation) => Transformation + ( + self: Transformation, + other: Transformation + ): Transformation +} = dual(2, ( + self: Transformation, + other: Transformation +): Transformation => + new Transformation( + SchemaGetter.compose(self.decode, other.decode), + SchemaGetter.compose(other.encode, self.encode) + )) + /** * Returns `true` if `u` is a `Transformation` instance. * @@ -234,7 +282,7 @@ export const Transformation: new( * ``` * * @see {@link Transformation} - * @see {@link make} + * @see {@link makeTransformation} * * @category guards * @since 4.0.0 @@ -262,7 +310,7 @@ export function isTransformation(u: unknown): u is Transformation((s) => Number(s)), * encode: SchemaGetter.transform((n) => String(n)) * }) @@ -276,7 +324,7 @@ export function isTransformation(u: unknown): u is Transformation(options: { +export const makeTransformation = (options: { readonly decode: SchemaGetter.Getter readonly encode: SchemaGetter.Getter }): Transformation => { @@ -324,7 +372,7 @@ export const make = (options: { * * @see {@link transform} — for infallible, pure transformations * @see {@link transformOptional} — for transformations that handle missing keys - * @see {@link make} — for transformations from existing Getters + * @see {@link makeTransformation} — for transformations from existing Getters * * @category transforming * @since 3.10.0 @@ -1125,7 +1173,7 @@ export const durationFromMillis: Transformation = tra */ export const byteSizeFromString: Transformation = transformEffect({ decode: (input, options) => - Option.match(ByteSize.fromInput(input), { + Option.match(ByteSize.fromString(input), { onNone: () => Effect.fail( new SchemaIssue.InvalidValue( diff --git a/repos/effect/packages/effect/src/Scope.ts b/repos/effect/packages/effect/src/Scope.ts index 12e1c6b1bf..8e2cf1904e 100644 --- a/repos/effect/packages/effect/src/Scope.ts +++ b/repos/effect/packages/effect/src/Scope.ts @@ -45,6 +45,7 @@ const CloseableTypeId = effect.ScopeCloseableTypeId export interface Scope { readonly [TypeId]: typeof TypeId readonly strategy: "sequential" | "parallel" + readonly parent: Scope | undefined state: State.Open | State.Closed | State.Empty } /** @@ -75,9 +76,8 @@ export interface Closeable extends Scope { } /** - * The `State` namespace contains the concrete states of a scope: `Empty` - * before any finalizers are registered, `Open` with registered finalizers, and - * `Closed` with the exit value used to close the scope. + * Scope states: `Empty` has no finalizers, `Open` has at least one, and + * `Closed` holds the exit value. * * **Example** (Checking scope states) * @@ -98,13 +98,12 @@ export interface Closeable extends Scope { */ export declare namespace State { /** - * Represents an open scope with no registered finalizers yet. + * Represents an open scope with no finalizers currently registered. * * **Details** * - * Adding the first finalizer transitions the scope to `Open`; closing an - * empty scope transitions directly to `Closed` without producing a finalizer - * effect. + * Adding a finalizer moves it to `Open`; removing the last one returns it + * to `Empty`. * * **Example** (Inspecting an empty scope state) * @@ -467,6 +466,8 @@ export const forkUnsafe: (scope: Scope, finalizerStrategy?: "sequential" | "para * * Finalizers run in the scope's configured order and receive the supplied * `Exit`. + * By default, finalizers run uninterruptibly, so interrupting the closing fiber + * waits for them to finish; a finalizer can explicitly restore interruptibility. * * **Example** (Running scope finalizers) * @@ -490,7 +491,7 @@ export const forkUnsafe: (scope: Scope, finalizerStrategy?: "sequential" | "para * @category combinators * @since 2.0.0 */ -export const close: (self: Scope, exit: Exit) => Effect = effect.scopeClose +export const close: (self: Closeable, exit: Exit) => Effect = effect.scopeClose /** * Closes a scope unsafely with the provided exit value. @@ -508,14 +509,16 @@ export const close: (self: Scope, exit: Exit) => Effect = effe * * **Gotchas** * - * Ignoring the returned effect skips registered finalizers. + * Ignoring the returned effect skips registered finalizers. The caller must + * run the returned effect uninterruptibly: the scope is already closed, so + * interruption during finalization can permanently skip remaining finalizers. * * @see {@link close} for the usual effectful close operation that always returns an `Effect` * * @category unsafe * @since 4.0.0 */ -export const closeUnsafe: (self: Scope, exit_: Exit) => Effect | undefined = +export const closeUnsafe: (self: Closeable, exit_: Exit) => Effect | undefined = effect.scopeCloseUnsafe /** diff --git a/repos/effect/packages/effect/src/ScopedCache.ts b/repos/effect/packages/effect/src/ScopedCache.ts index c5d6abad5f..61b4fb7240 100644 --- a/repos/effect/packages/effect/src/ScopedCache.ts +++ b/repos/effect/packages/effect/src/ScopedCache.ts @@ -11,11 +11,12 @@ * @since 4.0.0 */ import * as Arr from "./Array.ts" +import * as Cause from "./Cause.ts" import * as Context from "./Context.ts" import * as Deferred from "./Deferred.ts" import * as Duration from "./Duration.ts" import type * as Effect from "./Effect.ts" -import type * as Exit from "./Exit.ts" +import * as Exit from "./Exit.ts" import * as Fiber from "./Fiber.ts" import { dual, identity } from "./Function.ts" import * as core from "./internal/core.ts" @@ -105,6 +106,8 @@ export interface Entry { expiresAt: number | undefined readonly deferred: Deferred.Deferred readonly scope: Scope.Closeable + awaiters: number + fiber?: Fiber.Fiber } /** @@ -268,32 +271,65 @@ export const get: { // Move the entry to the end of the map to keep it fresh MutableHashMap.remove(state.map, key) MutableHashMap.set(state.map, key, oentry.value) - return restore(Deferred.await(oentry.value.deferred)) + return awaitEntry(oentry.value, restore) } const scope = Scope.makeUnsafe() const deferred = Deferred.makeUnsafe() const entry: Entry = { expiresAt: undefined, deferred, - scope + scope, + awaiters: 0 } MutableHashMap.set(state.map, key, entry) return checkCapacity(fiber, state.map, self.capacity).pipe( Option.isSome(oentry) ? effect.flatMap(() => Scope.close(oentry.value.scope, effect.exitVoid)) : identity, - effect.flatMap(() => Scope.provide(restore(self.lookup(key)), scope)), - effect.onExit((exit) => { - Deferred.doneUnsafe(deferred, exit) - const ttl = self.timeToLive(exit, key) - if (Duration.isFinite(ttl)) { - entry.expiresAt = fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) - } - return effect.void + effect.flatMap(() => { + entry.fiber = effect.forkUnsafe( + fiber, + effect.onExit(effect.suspend(() => Scope.provide(self.lookup(key), scope)), (exit) => { + Deferred.doneUnsafe(deferred, exit) + if (effect.exitHasInterrupts(exit)) { + if (self.state._tag === "Open") { + const current = MutableHashMap.get(self.state.map, key) + if (Option.isSome(current) && current.value === entry) MutableHashMap.remove(self.state.map, key) + } + return Scope.close(scope, exit) + } + const ttl = self.timeToLive(exit, key) + if (Duration.isFinite(ttl)) { + entry.expiresAt = fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) + } + return effect.void + }), + true, + true + ) + return awaitEntry(entry, restore) }) ) }) ) ) +const awaitEntry = ( + entry: Entry, + restore: (effect: Effect.Effect) => Effect.Effect +): Effect.Effect => { + const fiber = entry.fiber + if (fiber === undefined || Deferred.isDoneUnsafe(entry.deferred)) return restore(Deferred.await(entry.deferred)) + entry.awaiters++ + // Install cleanup before restore so a pending interrupt cannot skip the decrement. + return effect.onExit(restore(Deferred.await(entry.deferred)), () => { + entry.awaiters-- + if (entry.awaiters > 0 || Deferred.isDoneUnsafe(entry.deferred)) return effect.void + return effect.flatMap(effect.fiberInterrupt(fiber), () => { + const exit = fiber.pollUnsafe()! + return Exit.isFailure(exit) && Cause.hasDies(exit.cause) ? effect.failCause(exit.cause) : effect.void + }) + }) +} + const hasExpired = (entry: Entry, fiber: Fiber.Fiber): boolean => { if (entry.expiresAt === undefined) { return false @@ -350,7 +386,7 @@ export const getOption: { core.withFiber((fiber) => effect.flatMap( getImpl(self, key, fiber), - (entry) => entry ? effect.asSome(restore(Deferred.await(entry.deferred))) : effect.succeedNone + (entry) => entry ? effect.asSome(awaitEntry(entry, restore)) : effect.succeedNone ) ) ) @@ -464,6 +500,7 @@ export const set: { MutableHashMap.set(state.map, key, { scope: Scope.makeUnsafe(), deferred, + awaiters: 0, expiresAt: Duration.isFinite(ttl) ? fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) : undefined @@ -583,7 +620,7 @@ export const invalidateWhen: { if (entry === undefined) { return effect.succeed(false) } - return restore(Deferred.await(entry.deferred)).pipe( + return awaitEntry(entry, restore).pipe( effect.flatMap((value) => { if (self.state._tag === "Closed") { return effect.succeed(false) @@ -631,39 +668,35 @@ export const refresh: { (self: ScopedCache, key: Key): Effect.Effect => effect.uninterruptibleMask(effect.fnUntraced(function*(restore) { if (self.state._tag === "Closed") return yield* effect.interrupt + if (!MutableHashMap.has(self.state.map, key)) return yield* restore(get(self, key)) const fiber = Fiber.getCurrent()! const scope = Scope.makeUnsafe() const deferred = Deferred.makeUnsafe() - const entry: Entry = { - scope, - expiresAt: undefined, - deferred - } - const newEntry = !MutableHashMap.has(self.state.map, key) - if (newEntry) { - MutableHashMap.set(self.state.map, key, entry) - yield* checkCapacity(fiber, self.state.map, self.capacity) - } const exit = yield* effect.exit(effect.suspend(() => restore(Scope.provide(self.lookup(key), scope)))) Deferred.doneUnsafe(deferred, exit) + if (effect.exitHasInterrupts(exit)) { + yield* Scope.close(scope, exit) + return yield* exit + } // @ts-ignore async gap if (self.state._tag === "Closed") { - if (!newEntry) { - yield* Scope.close(scope, effect.exitVoid) - } + yield* Scope.close(scope, effect.exitVoid) return yield* effect.interrupt } const ttl = self.timeToLive(exit, key) - entry.expiresAt = Duration.isFinite(ttl) - ? fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) - : undefined - if (!newEntry) { - const oentry = MutableHashMap.get(self.state.map, key) - MutableHashMap.set(self.state.map, key, entry) - yield* checkCapacity(fiber, self.state.map, self.capacity) - if (Option.isSome(oentry)) { - yield* Scope.close(oentry.value.scope, effect.exitVoid) - } + const entry: Entry = { + scope, + deferred, + awaiters: 0, + expiresAt: Duration.isFinite(ttl) + ? fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl) + : undefined + } + const oentry = MutableHashMap.get(self.state.map, key) + MutableHashMap.set(self.state.map, key, entry) + yield* checkCapacity(fiber, self.state.map, self.capacity) + if (Option.isSome(oentry)) { + yield* Scope.close(oentry.value.scope, effect.exitVoid) } return yield* exit })) diff --git a/repos/effect/packages/effect/src/ScopedRef.ts b/repos/effect/packages/effect/src/ScopedRef.ts index 8cbb2609e8..af4a352f79 100644 --- a/repos/effect/packages/effect/src/ScopedRef.ts +++ b/repos/effect/packages/effect/src/ScopedRef.ts @@ -51,15 +51,24 @@ const Proto = { } } +interface ScopedRefImpl extends ScopedRef { + // Keep every generation at the ref's creation slot in the owner's finalizer order. + readonly scope: Scope.Scope +} + const makeUnsafe = ( - scope: Scope.Closeable, + scope: Scope.Scope, + generation: Scope.Closeable, value: A -): ScopedRef => { +): ScopedRefImpl => { const self = Object.create(Proto) - self.backing = Synchronized.makeUnsafe([scope, value] as const) + self.scope = scope + self.backing = Synchronized.makeUnsafe([generation, value] as const) return self } +const isClosed = (scope: Scope.Scope): boolean => scope.state._tag === "Closed" + /** * Creates a new `ScopedRef` from an effect that acquires the initial value. * @@ -77,14 +86,14 @@ export const fromAcquire: ( ) => Effect.Effect, E, Scope.Scope | R> = Effect.fnUntraced(function*( acquire: Effect.Effect ) { - const scope = Scope.makeUnsafe() + const scope = Scope.forkUnsafe(yield* Effect.scope) + const generation = Scope.forkUnsafe(scope) const value = yield* acquire.pipe( - Scope.provide(scope), + Scope.provide(generation), Effect.tapCause((cause) => Scope.close(scope, Exit.failCause(cause))) ) - const self = makeUnsafe(scope, value) - yield* Effect.addFinalizer((exit) => Scope.close(self.backing.backing.ref.current[0], exit)) - return self + if (isClosed(generation)) return yield* Effect.interrupt + return makeUnsafe(scope, generation, value) }, Effect.uninterruptible) /** @@ -143,12 +152,7 @@ export const get = (self: ScopedRef): Effect.Effect => Effect.sync(() = * @since 2.0.0 */ export const make = (evaluate: LazyArg): Effect.Effect, never, Scope.Scope> => - Effect.suspend(() => { - const scope = Scope.makeUnsafe() - const value = evaluate() - const self = makeUnsafe(scope, value) - return Effect.as(Effect.addFinalizer((exit) => Scope.close(self.backing.backing.ref.current[0], exit)), self) - }) + fromAcquire(Effect.sync(evaluate)) /** * Sets the value of this reference to a newly acquired scoped value, releasing @@ -178,15 +182,16 @@ export const set: { self: ScopedRef, acquire: Effect.Effect ) { - const scope = Scope.makeUnsafe() + const generation = Scope.forkUnsafe((self as ScopedRefImpl).scope) const value = yield* acquire.pipe( - Scope.provide(scope), - Effect.tapCause((cause) => Scope.close(scope, Exit.failCause(cause))) + Scope.provide(generation), + Effect.tapCause((cause) => Scope.close(generation, Exit.failCause(cause))) ) yield* Scope.close(self.backing.backing.ref.current[0], Exit.void).pipe( - Effect.tapCause((cause) => Scope.close(scope, Exit.failCause(cause))) + Effect.tapCause((cause) => Scope.close(generation, Exit.failCause(cause))) ) - self.backing.backing.ref.current = [scope, value] + if (isClosed(generation)) return yield* Effect.interrupt + self.backing.backing.ref.current = [generation, value] }, Effect.uninterruptible, (effect, self) => self.backing.semaphore.withPermit(effect) diff --git a/repos/effect/packages/effect/src/Semaphore.ts b/repos/effect/packages/effect/src/Semaphore.ts index 217e80920c..eaa8e59c69 100644 --- a/repos/effect/packages/effect/src/Semaphore.ts +++ b/repos/effect/packages/effect/src/Semaphore.ts @@ -159,6 +159,13 @@ export interface Semaphore { * **When to use** * * Use to return every currently taken permit to the semaphore at once. + * + * **Gotchas** + * + * This does not stop effects already running with `withPermit` or `withPermits`. + * Their permits become available immediately, but those effects still release + * their permits when they finish. The resulting available count can then + * exceed the semaphore's configured number of permits. */ readonly releaseAll: Effect.Effect } @@ -565,6 +572,13 @@ export const release: { * Use to return every currently taken permit to a semaphore at once, typically * during cleanup of manual `take` / `release` protocols. * + * **Gotchas** + * + * This does not stop effects already running with `withPermit` or `withPermits`. + * Their permits become available immediately, but those effects still release + * their permits when they finish. The resulting available count can then + * exceed the semaphore's configured number of permits. + * * @see {@link release} for releasing a known permit count * @see {@link withPermits} for automatic acquire and release around an effect * diff --git a/repos/effect/packages/effect/src/Stream.ts b/repos/effect/packages/effect/src/Stream.ts index 6cd8b5514b..3554bad47c 100644 --- a/repos/effect/packages/effect/src/Stream.ts +++ b/repos/effect/packages/effect/src/Stream.ts @@ -54,6 +54,7 @@ import { isString } from "./String.ts" import type * as Take from "./Take.ts" import type { ParentSpan, SpanOptions } from "./Tracer.ts" import type { + Concurrency, Covariant, ExcludeReason, ExcludeTag, @@ -62,7 +63,9 @@ import type { NarrowReason, NoInfer, OmitReason, + ReasonOf, ReasonTags, + Simplify, Tags, TupleOf, unassigned @@ -217,7 +220,7 @@ export interface VarianceStruct { * @category utility types * @since 3.4.0 */ -export type Success> = [T] extends [Stream] ? _A : never +export type Success = T extends Stream ? _A : never /** * Extract the error type from a Stream type. @@ -235,7 +238,7 @@ export type Success> = [T] extends [Stream> = [T] extends [Stream] ? _E : never +export type Error = T extends Stream ? _E : never /** * Extract the services type from a Stream type. @@ -257,8 +260,7 @@ export type Error> = [T] extends [Stream> = [T] extends [Stream] ? _R - : never +export type Services = T extends Stream ? _R : never /** * Checks whether a value is a Stream. @@ -651,7 +653,7 @@ export const transformPullBracket = ( * values.flat() // => [1, 2, 3] * ``` * - * @category constructors + * @category destructors * @since 2.0.0 */ export const toChannel = ( @@ -1669,7 +1671,7 @@ export const unwrap = ( * events // => ["acquire", "release"] * ``` * - * @category constructors + * @category resource management * @since 2.0.0 */ export const scoped = ( @@ -1703,6 +1705,35 @@ export const map: { )) })) +/** + * Replaces every element of the stream with the provided constant value. + * + * **Example** (Replacing stream elements) + * + * ```ts import.meta.vitest + * import { Effect, Stream } from "effect" + * + * const program = Effect.gen(function*() { + * const values = yield* Stream.make(1, 2, 3).pipe( + * Stream.as("x"), + * Stream.runCollect + * ) + * values // => [ 'x', 'x', 'x' ] + * }) + * + * await Effect.runPromise(program) + * ``` + * + * @see {@link map} for deriving the replacement value from each element + * + * @category mapping + * @since 4.0.0 + */ +export const as: { + (value: B): (self: Stream) => Stream + (self: Stream, value: B): Stream +} = dual(2, (self: Stream, value: B): Stream => map(self, () => value)) + /** * Maps both the failure and success channels of a stream. * @@ -1712,8 +1743,8 @@ export const map: { * import { Effect, Stream } from "effect" * * const mapper = { - * onFailure: (error: string) => `error: ${error}`, - * onSuccess: (value: number) => value * 2 + * onElement: (value: number) => value * 2, + * onError: (error: string) => `error: ${error}` * } * * const program = Effect.gen(function*() { @@ -1738,20 +1769,20 @@ export const map: { * @since 2.0.0 */ export const mapBoth: { - ( - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ( + options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 } ): (self: Stream) => Stream - ( + ( self: Stream, - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 } ): Stream -} = dual(2, ( +} = dual(2, ( self: Stream, - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 } ): Stream => self.pipe( - map(options.onSuccess), - mapError(options.onFailure) + map(options.onElement), + mapError(options.onError) )) /** @@ -1831,7 +1862,7 @@ export const mapEffect: { ( f: (a: A, i: number) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined ): (self: Stream) => Stream @@ -1839,7 +1870,7 @@ export const mapEffect: { self: Stream, f: (a: A, i: number) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined ): Stream @@ -1847,7 +1878,7 @@ export const mapEffect: { self: Stream, f: (a: A, i: number) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined ): Stream => @@ -1881,21 +1912,21 @@ export const mapEffect: { * await Effect.runPromise(program) * ``` * - * @category mapping + * @category sequencing * @since 2.0.0 */ export const flattenEffect: < Arg extends Stream, any, any> | { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined = { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } >( selfOrOptions?: Arg, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined ) => [Arg] extends [Stream, infer _E, infer _R>] ? @@ -1905,7 +1936,7 @@ export const flattenEffect: < ( self: Stream, E, R>, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly unordered?: boolean | undefined } | undefined ): Stream => mapEffect(self, identity, options) @@ -2022,21 +2053,21 @@ export const tap: { ( f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } | undefined ): (self: Stream) => Stream ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } | undefined ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } | undefined ): Stream => mapEffect( @@ -2079,7 +2110,7 @@ export const tapBoth: { options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } ): (self: Stream) => Stream ( @@ -2087,7 +2118,7 @@ export const tapBoth: { options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } ): Stream } = dual(2, ( @@ -2095,7 +2126,7 @@ export const tapBoth: { options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } ): Stream => self.pipe( @@ -2225,14 +2256,14 @@ export const tapSink: { * await Effect.runPromise(program) * ``` * - * @category mapping + * @category sequencing * @since 2.0.0 */ export const flatMap: { ( f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): (self: Stream) => Stream @@ -2240,7 +2271,7 @@ export const flatMap: { self: Stream, f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): Stream @@ -2248,7 +2279,7 @@ export const flatMap: { self: Stream, f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => @@ -2285,7 +2316,7 @@ export const switchMap: { ( f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): (self: Stream) => Stream @@ -2293,7 +2324,7 @@ export const switchMap: { self: Stream, f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): Stream @@ -2301,7 +2332,7 @@ export const switchMap: { self: Stream, f: (a: A) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => @@ -2340,21 +2371,21 @@ export const switchMap: { * await Effect.runPromise(program) * ``` * - * @category mapping + * @category sequencing * @since 2.0.0 */ export const flatten: < Arg extends Stream, any, any> | { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined = { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } >( selfOrOptions?: Arg, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ) => [Arg] extends [Stream, infer _E2, infer _R2>] ? Stream<_A, _E | _E2, _R | _R2> @@ -2363,7 +2394,7 @@ export const flatten: < ( self: Stream, E2, R2>, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => flatMap(self, identity, options) @@ -2471,7 +2502,7 @@ export const drainFork: { * await Effect.runPromise(program) * ``` * - * @category sequencing + * @category repetition * @since 2.0.0 */ export const repeat: { @@ -2689,7 +2720,7 @@ export const timeoutOrElse: { * await Effect.runPromise(program) * ``` * - * @category sequencing + * @category repetition * @since 2.0.0 */ export const repeatElements: { @@ -2758,7 +2789,7 @@ export const repeatElements: { * await Effect.runPromise(program) * ``` * - * @category sequencing + * @category repetition * @since 2.0.0 */ export const forever = (self: Stream): Stream => fromChannel(Channel.forever(self.channel)) @@ -2780,7 +2811,7 @@ export const forever = (self: Stream): Stream => from * await Effect.runPromise(program) * ``` * - * @category mapping + * @category sequencing * @since 4.0.0 */ export const flattenIterable = (self: Stream, E, R>): Stream => @@ -2844,10 +2875,30 @@ export const concat: { (self: Stream, that: Stream): Stream } = dual( 2, - (self: Stream, that: Stream): Stream => - flatten(fromArray>([self, that])) + (self: Stream, that: Stream): Stream => { + const stream = fromChannel( + Channel.flatten(Channel.fromIterator(() => concatChannels(self, that))) + ) + concatParts.set(stream, [self, that]) + return stream + } ) +// The operands of each `concat` result, so a chain of `concat` calls can run as +// a single layer instead of one layer per call +const concatParts = new WeakMap, readonly [Stream, Stream]>() + +// Yields the channel of each non-`concat` stream in a chain, in order +function* concatChannels(self: Stream, that: Stream) { + const stack = [that, self] + while (stack.length > 0) { + const stream = stack.pop()! + const parts = concatParts.get(stream) + if (parts === undefined) yield stream.channel + else stack.push(parts[1], parts[0]) + } +} + /** * Prepends the values from the provided iterable before the stream's elements. * @@ -3164,21 +3215,21 @@ export const mergeRight: { export const mergeAll: { ( options: { - readonly concurrency: number | "unbounded" + readonly concurrency: Concurrency readonly bufferSize?: number | undefined } ): (streams: Iterable>) => Stream ( streams: Iterable>, options: { - readonly concurrency: number | "unbounded" + readonly concurrency: Concurrency readonly bufferSize?: number | undefined } ): Stream } = dual(2, ( streams: Iterable>, options: { - readonly concurrency: number | "unbounded" + readonly concurrency: Concurrency readonly bufferSize?: number | undefined } ): Stream => flatten(fromIterable(streams), options)) @@ -3211,7 +3262,7 @@ export const mergeAll: { */ export const cross: { (right: Stream): (left: Stream) => Stream<[AL, AR], EL | ER, RL | RR> - (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> + (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> } = dual(2, ( left: Stream, right: Stream @@ -4315,7 +4366,7 @@ export const partitionQueue: { * consumed while that scope remains open. The first stream emits success values * from the filter, and the second emits failure values. * - * @see {@link partition} for the pure `Filter` variant, which returns the failing stream before the passing stream + * @see {@link partition} for the pure `Filter` variant * @see {@link partitionQueue} for the lower-level queue result * @see {@link filterMapEffect} for effectful filtering that discards failed filter results * @@ -4325,7 +4376,7 @@ export const partitionQueue: { export const partitionEffect: { (filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined }): (self: Stream) => Effect.Effect< [ passes: Stream, @@ -4339,7 +4390,7 @@ export const partitionEffect: { filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } ): Effect.Effect< [ @@ -4356,7 +4407,7 @@ export const partitionEffect: { filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined } ): Effect.Effect< [ @@ -4377,14 +4428,16 @@ export const partitionEffect: { ) /** - * Splits a stream into scoped excluded and satisfying substreams using a + * Splits a stream into scoped passing and failing substreams using a * `Filter`. * * **Details** * * The returned streams are backed by queues in the current scope and should be * consumed while that scope remains open. The faster stream may advance up to - * `bufferSize` elements ahead of the slower one. + * `capacity` elements ahead of the slower one. The first stream emits the + * filter successes and the second emits the filter failures. The default + * capacity is 16. * * **Example** (Partitioning a stream) * @@ -4392,14 +4445,14 @@ export const partitionEffect: { * import { Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { - * const [excluded, satisfying] = yield* Stream.partition( + * const [passes, fails] = yield* Stream.partition( * Stream.make(1, 2, 3, 4), * (n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n) * ) - * const left = yield* Stream.runCollect(excluded) - * const right = yield* Stream.runCollect(satisfying) - * left // => [ 1, 3 ] - * right // => [ 2, 4 ] + * const evens = yield* Stream.runCollect(passes) + * const odds = yield* Stream.runCollect(fails) + * evens // => [ 2, 4 ] + * odds // => [ 1, 3 ] * }) * await Effect.runPromise(Effect.scoped(program)) * ``` @@ -4410,20 +4463,24 @@ export const partitionEffect: { export const partition: { ( filter: Filter.Filter, Pass, Fail>, - options?: { readonly bufferSize?: number | undefined } + options?: { + readonly capacity?: number | "unbounded" | undefined + } ): ( self: Stream ) => Effect.Effect< - [excluded: Stream, satisfying: Stream], + [passes: Stream, fails: Stream], never, R | Scope.Scope > ( self: Stream, filter: Filter.Filter, Pass, Fail>, - options?: { readonly bufferSize?: number | undefined } + options?: { + readonly capacity?: number | "unbounded" | undefined + } ): Effect.Effect< - [excluded: Stream, satisfying: Stream], + [passes: Stream, fails: Stream], never, R | Scope.Scope > @@ -4432,15 +4489,17 @@ export const partition: { ( self: Stream, filter: Filter.Filter, Pass, Fail>, - options?: { readonly bufferSize?: number | undefined } + options?: { + readonly capacity?: number | "unbounded" | undefined + } ): Effect.Effect< - [excluded: Stream, satisfying: Stream], + [passes: Stream, fails: Stream], never, R | Scope.Scope > => Effect.map( - partitionQueue(self, filter, { capacity: options?.bufferSize ?? 16 }), - ([passes, fails]) => [fromQueue(fails), fromQueue(passes)] as const + partitionQueue(self, filter, { capacity: options?.capacity ?? 16 }), + ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const ) ) @@ -4867,6 +4926,141 @@ export const tapError: { fromChannel )) +/** + * Peeks at errors with a matching `_tag` effectfully without changing the + * stream unless the tap fails. + * + * **Example** (Effectfully peeking at a tagged error) + * + * ```ts import.meta.vitest + * import { Data, Effect, Stream } from "effect" + * + * class NetworkError extends Data.TaggedError("NetworkError")<{ + * statusCode: number + * }> {} + * + * class ValidationError extends Data.TaggedError("ValidationError")<{ + * field: string + * }> {} + * + * const seen: Array = [] + * const stream: Stream.Stream = Stream.fail( + * new NetworkError({ statusCode: 504 }) + * ) + * + * const program = stream.pipe( + * Stream.tapErrorTag("NetworkError", (error) => Effect.sync(() => seen.push(error.statusCode))), + * Stream.catch(() => Stream.make(0)), + * Stream.runCollect + * ) + * + * await Effect.runPromise(program) // => [ 0 ] + * seen // => [ 504 ] + * ``` + * + * @see {@link tapError} for peeking at every typed error + * @see {@link catchTag} for recovering from a tagged error + * + * @category error handling + * @since 4.0.0 + */ +export const tapErrorTag: { + | Arr.NonEmptyReadonlyArray>, E, A1, E1, R1>( + k: K, + f: ( + e: ExtractTag, K extends Arr.NonEmptyReadonlyArray ? K[number] : K> + ) => Effect.Effect + ): (self: Stream) => Stream + < + A, + E, + R, + const K extends Tags | Arr.NonEmptyReadonlyArray>, + R1, + E1, + A1 + >( + self: Stream, + k: K, + f: (e: ExtractTag ? K[number] : K>) => Effect.Effect + ): Stream +} = dual( + 3, + < + A, + E, + R, + const K extends Tags | Arr.NonEmptyReadonlyArray>, + R1, + E1, + A1 + >( + self: Stream, + k: K, + f: (e: ExtractTag ? K[number] : K>) => Effect.Effect + ): Stream => { + const predicate = Array.isArray(k) + ? ((e: E): e is ExtractTag ? K[number] : K> => + hasProperty(e, "_tag") && k.includes(e._tag)) + : isTagged(k as string) + return tapError( + self, + (error) => + predicate(error) + ? f(error as ExtractTag ? K[number] : K>) + : Effect.void + ) + } +) + +/** + * Peeks at defects effectfully without changing the stream unless the tap + * fails. + * + * **Example** (Effectfully peeking at defects) + * + * ```ts import.meta.vitest + * import { Effect, Stream } from "effect" + * + * const defects: Array = [] + * const stream = Stream.make(1, 2).pipe( + * Stream.concat(Stream.die("boom")), + * Stream.tapDefect((defect) => Effect.sync(() => defects.push(defect))), + * Stream.catchCause(() => Stream.make(3)) + * ) + * + * const program = Effect.gen(function*() { + * const values = yield* Stream.runCollect(stream) + * values // => [ 1, 2, 3 ] + * }) + * + * await Effect.runPromise(program) + * defects // => [ 'boom' ] + * ``` + * + * @see {@link tapCause} for peeking at the full failure cause + * @see {@link catchDefect} for recovering from defects + * + * @category error handling + * @since 4.0.0 + */ +export const tapDefect: { + ( + f: (defect: unknown) => Effect.Effect + ): (self: Stream) => Stream + ( + self: Stream, + f: (defect: unknown) => Effect.Effect + ): Stream +} = dual(2, ( + self: Stream, + f: (defect: unknown) => Effect.Effect +): Stream => + tapCause(self, (cause) => { + const defect = Cause.findDefect(cause) + return Result.isSuccess(defect) ? f(defect.success) : Effect.void + })) + /** * Recovers from errors that match a predicate by switching to a recovery stream. * @@ -5168,10 +5362,9 @@ export const catchTag: { export const catchTags: { < E, - Cases extends (E extends { _tag: string } ? { - [K in E["_tag"]]+?: (error: Extract) => Stream - } : - {}), + Cases extends + & { [K in Extract["_tag"]]+?: ((error: Extract) => Stream) } + & (unknown extends E ? {} : { [K in Exclude["_tag"]>]: never }), A2 = unassigned, E2 = never, R2 = never @@ -5199,10 +5392,9 @@ export const catchTags: { R, E, A, - Cases extends (E extends { _tag: string } ? { - [K in E["_tag"]]+?: (error: Extract) => Stream - } : - {}), + Cases extends + & { [K in Extract["_tag"]]+?: ((error: Extract) => Stream) } + & (unknown extends E ? {} : { [K in Exclude["_tag"]>]: never }), A2 = unassigned, E2 = never, R2 = never @@ -5529,6 +5721,88 @@ export const catchReasons: { ) as any }) +/** + * Promotes nested reason errors into the stream error channel, replacing the + * parent error. + * + * **Example** (Extracting the reason from a tagged error) + * + * ```ts import.meta.vitest + * import { Data, Effect, Stream } from "effect" + * + * class RateLimitError extends Data.TaggedError("RateLimitError")<{ + * retryAfter: number + * }> {} + * + * class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{ + * limit: number + * }> {} + * + * class AiError extends Data.TaggedError("AiError")<{ + * reason: RateLimitError | QuotaExceededError + * }> {} + * + * const stream: Stream.Stream = Stream.fail( + * new AiError({ reason: new RateLimitError({ retryAfter: 30 }) }) + * ) + * + * // Before: Stream + * // After: Stream + * const unwrapped = stream.pipe(Stream.unwrapReason("AiError")) + * + * const program = Effect.gen(function*() { + * const error = yield* Effect.flip(Stream.runCollect(unwrapped)) + * error._tag // => "RateLimitError" + * }) + * + * await Effect.runPromise(program) + * ``` + * + * @see {@link catchReason} for recovering from a specific reason + * @see {@link catchReasons} for handling several reasons at once + * + * @category error handling + * @since 4.0.0 + */ +export const unwrapReason: { + < + K extends Effect.TagsWithReason, + E + >( + errorTag: K + ): (self: Stream) => Stream | ReasonOf>, R> + < + A, + E, + R, + K extends Effect.TagsWithReason + >( + self: Stream, + errorTag: K + ): Stream | ReasonOf>, R> +} = dual( + 2, + < + A, + E, + R, + K extends Effect.TagsWithReason + >( + self: Stream, + errorTag: K + ): Stream | ReasonOf>, R> => + catchFilter( + self, + (e: any) => { + if (isTagged(e, errorTag) && hasProperty(e, "reason")) { + return Result.succeed(e.reason) + } + return Result.fail(e) + }, + fail as any + ) as any +) + /** * Transforms the errors emitted by this stream using `f`. * @@ -7265,7 +7539,7 @@ export const combine: { * await Effect.runPromise(program) * ``` * - * @category sequencing + * @category merging * @since 4.0.0 */ export const combineArray: { @@ -7420,7 +7694,7 @@ export const mapAccumArray: { initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => readonly [state: S, values: ReadonlyArray], options?: { - readonly onHalt?: ((state: S) => Array) | undefined + readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream } = dual((args) => isStream(args[0]), ( @@ -7632,7 +7906,7 @@ export const mapAccumArrayEffect: { * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( - * Stream.scan(0, (acc, n) => acc + n), + * Stream.scan(() => 0, (acc, n) => acc + n), * Stream.runCollect * ) * values // => [ 0, 1, 3, 6 ] @@ -7646,22 +7920,22 @@ export const mapAccumArrayEffect: { */ export const scan: { ( - initial: S, + initial: LazyArg, f: (s: S, a: A) => S ): (self: Stream) => Stream ( self: Stream, - initial: S, + initial: LazyArg, f: (s: S, a: A) => S ): Stream } = dual(3, ( self: Stream, - initial: S, + initial: LazyArg, f: (s: S, a: A) => S ): Stream => suspend(() => { let isFirst = true - return fromChannel(Channel.mapAccum(self.channel, constant(initial), (state, arr) => { + return fromChannel(Channel.mapAccum(self.channel, initial, (state, arr) => { const states = Arr.empty() as Arr.NonEmptyArray if (isFirst) { isFirst = false @@ -7685,7 +7959,7 @@ export const scan: { * * const program = Effect.gen(function*() { * const states = yield* Stream.make(1, 2, 3).pipe( - * Stream.scanEffect(0, (sum, n) => Effect.succeed(sum + n)), + * Stream.scanEffect(() => 0, (sum, n) => Effect.succeed(sum + n)), * Stream.runCollect * ) * states // => [ 0, 1, 3, 6 ] @@ -7698,24 +7972,26 @@ export const scan: { */ export const scanEffect: { ( - initial: S, + initial: LazyArg, f: (s: S, a: A) => Effect.Effect ): (self: Stream) => Stream ( self: Stream, - initial: S, + initial: LazyArg, f: (s: S, a: A) => Effect.Effect ): Stream } = dual(3, ( self: Stream, - initial: S, + initial: LazyArg, f: (s: S, a: A) => Effect.Effect ): Stream => - self.channel.pipe( - Channel.flattenArray, - Channel.scanEffect(initial, f), - Channel.map(Arr.of), - fromChannel + suspend(() => + self.channel.pipe( + Channel.flattenArray, + Channel.scanEffect(initial(), f), + Channel.map(Arr.of), + fromChannel + ) )) /** @@ -8432,15 +8708,15 @@ export const groupAdjacentBy: { * @category aggregation * @since 2.0.0 */ -export const transduce = dual< +export const transduce: { ( sink: Sink.Sink - ) => (self: Stream) => Stream, + ): (self: Stream) => Stream ( self: Stream, sink: Sink.Sink - ) => Stream ->( + ): Stream +} = dual( 2, ( self: Stream, @@ -8736,7 +9012,7 @@ export const broadcastN: { ) } yield* Channel.runForEach(self.channel, (value) => PubSub.publish(pubsub, value)).pipe( - Effect.onExit((exit) => PubSub.publish(pubsub, exit)), + Effect.onExit((exit) => PubSub.end(pubsub, exit)), Effect.forkScoped ) return streams as TupleOf> @@ -9287,7 +9563,7 @@ export const changesWithEffect: { * await Effect.runPromise(program) * ``` * - * @category decoding + * @category text * @since 2.0.0 */ export const decodeText: < @@ -9332,7 +9608,7 @@ export const decodeText: < * await Effect.runPromise(program) * ``` * - * @category encoding + * @category text * @since 2.0.0 */ export const encodeText = (self: Stream): Stream => @@ -9357,7 +9633,7 @@ export const encodeText = (self: Stream): Stream(self: Stream): Stream => @@ -9733,7 +10009,7 @@ export const onExit: { * errors // => ["boom"] * ``` * - * @category error handling + * @category resource management * @since 2.0.0 */ export const onError: { @@ -10254,27 +10530,26 @@ export const withSpan: { * await Effect.runPromise(effect) * ``` * - * @category constructors + * @category do notation * @since 2.0.0 */ export const Do: Stream<{}> = succeed({}) const let_: { - ( - name: Exclude, + , B>( + name: N, f: (a: NoInfer) => B - ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> - ( + ): (self: Stream) => Stream & Record>, E, R> + , E, R, B, N extends string>( self: Stream, - name: Exclude, + name: N, f: (a: NoInfer) => B - ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> -} = dual(3, ( + ): Stream & Record>, E, R> +} = dual(3, , E, R, B, N extends string>( self: Stream, - name: Exclude, + name: N, f: (a: NoInfer) => B -): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> => - map(self, (a) => ({ ...a, [name]: f(a) } as any))) +): Stream & Record>, E, R> => map(self, (a) => ({ ...a, [name]: f(a) } as any))) export { /** * Adds a computed field to the current Do-notation record. @@ -10297,7 +10572,7 @@ export { * await Effect.runPromise(program) * ``` * - * @category mapping + * @category do notation * @since 2.0.0 */ let_ as let @@ -10321,37 +10596,37 @@ export { * await Effect.runPromise(result) // => [{ a: 1, b: 2 }, { a: 2, b: 3 }] * ``` * - * @category sequencing + * @category do notation * @since 2.0.0 */ export const bind: { - ( - tag: Exclude, - f: (_: NoInfer) => Stream, + , B, E2, R2>( + name: N, + f: (a: NoInfer) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined - ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E2 | E, R2 | R> - ( + ): (self: Stream) => Stream & Record>, E2 | E, R2 | R> + , E, R, B, E2, R2, N extends string>( self: Stream, - tag: Exclude, - f: (_: NoInfer) => Stream, + name: N, + f: (a: NoInfer) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined - ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E | E2, R | R2> -} = dual((args) => isStream(args[0]), ( + ): Stream & Record>, E | E2, R | R2> +} = dual((args) => isStream(args[0]), , E, R, B, E2, R2, N extends string>( self: Stream, - tag: Exclude, - f: (_: NoInfer) => Stream, + name: N, + f: (a: NoInfer) => Stream, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined } | undefined -): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E | E2, R | R2> => - flatMap(self, (a) => map(f(a), (b) => ({ ...a, [tag]: b } as any)), options)) +): Stream & Record>, E | E2, R | R2> => + flatMap(self, (a) => map(f(a), (b) => ({ ...a, [name]: b } as any)), options)) /** * Binds an Effect-produced value into the do-notation record for each stream element. @@ -10374,40 +10649,40 @@ export const bind: { * await Effect.runPromise(program) * ``` * - * @category sequencing + * @category do notation * @since 2.0.0 */ export const bindEffect: { - ( - tag: Exclude, - f: (_: NoInfer) => Effect.Effect, + , B, E2, R2>( + name: N, + f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined - } - ): (self: Stream) => Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> - ( + } | undefined + ): (self: Stream) => Stream & Record>, E | E2, R | R2> + , E, R, B, E2, R2, N extends string>( self: Stream, - tag: Exclude, - f: (_: NoInfer) => Effect.Effect, + name: N, + f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined - } - ): Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> -} = dual((args) => isStream(args[0]), ( + } | undefined + ): Stream & Record>, E | E2, R | R2> +} = dual((args) => isStream(args[0]), , E, R, B, E2, R2, N extends string>( self: Stream, - tag: Exclude, - f: (_: NoInfer) => Effect.Effect, + name: N, + f: (a: NoInfer) => Effect.Effect, options?: { - readonly concurrency?: number | "unbounded" | undefined + readonly concurrency?: Concurrency | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined } | undefined -): Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> => - mapEffect(self, (a) => Effect.map(f(a), (b) => ({ ...a, [tag]: b } as any)), options)) +): Stream & Record>, E | E2, R | R2> => + mapEffect(self, (a) => Effect.map(f(a), (b) => ({ ...a, [name]: b } as any)), options)) /** * Maps each element into a record keyed by the provided name. @@ -10422,7 +10697,7 @@ export const bindEffect: { * await Effect.runPromise(Stream.runCollect(stream)) // => [{ value: 1 }, { value: 2 }, { value: 3 }] * ``` * - * @category mapping + * @category do notation * @since 2.0.0 */ export const bindTo: { @@ -11015,17 +11290,17 @@ export const mkUint8Array = (self: Stream): Effect.Effec * @category destructors * @since 4.0.0 */ -export const toReadableStreamWith = dual< +export const toReadableStreamWith: { ( context: Context.Context, options?: { readonly strategy?: QueuingStrategy | undefined } - ) => (self: Stream) => ReadableStream, + ): (self: Stream) => ReadableStream ( self: Stream, context: Context.Context, options?: { readonly strategy?: QueuingStrategy | undefined } - ) => ReadableStream ->( + ): ReadableStream +} = dual( (args) => isStream(args[0]), ( self: Stream, @@ -11363,7 +11638,7 @@ export const runIntoPubSub: { options?: { readonly shutdownOnEnd?: boolean | undefined } | undefined - ): Effect.Effect + ): Effect.Effect } = dual((args) => isStream(args[0]), ( self: Stream, pubsub: PubSub.PubSub, @@ -11448,7 +11723,9 @@ export const toPubSub: { * * **Details** * - * `Take` values include the stream's end and failure signals. + * Chunks are published as `Take` values. When the stream ends, the PubSub is + * ended with the stream's `Exit`, so every subscriber, including one that + * subscribes later, observes completion or failure after its buffered chunks. * * **Example** (Converting to a PubSub of takes) * diff --git a/repos/effect/packages/effect/src/Tracer.ts b/repos/effect/packages/effect/src/Tracer.ts index 08708eeb00..024c10568b 100644 --- a/repos/effect/packages/effect/src/Tracer.ts +++ b/repos/effect/packages/effect/src/Tracer.ts @@ -10,7 +10,7 @@ * @since 2.0.0 */ import * as Context from "./Context.ts" -import * as Encoding from "./Encoding.ts" +import * as Hex from "./encoding/Hex.ts" import type * as Exit from "./Exit.ts" import type { Fiber } from "./Fiber.ts" import { constFalse, type LazyArg } from "./Function.ts" @@ -709,11 +709,11 @@ export class NativeSpan implements Span { } get traceId(): string { - return this._traceId ??= Option.getOrUndefined(this.parent)?.traceId ?? Encoding.randomHex(32) + return this._traceId ??= Option.getOrUndefined(this.parent)?.traceId ?? Hex.random(32) } get spanId(): string { - return this._spanId ??= Encoding.randomHex(16) + return this._spanId ??= Hex.random(16) } get attributes(): Map { diff --git a/repos/effect/packages/effect/src/TxChunk.ts b/repos/effect/packages/effect/src/TxChunk.ts index 8238fcf56e..29342867a8 100644 --- a/repos/effect/packages/effect/src/TxChunk.ts +++ b/repos/effect/packages/effect/src/TxChunk.ts @@ -18,6 +18,7 @@ import type { Inspectable } from "./Inspectable.ts" import { NodeInspectSymbol, toJson } from "./Inspectable.ts" import type { Pipeable } from "./Pipeable.ts" import { pipeArguments } from "./Pipeable.ts" +import { hasProperty } from "./Predicate.ts" import * as TxRef from "./TxRef.ts" import type { NoInfer } from "./Types.ts" @@ -229,6 +230,25 @@ export const makeUnsafe = (ref: TxRef.TxRef>): TxChunk => { return txChunk } +/** + * Checks whether `u` is a `TxChunk`. + * + * **Example** (Checking for transactional chunks) + * + * ```ts import.meta.vitest + * import { Chunk, TxChunk, TxRef } from "effect" + * + * const txChunk = TxChunk.makeUnsafe(TxRef.makeUnsafe(Chunk.empty())) + * + * TxChunk.isTxChunk(txChunk) // => true + * TxChunk.isTxChunk(Chunk.empty()) // => false + * ``` + * + * @category guards + * @since 4.0.0 + */ +export const isTxChunk = (u: unknown): u is TxChunk => hasProperty(u, TypeId) + /** * Modifies the value of the `TxChunk` using the provided function. * diff --git a/repos/effect/packages/effect/src/TxHashSet.ts b/repos/effect/packages/effect/src/TxHashSet.ts index 71d18fc4df..d95aa7a231 100644 --- a/repos/effect/packages/effect/src/TxHashSet.ts +++ b/repos/effect/packages/effect/src/TxHashSet.ts @@ -501,6 +501,32 @@ export const isEmpty = (self: TxHashSet): Effect.Effect => return HashSet.isEmpty(set) }) +/** + * Checks whether the TxHashSet is non-empty. + * + * **Example** (Checking whether a set is non-empty) + * + * ```ts import.meta.vitest + * import { Effect, TxHashSet } from "effect" + * + * const program = Effect.gen(function*() { + * const empty = yield* TxHashSet.empty() + * const emptyResult = yield* TxHashSet.isNonEmpty(empty) + * + * const nonEmpty = yield* TxHashSet.make("a") + * const nonEmptyResult = yield* TxHashSet.isNonEmpty(nonEmpty) + * return [emptyResult, nonEmptyResult] as const + * }) + * + * await Effect.runPromise(program) // => [false, true] + * ``` + * + * @category predicates + * @since 4.0.0 + */ +export const isNonEmpty = (self: TxHashSet): Effect.Effect => + Effect.map(isEmpty(self), (empty) => !empty) + /** * Removes all values from the TxHashSet. * diff --git a/repos/effect/packages/effect/src/TxPriorityQueue.ts b/repos/effect/packages/effect/src/TxPriorityQueue.ts index 15a754a879..2e42fdd526 100644 --- a/repos/effect/packages/effect/src/TxPriorityQueue.ts +++ b/repos/effect/packages/effect/src/TxPriorityQueue.ts @@ -84,22 +84,39 @@ const makeTxPriorityQueue = (ref: TxRef.TxRef>, ord: Order): TxPr return self } -const insertSorted = (chunk: Chunk, value: A, ord: Order): Chunk => { - const arr = C.toArray(chunk) as Array - let lo = 0 - let hi = arr.length - while (lo < hi) { - const mid = (lo + hi) >>> 1 - if (ord(arr[mid], value) <= 0) { - lo = mid + 1 - } else { - hi = mid +/** + * Merges the sorted `values` into the sorted `chunk`. Existing elements stay + * ahead of equal incoming ones, and incoming ties keep their input order. + * + * Each insertion point is found by galloping forward from the previous one and + * then bisecting, so a value that lands close to the last one costs only a few + * comparisons. + */ +const mergeSorted = (chunk: Chunk, values: ReadonlyArray, ord: Order): Chunk => { + const arr = C.toReadonlyArray(chunk) + const out: Array = Array(arr.length + values.length) + let i = 0 + let k = 0 + for (const value of values) { + let lo = i + let hi = i + for (let step = 1; hi < arr.length && ord(arr[hi], value) <= 0; step *= 2) { + lo = hi + 1 + hi += step } + hi = Math.min(hi, arr.length) + while (lo < hi) { + const mid = (lo + hi) >>> 1 + if (ord(arr[mid], value) <= 0) { + lo = mid + 1 + } else { + hi = mid + } + } + for (; i < lo; i++) out[k++] = arr[i] + out[k++] = value } - const out = Array(arr.length + 1) as Array - for (let i = 0; i < lo; i++) out[i] = arr[i] - out[lo] = value - for (let i = lo; i < arr.length; i++) out[i + 1] = arr[i] + for (; i < arr.length; i++) out[k++] = arr[i] return C.fromIterable(out) } @@ -144,19 +161,26 @@ export const empty = (order: Order): Effect.Effect> => * @category constructors * @since 2.0.0 */ -export const fromIterable: { - (order: Order): (iterable: Iterable) => Effect.Effect> - (order: Order, iterable: Iterable): Effect.Effect> -} = dual( - 2, - (order: Order, iterable: Iterable): Effect.Effect> => { - const arr = Array.from(iterable).sort((a, b) => order(a, b)) - return Effect.map( - TxRef.make>(C.fromIterable(arr)), - (ref) => makeTxPriorityQueue(ref, order) - ) +export function fromIterable( + order: Order +): (iterable: Iterable) => Effect.Effect> +export function fromIterable( + order: Order, + iterable: Iterable +): Effect.Effect> +export function fromIterable( + order: Order, + iterable?: Iterable +): Effect.Effect> | ((iterable: Iterable) => Effect.Effect>) { + if (iterable === undefined) { + return (iterable) => fromIterable(order, iterable) } -) + const arr = Array.from(iterable).sort((a, b) => order(a, b)) + return Effect.map( + TxRef.make>(C.fromIterable(arr)), + (ref) => makeTxPriorityQueue(ref, order) + ) +} /** * Creates a `TxPriorityQueue` from variadic elements. @@ -331,7 +355,7 @@ export const offer: { } = dual( 2, (self: TxPriorityQueue, value: A): Effect.Effect => - TxRef.update(self.ref, (chunk) => insertSorted(chunk, value, self.ord)) + TxRef.update(self.ref, (chunk) => mergeSorted(chunk, [value], self.ord)) ) /** @@ -360,10 +384,7 @@ export const offerAll: { } = dual( 2, (self: TxPriorityQueue, values: Iterable): Effect.Effect => - TxRef.update(self.ref, (chunk) => { - const arr = [...C.toArray(chunk), ...values].sort((a, b) => self.ord(a, b)) - return C.fromIterable(arr) - }) + TxRef.update(self.ref, (chunk) => mergeSorted(chunk, Array.from(values).sort(self.ord), self.ord)) ) /** diff --git a/repos/effect/packages/effect/src/TxPubSub.ts b/repos/effect/packages/effect/src/TxPubSub.ts index eb2b32cbee..d31ee9cf9f 100644 --- a/repos/effect/packages/effect/src/TxPubSub.ts +++ b/repos/effect/packages/effect/src/TxPubSub.ts @@ -323,6 +323,35 @@ export const size = (self: TxPubSub): Effect.Effect => */ export const isEmpty = (self: TxPubSub): Effect.Effect => Effect.map(size(self), (s) => s === 0) +/** + * Checks whether any subscriber queue has pending messages. + * A pub/sub with no subscribers is empty, even if values have been published. + * + * **Example** (Checking whether a pub/sub is non-empty) + * + * ```ts import.meta.vitest + * import { Effect, TxPubSub } from "effect" + * + * const program = Effect.gen(function*() { + * const hub = yield* TxPubSub.unbounded() + * const empty = yield* TxPubSub.isNonEmpty(hub) + * + * const nonEmpty = yield* Effect.scoped(Effect.gen(function*() { + * yield* TxPubSub.subscribe(hub) + * yield* TxPubSub.publish(hub, 1) + * return yield* TxPubSub.isNonEmpty(hub) + * })) + * return [empty, nonEmpty] as const + * }) + * + * await Effect.runPromise(program) // => [false, true] + * ``` + * + * @category predicates + * @since 4.0.0 + */ +export const isNonEmpty = (self: TxPubSub): Effect.Effect => Effect.map(size(self), (s) => s > 0) + /** * Checks whether any subscriber queue is at capacity. * diff --git a/repos/effect/packages/effect/src/TxQueue.ts b/repos/effect/packages/effect/src/TxQueue.ts index 1de5b62dab..1348abefec 100644 --- a/repos/effect/packages/effect/src/TxQueue.ts +++ b/repos/effect/packages/effect/src/TxQueue.ts @@ -1091,6 +1091,32 @@ export const size = (self: TxQueueState): Effect.Effect => TxChunk.size( */ export const isEmpty = (self: TxQueueState): Effect.Effect => TxChunk.isEmpty(self.items) +/** + * Checks whether the queue is non-empty. + * + * **Example** (Checking whether a queue is non-empty) + * + * ```ts import.meta.vitest + * import { Effect, TxQueue } from "effect" + * + * const program = Effect.gen(function*() { + * const queue = yield* TxQueue.bounded(10) + * + * const empty = yield* TxQueue.isNonEmpty(queue) + * + * yield* TxQueue.offer(queue, 42) + * const nonEmpty = yield* TxQueue.isNonEmpty(queue) + * return [empty, nonEmpty] as const + * }) + * + * await Effect.runPromise(program) // => [false, true] + * ``` + * + * @category predicates + * @since 4.0.0 + */ +export const isNonEmpty = (self: TxQueueState): Effect.Effect => TxChunk.isNonEmpty(self.items) + /** * Checks whether the queue is at capacity. * diff --git a/repos/effect/packages/effect/src/TxRef.ts b/repos/effect/packages/effect/src/TxRef.ts index 4d2a9798d7..d5a86f6589 100644 --- a/repos/effect/packages/effect/src/TxRef.ts +++ b/repos/effect/packages/effect/src/TxRef.ts @@ -135,6 +135,15 @@ export const makeUnsafe = (initial: A): TxRef => ({ value: initial }) +const journalEntry = (state: Effect.Transaction["Service"], self: TxRef) => { + let entry = state.journal.get(self) + if (entry === undefined) { + entry = { version: self.version, value: self.value, written: false } + state.journal.set(self, entry) + } + return entry +} + /** * Modifies the value of the `TxRef` using the provided function. * @@ -173,12 +182,10 @@ export const modify: { Effect.Transaction.pipe( Effect.flatMap((state) => Effect.sync(() => { - if (!state.journal.has(self)) { - state.journal.set(self, { version: self.version, value: self.value }) - } - const current = state.journal.get(self)! + const current = journalEntry(state, self) const [returnValue, next] = f(current.value) current.value = next + current.written = true return returnValue }) ), @@ -251,7 +258,11 @@ export const update: { * @category combinators * @since 2.0.0 */ -export const get = (self: TxRef): Effect.Effect => modify(self, (current) => [current, current]) +export const get = (self: TxRef): Effect.Effect => + Effect.Transaction.pipe( + Effect.map((state) => journalEntry(state, self).value), + Effect.tx + ) /** * Sets the value of the `TxRef`. diff --git a/repos/effect/packages/effect/src/TxSemaphore.ts b/repos/effect/packages/effect/src/TxSemaphore.ts index be031b81bc..e42fd26319 100644 --- a/repos/effect/packages/effect/src/TxSemaphore.ts +++ b/repos/effect/packages/effect/src/TxSemaphore.ts @@ -11,6 +11,7 @@ */ import * as Effect from "./Effect.ts" +import { dual } from "./Function.ts" import type { Inspectable } from "./Inspectable.ts" import { NodeInspectSymbol, toJson } from "./Inspectable.ts" import type { Pipeable } from "./Pipeable.ts" @@ -288,7 +289,10 @@ export const acquire = (self: TxSemaphore): Effect.Effect => * @category combinators * @since 2.0.0 */ -export const acquireN = (self: TxSemaphore, n: number): Effect.Effect => { +export const acquireN: { + (n: number): (self: TxSemaphore) => Effect.Effect + (self: TxSemaphore, n: number): Effect.Effect +} = dual(2, (self: TxSemaphore, n: number): Effect.Effect => { if (n <= 0) { return Effect.die(new Error("Number of permits must be positive")) } @@ -299,7 +303,7 @@ export const acquireN = (self: TxSemaphore, n: number): Effect.Effect => { } yield* TxRef.set(self.permitsRef, permits - n) }).pipe(Effect.tx) -} +}) /** * Tries to acquire a single permit from the semaphore without blocking, @@ -378,7 +382,10 @@ export const tryAcquire = (self: TxSemaphore): Effect.Effect => * @category combinators * @since 4.0.0 */ -export const tryAcquireN = (self: TxSemaphore, n: number): Effect.Effect => { +export const tryAcquireN: { + (n: number): (self: TxSemaphore) => Effect.Effect + (self: TxSemaphore, n: number): Effect.Effect +} = dual(2, (self: TxSemaphore, n: number): Effect.Effect => { if (n <= 0) { return Effect.die(new Error("Number of permits must be positive")) } @@ -388,7 +395,7 @@ export const tryAcquireN = (self: TxSemaphore, n: number): Effect.Effect => * @category combinators * @since 2.0.0 */ -export const releaseN = (self: TxSemaphore, n: number): Effect.Effect => { +export const releaseN: { + (n: number): (self: TxSemaphore) => Effect.Effect + (self: TxSemaphore, n: number): Effect.Effect +} = dual(2, (self: TxSemaphore, n: number): Effect.Effect => { if (n <= 0) { return Effect.die(new Error("Number of permits must be positive")) } @@ -483,7 +493,7 @@ export const releaseN = (self: TxSemaphore, n: number): Effect.Effect => { const newPermits = permits + n return newPermits > self.capacity ? self.capacity : newPermits }) -} +}) /** * Executes an effect with a single permit from the semaphore. The permit is diff --git a/repos/effect/packages/effect/src/Utils.ts b/repos/effect/packages/effect/src/Utils.ts index 4bf1759901..9c9a3c83a5 100644 --- a/repos/effect/packages/effect/src/Utils.ts +++ b/repos/effect/packages/effect/src/Utils.ts @@ -15,7 +15,8 @@ import { getStackTraceLimit } from "./internal/stackTraceLimit.ts" import type * as Types from "./Types.ts" /** - * Yields its wrapped value exactly once through an `IterableIterator`. + * Yields its wrapped value exactly once, then completes with the value sent + * back in. * * **When to use** * @@ -25,10 +26,11 @@ import type * as Types from "./Types.ts" * * **Details** * - * The first call to `next()` returns `{ value: self, done: false }`. Every - * subsequent call returns `{ value: a, done: true }` where `a` is the argument - * passed to `next()`. `[Symbol.iterator]()` returns a **new** `SingleShotGen` - * wrapping the same value, so the outer type can be iterated multiple times. + * The first call to `next()` returns a fresh `{ value: self, done: false }`. + * Every subsequent call returns `{ value: a, done: true }` where `a` is the + * argument passed to `next()`. To keep `yield*` cheap, the completion result + * is the iterator itself rather than a new object, so only the iterator and + * the single yielded result are allocated per `yield*`. * * **Example** (Yielding a wrapped value in a generator) * @@ -37,21 +39,22 @@ import type * as Types from "./Types.ts" * * const gen = new Utils.SingleShotGen("hello") * - * gen.next(0) // => { value: "hello", done: false } + * gen.next(0).value // => "hello" * - * gen.next(42) // => { value: 42, done: true } + * gen.next(42).value // => 42 * ``` * * @see {@link Gen} for the type-level signature that relies on `SingleShotGen` * @category constructors * @since 2.0.0 */ -export class SingleShotGen implements IterableIterator { - private called = false - readonly self: T +export class SingleShotGen implements Iterator { + declare private value: T | A + declare private done: boolean constructor(self: T) { - this.self = self + this.value = self + this.done = false } /** @@ -65,30 +68,12 @@ export class SingleShotGen implements IterableIterator { * @since 2.0.0 */ next(a: A): IteratorResult { - return this.called ? - ({ - value: a, - done: true - }) : - (this.called = true, - ({ - value: this.self, - done: false - })) - } - - /** - * Creates a fresh single-shot iterator over the stored value. - * - * **When to use** - * - * Use to iterate the wrapped value again without reusing the consumed - * iterator state. - * - * @since 2.0.0 - */ - [Symbol.iterator](): IterableIterator { - return new SingleShotGen(this.self) + if (this.done) { + this.value = a + return this as unknown as IteratorReturnResult + } + this.done = true + return { value: this.value as T, done: false } } } diff --git a/repos/effect/packages/effect/src/unstable/ai/AiError.ts b/repos/effect/packages/effect/src/ai/AiError.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/ai/AiError.ts rename to repos/effect/packages/effect/src/ai/AiError.ts index 636af1e3c3..495e10beb7 100644 --- a/repos/effect/packages/effect/src/unstable/ai/AiError.ts +++ b/repos/effect/packages/effect/src/ai/AiError.ts @@ -9,17 +9,18 @@ * constructors, and helpers for converting HTTP response information into AI * error reasons. * + * @stability unstable * @since 4.0.0 */ -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as InternalRecord from "../../internal/record.ts" -import * as Option from "../../Option.ts" -import * as Predicate from "../../Predicate.ts" -import { redact } from "../../Redactable.ts" -import * as Redacted from "../../Redacted.ts" -import * as Schema from "../../Schema.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" import type * as HttpClientError from "../http/HttpClientError.ts" +import * as InternalRecord from "../internal/record.ts" +import * as Option from "../Option.ts" +import * as Predicate from "../Predicate.ts" +import { redact } from "../Redactable.ts" +import * as Redacted from "../Redacted.ts" +import * as Schema from "../Schema.ts" /** * Schema for HTTP requests to an AI provider. @@ -27,7 +28,7 @@ import type * as HttpClientError from "../http/HttpClientError.ts" * **Example** (Describing an HTTP request) * * ```ts import.meta.vitest - * import type { AiError } from "effect/unstable/ai" + * import type { AiError } from "effect/ai" * * const requestDetails: typeof AiError.HttpRequestDetails.Type = { * method: "POST", @@ -39,11 +40,12 @@ import type * as HttpClientError from "../http/HttpClientError.ts" * const result = [requestDetails.method, requestDetails.urlParams] // => ["POST", []] * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ export const HttpRequestDetails = Schema.Struct({ - method: Schema.Literals(["GET", "POST", "PATCH", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE"]), + method: Schema.Literals(["GET", "POST", "PATCH", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "QUERY"]), url: Schema.String, urlParams: Schema.Array(Schema.Tuple([Schema.String, Schema.String])), hash: Schema.optional(Schema.String), @@ -62,7 +64,7 @@ export const HttpRequestDetails = Schema.Struct({ * **Example** (Describing an HTTP response) * * ```ts import.meta.vitest - * import type { AiError } from "effect/unstable/ai" + * import type { AiError } from "effect/ai" * * const responseDetails: typeof AiError.HttpResponseDetails.Type = { * status: 200, @@ -74,6 +76,7 @@ export const HttpRequestDetails = Schema.Struct({ * const result = [responseDetails.status, responseDetails.headers["X-Request-Id"]] // => [200, "req_abc123"] * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -122,7 +125,7 @@ const redactHeaders = (headers: Record): Record * **Example** (Creating a network error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.NetworkError({ * reason: "TransportError", @@ -139,6 +142,7 @@ const redactHeaders = (headers: Record): Record * const result = [error.reason, error.isRetryable] // => ["TransportError", true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -153,6 +157,7 @@ export class NetworkError extends Schema.Error( /** * Marks `NetworkError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -160,6 +165,7 @@ export class NetworkError extends Schema.Error( /** * Transport errors are retryable; encoding and URL errors are not. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -172,8 +178,8 @@ export class NetworkError extends Schema.Error( * **Example** (Creating a network error from a request error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" - * import { HttpClientError, HttpClientRequest } from "effect/unstable/http" + * import { AiError } from "effect/ai" + * import { HttpClientError, HttpClientRequest } from "effect/http" * * const platformError = new HttpClientError.TransportError({ * request: HttpClientRequest.get("https://example.com/models"), @@ -184,6 +190,7 @@ export class NetworkError extends Schema.Error( * aiError.reason // => "TransportError" * ``` * + * @stability unstable * @since 4.0.0 */ static fromRequestError(error: HttpClientError.RequestError): NetworkError { @@ -264,6 +271,7 @@ export class NetworkError extends Schema.Error( * Array.of(metadata.openai.errorCode, metadata.anthropic) // => ["rate_limit_exceeded", null] * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -280,6 +288,7 @@ export const ProviderMetadata: Schema.$Record< * Metadata is keyed by provider name, and each provider value is either mutable * JSON metadata or `null`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -288,6 +297,7 @@ export type ProviderMetadata = typeof ProviderMetadata.Type /** * Provider-specific metadata attached to `RateLimitError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -296,6 +306,7 @@ export interface RateLimitErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `QuotaExhaustedError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -304,6 +315,7 @@ export interface QuotaExhaustedErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `AuthenticationError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -312,6 +324,7 @@ export interface AuthenticationErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `ContentPolicyError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -320,6 +333,7 @@ export interface ContentPolicyErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `InvalidRequestError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -328,6 +342,7 @@ export interface InvalidRequestErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `InternalProviderError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -336,6 +351,7 @@ export interface InternalProviderErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `InvalidOutputError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -344,6 +360,7 @@ export interface InvalidOutputErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `StructuredOutputError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -352,6 +369,7 @@ export interface StructuredOutputErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `UnsupportedSchemaError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -360,6 +378,7 @@ export interface UnsupportedSchemaErrorMetadata extends ProviderMetadata {} /** * Provider-specific metadata attached to `UnknownError`. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -373,6 +392,7 @@ export interface UnknownErrorMetadata extends ProviderMetadata {} * Schema for optional provider-reported token counts for prompt tokens, * completion tokens, and total tokens. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -398,6 +418,7 @@ export const UsageInfo = Schema.Struct({ * @see {@link HttpRequestDetails} for captured request details * @see {@link HttpResponseDetails} for captured response details * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -410,6 +431,7 @@ export const HttpContext = Schema.Struct({ /** * Builds a description for an HTTP error returned by an AI provider. * + * @stability unstable * @category utilities * @since 4.0.0 */ @@ -469,7 +491,7 @@ export const buildErrorDescription = (params: { * * ```ts import.meta.vitest * import { Duration } from "effect" - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const rateLimitError = new AiError.RateLimitError({ * retryAfter: Duration.seconds(60) @@ -478,6 +500,7 @@ export const buildErrorDescription = (params: { * const result = [rateLimitError._tag, rateLimitError.isRetryable] // => ["RateLimitError", true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -492,6 +515,7 @@ export class RateLimitError extends Schema.Error( /** * Marks `RateLimitError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -499,6 +523,7 @@ export class RateLimitError extends Schema.Error( /** * Rate limit errors are always retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -522,13 +547,14 @@ export class RateLimitError extends Schema.Error( * **Example** (Creating a quota exhausted error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const quotaError = new AiError.QuotaExhaustedError({}) * * const result = [quotaError._tag, quotaError.isRetryable] // => ["QuotaExhaustedError", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -543,6 +569,7 @@ export class QuotaExhaustedError extends Schema.Error( /** * Marks `QuotaExhaustedError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -550,6 +577,7 @@ export class QuotaExhaustedError extends Schema.Error( /** * Quota exhausted errors require user action and are not retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -573,7 +601,7 @@ export class QuotaExhaustedError extends Schema.Error( * **Example** (Creating an authentication error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const authError = new AiError.AuthenticationError({ * kind: "InvalidKey" @@ -589,6 +617,7 @@ export class QuotaExhaustedError extends Schema.Error( * detailed.message // => "InsufficientPermissions: Your API key lacks required permissions. Token expired" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -604,6 +633,7 @@ export class AuthenticationError extends Schema.Error( /** * Marks `AuthenticationError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -611,6 +641,7 @@ export class AuthenticationError extends Schema.Error( /** * Authentication errors require credential changes and are not retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -641,7 +672,7 @@ export class AuthenticationError extends Schema.Error( * **Example** (Creating a content policy error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const policyError = new AiError.ContentPolicyError({ * description: "Input contains prohibited content" @@ -650,6 +681,7 @@ export class AuthenticationError extends Schema.Error( * const result = [policyError.description, policyError.isRetryable] // => ["Input contains prohibited content", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -664,6 +696,7 @@ export class ContentPolicyError extends Schema.Error( /** * Marks `ContentPolicyError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -671,6 +704,7 @@ export class ContentPolicyError extends Schema.Error( /** * Content policy errors require content changes and are not retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -692,7 +726,7 @@ export class ContentPolicyError extends Schema.Error( * **Example** (Creating an invalid request error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const invalidRequestError = new AiError.InvalidRequestError({ * parameter: "temperature", @@ -703,6 +737,7 @@ export class ContentPolicyError extends Schema.Error( * const result = [invalidRequestError.parameter, invalidRequestError.isRetryable] // => ["temperature", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -719,6 +754,7 @@ export class InvalidRequestError extends Schema.Error( /** * Marks `InvalidRequestError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -726,6 +762,7 @@ export class InvalidRequestError extends Schema.Error( /** * Invalid request errors require fixing the request and are not retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -751,7 +788,7 @@ export class InvalidRequestError extends Schema.Error( * **Example** (Creating an internal provider error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const providerError = new AiError.InternalProviderError({ * description: "Server encountered an unexpected error" @@ -760,6 +797,7 @@ export class InvalidRequestError extends Schema.Error( * const result = [providerError.description, providerError.isRetryable] // => ["Server encountered an unexpected error", true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -774,6 +812,7 @@ export class InternalProviderError extends Schema.Error( /** * Marks `InternalProviderError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -781,6 +820,7 @@ export class InternalProviderError extends Schema.Error( /** * Internal provider errors are typically transient and are retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -802,7 +842,7 @@ export class InternalProviderError extends Schema.Error( * **Example** (Creating an invalid output error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const parseError = new AiError.InvalidOutputError({ * description: "Expected a string but received a number" @@ -811,6 +851,7 @@ export class InternalProviderError extends Schema.Error( * const result = [parseError.description, parseError.isRetryable] // => ["Expected a string but received a number", true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -825,6 +866,7 @@ export class InvalidOutputError extends Schema.Error( /** * Marks `InvalidOutputError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -832,6 +874,7 @@ export class InvalidOutputError extends Schema.Error( /** * Invalid output errors are retryable since LLM outputs are non-deterministic. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -845,7 +888,7 @@ export class InvalidOutputError extends Schema.Error( * * ```ts import.meta.vitest * import { Effect, Schema } from "effect" - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const schemaError = await Effect.runPromise( * Schema.decodeUnknownEffect(Schema.Number)("not a number").pipe(Effect.flip) @@ -854,6 +897,7 @@ export class InvalidOutputError extends Schema.Error( * parseError.description // => "Expected number" * ``` * + * @stability unstable * @since 4.0.0 */ static fromSchemaError(error: Schema.SchemaError): InvalidOutputError { @@ -878,7 +922,7 @@ export class InvalidOutputError extends Schema.Error( * **Example** (Creating a structured output error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.StructuredOutputError({ * description: "Expected a valid JSON object", @@ -888,6 +932,7 @@ export class InvalidOutputError extends Schema.Error( * const result = [error.description, error.responseText, error.isRetryable] // => ["Expected a valid JSON object", '{"foo":}', true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -903,6 +948,7 @@ export class StructuredOutputError extends Schema.Error( /** * Marks `StructuredOutputError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -910,6 +956,7 @@ export class StructuredOutputError extends Schema.Error( /** * Structured output errors are retryable since LLM outputs are non-deterministic. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -923,7 +970,7 @@ export class StructuredOutputError extends Schema.Error( * * ```ts import.meta.vitest * import { Effect, Schema } from "effect" - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const schemaError = await Effect.runPromise( * Schema.decodeUnknownEffect(Schema.Struct({ name: Schema.String }))({}).pipe(Effect.flip) @@ -932,6 +979,7 @@ export class StructuredOutputError extends Schema.Error( * parseError.responseText // => "{}" * ``` * + * @stability unstable * @since 4.0.0 */ static fromSchemaError(error: Schema.SchemaError, responseText: string): StructuredOutputError { @@ -958,7 +1006,7 @@ export class StructuredOutputError extends Schema.Error( * **Example** (Creating an unsupported schema error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.UnsupportedSchemaError({ * description: "Unions are not supported in Anthropic structured output" @@ -967,6 +1015,7 @@ export class StructuredOutputError extends Schema.Error( * const result = [error.description, error.isRetryable] // => ["Unions are not supported in Anthropic structured output", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -980,6 +1029,7 @@ export class UnsupportedSchemaError extends Schema.Error /** * Marks `UnsupportedSchemaError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -987,6 +1037,7 @@ export class UnsupportedSchemaError extends Schema.Error /** * Unsupported schema errors are not retryable because they indicate a programmer error. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1008,7 +1059,7 @@ export class UnsupportedSchemaError extends Schema.Error * **Example** (Creating an unknown error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const unknownError = new AiError.UnknownError({ * description: "An unexpected error occurred" @@ -1017,6 +1068,7 @@ export class UnsupportedSchemaError extends Schema.Error * const result = [unknownError.description, unknownError.isRetryable] // => ["An unexpected error occurred", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1031,6 +1083,7 @@ export class UnknownError extends Schema.Error( /** * Marks `UnknownError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1038,6 +1091,7 @@ export class UnknownError extends Schema.Error( /** * Unknown errors are not retryable by default. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1064,7 +1118,7 @@ export class UnknownError extends Schema.Error( * **Example** (Creating a tool not found error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.ToolNotFoundError({ * toolName: "unknownTool", @@ -1074,6 +1128,7 @@ export class UnknownError extends Schema.Error( * const result = [error.toolName, error.availableTools, error.isRetryable] // => ["unknownTool", ["GetWeather", "GetTime"], true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1087,6 +1142,7 @@ export class ToolNotFoundError extends Schema.Error( /** * Marks `ToolNotFoundError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1094,6 +1150,7 @@ export class ToolNotFoundError extends Schema.Error( /** * Tool not found errors are retryable because the model may self-correct. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1117,7 +1174,7 @@ export class ToolNotFoundError extends Schema.Error( * **Example** (Creating a tool parameter validation error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.ToolParameterValidationError({ * toolName: "GetWeather", @@ -1127,6 +1184,7 @@ export class ToolNotFoundError extends Schema.Error( * const result = [error.toolName, error.description, error.isRetryable] // => ["GetWeather", "Expected string, got number", true] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1140,6 +1198,7 @@ export class ToolParameterValidationError extends Schema.Error ["GetWeather", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1193,6 +1254,7 @@ export class InvalidToolResultError extends Schema.Error /** * Marks `InvalidToolResultError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1200,6 +1262,7 @@ export class InvalidToolResultError extends Schema.Error /** * Invalid tool result errors are not retryable because they indicate a bug in the handler. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1222,7 +1285,7 @@ export class InvalidToolResultError extends Schema.Error * **Example** (Creating a tool result encoding error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.ToolResultEncodingError({ * toolName: "GetWeather", @@ -1233,6 +1296,7 @@ export class InvalidToolResultError extends Schema.Error * const result = [error.toolName, error.description, error.isRetryable] // => ["GetWeather", "Cannot encode bigint values as JSON", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1247,6 +1311,7 @@ export class ToolResultEncodingError extends Schema.Error ["OpenAiCodeInterpreter", "Invalid container ID format", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1299,6 +1366,7 @@ export class ToolConfigurationError extends Schema.Error /** * Marks `ToolConfigurationError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1306,6 +1374,7 @@ export class ToolConfigurationError extends Schema.Error /** * Configuration errors are not retryable because they indicate a code bug. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1328,7 +1397,7 @@ export class ToolConfigurationError extends Schema.Error * **Example** (Creating a toolkit required error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.ToolkitRequiredError({ * pendingApprovals: ["GetWeather", "SendEmail"] @@ -1337,6 +1406,7 @@ export class ToolConfigurationError extends Schema.Error * const result = [error.pendingApprovals, error.isRetryable] // => [["GetWeather", "SendEmail"], false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1350,6 +1420,7 @@ export class ToolkitRequiredError extends Schema.Error( /** * Marks `ToolkitRequiredError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1357,6 +1428,7 @@ export class ToolkitRequiredError extends Schema.Error( /** * Toolkit required errors are not retryable without providing a toolkit. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1381,7 +1453,7 @@ export class ToolkitRequiredError extends Schema.Error( * **Example** (Creating an invalid user input error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = new AiError.InvalidUserInputError({ * description: "Unsupported media type 'video/mp4'. Supported types include images, application/pdf, text/plain" @@ -1390,6 +1462,7 @@ export class ToolkitRequiredError extends Schema.Error( * const result = [error._tag, error.isRetryable] // => ["InvalidUserInputError", false] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1402,6 +1475,7 @@ export class InvalidUserInputError extends Schema.Error( /** * Marks `InvalidUserInputError` as a semantic AI error reason for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [ReasonTypeId] = ReasonTypeId @@ -1409,6 +1483,7 @@ export class InvalidUserInputError extends Schema.Error( /** * Invalid user input errors require fixing the input and are not retryable. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1433,6 +1508,7 @@ export class InvalidUserInputError extends Schema.Error( * `isRetryable` getter. Provider-facing reasons may also include retry timing, * provider metadata, usage information, or HTTP context. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -1469,6 +1545,7 @@ export type AiErrorReason = * * @see {@link isAiErrorReason} for checking an existing value without Schema decoding * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1535,7 +1612,7 @@ const TypeId = "~effect/ai/AiError" as const * * ```ts import.meta.vitest * import { Duration, Effect } from "effect" - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const aiOperation = Effect.fail(new AiError.AiError({ * module: "OpenAI", @@ -1556,6 +1633,7 @@ const TypeId = "~effect/ai/AiError" as const * await Effect.runPromise(handled) // => "Retry after 30000 millis" * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1573,6 +1651,7 @@ export class AiError extends Schema.Error( /** * Delegates to the underlying reason's `isRetryable` getter. * + * @stability unstable * @since 4.0.0 */ get isRetryable(): boolean { @@ -1582,6 +1661,7 @@ export class AiError extends Schema.Error( /** * Delegates to the underlying reason's `retryAfter` if present. * + * @stability unstable * @since 4.0.0 */ get retryAfter(): Duration.Duration | undefined { @@ -1596,6 +1676,7 @@ export class AiError extends Schema.Error( /** * The encoded (serialized) form of an `AiError`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1607,7 +1688,7 @@ export type AiErrorEncoded = typeof AiError["Encoded"] * **Example** (Checking for an AI error) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const someError = new Error("generic error") * const aiError = AiError.make({ @@ -1619,6 +1700,7 @@ export type AiErrorEncoded = typeof AiError["Encoded"] * const result = [AiError.isAiError(someError), AiError.isAiError(aiError)] // => [false, true] * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -1630,7 +1712,7 @@ export const isAiError = (u: unknown): u is AiError => Predicate.hasProperty(u, * **Example** (Checking for an AI error reason) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const rateLimitError = new AiError.RateLimitError({}) * const genericError = new Error("generic error") @@ -1638,6 +1720,7 @@ export const isAiError = (u: unknown): u is AiError => Predicate.hasProperty(u, * const result = [AiError.isAiErrorReason(rateLimitError), AiError.isAiErrorReason(genericError)] // => [true, false] * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -1650,7 +1733,7 @@ export const isAiErrorReason = (u: unknown): u is AiErrorReason => Predicate.has * * ```ts import.meta.vitest * import { Duration } from "effect" - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const error = AiError.make({ * module: "OpenAI", @@ -1663,6 +1746,7 @@ export const isAiErrorReason = (u: unknown): u is AiErrorReason => Predicate.has * const result = [error.module, error.method, error.reason._tag] // => ["OpenAI", "completion", "RateLimitError"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1683,7 +1767,7 @@ export const make = (params: { * **Example** (Mapping an HTTP status to a reason) * * ```ts import.meta.vitest - * import { AiError } from "effect/unstable/ai" + * import { AiError } from "effect/ai" * * const reason = AiError.reasonFromHttpStatus({ * status: 429, @@ -1693,6 +1777,7 @@ export const make = (params: { * reason._tag // => "RateLimitError" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1700,7 +1785,7 @@ export const reasonFromHttpStatus = (params: { readonly status: number readonly body?: unknown readonly http?: typeof HttpContext.Type - readonly metadata?: typeof ProviderMetadata.Type + readonly metadata?: ProviderMetadata readonly description?: string | undefined }): AiErrorReason => { const { status, http, metadata, description } = params diff --git a/repos/effect/packages/effect/src/unstable/ai/AnthropicStructuredOutput.ts b/repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/ai/AnthropicStructuredOutput.ts rename to repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts index 280c14f7f4..e945865261 100644 --- a/repos/effect/packages/effect/src/unstable/ai/AnthropicStructuredOutput.ts +++ b/repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts @@ -7,11 +7,12 @@ * Unsupported constraints can be omitted from the provider schema and remain * enforced by the returned codec. * + * @stability unstable * @since 4.0.0 */ -import type * as JsonSchema from "../../JsonSchema.ts" -import * as Rec from "../../Record.ts" -import * as Schema from "../../Schema.ts" +import type * as JsonSchema from "../JsonSchema.ts" +import * as Rec from "../Record.ts" +import * as Schema from "../Schema.ts" import * as InternalStructuredOutput from "./internal/structured-output.ts" import * as LanguageModel from "./LanguageModel.ts" import * as OpenAiStructuredOutput from "./OpenAiStructuredOutput.ts" @@ -48,6 +49,7 @@ import * as OpenAiStructuredOutput from "./OpenAiStructuredOutput.ts" * @see {@link LanguageModel.CodecTransformer} for the structured-output transformer contract * @see {@link OpenAiStructuredOutput.toCodecOpenAI} for the OpenAI-specific transformer * + * @stability unstable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/ai/Chat.ts b/repos/effect/packages/effect/src/ai/Chat.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/ai/Chat.ts rename to repos/effect/packages/effect/src/ai/Chat.ts index 5572c8657b..c55c69b41d 100644 --- a/repos/effect/packages/effect/src/unstable/ai/Chat.ts +++ b/repos/effect/packages/effect/src/ai/Chat.ts @@ -8,23 +8,24 @@ * sessions, seed sessions from prompts, restore exported history, or connect a * chat to persistence. * + * @stability unstable * @since 4.0.0 */ -import * as Channel from "../../Channel.ts" -import * as Chunk from "../../Chunk.ts" -import * as Context from "../../Context.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Predicate from "../../Predicate.ts" -import * as Ref from "../../Ref.ts" -import * as Schema from "../../Schema.ts" -import * as Semaphore from "../../Semaphore.ts" -import * as Stream from "../../Stream.ts" -import type { NoExcessProperties } from "../../Types.ts" +import * as Channel from "../Channel.ts" +import * as Chunk from "../Chunk.ts" +import * as Context from "../Context.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" import type { PersistenceError } from "../persistence/Persistence.ts" import { BackingPersistence } from "../persistence/Persistence.ts" +import * as Predicate from "../Predicate.ts" +import * as Ref from "../Ref.ts" +import * as Schema from "../Schema.ts" +import * as Semaphore from "../Semaphore.ts" +import * as Stream from "../Stream.ts" +import type { NoExcessProperties } from "../Types.ts" import * as AiError from "./AiError.ts" import * as IdGenerator from "./IdGenerator.ts" import * as LanguageModel from "./LanguageModel.ts" @@ -39,7 +40,7 @@ import type * as Tool from "./Tool.ts" * * ```ts import.meta.vitest * import { Effect, Layer, Stream } from "effect" - * import { Chat, LanguageModel } from "effect/unstable/ai" + * import { Chat, LanguageModel } from "effect/ai" * * const FakeLanguageModel = Layer.effect( * LanguageModel.LanguageModel, @@ -68,6 +69,7 @@ import type * as Tool from "./Tool.ts" * ) // => "Quantum computers use quantum states to process information." * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -76,6 +78,7 @@ export const Chat: Context.Service = Context.Service("effect/ai/Chat /** * Brand type for `Chat`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -84,6 +87,7 @@ export type TypeId = "~effect/ai/Chat" /** * Brand for `Chat` implementations. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -94,6 +98,7 @@ export const TypeId: TypeId = "~effect/ai/Chat" * * @see {@link Persisted} for the persistence-backed extension * + * @stability unstable * @category models * @since 4.0.0 */ @@ -112,7 +117,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect, Ref } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const inspectHistory = Effect.gen(function*() { * const chat = yield* Chat.fromPrompt("Hello") @@ -137,7 +142,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const saveChat = Effect.gen(function*() { * const chat = yield* Chat.fromPrompt("Hello!") @@ -162,7 +167,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const backupChat = Effect.gen(function*() { * const chat = yield* Chat.fromPrompt("Explain photosynthesis") @@ -188,7 +193,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect, Layer, Stream } from "effect" - * import { Chat, LanguageModel } from "effect/unstable/ai" + * import { Chat, LanguageModel } from "effect/ai" * * const FakeLanguageModel = Layer.effect( * LanguageModel.LanguageModel, @@ -272,7 +277,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect, Layer, Stream } from "effect" - * import { Chat, LanguageModel } from "effect/unstable/ai" + * import { Chat, LanguageModel } from "effect/ai" * * const FakeLanguageModel = Layer.effect( * LanguageModel.LanguageModel, @@ -355,7 +360,7 @@ export interface Chat { * * ```ts import.meta.vitest * import { Effect, Layer, Schema, Stream } from "effect" - * import { Chat, LanguageModel } from "effect/unstable/ai" + * import { Chat, LanguageModel } from "effect/ai" * * const ContactSchema = Schema.Struct({ * name: Schema.String, @@ -524,7 +529,7 @@ const makeUnsafe = (history: Ref.Ref) => { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const freshChat = Effect.gen(function*() { * const chat = yield* Chat.empty @@ -535,6 +540,7 @@ const makeUnsafe = (history: Ref.Ref) => { * await Effect.runPromise(freshChat) // => 0 * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -552,7 +558,7 @@ export const empty: Effect.Effect = Effect.sync(() => makeUnsafe(Ref.makeU * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const chatWithSystemPrompt = Effect.gen(function*() { * const chat = yield* Chat.fromPrompt([{ @@ -571,7 +577,7 @@ export const empty: Effect.Effect = Effect.sync(() => makeUnsafe(Ref.makeU * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * // Initialize with conversation history * const existingChat = Effect.gen(function*() { @@ -597,6 +603,7 @@ export const empty: Effect.Effect = Effect.sync(() => makeUnsafe(Ref.makeU * await Effect.runPromise(existingChat) // => 3 * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -616,7 +623,7 @@ export const fromPrompt = (prompt: Prompt.RawInput) => * * ```ts import.meta.vitest * import { Effect, Ref } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const restoreChat = Effect.gen(function*() { * const originalChat = yield* Chat.fromPrompt([ @@ -650,6 +657,7 @@ export const fromPrompt = (prompt: Prompt.RawInput) => * await Effect.runPromise(restoreChat) // => { roles: ["user", "assistant"], text: "The project uses Effect." } * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -671,7 +679,7 @@ export const fromExport = (data: unknown): Effect.Effect< * * ```ts import.meta.vitest * import { Effect, Ref } from "effect" - * import { Chat } from "effect/unstable/ai" + * import { Chat } from "effect/ai" * * const restoreFromJson = Effect.gen(function*() { * const original = yield* Chat.fromPrompt("Hello") @@ -684,6 +692,7 @@ export const fromExport = (data: unknown): Effect.Effect< * await Effect.runPromise(restoreFromJson) // => 1 * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -705,6 +714,7 @@ export const fromJson = (data: string): Effect.Effect< * Use to represent a missing persisted conversation when lookup by id cannot * find stored history. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -723,6 +733,7 @@ export class ChatNotFoundError extends Schema.Error( * Use to provide the storage operations needed by persisted conversation * sessions. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -734,6 +745,7 @@ export class Persistence extends Context.Service { + readonly _tag: "Classify" + readonly instructions: string + readonly criteria: { readonly [L in Label]: string } +} + +/** + * Decision that places the input on an ordered scale of criteria. + * `criteria` lists levels from lowest to highest. + * + * @see {@link rate} for the constructor + * @see {@link RateAnswer} for the answer produced by this decision + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Rate { + readonly _tag: "Rate" + readonly instructions: string + readonly criteria: ReadonlyArray +} + +/** + * Decision that estimates how likely a statement about the input is to hold. + * Optional `criteria` describes both outcomes when supplied. + * The answer is the probability of `true`. + * + * @see {@link probability} for the constructor + * @see {@link ProbabilityAnswer} for the answer produced by this decision + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Probability { + readonly _tag: "Probability" + readonly instructions: string + readonly criteria?: { + readonly false: string + readonly true: string + } | undefined +} + +/** + * Union of every decision kind. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Any = Classify | Rate | Probability + +/** + * Answer to a {@link Classify} decision. + * `label` is chosen by the provider and need not have the highest probability. + * `confidence` is an optional, provider-defined measure in `[0, 1]`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ClassifyAnswer() => { + const router = FindMyWay.make({ + ignoreTrailingSlash: true, + ignoreDuplicateSlashes: true, + caseSensitive: true + }) + const add = (uri: string, value: A) => { + router.on("GET", `/${uri}`, value) + } + const find = (uri: string) => router.find("GET", `/${uri}`) + + return { add, find } as const +} + +const compileUriTemplate = (segments: TemplateStringsArray, ...schemas: ReadonlyArray) => { + let routerPath = segments[0].replace(":", "::") + let uriPath = segments[0] + const params: Record = Object.create(null) + let pathSchema = Schema.Tuple([]) as Schema.Top + if (schemas.length > 0) { + const arr: Array = [] + for (let i = 0; i < schemas.length; i++) { + const toCodecStringTree = Schema.toCodecStringTree(schemas[i]) + const segment = segments[i + 1] + const key = String(i) + arr.push(toCodecStringTree) + routerPath += `:${key}${segment.replace(":", "::")}` + const schema = schemas[i] + const paramName = isParam(schema) ? (schema as Param).name : `param${key}` + params[paramName] = toCodecStringTree + uriPath += `{${paramName}}${segment}` + } + pathSchema = Schema.Tuple(arr) + } + return { + routerPath, + uriPath, + schema: pathSchema, + params + } as const +} + +const resolveResourceContent = ( + uri: string, + content: ReadResourceResult | string | Uint8Array +): ReadResourceResult => { + if (typeof content === "string") { + return { + contents: [{ + uri, + text: content + }] + } + } else if (content instanceof Uint8Array) { + return { + contents: [{ + uri, + blob: content + }] + } + } + return content +} + +const InvalidBatchExit = Schema.TaggedStruct("Exit", { + requestId: Schema.Null, + exit: Schema.TaggedStruct("Failure", { + cause: Schema.Unknown + }) +}) + +const decodeInvalidBatchExit = Schema.decodeUnknownResult(InvalidBatchExit) + +const isProtocolVersion = (version: string): version is McpProtocol.ProtocolVersion => + version === "2024-11-05" || + version === "2025-03-26" || + version === "2025-06-18" || + version === "2025-11-25" || + version === "2026-07-28" diff --git a/repos/effect/packages/effect/src/ai/Model.ts b/repos/effect/packages/effect/src/ai/Model.ts new file mode 100644 index 0000000000..35c00143f4 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/Model.ts @@ -0,0 +1,160 @@ +/** + * Wraps an AI model layer with provider and model metadata. + * + * A `Model` can be used anywhere its underlying `Layer` can be used. It also + * provides the current provider name and model name through the Effect context. + * This module includes the `Model` interface, the `ProviderName` and + * `ModelName` service tags, and the `make` constructor. Models can also capture + * their required services from the current context when they need to be used + * inside another Effect service. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import { identity } from "../Function.ts" +import { PipeInspectableProto } from "../internal/core.ts" +import * as Layer from "../Layer.ts" + +const TypeId = "~effect/ai/Model" as const + +/** + * A Model represents a provider-specific AI service. + * + * **When to use** + * + * Use when you use a Model directly as a Layer to provide a particular model implementation + * to an Effect program, or use it as an Effect to "lift" dependencies of the + * Model constructor into the parent Effect when you want to use a Model from + * within an Effect service. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Model + extends Layer.Layer +{ + readonly [TypeId]: typeof TypeId + + /** + * The provider identifier (e.g., "openai", "anthropic", "amazon-bedrock"). + */ + readonly provider: Provider + + /** + * Returns a `Layer` with the requirements satisfied, using the current context. + */ + readonly captureRequirements: Effect.Effect< + Layer.Layer, + never, + Requires + > +} + +/** + * Service tag that provides the current large language model provider name. + * + * **Details** + * + * This tag is automatically provided by Model instances and can be used to + * access the name of the provider that is currently in use within a given + * Effect program. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ProviderName extends Context.Service()( + "effect/ai/Model/ProviderName" +) {} + +/** + * Service tag that provides the current large language model name. + * + * **Details** + * + * This tag is automatically provided by Model instances and can be used to + * access the name of the model that is currently in use within a given Effect + * program. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ModelName extends Context.Service()( + "effect/ai/Model/ModelName" +) {} + +const Proto = { + [TypeId]: TypeId, + ["~effect/Layer"]: { + _ROut: identity, + _E: identity, + _RIn: identity + }, + get captureRequirements() { + const self = this as any as Model + return Effect.contextWith((context: Context.Context) => + Effect.succeed(Layer.provide(self, Layer.succeedContext(context))) + ) + }, + ...PipeInspectableProto, + toJSON(this: Model): unknown { + return { + _id: "effect/ai/Model", + provider: this.provider + } + } +} + +/** + * Creates a Model from a provider name and a Layer that constructs AI services. + * + * **Example** (Providing model metadata) + * + * ```ts import.meta.vitest + * import { Effect, Layer } from "effect" + * import { Model } from "effect/ai" + * + * const model = Model.make("amazon-bedrock", "claude-3-5-haiku", Layer.empty) + * const program = Effect.gen(function*() { + * const provider = yield* Model.ProviderName + * const modelName = yield* Model.ModelName + * return { provider, modelName } + * }).pipe(Effect.provide(model)) + * + * await Effect.runPromise(program) // => { provider: "amazon-bedrock", modelName: "claude-3-5-haiku" } + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = ( + /** + * Provider identifier (e.g., "openai", "anthropic", "amazon-bedrock"). + */ + provider: Provider, + /** + * Model identifier (e.g., "gpt-5", "claude-3-5-haiku"). + */ + modelName: Name, + /** + * Layer that provides the AI services for this provider. + */ + layer: Layer.Layer +): Model => + Object.assign( + Object.create(Proto), + { provider }, + Layer.merge( + layer, + Layer.succeedContext( + ProviderName.context(provider).pipe( + Context.add(ModelName, modelName) + ) + ) + ) + ) diff --git a/repos/effect/packages/effect/src/unstable/ai/OpenAiStructuredOutput.ts b/repos/effect/packages/effect/src/ai/OpenAiStructuredOutput.ts similarity index 98% rename from repos/effect/packages/effect/src/unstable/ai/OpenAiStructuredOutput.ts rename to repos/effect/packages/effect/src/ai/OpenAiStructuredOutput.ts index b0325080d1..39bbdf060a 100644 --- a/repos/effect/packages/effect/src/unstable/ai/OpenAiStructuredOutput.ts +++ b/repos/effect/packages/effect/src/ai/OpenAiStructuredOutput.ts @@ -7,11 +7,12 @@ * application type. Unsupported constraints can be omitted from the provider * schema and remain enforced by the returned codec. * + * @stability unstable * @since 4.0.0 */ -import type * as JsonSchema from "../../JsonSchema.ts" -import * as Rec from "../../Record.ts" -import * as Schema from "../../Schema.ts" +import type * as JsonSchema from "../JsonSchema.ts" +import * as Rec from "../Record.ts" +import * as Schema from "../Schema.ts" import * as InternalStructuredOutput from "./internal/structured-output.ts" /** @@ -52,6 +53,7 @@ import * as InternalStructuredOutput from "./internal/structured-output.ts" * - Compatibility targets standard OpenAI models. Fine-tuned models support a * smaller JSON Schema subset. * + * @stability unstable * @category transforming * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ai/Prompt.ts b/repos/effect/packages/effect/src/ai/Prompt.ts new file mode 100644 index 0000000000..b5c61bacb5 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/Prompt.ts @@ -0,0 +1,2487 @@ +/** + * Defines prompts sent to AI language models. + * + * A prompt is an ordered list of messages. Messages can use roles such as + * system, user, assistant, and tool, and their content can be split into typed + * parts such as text, files, reasoning, tool calls, tool results, and approval + * messages. This module helps build prompts, combine them, and convert raw + * input or response parts into the shared prompt shape. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Arr from "../Array.ts" +import * as Effect from "../Effect.ts" +import * as Base64 from "../encoding/Base64.ts" +import { dual } from "../Function.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as SchemaIssue from "../SchemaIssue.ts" +import * as SchemaParser from "../SchemaParser.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" +import type * as Response from "./Response.ts" + +// ============================================================================= +// Options +// ============================================================================= + +/** + * Schema for provider-specific options that can be attached to content parts + * and messages. + * + * **Details** + * + * Provider-specific options are keyed by provider-specific names, and each + * value is JSON or `null`. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export const ProviderOptions: Schema.$Record< + Schema.String, + Schema.NullOr> +> = Schema.Record(Schema.String, Schema.NullOr(Schema.Json)) + +/** + * Type of provider-specific options that can be attached to prompt messages + * and content parts. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export type ProviderOptions = typeof ProviderOptions.Type + +// ============================================================================= +// Base Part +// ============================================================================= + +const PartTypeId = "~effect/ai/Prompt/Part" as const + +/** + * Type guard to check if a value is a Part. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isPart = (u: unknown): u is Part => Predicate.hasProperty(u, PartTypeId) + +/** + * Union type representing all possible content parts within messages. + * + * **Details** + * + * Parts are the building blocks of message content, supporting text, files, + * reasoning, tool calls, tool results, tool approval responses, and tool + * approval requests. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Part = + | TextPart + | ReasoningPart + | FilePart + | ToolCallPart + | ToolResultPart + | ToolApprovalResponsePart + | ToolApprovalRequestPart + +/** + * Encoded representation of a Part. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type PartEncoded = + | TextPartEncoded + | ReasoningPartEncoded + | FilePartEncoded + | ToolCallPartEncoded + | ToolResultPartEncoded + | ToolApprovalResponsePartEncoded + | ToolApprovalRequestPartEncoded + +/** + * Base interface for all content parts. + * + * **Details** + * + * It provides the common structure shared by all content parts, including the + * part type and provider options. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface BasePart { + readonly [PartTypeId]: typeof PartTypeId + /** + * The type of this content part. + */ + readonly type: Type + /** + * Provider-specific options for this part. + */ + readonly options: Options +} + +/** + * Base interface for encoded content parts. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface BasePartEncoded { + /** + * The type of this content part. + */ + readonly type: Type + /** + * Provider-specific options for this part. + */ + readonly options?: Options | undefined +} + +const BasePart = Schema.Struct({ + [PartTypeId]: Schema.Literal(PartTypeId).pipe( + Schema.withDecodingDefaultKey(Effect.succeed(PartTypeId), { encodingStrategy: "omit" }) + ), + options: ProviderOptions.pipe(Schema.withDecodingDefault(Effect.succeed({}))) +}) + +/** + * Creates a new content part of the specified type. + * + * **Example** (Creating content parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const textPart = Prompt.makePart("text", { + * text: "Hello, world!" + * }) + * + * const filePart = Prompt.makePart("file", { + * mediaType: "image/png", + * fileName: "screenshot.png", + * data: new Uint8Array([1, 2, 3]) + * }) + * + * const result = [textPart.type, filePart.type] // => ["text", "file"] + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const makePart = ( + /** + * The type of part to create. + */ + type: Type, + /** + * Parameters specific to the part type being created. + */ + params: Omit, typeof PartTypeId | "type" | "options"> & { + /** + * Optional provider-specific options for this part. + */ + readonly options?: Extract["options"] | undefined + } +): Extract => (({ + ...params, + [PartTypeId]: PartTypeId, + type, + options: params.options ?? {} +}) as any) + +/** + * A utility type for specifying the parameters required to construct a + * specific part of a prompt. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ +export type PartConstructorParams

= Omit & { + /** + * Optional provider-specific options for this part. + */ + readonly options?: Part["options"] | undefined +} + +// ============================================================================= +// Text Part +// ============================================================================= + +/** + * Content part representing plain text. + * + * **Details** + * + * Text parts are the basic content type used for textual information in + * messages. + * + * **Example** (Creating text parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const textPart: Prompt.TextPart = Prompt.makePart("text", { + * text: "Hello, how can I help you today?" + * }) + * textPart.text // => "Hello, how can I help you today?" + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface TextPart extends BasePart<"text", TextPartOptions> { + /** + * The text content. + */ + readonly text: string +} + +/** + * Encoded representation of text parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface TextPartEncoded extends BasePartEncoded<"text", TextPartOptions> { + /** + * The text content. + */ + readonly text: string +} + +/** + * Represents provider-specific options that can be associated with a + * `TextPart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface TextPartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of text parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const TextPart: Schema.Struct< + { + readonly type: Schema.Literal<"text"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + } +> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("text"), + text: Schema.String +}).annotate({ identifier: "TextPart" }) + +/** + * Constructs a new text part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const textPart = (params: PartConstructorParams): TextPart => makePart("text", params as any) + +// ============================================================================= +// Reasoning Part +// ============================================================================= + +/** + * Content part carrying reasoning text in an assistant message, such as a + * provider-supplied reasoning summary or explanation. + * + * **Example** (Creating reasoning parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const reasoningPart: Prompt.ReasoningPart = Prompt.makePart("reasoning", { + * text: + * "Summary: the response compares the requested options by price and availability." + * }) + * reasoningPart.type // => "reasoning" + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ReasoningPart extends BasePart<"reasoning", ReasoningPartOptions> { + /** + * The reasoning or thought process text. + */ + readonly text: string +} + +/** + * Encoded representation of reasoning parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ReasoningPartEncoded extends BasePartEncoded<"reasoning", ReasoningPartOptions> { + /** + * The reasoning or thought process text. + */ + readonly text: string +} + +/** + * Represents provider-specific options that can be associated with a + * `ReasoningPart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ReasoningPartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of reasoning parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ReasoningPart: Schema.Struct<{ + readonly type: Schema.Literal<"reasoning"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("reasoning"), + text: Schema.String +}).annotate({ identifier: "ReasoningPart" }) + +/** + * Constructs a new reasoning part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const reasoningPart = (params: PartConstructorParams): ReasoningPart => + makePart("reasoning", params as any) + +// ============================================================================= +// File Part +// ============================================================================= + +/** + * Content part representing a file attachment. + * + * **Details** + * + * Files can be provided as base64 data strings, byte arrays, or URLs, and can + * represent images, documents, or other binary data. + * + * **Example** (Creating file parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const imagePart: Prompt.FilePart = Prompt.makePart("file", { + * mediaType: "image/jpeg", + * fileName: "photo.jpg", + * data: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..." + * }) + * + * const documentPart: Prompt.FilePart = Prompt.makePart("file", { + * mediaType: "application/pdf", + * fileName: "report.pdf", + * data: new Uint8Array([1, 2, 3]) + * }) + * + * const result = [imagePart.mediaType, documentPart.fileName] // => ["image/jpeg", "report.pdf"] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface FilePart extends BasePart<"file", FilePartOptions> { + /** + * MIME type of the file (e.g., "image/jpeg", "application/pdf"). + */ + readonly mediaType: string + /** + * Optional filename for the file. + */ + readonly fileName?: string | undefined + /** + * File data as base64 string of data, a byte array, or a URL. + */ + readonly data: string | Uint8Array | URL +} + +/** + * Encoded representation of file parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface FilePartEncoded extends BasePartEncoded<"file", FilePartOptions> { + /** + * MIME type of the file (e.g., "image/jpeg", "application/pdf"). + */ + readonly mediaType: string + /** + * Optional filename for the file. + */ + readonly fileName?: string | undefined + /** + * File data as base64 string of data, a byte array, or a URL. + */ + readonly data: string | Uint8Array | URL +} + +/** + * Represents provider-specific options that can be associated with a + * `FilePart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface FilePartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of file parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const FilePart: Schema.Struct<{ + readonly type: Schema.Literal<"file"> + readonly mediaType: Schema.String + readonly fileName: Schema.optional + readonly data: Schema.Union + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("file"), + mediaType: Schema.String, + fileName: Schema.optional(Schema.String), + data: Schema.Union([Schema.String, Schema.Uint8Array, Schema.URL]) +}).annotate({ identifier: "FilePart" }) + +/** + * Constructs a `FilePart` for prompt file attachments. + * + * **When to use** + * + * Use to create the file-attachment part of a prompt from typed file part + * parameters. + * + * @see {@link makePart} for the generic part constructor + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const filePart = (params: PartConstructorParams): FilePart => makePart("file", params as any) + +// ============================================================================= +// Tool Call Part +// ============================================================================= + +/** + * Content part representing a tool call request. + * + * **Example** (Creating tool call parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const toolCallPart: Prompt.ToolCallPart = Prompt.makePart("tool-call", { + * id: "call_123", + * name: "get_weather", + * params: { city: "San Francisco", units: "celsius" }, + * providerExecuted: false + * }) + * toolCallPart.name // => "get_weather" + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolCallPart extends BasePart<"tool-call", ToolCallPartOptions> { + /** + * Unique identifier for this tool call. + */ + readonly id: string + /** + * Name of the tool to invoke. + */ + readonly name: string + /** + * Parameters to pass to the tool. + */ + readonly params: unknown + /** + * Whether the tool was executed by the provider (true) or framework (false). + */ + readonly providerExecuted: boolean +} + +/** + * Encoded representation of tool call parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolCallPartEncoded extends BasePartEncoded<"tool-call", ToolCallPartOptions> { + /** + * Unique identifier for this tool call. + */ + readonly id: string + /** + * Name of the tool to invoke. + */ + readonly name: string + /** + * Parameters to pass to the tool. + */ + readonly params: unknown + /** + * Whether the tool was executed by the provider (true) or framework (false). + */ + readonly providerExecuted?: boolean | undefined +} + +/** + * Represents provider-specific options that can be associated with a + * `ToolCallPart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToolCallPartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of tool call parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolCallPart: Schema.Struct<{ + readonly type: Schema.Literal<"tool-call"> + readonly id: Schema.String + readonly name: Schema.String + readonly params: Schema.Unknown + readonly providerExecuted: Schema.withDecodingDefault + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("tool-call"), + id: Schema.String, + name: Schema.String, + params: Schema.Unknown, + providerExecuted: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))) +}).annotate({ identifier: "ToolCallPart" }) + +/** + * Constructs a new tool call part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const toolCallPart = (params: PartConstructorParams): ToolCallPart => + makePart("tool-call", params as any) + +// ============================================================================= +// Tool Result Part +// ============================================================================= + +/** + * Content part representing the result of a tool call. + * + * **Example** (Creating tool result parts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const toolResultPart: Prompt.ToolResultPart = Prompt.makePart("tool-result", { + * id: "call_123", + * name: "get_weather", + * isFailure: false, + * result: { + * temperature: 22, + * condition: "sunny", + * humidity: 65 + * }, + * providerExecuted: false + * }) + * const result = [toolResultPart.name, toolResultPart.isFailure] // => ["get_weather", false] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolResultPart extends BasePart<"tool-result", ToolResultPartOptions> { + /** + * Unique identifier matching the original tool call. + */ + readonly id: string + /** + * Name of the tool that was executed. + */ + readonly name: string + /** + * Whether or not the result of executing the tool call handler was an error. + */ + readonly isFailure: boolean + /** + * The result returned by the tool execution. + */ + readonly result: unknown + /** + * Whether the tool was executed by the provider (true) or framework (false). + */ + readonly providerExecuted: boolean +} + +/** + * Encoded representation of tool result parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolResultPartEncoded extends BasePartEncoded<"tool-result", ToolResultPartOptions> { + /** + * Unique identifier matching the original tool call. + */ + readonly id: string + /** + * Name of the tool that was executed. + */ + readonly name: string + /** + * Whether or not the result of executing the tool call handler was an error. + */ + readonly isFailure: boolean + /** + * The result returned by the tool execution. + */ + readonly result: unknown + /** + * Whether the tool was executed by the provider (true) or framework (false). + */ + readonly providerExecuted?: boolean | undefined +} + +/** + * Represents provider-specific options that can be associated with a + * `ToolResultPart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToolResultPartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of tool result parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolResultPart: Schema.Struct<{ + readonly type: Schema.Literal<"tool-result"> + readonly id: Schema.String + readonly name: Schema.String + readonly isFailure: Schema.Boolean + readonly result: Schema.Unknown + readonly providerExecuted: Schema.withDecodingDefault + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("tool-result"), + id: Schema.String, + name: Schema.String, + isFailure: Schema.Boolean, + result: Schema.Unknown, + providerExecuted: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))) +}).annotate({ identifier: "ToolResultPart" }) + +/** + * Constructs a new tool result part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const toolResultPart = (params: PartConstructorParams): ToolResultPart => + makePart("tool-result", params as any) + +// ============================================================================= +// Tool Approval Response Part +// ============================================================================= + +/** + * Content part representing a user's response to a tool approval request. + * + * **When to use** + * + * Use when tool messages must approve or deny tool execution for tools with the + * `needsApproval` property set. + * + * **Example** (Creating tool approval responses) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const approvalResponse: Prompt.ToolApprovalResponsePart = Prompt.makePart( + * "tool-approval-response", + * { + * approvalId: "approval_123", + * approved: true + * } + * ) + * + * const denialResponse: Prompt.ToolApprovalResponsePart = Prompt.makePart( + * "tool-approval-response", + * { + * approvalId: "approval_456", + * approved: false, + * reason: "Operation not allowed" + * } + * ) + * + * const result = [approvalResponse.approved, denialResponse.approved] // => [true, false] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolApprovalResponsePart extends BasePart<"tool-approval-response", ToolApprovalResponsePartOptions> { + /** + * References the original approval request. + */ + readonly approvalId: string + /** + * User's decision to approve or deny the tool execution. + */ + readonly approved: boolean + /** + * Optional justification for the decision. + */ + readonly reason?: string | undefined +} + +/** + * Encoded representation of tool approval response parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolApprovalResponsePartEncoded + extends BasePartEncoded<"tool-approval-response", ToolApprovalResponsePartOptions> +{ + /** + * References the original approval request. + */ + readonly approvalId: string + /** + * User's decision to approve or deny the tool execution. + */ + readonly approved: boolean + /** + * Optional justification for the decision. + */ + readonly reason?: string | undefined +} + +/** + * Represents provider-specific options that can be associated with a + * `ToolApprovalResponsePart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToolApprovalResponsePartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of tool approval response parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolApprovalResponsePart: Schema.Struct<{ + readonly type: Schema.Literal<"tool-approval-response"> + readonly approvalId: Schema.String + readonly approved: Schema.Boolean + readonly reason: Schema.optional + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("tool-approval-response"), + approvalId: Schema.String, + approved: Schema.Boolean, + reason: Schema.optional(Schema.String) +}).annotate({ identifier: "ToolApprovalResponsePart" }) + +/** + * Constructs a new tool approval response part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const toolApprovalResponsePart = ( + params: PartConstructorParams +): ToolApprovalResponsePart => makePart("tool-approval-response", params as any) + +// ============================================================================= +// Tool Approval Request Part +// ============================================================================= + +/** + * Content part representing a tool approval request from the framework. + * + * **Details** + * + * Tool approval request parts are stored in assistant messages when a tool + * requires user approval before execution. The user responds with a + * `ToolApprovalResponsePart` in a tool message. + * + * **Example** (Creating tool approval requests) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const approvalRequest: Prompt.ToolApprovalRequestPart = Prompt.makePart( + * "tool-approval-request", + * { + * approvalId: "approval_123", + * toolCallId: "call_456" + * } + * ) + * const result = [approvalRequest.approvalId, approvalRequest.toolCallId] // => ["approval_123", "call_456"] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolApprovalRequestPart extends BasePart<"tool-approval-request", ToolApprovalRequestPartOptions> { + /** + * Unique identifier for this approval flow. + */ + readonly approvalId: string + /** + * The tool call ID requiring approval. + */ + readonly toolCallId: string +} + +/** + * Encoded representation of tool approval request parts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolApprovalRequestPartEncoded + extends BasePartEncoded<"tool-approval-request", ToolApprovalRequestPartOptions> +{ + /** + * Unique identifier for this approval flow. + */ + readonly approvalId: string + /** + * The tool call ID requiring approval. + */ + readonly toolCallId: string +} + +/** + * Represents provider-specific options that can be associated with a + * `ToolApprovalRequestPart` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToolApprovalRequestPartOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of tool approval request parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolApprovalRequestPart: Schema.Struct<{ + readonly type: Schema.Literal<"tool-approval-request"> + readonly approvalId: Schema.String + readonly toolCallId: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BasePart.fields, + type: Schema.Literal("tool-approval-request"), + approvalId: Schema.String, + toolCallId: Schema.String +}).annotate({ identifier: "ToolApprovalRequestPart" }) + +/** + * Constructs a new tool approval request part. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const toolApprovalRequestPart = ( + params: PartConstructorParams +): ToolApprovalRequestPart => makePart("tool-approval-request", params as any) + +/** + * Schema for validation and encoding of content parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Part: Schema.Union< + readonly [ + typeof TextPart, + typeof ReasoningPart, + typeof FilePart, + typeof ToolCallPart, + typeof ToolResultPart, + typeof ToolApprovalResponsePart, + typeof ToolApprovalRequestPart + ] +> = Schema.Union([ + TextPart, + ReasoningPart, + FilePart, + ToolCallPart, + ToolResultPart, + ToolApprovalResponsePart, + ToolApprovalRequestPart +]) + +// ============================================================================= +// Base Message +// ============================================================================= + +const MessageTypeId = "~effect/ai/Prompt/Message" as const + +/** + * Type guard to check if a value is a Message. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isMessage = (u: unknown): u is Message => Predicate.hasProperty(u, MessageTypeId) + +/** + * Base interface for all message types. + * + * **Details** + * + * It provides the common structure shared by all messages, including the role + * and provider options. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface BaseMessage { + readonly [MessageTypeId]: typeof MessageTypeId + /** + * The role of the message participant. + */ + readonly role: Role + /** + * Provider-specific options for this message. + */ + readonly options: Options +} + +/** + * Base interface for encoded message types. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface BaseMessageEncoded { + /** + * The role of the message participant. + */ + readonly role: Role + /** + * Provider-specific options for this message. + */ + readonly options?: Options | undefined +} + +const BaseMessage = Schema.Struct({ + [MessageTypeId]: Schema.Literal(MessageTypeId).pipe( + Schema.withDecodingDefaultKey(Effect.succeed(MessageTypeId), { encodingStrategy: "omit" }) + ), + options: ProviderOptions.pipe(Schema.withDecodingDefault(Effect.succeed({}))) +}) + +/** + * Creates a new message with the specified role. + * + * **Example** (Creating messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const textPart = Prompt.makePart("text", { + * text: "Hello, world!" + * }) + * + * const userMessage = Prompt.makeMessage("user", { + * content: [textPart] + * }) + * const result = [userMessage.role, userMessage.content.length] // => ["user", 1] + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const makeMessage = ( + role: Role, + params: Omit, typeof MessageTypeId | "role" | "options"> & { + readonly options?: Extract["options"] | undefined + } +): Extract => (({ + ...params, + [MessageTypeId]: MessageTypeId, + role, + options: params.options ?? {} +}) as any) + +/** + * A utility type for specifying the parameters required to construct a + * specific message for a prompt. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ +export type MessageConstructorParams = Omit & { + /** + * Optional provider-specific options for this message. + */ + readonly options?: M["options"] | undefined +} + +/** + * Schema that decodes a string into content containing a single `TextPart` and, + * when encoding, emits the `text` value of the first part. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ContentFromString: Schema.decodeTo< + Schema.NonEmptyArray< + Schema.toType< + Schema.Struct<{ + readonly type: Schema.Literal<"text"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + }> + > + >, + Schema.String +> = Schema.String.pipe( + Schema.decodeTo( + Schema.NonEmptyArray(Schema.toType(TextPart)), + SchemaTransformation.transform({ + decode: (text) => Arr.of(makePart("text", { text })) as Arr.NonEmptyReadonlyArray, + encode: (content) => content[0].text + }) + ) +) + +// ============================================================================= +// System Message +// ============================================================================= + +/** + * Message representing system instructions or context. + * + * **Example** (Creating system messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const systemMessage: Prompt.SystemMessage = Prompt.makeMessage("system", { + * content: "You are a helpful assistant specialized in mathematics. " + + * "Always show your work step by step." + * }) + * systemMessage.role // => "system" + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface SystemMessage extends BaseMessage<"system", SystemMessageOptions> { + /** + * The system instruction or context as plain text. + */ + readonly content: string +} + +/** + * Encoded representation of system messages for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface SystemMessageEncoded extends BaseMessageEncoded<"system", SystemMessageOptions> { + /** + * The system instruction or context as plain text. + */ + readonly content: string +} + +/** + * Represents provider-specific options that can be associated with a + * `SystemMessage` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface SystemMessageOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of system messages. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const SystemMessage: Schema.Struct<{ + readonly role: Schema.Literal<"system"> + readonly content: Schema.String + readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BaseMessage.fields, + role: Schema.Literal("system"), + content: Schema.String +}).annotate({ identifier: "SystemMessage" }) + +/** + * Constructs a new system message. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const systemMessage = (params: MessageConstructorParams): SystemMessage => + makeMessage("system", params) + +// ============================================================================= +// User Message +// ============================================================================= + +/** + * Message representing user input or questions. + * + * **Example** (Creating user messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const textUserMessage: Prompt.UserMessage = Prompt.makeMessage("user", { + * content: [ + * Prompt.makePart("text", { + * text: "Can you analyze this image for me?" + * }) + * ] + * }) + * + * const multimodalUserMessage: Prompt.UserMessage = Prompt.makeMessage("user", { + * content: [ + * Prompt.makePart("text", { + * text: "What do you see in this image?" + * }), + * Prompt.makePart("file", { + * mediaType: "image/jpeg", + * fileName: "vacation.jpg", + * data: "data:image/jpeg;base64,..." + * }) + * ] + * }) + * + * const result = [textUserMessage.content.length, multimodalUserMessage.content.length] // => [1, 2] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface UserMessage extends BaseMessage<"user", UserMessageOptions> { + /** + * Array of content parts that make up the user's message. + */ + readonly content: ReadonlyArray +} + +/** + * Union type of content parts allowed in user messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type UserMessagePart = TextPart | FilePart + +/** + * Encoded representation of user messages for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface UserMessageEncoded extends BaseMessageEncoded<"user", UserMessageOptions> { + /** + * Array of content parts that make up the user's message. + */ + readonly content: string | ReadonlyArray +} + +/** + * Union type of encoded content parts for user messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type UserMessagePartEncoded = TextPartEncoded | FilePartEncoded + +/** + * Schema for validation and encoding of user message content parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const UserMessagePart: Schema.Union = Schema.Union([ + TextPart, + FilePart +]) + +/** + * Represents provider-specific options that can be associated with a + * `UserMessage` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface UserMessageOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of user messages. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const UserMessage: Schema.Struct<{ + readonly role: Schema.Literal<"user"> + readonly content: Schema.Union< + readonly [ + Schema.decodeTo< + Schema.NonEmptyArray< + Schema.toType< + Schema.Struct<{ + readonly type: Schema.Literal<"text"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + }> + > + >, + Schema.String, + never, + never + >, + Schema.$Array< + Schema.Union< + readonly [ + Schema.Struct<{ + readonly type: Schema.Literal<"text"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + }>, + Schema.Struct<{ + readonly type: Schema.Literal<"file"> + readonly mediaType: Schema.String + readonly fileName: Schema.optional + readonly data: Schema.Union + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + }> + ] + > + > + ] + > + readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BaseMessage.fields, + role: Schema.Literal("user"), + content: Schema.Union([ + ContentFromString.check( + Schema.makeFilter((content) => content.length === 1 && Object.keys(content[0].options).length === 0) + ), + Schema.Array(Schema.Union([TextPart, FilePart])) + ]) +}).annotate({ identifier: "UserMessage" }) + +/** + * Constructs a new user message. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const userMessage = (params: MessageConstructorParams): UserMessage => makeMessage("user", params) + +// ============================================================================= +// Assistant Message +// ============================================================================= + +/** + * Message representing large language model assistant responses. + * + * **Example** (Creating assistant messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const assistantMessage: Prompt.AssistantMessage = Prompt.makeMessage( + * "assistant", + * { + * content: [ + * Prompt.makePart("text", { + * text: + * "I can check the current weather for San Francisco." + * }), + * Prompt.makePart("tool-call", { + * id: "call_123", + * name: "get_weather", + * params: { city: "San Francisco" }, + * providerExecuted: false + * }), + * Prompt.makePart("tool-result", { + * id: "call_123", + * name: "get_weather", + * isFailure: false, + * result: { + * temperature: 72, + * condition: "sunny" + * }, + * providerExecuted: true + * }), + * Prompt.makePart("text", { + * text: "The weather in San Francisco is currently 72°F and sunny." + * }) + * ] + * } + * ) + * assistantMessage.content.map((part) => part.type) // => ["text", "tool-call", "tool-result", "text"] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface AssistantMessage extends BaseMessage<"assistant", AssistantMessageOptions> { + /** + * Array of content parts that make up the assistant's response. + */ + readonly content: ReadonlyArray +} + +/** + * Union type of content parts allowed in assistant messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type AssistantMessagePart = + | TextPart + | FilePart + | ReasoningPart + | ToolCallPart + | ToolResultPart + | ToolApprovalRequestPart + +/** + * Encoded representation of assistant messages for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface AssistantMessageEncoded extends BaseMessageEncoded<"assistant", AssistantMessageOptions> { + readonly content: string | ReadonlyArray +} + +/** + * Union type of encoded content parts for assistant messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type AssistantMessagePartEncoded = + | TextPartEncoded + | FilePartEncoded + | ReasoningPartEncoded + | ToolCallPartEncoded + | ToolResultPartEncoded + | ToolApprovalRequestPartEncoded + +/** + * Schema for validation and encoding of assistant message content parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const AssistantMessagePart: Schema.Union< + readonly [ + typeof TextPart, + typeof FilePart, + typeof ReasoningPart, + typeof ToolCallPart, + typeof ToolResultPart, + typeof ToolApprovalRequestPart + ] +> = Schema.Union([ + TextPart, + FilePart, + ReasoningPart, + ToolCallPart, + ToolResultPart, + ToolApprovalRequestPart +]) + +/** + * Represents provider-specific options that can be associated with a + * `AssistantMessage` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface AssistantMessageOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of assistant messages. + * + * **Details** + * + * Assistant content can be a string decoded through `ContentFromString` or an + * array of text, file, reasoning, tool-call, tool-result, and + * tool-approval-request parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const AssistantMessage: Schema.Struct<{ + readonly role: Schema.Literal<"assistant"> + readonly content: Schema.Union< + readonly [ + Schema.decodeTo< + Schema.NonEmptyArray< + Schema.toType< + Schema.Struct<{ + readonly type: Schema.Literal<"text"> + readonly text: Schema.String + readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > + }> + > + >, + Schema.String, + never, + never + >, + Schema.$Array< + Schema.Union< + readonly [ + typeof TextPart, + typeof FilePart, + typeof ReasoningPart, + typeof ToolCallPart, + typeof ToolResultPart, + typeof ToolApprovalRequestPart + ] + > + > + ] + > + readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BaseMessage.fields, + role: Schema.Literal("assistant"), + content: Schema.Union([ + ContentFromString.check( + Schema.makeFilter((content) => content.length === 1 && Object.keys(content[0].options).length === 0) + ), + Schema.Array(Schema.Union([ + TextPart, + FilePart, + ReasoningPart, + ToolCallPart, + ToolResultPart, + ToolApprovalRequestPart + ])) + ]) +}).annotate({ identifier: "AssistantMessage" }) + +/** + * Constructs a new assistant message. + * + * **When to use** + * + * Use to add assistant-role prompt history or model responses. + * + * **Details** + * + * This is the role-specific wrapper around `makeMessage("assistant", params)`. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const assistantMessage = (params: MessageConstructorParams): AssistantMessage => + makeMessage("assistant", params) + +// ============================================================================= +// Tool Message +// ============================================================================= + +/** + * Message carrying tool-side content, including tool execution results and + * responses to tool approval requests. + * + * **Example** (Creating tool messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const toolMessage: Prompt.ToolMessage = Prompt.makeMessage("tool", { + * content: [ + * Prompt.makePart("tool-result", { + * id: "call_123", + * name: "search_web", + * isFailure: false, + * result: { + * query: "TypeScript best practices", + * results: [ + * { title: "TypeScript Handbook", url: "https://..." }, + * { title: "Effective TypeScript", url: "https://..." } + * ] + * }, + * providerExecuted: false + * }) + * ] + * }) + * const result = [toolMessage.role, toolMessage.content[0].type] // => ["tool", "tool-result"] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolMessage extends BaseMessage<"tool", ToolMessageOptions> { + /** + * Array of tool result parts. + */ + readonly content: ReadonlyArray +} + +/** + * Union type of content parts allowed in tool messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type ToolMessagePart = ToolResultPart | ToolApprovalResponsePart + +/** + * Encoded representation of tool messages for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ToolMessageEncoded extends BaseMessageEncoded<"tool", ToolMessageOptions> { + /** + * Array of tool result parts. + */ + readonly content: ReadonlyArray +} + +/** + * Union type of encoded content parts for tool messages. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type ToolMessagePartEncoded = ToolResultPartEncoded | ToolApprovalResponsePartEncoded + +/** + * Schema for validation and encoding of tool message content parts. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolMessagePart: Schema.Union< + readonly [typeof ToolResultPart, typeof ToolApprovalResponsePart] +> = Schema.Union([ + ToolResultPart, + ToolApprovalResponsePart +]) + +/** + * Represents provider-specific options that can be associated with a + * `ToolMessage` through module augmentation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToolMessageOptions extends ProviderOptions {} + +/** + * Schema for validation and encoding of tool messages. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const ToolMessage: Schema.Struct<{ + readonly role: Schema.Literal<"tool"> + readonly content: Schema.$Array< + Schema.Union + > + readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey> + readonly options: Schema.withDecodingDefault< + Schema.$Record< + Schema.String, + Schema.NullOr> + > + > +}> = Schema.Struct({ + ...BaseMessage.fields, + role: Schema.Literal("tool"), + content: Schema.Array(Schema.Union([ToolResultPart, ToolApprovalResponsePart])) +}).annotate({ identifier: "ToolMessage" }) + +/** + * Constructs a new tool message. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const toolMessage = (params: MessageConstructorParams): ToolMessage => makeMessage("tool", params) + +// ============================================================================= +// Message +// ============================================================================= + +/** + * A type representing all possible message types in a conversation. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Message = + | SystemMessage + | UserMessage + | AssistantMessage + | ToolMessage + +/** + * A type representing all possible encoded message types for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type MessageEncoded = + | SystemMessageEncoded + | UserMessageEncoded + | AssistantMessageEncoded + | ToolMessageEncoded + +/** + * Schema for validation and encoding of messages. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Message: Schema.Codec = Schema.Union([ + SystemMessage, + UserMessage, + AssistantMessage, + ToolMessage +]) + +// ============================================================================= +// Prompt +// ============================================================================= + +const TypeId = "~effect/ai/Prompt" as const + +/** + * Type guard to check if a value is a Prompt. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isPrompt = (u: unknown): u is Prompt => Predicate.hasProperty(u, TypeId) + +/** + * A Prompt contains a sequence of messages that form the context of a + * conversation with a large language model. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Prompt extends Pipeable { + readonly [TypeId]: typeof TypeId + /** + * Array of messages that make up the conversation. + */ + readonly content: ReadonlyArray +} + +/** + * Encoded representation of prompts for serialization. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface PromptEncoded { + /** + * Array of messages that make up the conversation. + */ + readonly content: ReadonlyArray +} + +const $Prompt = Schema.declare((u) => isPrompt(u), { identifier: "Prompt" }) + +// TODO: is the type annotation necessary? +// TODO: shoudn't the name be `PromptFrom...`? +// TODO: is the explicit encoding necessary? maybe use the default JSON serializer? +/** + * Schema for AI prompt instances. + * + * @stability unstable + * @category schemas + * @since 4.0.0 + */ +export const Prompt: Schema.Codec = Schema.Struct({ + content: Schema.Array(Schema.toEncoded(Message)) +}).pipe( + Schema.decodeTo( + $Prompt, + SchemaTransformation.transformEffect({ + decode: (input, options) => + Effect.mapBothEager( + SchemaParser.decodeEffect(Schema.Array(Message))(input.content), + { + onSuccess: makePrompt, + onFailure: () => + new SchemaIssue.InvalidValue( + { message: "Invalid Prompt messages" }, + input.content, + options + ) + } + ), + encode: (prompt, options) => + Effect.mapBothEager( + SchemaParser.encodeEffect(Schema.Array(Message))(prompt.content), + { + onSuccess: (messages) => ({ content: messages }), + onFailure: () => + new SchemaIssue.InvalidValue( + { message: "Invalid Prompt messages" }, + prompt.content, + options + ) + } + ) + }) + ) +) + +/** + * Raw input accepted by `make`: a string, an iterable of encoded messages, or + * an existing `Prompt`. + * + * **Example** (Accepting raw prompt input) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * // String input - creates a user message + * const stringInput: Prompt.RawInput = "Hello, world!" + * + * // Message array input + * const messagesInput: Prompt.RawInput = [ + * { role: "system", content: "You are helpful." }, + * { role: "user", content: [{ type: "text", text: "Hi!" }] } + * ] + * + * const promptInput: Prompt.RawInput = Prompt.empty + * + * const result = [typeof stringInput, Array.isArray(messagesInput), promptInput.content.length] // => ["string", true, 0] + * ``` + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type RawInput = + | string + | Iterable + | Prompt + +const Proto = { + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + } +} + +const makePrompt = (content: ReadonlyArray): Prompt => + Object.assign(Object.create(Proto), { + content + }) + +const decodeMessagesSync = Schema.decodeSync(Schema.Array(Message)) + +/** + * An empty prompt with no messages. + * + * **Example** (Creating an empty prompt) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const emptyPrompt = Prompt.empty + * emptyPrompt.content // => [] + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const empty: Prompt = makePrompt([]) + +/** + * Creates a `Prompt` from an input. + * + * **Details** + * + * This is the primary constructor for creating prompts, supporting multiple + * input formats for convenience and flexibility. + * + * **Example** (Creating prompts from inputs) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * // From string - creates a user message + * const textPrompt = Prompt.make("Hello, how are you?") + * + * // From messages array + * const structuredPrompt = Prompt.make([ + * { role: "system", content: "You are a helpful assistant." }, + * { role: "user", content: [{ type: "text", text: "Hi!" }] } + * ]) + * + * const copiedPrompt = Prompt.make(Prompt.empty) + * + * const result = [textPrompt.content[0].role, structuredPrompt.content.length, copiedPrompt.content.length] // => ["user", 2, 0] + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = (input: RawInput): Prompt => { + if (typeof input === "string") { + const part = makePart("text", { text: input }) + const message = makeMessage("user", { content: [part] }) + return makePrompt([message]) + } + + if (Predicate.isIterable(input)) { + return makePrompt(decodeMessagesSync(Arr.fromIterable(input), { + errors: "all" + })) + } + + return input +} + +/** + * Creates a Prompt from an array of messages. + * + * **Example** (Creating prompts from messages) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const messages: ReadonlyArray = [ + * Prompt.makeMessage("system", { + * content: "You are a coding assistant." + * }), + * Prompt.makeMessage("user", { + * content: [Prompt.makePart("text", { text: "Help me with TypeScript" })] + * }) + * ] + * + * const prompt = Prompt.fromMessages(messages) + * prompt.content.length // => 2 + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const fromMessages = (messages: ReadonlyArray): Prompt => makePrompt(messages) + +const mergeOptions = (left: ProviderOptions, right: ProviderOptions): ProviderOptions => { + const result: Record = { ...left } + for (const [provider, metadata] of Object.entries(right)) { + const previous = result[provider] + result[provider] = Predicate.isObject(previous) && Predicate.isObject(metadata) + ? Object.assign({}, previous, metadata) + : metadata + } + return result +} + +/** + * Creates a `Prompt` from response parts by folding completed text and + * reasoning streams into assistant parts, preserving provider metadata as + * prompt options, placing tool calls and approval requests in an assistant + * message, and placing non-preliminary tool results in a tool message using + * their encoded results. + * + * **Example** (Creating prompts from response parts) + * + * ```ts import.meta.vitest + * import { Prompt, Response } from "effect/ai" + * + * const responseParts: ReadonlyArray = [ + * Response.makePart("text", { + * text: "Hello there!" + * }), + * Response.makePart("tool-call", { + * id: "call_1", + * name: "get_time", + * params: {}, + * providerExecuted: false + * }), + * Response.makePart("tool-result", { + * id: "call_1", + * name: "get_time", + * isFailure: false, + * result: "10:30 AM", + * encodedResult: "10:30 AM", + * providerExecuted: false, + * preliminary: false + * }) + * ] + * + * const prompt = Prompt.fromResponseParts(responseParts) + * // Creates an assistant message with the response content + * prompt.content.map((message) => message.role) // => ["assistant", "tool"] + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const fromResponseParts = (parts: ReadonlyArray): Prompt => { + if (parts.length === 0) { + return empty + } + + const assistantParts: Array = [] + const toolParts: Array = [] + + const activeTextDeltas = new Map() + const activeReasoningDeltas = new Map() + + for (const part of parts) { + switch (part.type) { + // Text Parts + case "text": { + assistantParts.push(makePart("text", { text: part.text, options: part.metadata })) + break + } + + // Text Parts (streaming) + case "text-start": { + activeTextDeltas.set(part.id, { text: "", options: part.metadata }) + break + } + case "text-delta": { + if (activeTextDeltas.has(part.id)) { + const active = activeTextDeltas.get(part.id)! + active.text += part.delta + active.options = mergeOptions(active.options, part.metadata) + } + break + } + case "text-end": { + if (activeTextDeltas.has(part.id)) { + const active = activeTextDeltas.get(part.id)! + active.options = mergeOptions(active.options, part.metadata) + assistantParts.push(makePart("text", active)) + } + break + } + + // Reasoning Parts + case "reasoning": { + assistantParts.push(makePart("reasoning", { text: part.text, options: part.metadata })) + break + } + + // Reasoning Parts (streaming) + case "reasoning-start": { + activeReasoningDeltas.set(part.id, { text: "", options: part.metadata }) + break + } + case "reasoning-delta": { + if (activeReasoningDeltas.has(part.id)) { + const active = activeReasoningDeltas.get(part.id)! + active.text += part.delta + active.options = mergeOptions(active.options, part.metadata) + } + break + } + case "reasoning-end": { + if (activeReasoningDeltas.has(part.id)) { + const active = activeReasoningDeltas.get(part.id)! + active.options = mergeOptions(active.options, part.metadata) + assistantParts.push(makePart("reasoning", active)) + } + break + } + + // File Parts + case "file": { + assistantParts.push(makePart("file", { + data: Base64.encode(part.data), + mediaType: part.mediaType, + options: part.metadata + })) + break + } + + // Tool Call Parts + case "tool-call": { + assistantParts.push(makePart("tool-call", { + id: part.id, + name: part.name, + params: part.params, + providerExecuted: part.providerExecuted ?? false, + options: part.metadata + })) + break + } + + // Tool Result Parts (skip preliminary results) + case "tool-result": { + if (part.preliminary !== true) { + const target = part.providerExecuted === true ? assistantParts : toolParts + target.push(makePart("tool-result", { + id: part.id, + name: part.name, + isFailure: part.isFailure, + result: part.encodedResult, + providerExecuted: part.providerExecuted ?? false, + options: part.metadata + })) + } + break + } + + // Tool Approval Request Parts + case "tool-approval-request": { + assistantParts.push(makePart("tool-approval-request", { + approvalId: part.approvalId, + toolCallId: part.toolCallId + })) + break + } + } + } + + if (assistantParts.length === 0 && toolParts.length === 0) { + return empty + } + + const messages: Array = [] + + if (assistantParts.length > 0) { + messages.push(makeMessage("assistant", { content: assistantParts })) + } + + if (toolParts.length > 0) { + messages.push(makeMessage("tool", { content: toolParts })) + } + + return makePrompt(messages) +} + +// ============================================================================= +// Merging Prompts +// ============================================================================= + +/** + * Concatenates a prompt with additional raw input by concatenating messages. + * + * **Details** + * + * The returned prompt contains all messages from the original prompt followed + * by the provided raw input, preserving message order. + * + * **Example** (Concatenating prompts) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const systemPrompt = Prompt.make([{ + * role: "system", + * content: "You are a helpful assistant." + * }]) + * + * const merged = Prompt.concat(systemPrompt, "Hello, world!") + * merged.content.map((message) => message.role) // => ["system", "user"] + * ``` + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const concat: { + (input: RawInput): (self: Prompt) => Prompt + (self: Prompt, input: RawInput): Prompt +} = dual(2, (self: Prompt, input: RawInput): Prompt => { + const other = make(input) + if (self.content.length === 0) { + return other + } + if (other.content.length === 0) { + return self + } + return fromMessages([...self.content, ...other.content]) +}) + +// ============================================================================= +// Manipulating Prompts +// ============================================================================= + +/** + * Creates a new prompt from the specified prompt with the system message set + * to the specified text content. + * + * **Gotchas** + * + * This method removes and replaces any previous system message from the + * prompt. + * + * **Example** (Replacing system instructions) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const systemPrompt = Prompt.make([{ + * role: "system", + * content: "You are a helpful assistant." + * }]) + * + * const userPrompt = Prompt.make("Hello, world!") + * + * const prompt = Prompt.concat(systemPrompt, userPrompt) + * + * const replaced = Prompt.setSystem( + * prompt, + * "You are an expert in programming" + * ) + * replaced.content[0].content // => "You are an expert in programming" + * ``` + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const setSystem: { + (content: string): (self: Prompt) => Prompt + (self: Prompt, content: string): Prompt +} = dual(2, (self: Prompt, content: string): Prompt => { + const messages: Array = [makeMessage("system", { content })] + for (const message of self.content) { + if (message.role !== "system") { + messages.push(message) + } + } + return makePrompt(messages) +}) + +/** + * Creates a new prompt with a leading system message. If the prompt already has + * a system message, the new message uses the provided content prepended to the + * first existing system message's content; the original messages remain after + * it. + * + * **Example** (Prepending system instructions) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const systemPrompt = Prompt.make([{ + * role: "system", + * content: "You are an expert in programming." + * }]) + * + * const userPrompt = Prompt.make("Hello, world!") + * + * const prompt = Prompt.concat(systemPrompt, userPrompt) + * + * const replaced = Prompt.prependSystem( + * prompt, + * "You are a helpful assistant. " + * ) + * // result content: "You are a helpful assistant. You are an expert in programming." + * replaced.content[0].content // => "You are a helpful assistant. You are an expert in programming." + * ``` + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const prependSystem: { + (content: string): (self: Prompt) => Prompt + (self: Prompt, content: string): Prompt +} = dual(2, (self: Prompt, content: string): Prompt => { + let system: SystemMessage | undefined = undefined + for (const message of self.content) { + if (message.role === "system") { + system = makeMessage("system", { + content: content + message.content + }) + break + } + } + if (Predicate.isUndefined(system)) { + system = makeMessage("system", { content }) + } + return makePrompt([system, ...self.content]) +}) + +/** + * Creates a new prompt with a leading system message. If the prompt already has + * a system message, the new message uses the provided content appended to the + * first existing system message's content; the original messages remain after + * it. + * + * **Example** (Appending system instructions) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/ai" + * + * const systemPrompt = Prompt.make([{ + * role: "system", + * content: "You are an expert in programming." + * }]) + * + * const userPrompt = Prompt.make("Hello, world!") + * + * const prompt = Prompt.concat(systemPrompt, userPrompt) + * + * const replaced = Prompt.appendSystem( + * prompt, + * " You are a helpful assistant." + * ) + * // result content: "You are an expert in programming. You are a helpful assistant." + * replaced.content[0].content // => "You are an expert in programming. You are a helpful assistant." + * ``` + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const appendSystem: { + (content: string): (self: Prompt) => Prompt + (self: Prompt, content: string): Prompt +} = dual(2, (self: Prompt, content: string): Prompt => { + let system: SystemMessage | undefined = undefined + for (const message of self.content) { + if (message.role === "system") { + system = makeMessage("system", { + content: message.content + content + }) + break + } + } + if (Predicate.isUndefined(system)) { + system = makeMessage("system", { content }) + } + return makePrompt([system, ...self.content]) +}) diff --git a/repos/effect/packages/effect/src/unstable/ai/Response.ts b/repos/effect/packages/effect/src/ai/Response.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/ai/Response.ts rename to repos/effect/packages/effect/src/ai/Response.ts index 48db928099..3af65de559 100644 --- a/repos/effect/packages/effect/src/unstable/ai/Response.ts +++ b/repos/effect/packages/effect/src/ai/Response.ts @@ -8,14 +8,15 @@ * arrives. This module also carries provider metadata and schemas used by tools * that need to validate response parts. * + * @stability unstable * @since 4.0.0 */ -import type * as DateTime from "../../DateTime.ts" -import * as Effect from "../../Effect.ts" -import { identity } from "../../Function.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import type * as DateTime from "../DateTime.ts" +import * as Effect from "../Effect.ts" +import { identity } from "../Function.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" import * as AiError from "./AiError.ts" import * as Tool from "./Tool.ts" import type * as Toolkit from "./Toolkit.ts" @@ -29,6 +30,7 @@ const PartTypeId = "~effect/ai/Response/Part" as const /** * Type guard to check if a value is a Response Part. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -37,6 +39,7 @@ export const isPart = (u: unknown): u is AnyPart => Predicate.hasProperty(u, Par /** * Union type representing all possible response content parts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -65,6 +68,7 @@ export type AnyPart = /** * Encoded representation of all possible response content parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -93,6 +97,7 @@ export type AnyPartEncoded = /** * Union type for all response parts with tool-specific typing. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -121,6 +126,7 @@ export type AllParts> = /** * Encoded representation of all response parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -158,7 +164,7 @@ export type AllPartsEncoded = * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Response, Tool, Toolkit } from "effect/unstable/ai" + * import { Response, Tool, Toolkit } from "effect/ai" * * const myToolkit = Toolkit.make( * Tool.make("GetWeather", { @@ -171,6 +177,7 @@ export type AllPartsEncoded = * Schema.isSchema(allPartsSchema) // => true * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -222,6 +229,7 @@ export const AllParts = >( * A type for representing non-streaming response parts with tool-specific * typing. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -243,6 +251,7 @@ export type Part< /** * Encoded representation of non-streaming response parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -263,6 +272,7 @@ export type PartEncoded = /** * Creates a Schema for non-streaming response parts based on a toolkit. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -303,6 +313,7 @@ export const Part = >( /** * A type for representing streaming response parts with tool-specific typing. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -332,6 +343,7 @@ export type StreamPart< /** * Encoded representation of streaming response parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -358,6 +370,7 @@ export type StreamPartEncoded = /** * Creates a Schema for streaming response parts based on a toolkit. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -406,6 +419,7 @@ export const StreamPart = >( /** * Utility type that extracts tool call parts from a set of tools. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -417,6 +431,7 @@ export type ToolCallParts< /** * Controls whether tool parameters are decoded, encoded, or opaque. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -437,6 +452,7 @@ type ToolCallPartForName< /** * Utility type that extracts tool result parts from a set of tools. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -455,6 +471,7 @@ type ToolResultPartForName< * Schema for provider-specific metadata attached to response parts, * represented as a record from provider-specific keys to JSON values or `null`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -467,6 +484,7 @@ export const ProviderMetadata: Schema.$Record< * Type of provider-specific metadata attached to response parts, keyed by * provider-specific names with JSON or `null` values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -476,6 +494,7 @@ export type ProviderMetadata = typeof ProviderMetadata.Type * Base interface for all response content parts, including the type identifier * and optional metadata. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -494,6 +513,7 @@ export interface BasePart ["text", "get_weather"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -567,6 +588,7 @@ export const makePart = ( * A utility type for specifying the parameters required to construct a * specific response part. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -589,7 +611,7 @@ export type ConstructorParams = * **Example** (Creating a text part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const textPart: Response.TextPart = Response.makePart("text", { * text: "The answer to your question is 42." @@ -597,6 +619,7 @@ export type ConstructorParams = * textPart.text // => "The answer to your question is 42." * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -610,6 +633,7 @@ export interface TextPart extends BasePart<"text", TextPartMetadata> { /** * Encoded representation of text parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -624,6 +648,7 @@ export interface TextPartEncoded extends BasePartEncoded<"text", TextPartMetadat * Represents provider-specific metadata that can be associated with a * `TextPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -632,6 +657,7 @@ export interface TextPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of text parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -656,6 +682,7 @@ export const TextPart: Schema.Struct<{ * Response part indicating the start of streaming text content with a unique * text chunk identifier. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -669,6 +696,7 @@ export interface TextStartPart extends BasePart<"text-start", TextStartPartMetad /** * Encoded representation of text start parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -683,6 +711,7 @@ export interface TextStartPartEncoded extends BasePartEncoded<"text-start", Text * Represents provider-specific metadata that can be associated with a * `TextStartPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -691,6 +720,7 @@ export interface TextStartPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of text start parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -715,6 +745,7 @@ export const TextStartPart: Schema.Struct<{ * Response part containing incremental text content to be added to the existing * text chunk with the same unique identifier. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -732,6 +763,7 @@ export interface TextDeltaPart extends BasePart<"text-delta", TextDeltaPartMetad /** * Encoded representation of text delta parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -750,6 +782,7 @@ export interface TextDeltaPartEncoded extends BasePartEncoded<"text-delta", Text * Represents provider-specific metadata that can be associated with a * `TextDeltaPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -758,6 +791,7 @@ export interface TextDeltaPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of text delta parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -783,6 +817,7 @@ export const TextDeltaPart: Schema.Struct<{ /** * Response part indicating the completion of a streaming text chunk. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -796,6 +831,7 @@ export interface TextEndPart extends BasePart<"text-end", TextEndPartMetadata> { /** * Encoded representation of text end parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -810,6 +846,7 @@ export interface TextEndPartEncoded extends BasePartEncoded<"text-end", TextEndP * Represents provider-specific metadata that can be associated with a * `TextEndPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -818,6 +855,7 @@ export interface TextEndPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of text end parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -846,7 +884,7 @@ export const TextEndPart: Schema.Struct<{ * **Example** (Creating a reasoning part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const reasoningPart: Response.ReasoningPart = Response.makePart("reasoning", { * text: @@ -855,6 +893,7 @@ export const TextEndPart: Schema.Struct<{ * reasoningPart.type // => "reasoning" * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -868,6 +907,7 @@ export interface ReasoningPart extends BasePart<"reasoning", ReasoningPartMetada /** * Encoded representation of reasoning parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -882,6 +922,7 @@ export interface ReasoningPartEncoded extends BasePartEncoded<"reasoning", Reaso * Represents provider-specific metadata that can be associated with a * `ReasoningPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -890,6 +931,7 @@ export interface ReasoningPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of reasoning parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -914,6 +956,7 @@ export const ReasoningPart: Schema.Struct<{ * Response part indicating the start of streaming reasoning content with a * unique reasoning chunk identifier. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -927,6 +970,7 @@ export interface ReasoningStartPart extends BasePart<"reasoning-start", Reasonin /** * Encoded representation of reasoning start parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -941,6 +985,7 @@ export interface ReasoningStartPartEncoded extends BasePartEncoded<"reasoning-st * Represents provider-specific metadata that can be associated with a * `ReasoningStartPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -949,6 +994,7 @@ export interface ReasoningStartPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of reasoning start parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -973,6 +1019,7 @@ export const ReasoningStartPart: Schema.Struct<{ * Response part containing incremental reasoning content to be added to the * existing chunk of reasoning text with the same unique identifier. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -990,6 +1037,7 @@ export interface ReasoningDeltaPart extends BasePart<"reasoning-delta", Reasonin /** * Encoded representation of reasoning delta parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1008,6 +1056,7 @@ export interface ReasoningDeltaPartEncoded extends BasePartEncoded<"reasoning-de * Represents provider-specific metadata that can be associated with a * `ReasoningDeltaPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1016,6 +1065,7 @@ export interface ReasoningDeltaPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of reasoning delta parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1041,6 +1091,7 @@ export const ReasoningDeltaPart: Schema.Struct<{ /** * Response part indicating the completion of a streaming reasoning chunk. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1054,6 +1105,7 @@ export interface ReasoningEndPart extends BasePart<"reasoning-end", ReasoningEnd /** * Encoded representation of reasoning end parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1068,6 +1120,7 @@ export interface ReasoningEndPartEncoded extends BasePartEncoded<"reasoning-end" * Represents provider-specific metadata that can be associated with a * `ReasoningEndPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1076,6 +1129,7 @@ export interface ReasoningEndPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of reasoning end parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1104,6 +1158,7 @@ export const ReasoningEndPart: Schema.Struct<{ * Marks the beginning of tool parameter streaming with metadata about the tool * call. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1126,6 +1181,7 @@ export interface ToolParamsStartPart extends BasePart<"tool-params-start", ToolP /** * Encoded representation of tool params start parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1149,6 +1205,7 @@ export interface ToolParamsStartPartEncoded extends BasePartEncoded<"tool-params * Represents provider-specific metadata that can be associated with a * `ToolParamsStartPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1157,6 +1214,7 @@ export interface ToolParamsStartPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of tool params start parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1192,6 +1250,7 @@ export const ToolParamsStartPart: Schema.Struct<{ * Represents a chunk of tool parameters being streamed, containing the * incremental JSON content that forms the tool parameters. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1209,6 +1268,7 @@ export interface ToolParamsDeltaPart extends BasePart<"tool-params-delta", ToolP /** * Encoded representation of tool params delta parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1227,6 +1287,7 @@ export interface ToolParamsDeltaPartEncoded extends BasePartEncoded<"tool-params * Represents provider-specific metadata that can be associated with a * `ToolParamsDeltaPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1235,6 +1296,7 @@ export interface ToolParamsDeltaPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of tool params delta parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1268,6 +1330,7 @@ export const ToolParamsDeltaPart: Schema.Struct<{ * Marks the completion of a tool parameter stream, indicating that all * parameter data has been sent and the tool call is ready to be executed. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1281,6 +1344,7 @@ export interface ToolParamsEndPart extends BasePart<"tool-params-end", ToolParam /** * Encoded representation of tool params end parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1295,6 +1359,7 @@ export interface ToolParamsEndPartEncoded extends BasePartEncoded<"tool-params-e * Represents provider-specific metadata that can be associated with a * `ToolParamsEndPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1303,6 +1368,7 @@ export interface ToolParamsEndPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of tool params end parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1330,7 +1396,7 @@ export const ToolParamsEndPart: Schema.Struct<{ * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const weatherParams = Schema.Struct({ * city: Schema.String, @@ -1352,6 +1418,7 @@ export const ToolParamsEndPart: Schema.Struct<{ * const result = [toolCallPart.name, toolCallPart.params.city] // => ["get_weather", "San Francisco"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1378,6 +1445,7 @@ export interface ToolCallPart extends BasePart<"too /** * Encoded representation of tool call parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1405,6 +1473,7 @@ export interface ToolCallPartEncoded extends BasePartEncoded<"tool-call", ToolCa * Represents provider-specific metadata that can be associated with a * `ToolCallPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1413,6 +1482,7 @@ export interface ToolCallPartMetadata extends ProviderMetadata {} /** * Creates a Schema for tool call parts with specific tool name and parameters. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1447,6 +1517,7 @@ export const ToolCallPart: ( /** * The base fields of a tool result part. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1501,6 +1573,7 @@ export interface BaseToolResult extends BasePart<"tool-resu /** * Represents a successful tool call result. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1518,6 +1591,7 @@ export interface ToolResultSuccess extends BaseToo /** * Represents a failed tool call result. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1538,7 +1612,7 @@ export interface ToolResultFailure extends BaseToo * **Example** (Creating a tool result part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * interface WeatherData { * temperature: number @@ -1570,6 +1644,7 @@ export interface ToolResultFailure extends BaseToo * const result = [toolResultPart.name, toolResultPart.result.temperature] // => ["get_weather", 22] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1580,6 +1655,7 @@ export type ToolResultPart = /** * Encoded representation of tool result parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1619,6 +1695,7 @@ export interface ToolResultPartEncoded extends BasePartEncoded<"tool-result", To * Represents provider-specific metadata that can be associated with a * `ToolResultPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1627,6 +1704,7 @@ export interface ToolResultPartMetadata extends ProviderMetadata {} /** * Creates a Schema for tool result parts with specific tool name and result type. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1691,6 +1769,7 @@ export const ToolResultPart = < /** * Constructs a new tool result part. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1724,7 +1803,7 @@ export const toolResultPart = ["approval_123", "call_456"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1753,6 +1833,7 @@ export interface ToolApprovalRequestPart extends BasePart<"tool-approval-request /** * Encoded representation of tool approval request parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1773,6 +1854,7 @@ export interface ToolApprovalRequestPartEncoded * Represents provider-specific metadata that can be associated with a * `ToolApprovalRequestPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1781,6 +1863,7 @@ export interface ToolApprovalRequestPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of tool approval request parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1805,6 +1888,7 @@ export const ToolApprovalRequestPart: Schema.Struct<{ /** * Constructs a new tool approval request part. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1826,7 +1910,7 @@ export const toolApprovalRequestPart = ( * **Example** (Creating a file part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const imagePart: Response.FilePart = Response.makePart("file", { * mediaType: "image/jpeg", @@ -1835,6 +1919,7 @@ export const toolApprovalRequestPart = ( * const result = [imagePart.mediaType, imagePart.data] // => ["image/jpeg", new Uint8Array([1, 2, 3])] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1852,6 +1937,7 @@ export interface FilePart extends BasePart<"file", FilePartMetadata> { /** * Encoded representation of file parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1870,6 +1956,7 @@ export interface FilePartEncoded extends BasePartEncoded<"file", FilePartMetadat * Represents provider-specific metadata that can be associated with a * `FilePart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1883,6 +1970,7 @@ export interface FilePartMetadata extends ProviderMetadata {} * Decoded `data` is a `Uint8Array`; encoded `data` is a base64 string through * `Schema.Uint8ArrayFromBase64`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -1909,6 +1997,7 @@ export const FilePart: Schema.Struct<{ * Response part representing a document source reference used in generating the * response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1938,6 +2027,7 @@ export interface DocumentSourcePart extends BasePart<"source", DocumentSourcePar /** * Encoded representation of document source parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1968,6 +2058,7 @@ export interface DocumentSourcePartEncoded extends BasePartEncoded<"source", Doc * Represents provider-specific metadata that can be associated with a * `DocumentSourcePart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -1990,6 +2081,7 @@ export interface DocumentSourcePartMetadata extends ProviderMetadata {} * @see {@link UrlSourcePart} for URL source references * @see {@link DocumentSourcePartEncoded} for the encoded document source representation * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2022,6 +2114,7 @@ export const DocumentSourcePart: Schema.Struct<{ * Response part representing a URL source reference used in generating the * response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2047,6 +2140,7 @@ export interface UrlSourcePart extends BasePart<"source", UrlSourcePartMetadata> /** * Encoded representation of URL source parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2073,6 +2167,7 @@ export interface UrlSourcePartEncoded extends BasePartEncoded<"source", UrlSourc * Represents provider-specific metadata that can be associated with a * `UrlSourcePart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -2081,6 +2176,7 @@ export interface UrlSourcePartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of url source parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2110,6 +2206,7 @@ export const UrlSourcePart: Schema.Struct<{ /** * Alias of {@link AiError.HttpRequestDetails}. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2118,6 +2215,7 @@ export const HttpRequestDetails = AiError.HttpRequestDetails /** * Alias of {@link AiError.HttpResponseDetails}. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2134,7 +2232,7 @@ export const HttpResponseDetails = AiError.HttpResponseDetails * * ```ts import.meta.vitest * import { DateTime } from "effect" - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const metadataPart: Response.ResponseMetadataPart = Response.makePart( * "response-metadata", @@ -2148,6 +2246,7 @@ export const HttpResponseDetails = AiError.HttpResponseDetails * const result = [metadataPart.id, metadataPart.modelId] // => ["resp_123", "gpt-4"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2173,6 +2272,7 @@ export interface ResponseMetadataPart extends BasePart<"response-metadata", Resp /** * Encoded representation of response metadata parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2201,6 +2301,7 @@ export interface ResponseMetadataPartEncoded * Represents provider-specific metadata that can be associated with a * `ResponseMetadataPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -2209,6 +2310,7 @@ export interface ResponseMetadataPartMetadata extends ProviderMetadata {} /** * Schema for validation and encoding of response metadata parts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2253,6 +2355,7 @@ export const ResponseMetadataPart: Schema.Struct<{ * - `"other"`: The model stopped for a reason not supported by this protocol. * - `"unknown"`: The model did not specify a finish reason. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2285,6 +2388,7 @@ export const FinishReason: Schema.Literals<[ * tool-call pauses, provider errors, and unknown provider-specific finish * reasons. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2299,6 +2403,7 @@ export type FinishReason = typeof FinishReason.Type * specified here, you can generally find that information under the provider * metadata of the finish part of the response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2349,7 +2454,7 @@ export class Usage extends Schema.Class("effect/ai/AiResponse/Usage")({ * **Example** (Creating a finish part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const finishPart: Response.FinishPart = Response.makePart("finish", { * reason: "stop", @@ -2371,6 +2476,7 @@ export class Usage extends Schema.Class("effect/ai/AiResponse/Usage")({ * const result = [finishPart.reason, finishPart.usage.inputTokens.total] // => ["stop", 50] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2392,6 +2498,7 @@ export interface FinishPart extends BasePart<"finish", FinishPartMetadata> { /** * Encoded representation of finish parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2414,6 +2521,7 @@ export interface FinishPartEncoded extends BasePartEncoded<"finish", FinishPartM * Represents provider-specific metadata that can be associated with a * `FinishPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -2427,6 +2535,7 @@ export interface FinishPartMetadata extends ProviderMetadata {} * Validates `type: "finish"`, `reason` through `FinishReason`, `usage` * through `Usage`, and optional provider HTTP response details. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2466,7 +2575,7 @@ export const FinishPart: Schema.Struct<{ * **Example** (Creating an error part) * * ```ts import.meta.vitest - * import { Response } from "effect/unstable/ai" + * import { Response } from "effect/ai" * * const errorPart: Response.ErrorPart = Response.makePart("error", { * error: new Error("boom") @@ -2474,6 +2583,7 @@ export const FinishPart: Schema.Struct<{ * const result = [errorPart.type, errorPart.error instanceof Error] // => ["error", true] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2484,6 +2594,7 @@ export interface ErrorPart extends BasePart<"error", ErrorPartMetadata> { /** * Encoded representation of error parts for serialization. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2495,6 +2606,7 @@ export interface ErrorPartEncoded extends BasePartEncoded<"error", ErrorPartMeta * Represents provider-specific metadata that can be associated with a * `ErrorPart` through module augmentation. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -2513,6 +2625,7 @@ export interface ErrorPartMetadata extends ProviderMetadata {} * The decoded `error` value is not guaranteed to be an `Error`; narrow it before * reading `Error`-specific fields. * + * @stability unstable * @category schemas * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/ai/ResponseIdTracker.ts b/repos/effect/packages/effect/src/ai/ResponseIdTracker.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/ai/ResponseIdTracker.ts rename to repos/effect/packages/effect/src/ai/ResponseIdTracker.ts index b16791724b..b99fc30387 100644 --- a/repos/effect/packages/effect/src/unstable/ai/ResponseIdTracker.ts +++ b/repos/effect/packages/effect/src/ai/ResponseIdTracker.ts @@ -7,11 +7,12 @@ * objects were included in each provider response and prepares a shorter prompt * when a later call extends the same conversation. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Option from "../../Option.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Option from "../Option.ts" import * as Prompt from "./Prompt.ts" /** @@ -23,6 +24,7 @@ import * as Prompt from "./Prompt.ts" * prompt fragment containing only the new messages after the latest assistant * turn. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -42,6 +44,7 @@ export interface PrepareResult { * the prompt prefix is fully recognized, and `clearUnsafe` drops all tracked * state. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -61,6 +64,7 @@ export interface Service { * later calls can send only new prompt messages together with the provider's * prior response ID. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -76,6 +80,7 @@ export class ResponseIdTracker extends Context.Service ["gen_ai.request.model_name", "gen_ai.request.max_tokens"] * ``` * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -270,7 +282,7 @@ export type AttributesWithPrefix, Prefix * **Example** (Formatting attribute names) * * ```ts import.meta.vitest - * import type { Telemetry } from "effect/unstable/ai" + * import type { Telemetry } from "effect/ai" * * type Formatted1 = Telemetry.FormatAttributeName<"modelName"> // "model_name" * type Formatted2 = Telemetry.FormatAttributeName<"maxTokens"> // "max_tokens" @@ -284,6 +296,7 @@ export type AttributesWithPrefix, Prefix * formatted // => ["model_name", "max_tokens", "temperature"] * ``` * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -306,7 +319,7 @@ export type FormatAttributeName = T extends * * ```ts import.meta.vitest * import { Context, Option, String, Tracer } from "effect" - * import { Telemetry } from "effect/unstable/ai" + * import { Telemetry } from "effect/ai" * * const addCustomAttributes = Telemetry.addSpanAttributes( * "custom.ai", @@ -331,6 +344,7 @@ export type FormatAttributeName = T extends * Array.from(span.attributes.keys()) // => ["custom.ai.model_name", "custom.ai.max_tokens"] * ``` * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -379,7 +393,7 @@ const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToS * **Example** (Configuring GenAI telemetry attributes) * * ```ts import.meta.vitest - * import type { Telemetry } from "effect/unstable/ai" + * import type { Telemetry } from "effect/ai" * * const telemetryOptions: Telemetry.GenAITelemetryAttributeOptions = { * system: "openai", @@ -405,6 +419,7 @@ const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToS * const result = [telemetryOptions.system, telemetryOptions.usage?.inputTokens] // => ["openai", 50] * ``` * + * @stability unstable * @category options * @since 4.0.0 */ @@ -452,7 +467,7 @@ export type GenAITelemetryAttributeOptions = BaseAttributes & { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Telemetry } from "effect/unstable/ai" + * import { Telemetry } from "effect/ai" * * const directUsage = Effect.gen(function*() { * const span = yield* Effect.currentSpan @@ -468,6 +483,7 @@ export type GenAITelemetryAttributeOptions = BaseAttributes & { * await Effect.runPromise(Effect.withSpan(directUsage, "example")) // => 5 * ``` * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -494,7 +510,7 @@ export const addGenAIAnnotations: { * **Example** (Transforming AI spans) * * ```ts import.meta.vitest - * import type { Telemetry } from "effect/unstable/ai" + * import type { Telemetry } from "effect/ai" * * const customTransformer: Telemetry.SpanTransformer = ({ response, span }) => { * // Add custom attributes based on the response @@ -509,6 +525,7 @@ export const addGenAIAnnotations: { * typeof customTransformer // => "function" * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -534,6 +551,7 @@ export interface SpanTransformer { * * @see {@link SpanTransformer} for the transformer contract provided by this service * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/ai/Tokenizer.ts b/repos/effect/packages/effect/src/ai/Tokenizer.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/ai/Tokenizer.ts rename to repos/effect/packages/effect/src/ai/Tokenizer.ts index 65f7cece60..8708e51380 100644 --- a/repos/effect/packages/effect/src/unstable/ai/Tokenizer.ts +++ b/repos/effect/packages/effect/src/ai/Tokenizer.ts @@ -8,11 +8,12 @@ * defines the service tag, the service interface, and a `make` constructor that * builds a full tokenizer service from a token-counting function. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Predicate from "../../Predicate.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Predicate from "../Predicate.ts" import type * as AiError from "./AiError.ts" import * as Prompt from "./Prompt.ts" @@ -33,7 +34,7 @@ import * as Prompt from "./Prompt.ts" * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Tokenizer } from "effect/unstable/ai" + * import { Tokenizer } from "effect/ai" * * const useTokenizer = Effect.gen(function*() { * const tokenizer = yield* Tokenizer.Tokenizer @@ -48,6 +49,7 @@ import * as Prompt from "./Prompt.ts" * await Effect.runPromise(result) // => 1 * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -68,8 +70,8 @@ export class Tokenizer extends Context.Service()( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Prompt } from "effect/unstable/ai" - * import type { Tokenizer } from "effect/unstable/ai" + * import { Prompt } from "effect/ai" + * import type { Tokenizer } from "effect/ai" * * const customTokenizer: Tokenizer.Service = { * tokenize: (input) => @@ -82,6 +84,7 @@ export class Tokenizer extends Context.Service()( * const messageCount = (await Effect.runPromise(customTokenizer.truncate("hello world", 1))).content.length // => 1 * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -123,7 +126,7 @@ export interface Service { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { Tokenizer } from "effect/unstable/ai" + * import { Tokenizer } from "effect/ai" * * // Simple word-based tokenizer * const wordTokenizer = Tokenizer.make({ @@ -144,6 +147,7 @@ export interface Service { * await Effect.runPromise(wordTokenizer.tokenize("hello effect world")) // => [0, 1, 2] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/ai/Tool.ts b/repos/effect/packages/effect/src/ai/Tool.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/ai/Tool.ts rename to repos/effect/packages/effect/src/ai/Tool.ts index 8bd58a001e..b194655355 100644 --- a/repos/effect/packages/effect/src/unstable/ai/Tool.ts +++ b/repos/effect/packages/effect/src/ai/Tool.ts @@ -9,19 +9,20 @@ * includes the shared types and conversion helpers needed by language-model * requests, tool handlers, and provider integrations. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import type * as Effect from "../../Effect.ts" -import { constFalse, constTrue, identity } from "../../Function.ts" -import * as StackTraceLimit from "../../internal/stackTraceLimit.ts" -import type * as JsonSchema from "../../JsonSchema.ts" -import { pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import type * as Struct from "../../Struct.ts" -import type * as Types from "../../Types.ts" +import * as Context from "../Context.ts" +import type * as Effect from "../Effect.ts" +import { constFalse, constTrue, identity } from "../Function.ts" +import * as StackTraceLimit from "../internal/stackTraceLimit.ts" +import type * as JsonSchema from "../JsonSchema.ts" +import { pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import type * as Struct from "../Struct.ts" +import type * as Types from "../Types.ts" import * as AiError from "./AiError.ts" import type { CodecTransformer } from "./LanguageModel.ts" import type * as Prompt from "./Prompt.ts" @@ -38,6 +39,7 @@ import type * as Prompt from "./Prompt.ts" * The tool type guards use this marker, together with more specific markers, * to distinguish user-defined, provider-defined, and dynamic tools. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -46,6 +48,7 @@ export const TypeId: TypeId = "~effect/ai/Tool" /** * Type-level representation of the Effect AI tool runtime type identifier. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -59,6 +62,7 @@ export type TypeId = "~effect/ai/Tool" * `isProviderDefined` uses this marker to distinguish tools that are built into * an AI provider from user-defined and dynamic tools. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -68,6 +72,7 @@ export const ProviderDefinedTypeId: ProviderDefinedTypeId = "~effect/ai/Tool/Pro * Type-level representation of the provider-defined tool runtime type * identifier. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -81,6 +86,7 @@ export type ProviderDefinedTypeId = "~effect/ai/Tool/ProviderDefined" * `isDynamic` uses this marker to distinguish tools whose schema may be * provided at runtime from user-defined and provider-defined tools. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -89,6 +95,7 @@ export const DynamicTypeId: DynamicTypeId = "~effect/ai/Tool/Dynamic" /** * Type-level representation of the dynamic tool runtime type identifier. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -110,6 +117,7 @@ export type DynamicTypeId = "~effect/ai/Tool/Dynamic" * If set to `"return"`, errors that occur during tool call handler execution * will be captured and returned as part of the tool call result. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -119,6 +127,7 @@ export type FailureMode = "error" | "return" * Context provided to the `needsApproval` function when dynamically * determining if a tool requires user approval. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -136,6 +145,7 @@ export interface NeedsApprovalContext { /** * Function type for dynamically determining if a tool requires approval. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -153,6 +163,7 @@ export type NeedsApprovalFunction = ( * - `boolean`: Static approval requirement * - `NeedsApprovalFunction`: Dynamic approval based on parameters/context * + * @stability unstable * @category models * @since 4.0.0 */ @@ -173,7 +184,7 @@ export type NeedsApproval = * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // Create a weather lookup tool * const GetWeather = Tool.make("GetWeather", { @@ -191,6 +202,7 @@ export type NeedsApproval = * const result = [GetWeather.name, GetWeather.failureMode] // => ["GetWeather", "error"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -368,7 +380,7 @@ export interface Tool< * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // Define a web search tool provided by OpenAI * const WebSearch = Tool.providerDefined({ @@ -389,6 +401,7 @@ export interface Tool< * const result = [WebSearch.name, WebSearch.providerName] // => ["OpenAiWebSearch", "web_search"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -461,7 +474,7 @@ export interface ProviderDefined< * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // Dynamic tool with Effect Schema (typed) * const Calculator = Tool.dynamic("Calculator", { @@ -486,6 +499,7 @@ export interface ProviderDefined< * const result = [Calculator.name, McpTool.name] // => ["Calculator", "McpTool"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -530,7 +544,7 @@ export interface Dynamic< * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const UserDefinedTool = Tool.make("Calculator", { * description: "Performs basic arithmetic operations", @@ -561,6 +575,7 @@ export interface Dynamic< * const result = [Tool.isUserDefined(UserDefinedTool), Tool.isUserDefined(ProviderDefinedTool)] // => [true, false] * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -574,7 +589,7 @@ export const isUserDefined = (u: unknown): u is Tool => * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const UserDefinedTool = Tool.make("Calculator", { * description: "Performs basic arithmetic operations", @@ -605,6 +620,7 @@ export const isUserDefined = (u: unknown): u is Tool => * const result = [Tool.isProviderDefined(UserDefinedTool), Tool.isProviderDefined(ProviderDefinedTool)] // => [false, false] * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -619,7 +635,7 @@ export const isProviderDefined = ( * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const DynamicTool = Tool.dynamic("DynamicTool", { * parameters: { type: "object", properties: {} } @@ -633,6 +649,7 @@ export const isProviderDefined = ( * const result = [Tool.isDynamic(DynamicTool), Tool.isDynamic(UserDefinedTool)] // => [true, false] * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -645,6 +662,7 @@ export const isDynamic = (u: unknown): u is Dynamic => Predicate.ha /** * A type which represents any `Tool`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -660,6 +678,7 @@ export interface Any extends /** * A type which represents any provider-defined `Tool`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -676,6 +695,7 @@ export interface AnyProviderDefined extends /** * A type which represents any dynamic `Tool`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -699,6 +719,7 @@ export interface AnyDynamic extends /** * A utility type to extract the `Name` type from an `Tool`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -712,6 +733,7 @@ export type Name = T extends Tool< /** * A utility type to extract the type of the tool call parameters. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -725,6 +747,7 @@ export type Parameters = T extends Tool< /** * A utility type to extract the encoded type of the tool call parameters. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -739,6 +762,7 @@ export type ParametersEncoded = T extends Tool< * A utility type to extract the schema for the parameters which an `Tool` * must be called with. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -752,6 +776,7 @@ export type ParametersSchema = T extends Tool< /** * A utility type to extract the type of the tool call result when it succeeds. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -766,6 +791,7 @@ export type Success = T extends Tool< * A utility type to extract the encoded type of the tool call result when * it succeeds. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -780,6 +806,7 @@ export type SuccessEncoded = T extends Tool< * A utility type to extract the schema for the return type of a tool call when * the tool call succeeds. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -793,6 +820,7 @@ export type SuccessSchema = T extends Tool< /** * A utility type to extract the type of the tool call result when it fails. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -807,6 +835,7 @@ export type Failure = T extends Tool< * A utility type to extract the encoded type of the tool call result when * it fails. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -821,6 +850,7 @@ export type FailureEncoded = T extends Tool< * A tool's failure type, plus {@link ExecutionFailure} in both failure modes * and `AiError` in `"return"` mode. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -838,6 +868,7 @@ export type FailureResult = T extends Tool< /** * The encoded version of `FailureResult`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -855,6 +886,7 @@ export type FailureResultEncoded = T extends Tool< /** * A tool's success or {@link FailureResult} type. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -863,6 +895,7 @@ export type Result = Success | FailureResult /** * The encoded form of {@link Result}. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -871,6 +904,7 @@ export type ResultEncoded = SuccessEncoded | FailureResultEncoded /** * A utility type to extract the requirements of a `Tool` call handler. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -891,6 +925,7 @@ export type HandlerServices = T extends Tool< * A utility type to extract the requirements needed to encode the result of * a `Tool` call. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -905,6 +940,7 @@ export type ResultEncodingServices = T extends Tool< * A utility type to extract the requirements needed to decode the result of * a `Tool` call. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -918,6 +954,7 @@ export type ResultDecodingServices = T extends Tool< /** * Extracts the services required to encode tool parameters. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -931,6 +968,7 @@ export type ParametersEncodingServices = T extends Tool< /** * Represents an `Tool` that has been implemented within the application. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -941,9 +979,25 @@ export interface Handler { readonly handler: (params: any, ctx: any) => Effect.Effect } +/** + * The phase of handling a tool call that produced a failure. + * + * **Details** + * + * - `"parameters"`: the tool call arguments failed to decode + * - `"handler"`: the tool handler itself failed + * - `"result"`: the handler's output failed to validate or encode + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type FailureOrigin = "parameters" | "handler" | "result" + /** * Represents the result of calling the handler for a particular `Tool`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -962,6 +1016,11 @@ export interface HandlerResult { * Whether the result of executing the tool call handler was an error or not. */ readonly isFailure: boolean + /** + * Which phase of handling the tool call failed. Present when `isFailure` is + * `true`. + */ + readonly failureOrigin?: FailureOrigin /** * Whether this is a preliminary (intermediate) result or the final result. * Preliminary results represent progress updates; only the final result @@ -980,6 +1039,7 @@ export interface HandlerResult { * - `Preliminary`: An intermediate result representing progress * - `Final`: The last result, which is the authoritative output * + * @stability unstable * @category models * @since 4.0.0 */ @@ -991,6 +1051,7 @@ export type HandlerOutput = * A utility type which represents the possible errors that can be raised by * a tool call's handler. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -1006,6 +1067,7 @@ export type HandlerError = T extends Tool< * A utility type to create a union of `Handler` types for all tools in a * record. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -1018,6 +1080,7 @@ export type HandlersFor> = { * A utility type to determine if the specified tool requires a user-defined * handler to be implemented. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -1184,7 +1247,7 @@ const dynamicProto = < * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // Simple tool with no parameters * const GetCurrentTime = Tool.make("GetCurrentTime", { @@ -1194,6 +1257,7 @@ const dynamicProto = < * GetCurrentTime.name // => "GetCurrentTime" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1291,7 +1355,7 @@ export const make = < * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // With Effect Schema (typed parameters) * const Calculator = Tool.dynamic("Calculator", { @@ -1316,6 +1380,7 @@ export const make = < * const result = [Calculator.name, McpTool.name] // => ["Calculator", "McpTool"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1391,7 +1456,7 @@ export const dynamic: { * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * // Web search tool provided by OpenAI * const WebSearch = Tool.providerDefined({ @@ -1412,6 +1477,7 @@ export const dynamic: { * const result = [WebSearch.name, WebSearch.providerName] // => ["OpenAiWebSearch", "web_search"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1520,6 +1586,7 @@ export const providerDefined = < * naming conflicts (i.e. `"web_search"`) to instead use custom names (i.e. * `"OpenAiWebSearch"`). * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1588,7 +1655,7 @@ export class NameMapper> { * **Example** (Reading a tool description) * * ```ts import.meta.vitest - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const myTool = Tool.make("example", { * description: "This is an example tool" @@ -1598,6 +1665,7 @@ export class NameMapper> { * description // => "This is an example tool" * ``` * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -1628,7 +1696,7 @@ export const getDescription = (tool: Tool): string | undefined * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const weatherTool = Tool.make("get_weather", { * parameters: Schema.Struct({ @@ -1644,6 +1712,7 @@ export const getDescription = (tool: Tool): string | undefined * } * ``` * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -1666,6 +1735,7 @@ export const getJsonSchema = (tool: Tool, options?: { * `Schema.toJsonSchemaDocument` and any generated definitions are attached as * `$defs`. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -1701,13 +1771,14 @@ const getJsonSchemaFromSchemaWith = ( * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const myTool = Tool.make("calculate_tip") * .annotate(Tool.Title, "Tip Calculator") * Context.getUnsafe(myTool.annotations, Tool.Title) // => "Tip Calculator" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1720,13 +1791,14 @@ export class Title extends Context.Service()("effect/ai/Tool/Titl * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const myCalculatorUi = Tool.make("calculator_ui", {}) * .annotate(Tool.Meta, { ui: { resourceUri: "ui://example/calculator-ui" } }) * "ui" in Context.getUnsafe(myCalculatorUi.annotations, Tool.Meta) // => true * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1744,13 +1816,14 @@ export class Meta extends Context.Service>()("effe * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const readOnlyTool = Tool.make("get_user_info") * .annotate(Tool.Readonly, true) * Context.get(readOnlyTool.annotations, Tool.Readonly) // => true * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1770,13 +1843,14 @@ export const Readonly = Context.Reference("effect/ai/Tool/Readonly", { * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const safeTool = Tool.make("search_database") * .annotate(Tool.Destructive, false) * Context.get(safeTool.annotations, Tool.Destructive) // => false * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1797,13 +1871,14 @@ export const Destructive = Context.Reference("effect/ai/Tool/Destructiv * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const idempotentTool = Tool.make("get_current_time") * .annotate(Tool.Idempotent, true) * Context.get(idempotentTool.annotations, Tool.Idempotent) // => true * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1824,13 +1899,14 @@ export const Idempotent = Context.Reference("effect/ai/Tool/Idempotent" * * ```ts import.meta.vitest * import { Context } from "effect" - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const restrictedTool = Tool.make("internal_operation") * .annotate(Tool.OpenWorld, false) * Context.get(restrictedTool.annotations, Tool.OpenWorld) // => false * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1854,13 +1930,14 @@ export const OpenWorld = Context.Reference("effect/ai/Tool/OpenWorld", * **Example** (Disabling strict JSON schema mode) * * ```ts import.meta.vitest - * import { Tool } from "effect/unstable/ai" + * import { Tool } from "effect/ai" * * const flexibleTool = Tool.make("search") * .annotate(Tool.Strict, false) * Tool.getStrictMode(flexibleTool) // => false * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1883,6 +1960,7 @@ export const Strict = Context.Reference("effect/ai/Tool/Str * * @see {@link Strict} for the annotation read by this helper * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -1978,6 +2056,7 @@ function filter(obj: any) { * `__proto__` property or a dangerous `constructor.prototype` shape throw a * `SyntaxError`. * + * @stability unstable * @category unsafe * @since 4.0.0 */ @@ -1995,6 +2074,7 @@ export const unsafeSecureJsonParse = (text: string): unknown => { /** * Schema for denied or interrupted tool calls. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2011,6 +2091,7 @@ export const ExecutionFailure = Schema.Struct({ * `AiError` comes first to restore error instances. The user schema precedes * {@link ExecutionFailure} to preserve user fields. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2027,6 +2108,7 @@ export const failureResultSchema = ( * It is a record schema with string keys and `never` values, so the generated * parameter schema accepts an empty object shape with no properties. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -2046,12 +2128,15 @@ export interface EmptyParams extends Schema.$Record * * @see {@link make} for the tool constructor that defaults omitted parameters to this schema * + * @stability unstable * @category schemas * @since 4.0.0 */ export const EmptyParams: EmptyParams = Schema.Record(Schema.String, Schema.Never) -/** @internal */ +/** + * @internal + */ export function isEmptyParamsRecord(indexSignature: SchemaAST.IndexSignature): boolean { return indexSignature.parameter === SchemaAST.string && SchemaAST.isNever(indexSignature.type) } diff --git a/repos/effect/packages/effect/src/unstable/ai/Toolkit.ts b/repos/effect/packages/effect/src/ai/Toolkit.ts similarity index 82% rename from repos/effect/packages/effect/src/unstable/ai/Toolkit.ts rename to repos/effect/packages/effect/src/ai/Toolkit.ts index 956fe22ede..c017c35add 100644 --- a/repos/effect/packages/effect/src/unstable/ai/Toolkit.ts +++ b/repos/effect/packages/effect/src/ai/Toolkit.ts @@ -7,22 +7,24 @@ * handler, encodes the result, supports preliminary streamed results, and * applies the tool's failure mode. * + * @stability unstable * @since 4.0.0 */ -import type * as Cause from "../../Cause.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Effectable from "../../Effectable.ts" -import * as Fiber from "../../Fiber.ts" -import { identity } from "../../Function.ts" -import * as InternalRecord from "../../internal/record.ts" -import * as Layer from "../../Layer.ts" -import * as Predicate from "../../Predicate.ts" -import * as Queue from "../../Queue.ts" -import * as Result from "../../Result.ts" -import * as Schema from "../../Schema.ts" -import type * as Scope from "../../Scope.ts" -import * as Stream from "../../Stream.ts" +import * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Effectable from "../Effectable.ts" +import * as Fiber from "../Fiber.ts" +import { identity } from "../Function.ts" +import * as InternalRecord from "../internal/record.ts" +import * as Layer from "../Layer.ts" +import * as Predicate from "../Predicate.ts" +import * as Queue from "../Queue.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import type * as SchemaAST from "../SchemaAST.ts" +import type * as Scope from "../Scope.ts" +import * as Stream from "../Stream.ts" import * as AiError from "./AiError.ts" import * as Tool from "./Tool.ts" @@ -36,7 +38,7 @@ const TypeId = "~effect/ai/Toolkit" as const * * ```ts import.meta.vitest * import { Effect, Schema } from "effect" - * import { Tool, Toolkit } from "effect/unstable/ai" + * import { Tool, Toolkit } from "effect/ai" * * const SearchDocs = Tool.make("SearchDocs", { * description: "Search project documentation", @@ -60,6 +62,7 @@ const TypeId = "~effect/ai/Toolkit" as const * Object.keys((await Effect.runPromise(ready)).tools) // => ["SearchDocs", "SummarizeText"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -107,6 +110,7 @@ export interface Toolkit> extends /** * Context provided to tool handlers during execution. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -129,6 +133,7 @@ export interface HandlerContext { /** * Represents any `Toolkit` instance, used for generic constraints. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -141,6 +146,7 @@ export interface Any { * A utility type which can be used to extract the tool definitions from a * toolkit. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -150,6 +156,7 @@ export type Tools = T extends Toolkit ? Tools : never * A utility type which transforms either a record or an array of tools into * a record where keys are tool names and values are the tool instances. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -166,6 +173,7 @@ export type ToolsByName = Tools extends Record ? * Handlers can return either the tool's custom failure type, an `AiErrorReason` * (which will be wrapped in `AiError`), or a full `AiError`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -183,6 +191,7 @@ export type HandlersFrom> = { /** * A toolkit instance with registered handlers ready for tool execution. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -213,14 +222,19 @@ export interface WithHandler> { /** * The unique identifier of the tool call. */ - toolCallId?: string + toolCallId?: string, + /** + * Schema parse options for tool parameters. + */ + options?: SchemaAST.ParseOptions ) => Effect.Effect< Stream.Stream< Tool.HandlerResult, - Tool.HandlerError, + Tool.HandlerError | AiError.AiError, Tool.HandlerServices >, - AiError.AiError + AiError.AiError, + Tool.HandlerServices > } @@ -228,11 +242,63 @@ export interface WithHandler> { * A utility type which can be used to extract the tools from a toolkit with * handlers. * + * @stability unstable * @category utility types * @since 4.0.0 */ export type WithHandlerTools = T extends WithHandler ? Tools : never +/** + * Cause annotation identifying the phase of a typed `Toolkit.handle` failure. + * + * **Details** + * + * Typed failures raised by `Toolkit.handle` carry one of these origins: + * + * - `"parameters"`: the tool call arguments failed to decode + * - `"handler"`: the tool handler itself failed + * - `"result"`: the handler's output failed to validate or encode + * + * Unannotated causes default to `"result"`, allowing consumers to treat them + * as internal failures. + * + * Returned failures expose the same origin in `Tool.HandlerResult.failureOrigin`. + * + * **Example** (Reading a handler failure's origin) + * + * ```ts import.meta.vitest + * import { Cause, Context, Effect, Schema, Stream } from "effect" + * import { Tool, Toolkit } from "effect/ai" + * + * const toolkit = Toolkit.make(Tool.make("Lookup", { + * failure: Schema.String, + * failureMode: "error" + * })) + * + * const program = Effect.gen(function*() { + * const handlers = yield* toolkit + * yield* handlers.handle("Lookup", {}).pipe(Effect.flatMap(Stream.runDrain)) + * }).pipe( + * Effect.catchCause((cause) => + * Effect.succeed(Context.get(Cause.annotations(cause), Toolkit.FailureOrigin)) + * ), + * Effect.provide(toolkit.toLayer({ Lookup: () => Effect.fail("Not found") })) + * ) + * + * await Effect.runPromise(program) // => "handler" + * ``` + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const FailureOrigin = Context.Reference("effect/ai/Toolkit/FailureOrigin", { + defaultValue: () => "result" +}) + +const failureCause = (error: E, origin: Tool.FailureOrigin): Cause.Cause => + Cause.annotate(Cause.fail(error), Context.make(FailureOrigin, origin)) + const Proto = { ...Effectable.Prototype({ label: "Toolkit", @@ -242,7 +308,10 @@ const Proto = { const schemasCache = new WeakMap readonly handler: Tool.Handler["handler"] - readonly decodeParameters: (u: unknown) => Effect.Effect + readonly decodeParameters: ( + u: unknown, + options?: SchemaAST.ParseOptions + ) => Effect.Effect readonly encodeResult: (u: unknown, isFailure: boolean) => Effect.Effect }>() @@ -267,7 +336,12 @@ const Proto = { return schemas } - const handle = Effect.fnUntraced(function*(name: string, params: unknown, toolCallId?: string) { + const handle = Effect.fnUntraced(function*( + name: string, + params: unknown, + toolCallId?: string, + options?: SchemaAST.ParseOptions + ) { const tool = Object.hasOwn(tools, name) ? tools[name] : undefined yield* Effect.annotateCurrentSpan({ @@ -275,7 +349,6 @@ const Proto = { parameters: params }) - // If the tool is not found, return an error if (Predicate.isUndefined(tool)) { return yield* AiError.make({ module: "Toolkit", @@ -287,7 +360,6 @@ const Proto = { }) } - // Fetch cached schemas / handlers for the tool const schemas = getSchemas(tool) const encodeResult = (result: any, isFailure: boolean) => @@ -305,7 +377,7 @@ const Proto = { ) ) - const decodedParamsResult = yield* Effect.result(schemas.decodeParameters(params)) + const decodedParamsResult = yield* Effect.result(schemas.decodeParameters(params, options)) if (Result.isFailure(decodedParamsResult)) { const error = AiError.make({ module: "Toolkit", @@ -316,12 +388,13 @@ const Proto = { }) }) if (tool.failureMode === "error") { - return yield* error + return yield* Effect.failCause(failureCause(error, "parameters")) } return Stream.fromEffect( Effect.map(encodeResult(error, true), (encodedResult) => ({ result: error, isFailure: true, + failureOrigin: "parameters" as const, preliminary: false, encodedResult })) @@ -329,7 +402,6 @@ const Proto = { } const decodedParams = decodedParamsResult.success - // Setup the handler context const queue = yield* Queue.make<{ readonly result: any readonly isFailure: boolean @@ -356,7 +428,6 @@ const Proto = { ) const normalizeError = (error: unknown) => { - // Schema errors indicate handler returned invalid data const normalizedError = Schema.isSchemaError(error) ? AiError.make({ module: "Toolkit", @@ -377,13 +448,12 @@ const Proto = { } return Stream.fromQueue(queue).pipe( - // If the tool handler failed, check the tool's failure mode to - // determine how the result should be returned to the end user Stream.catch((error) => { const normalizedError = normalizeError(error) + const failureOrigin: Tool.FailureOrigin = Schema.isSchemaError(error) ? "result" : "handler" return tool.failureMode === "error" - ? Stream.fail(normalizedError) - : Stream.succeed({ result: normalizedError, isFailure: true, preliminary: false }) + ? Stream.failCause(failureCause(normalizedError, failureOrigin)) + : Stream.succeed({ result: normalizedError, isFailure: true, failureOrigin, preliminary: false }) }), Stream.mapEffect(Effect.fnUntraced(function*(output) { const encodedResult = yield* encodeResult(output.result, output.isFailure) @@ -453,6 +523,7 @@ const resolveInput = >( * Use when you need an empty starting point for building toolkits or a default * toolkit value that can be extended with `merge`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -471,7 +542,7 @@ export const empty: Toolkit<{}> = makeProto({}) * * ```ts import.meta.vitest * import { Effect, Schema } from "effect" - * import { Tool, Toolkit } from "effect/unstable/ai" + * import { Tool, Toolkit } from "effect/ai" * * const GetCurrentTime = Tool.make("GetCurrentTime", { * description: "Get the current timestamp", @@ -496,6 +567,7 @@ export const empty: Toolkit<{}> = makeProto({}) * Object.keys((await Effect.runPromise(ready)).tools) // => ["GetCurrentTime", "get_weather"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -506,6 +578,7 @@ export const make = >( /** * A utility type which flattens a record type for improved IDE display. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -514,6 +587,7 @@ export type SimplifyRecord = { [K in keyof T]: T[K] } & {} /** * A utility type which merges a union of tool records into a single record. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -528,6 +602,7 @@ export type MergeRecords = { * A utility type which merges the tools from multiple toolkits into a single * record. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -548,7 +623,7 @@ export type MergedTools> = SimplifyRecord< * * ```ts import.meta.vitest * import { Effect, Schema } from "effect" - * import { Tool, Toolkit } from "effect/unstable/ai" + * import { Tool, Toolkit } from "effect/ai" * * const mathToolkit = Toolkit.make( * Tool.make("add", { success: Schema.Number }), @@ -571,6 +646,7 @@ export type MergedTools> = SimplifyRecord< * Object.keys((await Effect.runPromise(ready)).tools) // => ["add", "subtract", "get_time", "get_weather"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/ai/index.ts b/repos/effect/packages/effect/src/ai/index.ts new file mode 100644 index 0000000000..30524cc1d4 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/index.ts @@ -0,0 +1,126 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as AiError from "./AiError.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as AnthropicStructuredOutput from "./AnthropicStructuredOutput.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Chat from "./Chat.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Decision from "./Decision.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DecisionModel from "./DecisionModel.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EmbeddingModel from "./EmbeddingModel.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as IdGenerator from "./IdGenerator.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as LanguageModel from "./LanguageModel.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as McpProtocol from "./McpProtocol.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as McpSchema from "./McpSchema.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as McpServer from "./McpServer.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Model from "./Model.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as OpenAiStructuredOutput from "./OpenAiStructuredOutput.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Prompt from "./Prompt.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Response from "./Response.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ResponseIdTracker from "./ResponseIdTracker.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Telemetry from "./Telemetry.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Tokenizer from "./Tokenizer.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Tool from "./Tool.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Toolkit from "./Toolkit.ts" diff --git a/repos/effect/packages/effect/src/unstable/ai/internal/codec-transformer.ts b/repos/effect/packages/effect/src/ai/internal/codec-transformer.ts similarity index 85% rename from repos/effect/packages/effect/src/unstable/ai/internal/codec-transformer.ts rename to repos/effect/packages/effect/src/ai/internal/codec-transformer.ts index eac92ca2da..11a6b6c3cd 100644 --- a/repos/effect/packages/effect/src/unstable/ai/internal/codec-transformer.ts +++ b/repos/effect/packages/effect/src/ai/internal/codec-transformer.ts @@ -1,5 +1,5 @@ -import type * as JsonSchema from "../../../JsonSchema.ts" -import * as Schema from "../../../Schema.ts" +import type * as JsonSchema from "../../JsonSchema.ts" +import * as Schema from "../../Schema.ts" import type { CodecTransformer } from "../LanguageModel.ts" import * as InternalStructuredOutput from "./structured-output.ts" @@ -16,5 +16,7 @@ const makeDefaultCodecTransformer = ( } } -/** @internal */ +/** + * @internal + */ export const defaultCodecTransformer = makeDefaultCodecTransformer(Schema.toJsonSchemaDocument) diff --git a/repos/effect/packages/effect/src/unstable/ai/internal/mcpCore.ts b/repos/effect/packages/effect/src/ai/internal/mcpCore.ts similarity index 81% rename from repos/effect/packages/effect/src/unstable/ai/internal/mcpCore.ts rename to repos/effect/packages/effect/src/ai/internal/mcpCore.ts index 571efbfa91..a7d200d33b 100644 --- a/repos/effect/packages/effect/src/unstable/ai/internal/mcpCore.ts +++ b/repos/effect/packages/effect/src/ai/internal/mcpCore.ts @@ -4,32 +4,67 @@ * @internal */ -import * as Arr from "../../../Array.ts" -import * as Data from "../../../Data.ts" -import * as Effect from "../../../Effect.ts" -import type * as Schema from "../../../Schema.ts" +import * as Arr from "../../Array.ts" +import * as Data from "../../Data.ts" +import * as Effect from "../../Effect.ts" +import type * as Schema from "../../Schema.ts" import type * as McpProtocol from "../McpProtocol.ts" import * as McpSchema from "../McpSchema.ts" -/** @internal */ +/** + * @internal + */ +export interface InputRequiredFields { + readonly inputRequests?: Readonly> | undefined + readonly requestState?: string | undefined +} + +/** + * @internal + */ +export type OperationOutcome = Data.TaggedEnum<{ + Complete: { readonly value: A } + InputRequired: InputRequiredFields +}> + +/** + * @internal + */ +export const OperationOutcome = { + Complete: (value: A): OperationOutcome => ({ _tag: "Complete", value }), + InputRequired: (fields: InputRequiredFields): OperationOutcome => ({ + _tag: "InputRequired", + ...fields + }) +} + +/** + * @internal + */ export type CanonicalRequestMetadata = NonNullable< typeof McpSchema.Initialize.payloadSchema.Type["_meta"] > -/** @internal */ -export interface NegotiatedProtocolProfile< - out Version extends string = McpProtocol.ProtocolVersion -> { +/** + * @internal + */ +export interface NegotiatedProtocolProfile { // Core decisions receive negotiated facts rather than dated wire requests. readonly protocolVersion: Version readonly clientCapabilities: McpSchema.ClientCapabilities - readonly clientInfo: McpSchema.Implementation - readonly requestMetadata?: CanonicalRequestMetadata | undefined + readonly clientInfo: Version extends McpProtocol.StatefulProtocolVersion ? McpSchema.Implementation + : McpSchema.Implementation | undefined + readonly requestMetadata?: + | (Version extends McpProtocol.StatefulProtocolVersion ? CanonicalRequestMetadata + : CanonicalRequestMetadata | Schema.JsonObject) + | undefined } // NOTE: Capabilities remain a normalized core model because dated revisions // advertise different fields and encode capability presence differently. -/** @internal */ +/** + * @internal + */ export interface CanonicalServerCapabilities { readonly experimental?: Readonly> | undefined readonly logging?: boolean | undefined @@ -45,110 +80,136 @@ export interface CanonicalServerCapabilities { readonly extensions?: Schema.JsonObject | undefined } -/** @internal */ +/** + * @internal + */ export interface CanonicalInitializeResult { readonly capabilities: CanonicalServerCapabilities readonly serverInfo: McpSchema.Implementation readonly instructions?: string | undefined } -/** @internal */ +/** + * @internal + */ export interface McpInvocation { readonly clientId: number - readonly protocol: NegotiatedProtocolProfile - readonly requestContext: McpSchema.McpServerClient["Service"] + readonly protocol: NegotiatedProtocolProfile + readonly requestContext: McpSchema.McpRequestContext["Service"] + readonly serverClient?: McpSchema.McpServerClient["Service"] | undefined } // NOTE: McpInvocation is runtime context, not a wire DTO. It combines the // negotiated profile with the request-scoped service used by handlers. -/** @internal */ +/** + * @internal + */ export class ResourceNotFound extends Data.TaggedError("ResourceNotFound")<{ readonly uri: string }> {} // NOTE: Core errors preserve semantic failure categories before adapters map // them to dated MCP error codes and messages. -/** @internal */ +/** + * @internal + */ export class ToolNotFound extends Data.TaggedError("ToolNotFound")<{ readonly name: string }> {} -/** @internal */ +/** + * @internal + */ export class InvalidToolInput extends Data.TaggedError("InvalidToolInput")<{ readonly name: string readonly message: string }> {} -/** @internal */ -export class ToolExecutionError extends Data.TaggedError("ToolExecutionError")<{ +/** + * @internal + */ +export class InvalidToolContinuation extends Data.TaggedError("InvalidToolContinuation")<{ readonly name: string readonly message: string }> {} -/** @internal */ -export class ToolResultProjectionError extends Data.TaggedError("ToolResultProjectionError")<{ +/** + * @internal + */ +export class ToolExecutionError extends Data.TaggedError("ToolExecutionError")<{ readonly name: string readonly message: string }> {} -/** @internal */ -/** @internal */ +/** + * @internal + */ export class UnsupportedByProtocol extends Data.TaggedError("UnsupportedByProtocol")<{ readonly protocolVersion: McpProtocol.ProtocolVersion readonly feature: string }> {} -/** @internal */ +/** + * @internal + */ export type ToolError = | ToolNotFound | InvalidToolInput + | InvalidToolContinuation | ToolExecutionError - | ToolResultProjectionError -/** @internal */ +/** + * @internal + */ export interface ToolRegistration { // The canonical Tool copy normalizes its top-level title from // `tool.title ?? tool.annotations?.title` at the public boundary. readonly descriptor: McpSchema.Tool - readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean + readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean readonly handle: ( call: typeof McpSchema.CallTool.payloadSchema.Type, invocation: McpInvocation ) => Effect.Effect< - McpSchema.CallToolResult, - InvalidToolInput | ToolExecutionError | ToolResultProjectionError, + OperationOutcome, + InvalidToolInput | InvalidToolContinuation | ToolExecutionError, never > } -/** @internal */ +/** + * @internal + */ export interface Tools { readonly register: ( registration: ToolRegistration ) => Effect.Effect readonly list: ( - profile: NegotiatedProtocolProfile + profile: NegotiatedProtocolProfile ) => Effect.Effect> readonly call: ( call: typeof McpSchema.CallTool.payloadSchema.Type, invocation: McpInvocation - ) => Effect.Effect + ) => Effect.Effect, ToolError> } -/** @internal */ +/** + * @internal + */ export interface ResourceRegistration { readonly descriptor: McpSchema.Resource - readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean + readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean readonly read: ( invocation: McpInvocation ) => Effect.Effect } -/** @internal */ +/** + * @internal + */ export interface ResourceTemplateRegistration { readonly descriptor: McpSchema.ResourceTemplate - readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean + readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean readonly match: (uri: string) => ReadonlyArray | undefined readonly read: ( uri: string, @@ -157,15 +218,17 @@ export interface ResourceTemplateRegistration { ) => Effect.Effect } -/** @internal */ +/** + * @internal + */ export interface Resources { readonly register: (registration: ResourceRegistration) => Effect.Effect readonly registerTemplate: (registration: ResourceTemplateRegistration) => Effect.Effect readonly list: ( - profile: NegotiatedProtocolProfile + profile: NegotiatedProtocolProfile ) => Effect.Effect> readonly listTemplates: ( - profile: NegotiatedProtocolProfile + profile: NegotiatedProtocolProfile ) => Effect.Effect> readonly read: ( uri: string, @@ -176,38 +239,46 @@ export interface Resources { > } -/** @internal */ +/** + * @internal + */ export class PromptNotFound extends Data.TaggedError("PromptNotFound")<{ readonly name: string }> {} -/** @internal */ +/** + * @internal + */ export interface PromptRegistration { readonly descriptor: McpSchema.Prompt - readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean + readonly isVisible: (profile: NegotiatedProtocolProfile) => boolean readonly get: ( args: Readonly>, invocation: McpInvocation - ) => Effect.Effect + ) => Effect.Effect, McpSchema.InvalidParams | McpSchema.InternalError> } -/** @internal */ +/** + * @internal + */ export interface Prompts { readonly register: (registration: PromptRegistration) => Effect.Effect readonly list: ( - profile: NegotiatedProtocolProfile + profile: NegotiatedProtocolProfile ) => Effect.Effect> readonly get: ( name: string, args: Readonly>, invocation: McpInvocation ) => Effect.Effect< - McpSchema.GetPromptResult, + OperationOutcome, PromptNotFound | McpSchema.InvalidParams | McpSchema.InternalError > } -/** @internal */ +/** + * @internal + */ export type CompletionReference = | { readonly type: "prompt"; readonly name: string; readonly title?: string | undefined } | { readonly type: "resourceTemplate"; readonly uriTemplate: string } @@ -215,7 +286,9 @@ export type CompletionReference = // NOTE: Completion requests/results normalize dated reference tags, optional // context, and nested CompleteResult values. -/** @internal */ +/** + * @internal + */ export interface CompletionRequest { readonly reference: CompletionReference readonly argument: Readonly<{ readonly name: string; readonly value: string }> @@ -223,7 +296,9 @@ export interface CompletionRequest { readonly metadata?: CanonicalRequestMetadata | undefined } -/** @internal */ +/** + * @internal + */ export interface CompletionResult { readonly values: ReadonlyArray readonly total?: number | undefined @@ -231,7 +306,9 @@ export interface CompletionResult { readonly metadata?: Schema.JsonObject | undefined } -/** @internal */ +/** + * @internal + */ export interface Completions { readonly register: ( key: string, @@ -246,7 +323,9 @@ export interface Completions { ) => Effect.Effect } -/** @internal */ +/** + * @internal + */ export type ClientNotification = Data.TaggedEnum<{ Initialized: {} Progress: { @@ -259,17 +338,23 @@ export type ClientNotification = Data.TaggedEnum<{ RootsChanged: {} }> -/** @internal */ +/** + * @internal + */ export const ClientNotification = Data.taggedEnum() -/** @internal */ +/** + * @internal + */ export interface Cancellation { readonly requestId: string | number readonly reason?: string | undefined readonly metadata?: Schema.JsonObject | undefined } -/** @internal */ +/** + * @internal + */ export type ServerNotification = Data.TaggedEnum<{ Cancelled: { readonly requestId: string | number @@ -296,10 +381,14 @@ export type ServerNotification = Data.TaggedEnum<{ ElicitationComplete: { readonly elicitationId: string } }> -/** @internal */ +/** + * @internal + */ export const ServerNotification = Data.taggedEnum() -/** @internal */ +/** + * @internal + */ export interface McpCore { readonly tools: Tools readonly resources: Resources @@ -312,7 +401,9 @@ export interface McpCore { }> } -/** @internal */ +/** + * @internal + */ export const make: Effect.Effect = Effect.sync(() => { const registrations = new Map() const resourceRegistrations: Array = [] @@ -342,7 +433,7 @@ export const make: Effect.Effect = Effect.sync(() => { return descriptors }), call: (call, invocation) => - Effect.suspend((): Effect.Effect => { + Effect.suspend((): Effect.Effect, ToolError> => { const registration = registrations.get(call.name) if (registration === undefined) { return new ToolNotFound({ name: call.name }) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol.ts new file mode 100644 index 0000000000..142656297b --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol.ts @@ -0,0 +1,597 @@ +import * as Data from "../../Data.ts" +import * as Effect from "../../Effect.ts" +import * as Base64 from "../../encoding/Base64.ts" +import type * as Headers from "../../http/Headers.ts" +import type * as LogLevel from "../../LogLevel.ts" +import * as Match from "../../Match.ts" +import * as Predicate from "../../Predicate.ts" +import type * as PubSub from "../../PubSub.ts" +import * as Result from "../../Result.ts" +import type * as Rpc from "../../rpc/Rpc.ts" +import * as RpcClient from "../../rpc/RpcClient.ts" +import type { RpcClientError } from "../../rpc/RpcClientError.ts" +import type * as RpcGroup from "../../rpc/RpcGroup.ts" +import type * as RpcMessage from "../../rpc/RpcMessage.ts" +import * as Schema from "../../Schema.ts" +import type * as Scope from "../../Scope.ts" +import type * as PublicMcpProtocol from "../McpProtocol.ts" +import * as PublicMcpSchema from "../McpSchema.ts" +import * as McpCore from "./mcpCore.ts" + +const LEGACY_RESOURCE_NOT_FOUND_ERROR_CODE = -32002 +const BASE64_SENTINEL_PREFIX = "=?base64?" +const BASE64_SENTINEL_SUFFIX = "?=" +// A leading U+FEFF belongs to the header value and must survive comparison with the JSON body. +const routingHeaderDecoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }) + +/** + * @internal + */ +export const mcpLogLevels: Readonly< + Record< + PublicMcpSchema.LoggingLevel, + { readonly effect: LogLevel.LogLevel; readonly order: number } + > +> = { + debug: { effect: "Debug", order: 0 }, + info: { effect: "Info", order: 1 }, + notice: { effect: "Info", order: 2 }, + warning: { effect: "Warn", order: 3 }, + error: { effect: "Error", order: 4 }, + critical: { effect: "Fatal", order: 5 }, + alert: { effect: "Fatal", order: 6 }, + emergency: { effect: "Fatal", order: 7 } +} + +/** + * @internal + */ +export const decodeRoutingHeader = (value: string): string | undefined => { + const startsWithSentinel = value.startsWith(BASE64_SENTINEL_PREFIX) + const endsWithSentinel = value.endsWith(BASE64_SENTINEL_SUFFIX) + if (!startsWithSentinel || !endsWithSentinel) { + return /^[\t\x20-\x7e]*$/.test(value) ? value : undefined + } + const decoded = Base64.decode( + value.slice(BASE64_SENTINEL_PREFIX.length, -BASE64_SENTINEL_SUFFIX.length) + ) + if (Result.isFailure(decoded)) return undefined + try { + return routingHeaderDecoder.decode(decoded.success) + } catch { + return undefined + } +} + +/** + * @internal + */ +export const profileFromClient = ( + request: PublicMcpSchema.McpServerClient["Service"] +): McpCore.NegotiatedProtocolProfile => ({ + protocolVersion: request.protocolVersion, + clientCapabilities: request.clientCapabilities, + clientInfo: request.clientInfo, + requestMetadata: request.initializePayload._meta +}) + +/** + * @internal + */ +export const invocationFromClient = ( + request: PublicMcpSchema.McpServerClient["Service"] +): McpCore.McpInvocation => ({ + clientId: request.clientId, + protocol: profileFromClient(request), + requestContext: PublicMcpSchema.McpRequestContext.of({ + clientId: request.clientId, + protocolVersion: request.protocolVersion, + clientCapabilities: request.clientCapabilities, + clientInfo: request.clientInfo, + requestMetadata: request.requestMetadata + }), + serverClient: request +}) + +/** + * @internal + */ +export const invocationFromRequestContext = ( + request: PublicMcpSchema.McpRequestContext["Service"] +): McpCore.McpInvocation => ({ + clientId: request.clientId, + protocol: { + protocolVersion: request.protocolVersion, + clientCapabilities: request.clientCapabilities, + clientInfo: request.clientInfo, + requestMetadata: request.requestMetadata + }, + requestContext: request +}) + +/** + * @internal + */ +export const requireCompleteOperation = ( + protocolVersion: PublicMcpProtocol.ProtocolVersion, + outcome: McpCore.OperationOutcome +): Effect.Effect => + outcome._tag === "Complete" + ? Effect.succeed(outcome.value) + : Effect.fail(new McpCore.UnsupportedByProtocol({ protocolVersion, feature: "Client input" })) + +/** + * Unquotes an exact text mirror when the protocol drops string `structuredContent`. + * + * @internal + */ +export const unwrapStringStructuredContent = ( + result: PublicMcpSchema.CallToolResult +): PublicMcpSchema.CallToolResult["content"] => { + const { content, structuredContent } = result + if (typeof structuredContent !== "string" || content.length !== 1) return content + const [block] = content + return block.type === "text" && block.text === JSON.stringify(structuredContent) + ? [{ ...block, text: structuredContent }] + : content +} + +const isSamplingToolContent = (content: unknown): boolean => + Predicate.isReadonlyObject(content) && (content.type === "tool_use" || content.type === "tool_result") + +/** + * @internal + */ +export const samplingRequestRequiresTools = (request: unknown): boolean => { + if (!Predicate.isReadonlyObject(request)) return false + if (request.tools !== undefined || request.toolChoice !== undefined) return true + if (!Array.isArray(request.messages)) return false + return request.messages.some((message) => { + if (!Predicate.isReadonlyObject(message)) return false + const content = message.content + return Array.isArray(content) ? content.some(isSamplingToolContent) : isSamplingToolContent(content) + }) +} + +// NOTE: Keep the two codec assertions below as the single documented +// existential-schema boundary. Rpc.AnyWithProps intentionally erases each +// request's payload type, while the runtime schema still performs decoding and +// encoding; do not spread this erasure into MCP domain records. + +/** + * @internal + */ +export interface LifecycleRuntime { + readonly initialize: ( + protocolVersion: PublicMcpProtocol.ProtocolVersion, + profile: McpCore.NegotiatedProtocolProfile, + clientId: number + ) => Effect.Effect + readonly setLogLevel: ( + level: PublicMcpSchema.LoggingLevel, + clientId: number, + headers: Headers.Headers + ) => Effect.Effect + readonly subscribe: (uri: string, clientId: number, headers: Headers.Headers) => Effect.Effect + readonly unsubscribe: (uri: string, clientId: number, headers: Headers.Headers) => Effect.Effect + readonly clientNotification: ( + notification: McpCore.ClientNotification, + clientId: number, + headers: Headers.Headers + ) => Effect.Effect +} + +/** + * @internal + */ +export class ProtocolError extends Data.TaggedError("ProtocolError")<{ + readonly code: number + readonly message: string + readonly data?: unknown +}> { + static fromTool( + error: McpCore.ToolError | McpCore.UnsupportedByProtocol + ): ProtocolError { + const message = Match.value(error).pipe( + Match.tag("ToolNotFound", (error) => `Tool '${error.name}' not found`), + Match.tag( + "UnsupportedByProtocol", + (error) => `${error.feature} is not supported by MCP ${error.protocolVersion}` + ), + Match.tags({ + InvalidToolInput: (error) => error.message, + InvalidToolContinuation: (error) => error.message, + ToolExecutionError: (error) => error.message + }), + Match.exhaustive + ) + return new ProtocolError({ + code: error._tag === "ToolExecutionError" + ? PublicMcpSchema.INTERNAL_ERROR_CODE + : PublicMcpSchema.INVALID_PARAMS_ERROR_CODE, + message + }) + } + + static fromFeature(error: unknown): ProtocolError { + if (Predicate.hasProperty(error, "_tag")) { + if (error._tag === "PromptNotFound" && Predicate.hasProperty(error, "name") && Predicate.isString(error.name)) { + return new ProtocolError({ + code: PublicMcpSchema.INVALID_PARAMS_ERROR_CODE, + message: `Prompt '${error.name}' not found` + }) + } + if (error._tag === "ResourceNotFound" && Predicate.hasProperty(error, "uri") && Predicate.isString(error.uri)) { + return new ProtocolError({ + code: LEGACY_RESOURCE_NOT_FOUND_ERROR_CODE, + message: `Resource '${error.uri}' not found` + }) + } + } + const decoded = decodeProtocolErrorFields(error) + return Result.isSuccess(decoded) + ? new ProtocolError(decoded.success) + : new ProtocolError({ + code: PublicMcpSchema.INTERNAL_ERROR_CODE, + message: "MCP feature handler failed" + }) + } +} + +const ProtocolErrorFields = Schema.Struct({ + code: Schema.Finite, + message: Schema.String, + data: Schema.optionalKey(Schema.Unknown) +}) + +const decodeProtocolErrorFields = Schema.decodeUnknownResult(ProtocolErrorFields) + +const isReverseOperationUnsupported = (cause: unknown): cause is PublicMcpSchema.McpReverseOperationUnsupported => + Predicate.isTagged(cause, "McpReverseOperationUnsupported") + +/** + * @internal + */ +export const reverseError = ( + operation: PublicMcpSchema.McpReverseOperationError["operation"] +) => +(cause: unknown) => + isReverseOperationUnsupported(cause) + ? cause + : new PublicMcpSchema.McpReverseOperationError({ operation, cause }) + +/** + * @internal + */ +export const transcode = < + From extends Schema.Constraint, + To extends Schema.Constraint +>( + from: From, + to: To, + input: Schema.Schema.Type +) => + Schema.encodeEffect(from)(input).pipe( + Effect.flatMap(Schema.decodeUnknownEffect(to)) + ) + +/** + * @internal + */ +export const transcodeStrict = < + From extends Schema.Constraint, + To extends Schema.Constraint +>( + from: From, + to: To, + input: Schema.Schema.Type +) => + Schema.encodeEffect(from)(input).pipe( + Effect.flatMap(Schema.decodeUnknownEffect(to, { onExcessProperty: "error" })) + ) + +/** + * @internal + */ +export const makeNotificationProjector = Effect.fn(function*( + options: { + readonly supportsProgressMessage: boolean + }, + notification: McpCore.ServerNotification +) { + return McpCore.ServerNotification.$match(notification, { + Cancelled: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.CancelledNotification._tag, + payload: PublicMcpSchema.CancelledNotification.payloadSchema.make({ + _meta: notification.metadata, + requestId: notification.requestId, + reason: notification.reason + }) + }), + Progress: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.ProgressNotification._tag, + payload: PublicMcpSchema.ProgressNotification.payloadSchema.make({ + _meta: notification.metadata, + progressToken: notification.progressToken, + progress: notification.progress, + total: notification.total, + message: options.supportsProgressMessage ? notification.message : undefined + }) + }), + LoggingMessage: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.LoggingMessageNotification._tag, + payload: PublicMcpSchema.LoggingMessageNotification.payloadSchema.make({ + _meta: notification.metadata, + level: notification.level, + logger: notification.logger, + data: notification.data + }) + }), + ResourceUpdated: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.ResourceUpdatedNotification._tag, + payload: PublicMcpSchema.ResourceUpdatedNotification.payloadSchema.make({ + _meta: notification.metadata, + uri: notification.uri + }) + }), + ResourcesChanged: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.ResourceListChangedNotification._tag, + payload: PublicMcpSchema.ResourceListChangedNotification.payloadSchema.make({ + _meta: notification.metadata + }) + }), + ToolsChanged: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.ToolListChangedNotification._tag, + payload: PublicMcpSchema.ToolListChangedNotification.payloadSchema.make({ + _meta: notification.metadata + }) + }), + PromptsChanged: (notification): PublicMcpProtocol.ProjectedNotification => ({ + tag: PublicMcpSchema.PromptListChangedNotification._tag, + payload: PublicMcpSchema.PromptListChangedNotification.payloadSchema.make({ + _meta: notification.metadata + }) + }), + ElicitationComplete: () => undefined + }) +}) + +/** + * @internal + */ +export interface HandlerInstallationTarget { + readonly context: HandlerInstallationContext + readonly install: < + Rpcs extends Rpc.Any, + Handlers extends RpcGroup.HandlersFrom + >( + protocol: { + readonly protocolVersion: string + }, + rpcs: RpcGroup.RpcGroup, + handlers: Handlers + ) => Effect.Effect> +} + +/** + * @internal + */ +export interface HandlerInstallationContext { + readonly subscribeServerNotifications: Effect.Effect< + PubSub.Subscription, + never, + Scope.Scope + > + readonly sendNotification?: ( + protocolVersion: string, + clientId: number, + notification: PublicMcpProtocol.ProjectedNotification + ) => Effect.Effect + readonly terminateSubscription?: ( + protocolVersion: string, + clientId: number, + requestId: RpcMessage.RequestId, + reason: string + ) => Effect.Effect + readonly markSubscriptionCancelled?: ( + clientId: number, + requestId: RpcMessage.RequestId + ) => Effect.Effect + readonly supportedVersions: ReadonlyArray + readonly serverInfo: { + readonly name: string + readonly version: string + readonly instructions?: string | undefined + readonly description?: string | undefined + readonly websiteUrl?: string | undefined + readonly icons?: ReadonlyArray | undefined + readonly extensions?: PublicMcpSchema.ServerCapabilities["extensions"] | undefined + } + readonly registrationPresence: Effect.Effect<{ + readonly tools: boolean + readonly resources: boolean + readonly prompts: boolean + }> +} + +/** + * @internal + */ +export interface CanonicalServerNotification { + readonly notification: SubscriptionServerNotification + readonly targetClientId?: number | undefined +} + +/** + * @internal + */ +export type SubscriptionServerNotification = Extract< + McpCore.ServerNotification, + { readonly _tag: "ToolsChanged" | "PromptsChanged" | "ResourcesChanged" | "ResourceUpdated" } +> + +/** + * @internal + */ +export const isSubscriptionServerNotification = ( + notification: McpCore.ServerNotification +): notification is SubscriptionServerNotification => { + switch (notification._tag) { + case "ToolsChanged": + case "PromptsChanged": + case "ResourcesChanged": + case "ResourceUpdated": + return true + default: + return false + } +} + +/** + * @internal + */ +export interface ProtocolAdapter< + out Version extends string = string, + out Runtime extends PublicMcpProtocol.RuntimeDescriptor = PublicMcpProtocol.RuntimeDescriptor, + ClientRpcs extends Rpc.Any = Rpc.Any, + ClientNotificationRpcs extends ClientRpcs = ClientRpcs, + ServerRequestRpcs extends Rpc.Any = Rpc.Any, + ServerNotificationRpcs extends Rpc.Any = Rpc.Any, + HandlerRpcs extends Rpc.Any = never, + HandlerRequirements = never +> { + // Each adapter owns its dated RPC vocabulary, transport policy, handler + // projection, and wire behavior. + readonly protocolVersion: Version + readonly runtime: Runtime + readonly clientRpcs: RpcGroup.RpcGroup + readonly clientNotificationRpcs: RpcGroup.RpcGroup + readonly serverRequestRpcs: RpcGroup.RpcGroup + readonly serverNotificationRpcs: RpcGroup.RpcGroup + readonly payloadCodecs: (rpc: Rpc.AnyWithProps) => PublicMcpProtocol.PayloadCodecs + readonly handlerRpcs?: RpcGroup.RpcGroup | undefined + readonly installHandlers: ( + core: McpCore.McpCore, + lifecycle: LifecycleRuntime | undefined, + target: HandlerInstallationTarget + ) => Effect.Effect + readonly makeReverseClient: ( + profile: McpCore.NegotiatedProtocolProfile + ) => Effect.Effect< + PublicMcpSchema.McpReverseClient, + never, + RpcClient.Protocol | Scope.Scope + > + readonly projectNotification: ( + notification: McpCore.ServerNotification + ) => Effect.Effect + readonly normalizeCancellation: ( + payload: unknown + ) => Effect.Effect +} + +type HandlerLifecycle = Runtime extends + PublicMcpProtocol.StatefulRuntimeDescriptor ? LifecycleRuntime : undefined + +/** + * @internal + */ +export const make = < + const Version extends string, + const Runtime extends PublicMcpProtocol.RuntimeDescriptor, + ClientRpcs extends Rpc.Any, + ClientNotificationRpcs extends ClientRpcs, + ServerRequestRpcs extends Rpc.Any, + ServerNotificationRpcs extends Rpc.Any, + HandlerRpcs extends Rpc.Any = never, + Handlers extends RpcGroup.HandlersFrom = RpcGroup.HandlersFrom +>(options: { + readonly protocolVersion: Version + readonly runtime: Runtime + readonly clientRpcs: RpcGroup.RpcGroup + readonly clientNotificationRpcs: RpcGroup.RpcGroup + readonly serverRequestRpcs: RpcGroup.RpcGroup + readonly serverNotificationRpcs: RpcGroup.RpcGroup + readonly handlerRpcs?: RpcGroup.RpcGroup | undefined + readonly makeHandlers?: + | (( + core: McpCore.McpCore, + lifecycle: HandlerLifecycle, + context: HandlerInstallationContext + ) => Handlers) + | undefined + readonly toReverseClient: ( + profile: McpCore.NegotiatedProtocolProfile, + client: RpcClient.RpcClient + ) => PublicMcpSchema.McpReverseClient + readonly projectNotification: ( + notification: McpCore.ServerNotification + ) => Effect.Effect + readonly normalizeCancellation: ( + payload: unknown + ) => Effect.Effect +}): ProtocolAdapter< + Version, + Runtime, + ClientRpcs, + ClientNotificationRpcs, + ServerRequestRpcs, + ServerNotificationRpcs, + HandlerRpcs, + RpcGroup.HandlersServices +> => { + const payloadCodecsCache = new WeakMap() + const payloadCodecs = (rpc: Rpc.AnyWithProps): PublicMcpProtocol.PayloadCodecs => { + let codecs = payloadCodecsCache.get(rpc) + if (codecs === undefined) { + const schema = Schema.toCodecJson(rpc.payloadSchema) + codecs = { + decode: Schema.decodeUnknownEffect(schema) as PublicMcpProtocol.PayloadCodecs["decode"], + encode: Schema.encodeUnknownEffect(schema) as PublicMcpProtocol.PayloadCodecs["encode"] + } + payloadCodecsCache.set(rpc, codecs) + } + return codecs + } + + const installHandlers = ( + core: McpCore.McpCore, + lifecycle: LifecycleRuntime | undefined, + target: HandlerInstallationTarget + ): Effect.Effect> => + options.handlerRpcs === undefined || options.makeHandlers === undefined + ? Effect.void + : lifecycle === undefined && options.runtime._tag === "Stateful" + ? Effect.die("MCP sessionful handler installation requires a lifecycle runtime") + : target.install( + options, + options.handlerRpcs, + options.makeHandlers( + core, + // TypeScript cannot narrow HandlerLifecycle from the generic runtime tag; the preceding guard + // enforces the stateful lifecycle invariant. + (options.runtime._tag === "Stateful" ? lifecycle : undefined) as HandlerLifecycle, + target.context + ) + ) + + const makeReverseClient = ( + profile: McpCore.NegotiatedProtocolProfile + ): Effect.Effect< + PublicMcpSchema.McpReverseClient, + never, + RpcClient.Protocol | Scope.Scope + > => + RpcClient.make(options.serverRequestRpcs, { + spanPrefix: "McpServer/Client" + }).pipe( + Effect.map((client) => options.toReverseClient(profile, client)) + ) + + return { + ...options, + payloadCodecs, + installHandlers, + makeReverseClient + } +} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts new file mode 100644 index 0000000000..647d522467 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts @@ -0,0 +1,337 @@ +/** + * @internal + */ +import * as Effect from "../../../Effect.ts" +import * as Base64 from "../../../encoding/Base64.ts" +import * as Match from "../../../Match.ts" +import * as Schema from "../../../Schema.ts" +import * as PublicMcpSchema from "../../McpSchema.ts" +import * as McpCore from "../mcpCore.ts" +import * as McpProtocol from "../mcpProtocol.ts" +import * as McpRuntime from "../mcpRuntime.ts" +import * as McpSchema from "../mcpSchema/v2024_11_05.ts" + +// Pings can arrive before initialize and do not require a client session. +const ClientRequestRpcs = McpSchema.ClientRequestRpcs.omit("ping").middleware( + PublicMcpSchema.McpServerClientMiddleware +).add(McpSchema.Ping) + +const ClientRpcs = ClientRequestRpcs.merge(McpSchema.ClientNotificationRpcs) + +const AdapterRpcs = ClientRpcs.omit("ping") + +const profileFromInitialize = ( + initialize: typeof McpSchema.Initialize.payloadSchema.Type +): McpCore.NegotiatedProtocolProfile => ({ + protocolVersion: McpSchema.protocolVersion, + clientCapabilities: PublicMcpSchema.ClientCapabilities.make(initialize.capabilities), + clientInfo: PublicMcpSchema.Implementation.make(initialize.clientInfo), + requestMetadata: initialize._meta +}) + +const unsupported = ( + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + reason: string +) => + new PublicMcpSchema.McpReverseOperationUnsupported({ + operation, + protocolVersion: McpSchema.protocolVersion, + reason + }) + +const requireCapability = ( + profile: McpCore.NegotiatedProtocolProfile, + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + capability: "roots" | "sampling" +) => + Object.hasOwn(profile.clientCapabilities, capability) && + profile.clientCapabilities[capability] !== undefined + ? Effect.void + : Effect.fail(unsupported(operation, `Client did not advertise the ${capability} capability`)) + +const projectCapabilities = ( + capabilities: McpCore.CanonicalServerCapabilities +): typeof McpSchema.ServerCapabilities.Type => ({ + experimental: capabilities.experimental, + logging: capabilities.logging ? {} : undefined, + prompts: capabilities.prompts, + resources: capabilities.resources, + tools: capabilities.tools +}) + +const projectContent = Effect.fnUntraced(function*(content: PublicMcpSchema.ContentBlock) { + const projected = Match.value(content).pipe( + Match.when({ type: "text" }, (content) => ({ + ...content, + annotations: content.annotations + })), + Match.when({ type: "image" }, (content) => + McpSchema.ImageContent.make({ + type: "image", + mimeType: content.mimeType, + data: Base64.encode(content.data), + annotations: content.annotations + })), + Match.when({ type: "resource" }, (content) => { + const resource = content.resource + if ("text" in resource) { + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + text: resource.text + }, + annotations: content.annotations + }) + } + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + blob: Base64.encode(resource.blob) + }, + annotations: content.annotations + }) + }), + Match.when({ type: Match.is("audio", "resource_link") }, (content) => + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: `${content.type} tool content` + })), + Match.exhaustive + ) + return projected instanceof McpCore.UnsupportedByProtocol + ? yield* projected + : projected +}) + +/** + * @internal + */ +export const protocol = McpProtocol.make({ + protocolVersion: McpSchema.protocolVersion, + runtime: McpRuntime.stateful({ + jsonRpc: { acceptsBatches: false }, + http: { requiresVersionHeader: false } + }), + clientRpcs: ClientRpcs, + clientNotificationRpcs: McpSchema.ClientNotificationRpcs, + serverRequestRpcs: McpSchema.ServerRequestRpcs, + serverNotificationRpcs: McpSchema.ServerNotificationRpcs, + handlerRpcs: AdapterRpcs, + makeHandlers: (core, lifecycle) => + AdapterRpcs.of({ + initialize: (request, { client }) => + lifecycle.initialize(McpSchema.protocolVersion, profileFromInitialize(request), client.id).pipe( + Effect.map((result) => + McpSchema.InitializeResult.make({ + protocolVersion: McpSchema.protocolVersion, + capabilities: projectCapabilities(result.capabilities), + serverInfo: result.serverInfo, + instructions: result.instructions + }) + ) + ), + "logging/setLevel": ({ level }, { client, headers }) => + lifecycle.setLogLevel(level, client.id, headers).pipe(Effect.as({})), + "notifications/cancelled": () => Effect.void, + "notifications/initialized": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.Initialized(), client.id, headers), + "notifications/progress": (progress, { client, headers }) => + lifecycle.clientNotification( + McpCore.ClientNotification.Progress({ + progressToken: progress.progressToken, + progress: progress.progress, + total: progress.total, + metadata: progress._meta + }), + client.id, + headers + ), + "notifications/roots/list_changed": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.RootsChanged(), client.id, headers), + "resources/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => core.resources.list(McpProtocol.profileFromClient(request))) + .pipe(Effect.map((resources) => McpSchema.ListResourcesResult.make({ resources }))), + "resources/templates/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => + core.resources.listTemplates(McpProtocol.profileFromClient(request)) + ).pipe( + Effect.map((resourceTemplates) => McpSchema.ListResourceTemplatesResult.make({ resourceTemplates })) + ), + "resources/read": Effect.fnUntraced(function*({ uri }) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.resources.read(uri, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + return McpSchema.ReadResourceResult.make({ + contents: result.contents.map((content) => + "text" in content + ? { + uri: content.uri, + mimeType: content.mimeType, + text: content.text + } + : { + uri: content.uri, + mimeType: content.mimeType, + blob: Base64.encode(content.blob) + } + ), + _meta: result._meta + }) + }), + "resources/subscribe": ({ uri }, { client, headers }) => + lifecycle.subscribe(uri, client.id, headers).pipe(Effect.as({})), + "resources/unsubscribe": ({ uri }, { client, headers }) => + lifecycle.unsubscribe(uri, client.id, headers).pipe(Effect.as({})), + "prompts/list": () => + PublicMcpSchema.McpServerClient.use((request) => core.prompts.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((prompts) => + McpSchema.ListPromptsResult.make({ + prompts: prompts.map((prompt) => ({ + name: prompt.name, + description: prompt.description, + arguments: prompt.arguments?.map((argument) => ({ + name: argument.name, + description: argument.description, + required: argument.required + })) + })) + }) + ) + ), + "prompts/get": Effect.fnUntraced(function*({ arguments: args, name }) { + const request = yield* PublicMcpSchema.McpServerClient + const outcome = yield* core.prompts.get(name, args ?? {}, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + if (outcome._tag === "InputRequired") { + return yield* McpProtocol.ProtocolError.fromFeature( + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: "prompt input requirements" + }) + ) + } + const result = outcome.value + const messages = yield* Effect.forEach(result.messages, (message) => + projectContent(message.content).pipe( + Effect.map((content) => ({ role: message.role, content })), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + )) + return McpSchema.GetPromptResult.make({ + description: result.description, + messages, + _meta: result._meta + }) + }), + "completion/complete": Effect.fnUntraced(function*(completeRequest) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.completions.complete({ + reference: completeRequest.ref.type === "ref/prompt" + ? { type: "prompt", name: completeRequest.ref.name } + : { type: "resourceTemplate", uriTemplate: completeRequest.ref.uri }, + argument: completeRequest.argument, + metadata: completeRequest._meta + }, McpProtocol.invocationFromClient(request)).pipe(Effect.mapError(McpProtocol.ProtocolError.fromFeature)) + return McpSchema.CompleteResult.make({ + completion: { + values: Array.from(result.values), + total: result.total, + hasMore: result.hasMore + }, + _meta: result.metadata + }) + }), + "tools/list": Effect.fnUntraced(function*() { + const request = yield* PublicMcpSchema.McpServerClient + return yield* core.tools.list(McpProtocol.profileFromClient(request)).pipe( + Effect.map((tools) => + McpSchema.ListToolsResult.make({ + tools: tools.map((tool) => + McpSchema.Tool.make({ + name: tool.name, + description: tool.description, + inputSchema: tool.inputSchema + }) + ) + }) + ) + ) + }), + "tools/call": Effect.fnUntraced(function*(call) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.tools.call( + { ...call, arguments: call.arguments ?? {} }, + McpProtocol.invocationFromClient(request) + ).pipe( + Effect.flatMap((outcome) => McpProtocol.requireCompleteOperation(McpSchema.protocolVersion, outcome)), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const content = yield* Effect.forEach(McpProtocol.unwrapStringStructuredContent(result), projectContent).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + return McpSchema.CallToolResult.make({ + content, + isError: result.isError, + _meta: result._meta + }) + }) + }), + toReverseClient: (profile, client) => ({ + listRoots: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "roots/list", "roots") + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.ListRoots.payloadSchema, + McpSchema.ListRoots.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("roots/list", "Request is not representable by this protocol")) + ) + const { roots } = yield* client["roots/list"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("roots/list")) + ) + return new PublicMcpSchema.ListRootsResult({ roots }) + }), + createMessage: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "sampling/createMessage", "sampling") + const wireRequest = yield* McpProtocol.transcodeStrict( + PublicMcpSchema.CreateMessage.payloadSchema, + McpSchema.CreateMessage.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("sampling/createMessage", "Request is not representable by this protocol")) + ) + const result = yield* client["sampling/createMessage"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("sampling/createMessage")) + ) + return yield* McpProtocol.transcode( + McpSchema.CreateMessage.successSchema, + PublicMcpSchema.CreateMessage.successSchema, + result + ).pipe( + Effect.mapError(() => + unsupported("sampling/createMessage", "Response is not representable by the canonical model") + ) + ) + }), + elicit: () => + Effect.fail(unsupported("elicitation/create", "Elicitation was introduced after this protocol revision")) + }), + projectNotification: (notification) => + McpProtocol.makeNotificationProjector({ + supportsProgressMessage: false + }, notification), + normalizeCancellation: (payload) => + Schema.decodeUnknownEffect(McpSchema.CancelledNotification.payloadSchema)(payload).pipe( + Effect.map((request) => ({ + requestId: request.requestId, + reason: request.reason, + metadata: request._meta + })) + ) +}) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts new file mode 100644 index 0000000000..7e952aeece --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts @@ -0,0 +1,363 @@ +/** + * @internal + */ +import * as Effect from "../../../Effect.ts" +import * as Base64 from "../../../encoding/Base64.ts" +import * as Match from "../../../Match.ts" +import * as Schema from "../../../Schema.ts" +import * as PublicMcpSchema from "../../McpSchema.ts" +import * as McpCore from "../mcpCore.ts" +import * as McpProtocol from "../mcpProtocol.ts" +import * as McpRuntime from "../mcpRuntime.ts" +import * as McpSchema from "../mcpSchema/v2025_03_26.ts" + +// Pings can arrive before initialize and do not require a client session. +const ClientRequestRpcs = McpSchema.ClientRequestRpcs.omit("ping").middleware( + PublicMcpSchema.McpServerClientMiddleware +).add(McpSchema.Ping) + +const ClientRpcs = ClientRequestRpcs.merge(McpSchema.ClientNotificationRpcs) + +const AdapterRpcs = ClientRpcs.omit("ping") + +const profileFromInitialize = ( + initialize: typeof McpSchema.Initialize.payloadSchema.Type +): McpCore.NegotiatedProtocolProfile => ({ + protocolVersion: McpSchema.protocolVersion, + clientCapabilities: PublicMcpSchema.ClientCapabilities.make(initialize.capabilities), + clientInfo: PublicMcpSchema.Implementation.make(initialize.clientInfo), + requestMetadata: initialize._meta +}) + +const unsupported = ( + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + reason: string +) => + new PublicMcpSchema.McpReverseOperationUnsupported({ + operation, + protocolVersion: McpSchema.protocolVersion, + reason + }) + +const requireCapability = ( + profile: McpCore.NegotiatedProtocolProfile, + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + capability: "roots" | "sampling" +) => + Object.hasOwn(profile.clientCapabilities, capability) && + profile.clientCapabilities[capability] !== undefined + ? Effect.void + : Effect.fail(unsupported(operation, `Client did not advertise the ${capability} capability`)) + +const projectContent = Effect.fnUntraced(function*(content: PublicMcpSchema.ContentBlock) { + const projected = Match.value(content).pipe( + Match.when({ type: "text" }, (content) => ({ + ...content, + annotations: content.annotations + })), + Match.when({ type: Match.is("image", "audio") }, (content) => ({ + type: content.type, + mimeType: content.mimeType, + data: Base64.encode(content.data), + annotations: content.annotations + })), + Match.when({ type: "resource" }, (content) => { + const resource = content.resource + if ("text" in resource) { + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + text: resource.text + }, + annotations: content.annotations + }) + } + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + blob: Base64.encode(resource.blob) + }, + annotations: content.annotations + }) + }), + Match.when({ type: "resource_link" }, () => + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: "resource_link tool content" + })), + Match.exhaustive + ) + return projected instanceof McpCore.UnsupportedByProtocol + ? yield* projected + : projected +}) + +const projectResourceContents = ( + content: PublicMcpSchema.TextResourceContents | PublicMcpSchema.BlobResourceContents +): typeof McpSchema.ResourceContents.Type => + "text" in content + ? { + uri: content.uri, + mimeType: content.mimeType, + text: content.text + } + : { + uri: content.uri, + mimeType: content.mimeType, + blob: Base64.encode(content.blob) + } + +/** + * @internal + */ +export const protocol = McpProtocol.make({ + protocolVersion: McpSchema.protocolVersion, + runtime: McpRuntime.stateful({ + jsonRpc: { acceptsBatches: true }, + http: { requiresVersionHeader: false } + }), + clientRpcs: ClientRpcs, + clientNotificationRpcs: McpSchema.ClientNotificationRpcs, + serverRequestRpcs: McpSchema.ServerRequestRpcs, + serverNotificationRpcs: McpSchema.ServerNotificationRpcs, + handlerRpcs: AdapterRpcs, + makeHandlers: (core, lifecycle) => + AdapterRpcs.of({ + initialize: (request, { client }) => + lifecycle.initialize(McpSchema.protocolVersion, profileFromInitialize(request), client.id).pipe( + Effect.map((result) => + McpSchema.InitializeResult.make({ + protocolVersion: McpSchema.protocolVersion, + capabilities: ({ + experimental: result.capabilities.experimental, + logging: result.capabilities.logging ? {} : undefined, + completions: result.capabilities.completions ? {} : undefined, + prompts: result.capabilities.prompts, + resources: result.capabilities.resources, + tools: result.capabilities.tools + }), + serverInfo: result.serverInfo, + instructions: result.instructions + }) + ) + ), + "logging/setLevel": ({ level }, { client, headers }) => + lifecycle.setLogLevel(level, client.id, headers).pipe(Effect.as({})), + "notifications/cancelled": () => Effect.void, + "notifications/initialized": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.Initialized(), client.id, headers), + "notifications/progress": (progress, { client, headers }) => + lifecycle.clientNotification( + McpCore.ClientNotification.Progress({ + progressToken: progress.progressToken, + progress: progress.progress, + total: progress.total, + message: progress.message, + metadata: progress._meta + }), + client.id, + headers + ), + "notifications/roots/list_changed": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.RootsChanged(), client.id, headers), + "resources/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => core.resources.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((resources) => + McpSchema.ListResourcesResult.make({ + resources: resources.map((resource) => ({ + uri: resource.uri, + name: resource.name, + description: resource.description, + mimeType: resource.mimeType, + size: resource.size, + annotations: resource.annotations + })) + }) + ) + ), + "resources/templates/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => + core.resources.listTemplates(McpProtocol.profileFromClient(request)) + ).pipe( + Effect.map((resourceTemplates) => + McpSchema.ListResourceTemplatesResult.make({ + resourceTemplates: resourceTemplates.map((template) => ({ + uriTemplate: template.uriTemplate, + name: template.name, + description: template.description, + mimeType: template.mimeType, + annotations: template.annotations + })) + }) + ) + ), + "resources/read": Effect.fnUntraced(function*({ uri }) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.resources.read(uri, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + return McpSchema.ReadResourceResult.make({ + contents: result.contents.map(projectResourceContents), + _meta: result._meta + }) + }), + "resources/subscribe": ({ uri }, { client, headers }) => + lifecycle.subscribe(uri, client.id, headers).pipe(Effect.as({})), + "resources/unsubscribe": ({ uri }, { client, headers }) => + lifecycle.unsubscribe(uri, client.id, headers).pipe(Effect.as({})), + "prompts/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => core.prompts.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((prompts) => + McpSchema.ListPromptsResult.make({ + prompts: prompts.map((prompt) => ({ + name: prompt.name, + description: prompt.description, + arguments: prompt.arguments?.map((argument) => ({ + name: argument.name, + description: argument.description, + required: argument.required + })) + })) + }) + ) + ), + "prompts/get": Effect.fnUntraced(function*({ arguments: args, name }) { + const request = yield* PublicMcpSchema.McpServerClient + const outcome = yield* core.prompts.get(name, args ?? {}, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + if (outcome._tag === "InputRequired") { + return yield* McpProtocol.ProtocolError.fromFeature( + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: "prompt input requirements" + }) + ) + } + const result = outcome.value + const messages = yield* Effect.forEach(result.messages, (message) => + projectContent(message.content).pipe( + Effect.map((content) => ({ role: message.role, content })), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + )) + return McpSchema.GetPromptResult.make({ + description: result.description, + messages, + _meta: result._meta + }) + }), + "completion/complete": Effect.fnUntraced(function*(completeRequest) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.completions.complete({ + reference: completeRequest.ref.type === "ref/prompt" + ? { type: "prompt", name: completeRequest.ref.name } + : { type: "resourceTemplate", uriTemplate: completeRequest.ref.uri }, + argument: completeRequest.argument, + metadata: completeRequest._meta + }, McpProtocol.invocationFromClient(request)).pipe(Effect.mapError(McpProtocol.ProtocolError.fromFeature)) + return McpSchema.CompleteResult.make({ + completion: { + values: Array.from(result.values), + total: result.total, + hasMore: result.hasMore + }, + _meta: result.metadata + }) + }), + "tools/list": Effect.fnUntraced(function*() { + const request = yield* PublicMcpSchema.McpServerClient + const tools = yield* core.tools.list(McpProtocol.profileFromClient(request)) + return McpSchema.ListToolsResult.make({ + tools: tools.map((tool) => + McpSchema.Tool.make({ + name: tool.name, + description: tool.description, + inputSchema: tool.inputSchema, + annotations: tool.title === undefined && tool.annotations === undefined + ? undefined + : McpSchema.ToolAnnotations.make({ + ...tool.annotations, + title: tool.title + }) + }) + ) + }) + }), + "tools/call": Effect.fnUntraced(function*(call) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.tools.call( + { ...call, arguments: call.arguments ?? {} }, + McpProtocol.invocationFromClient(request) + ).pipe( + Effect.flatMap((outcome) => McpProtocol.requireCompleteOperation(McpSchema.protocolVersion, outcome)), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const content = yield* Effect.forEach(McpProtocol.unwrapStringStructuredContent(result), projectContent).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + return McpSchema.CallToolResult.make({ + content, + isError: result.isError, + _meta: result._meta + }) + }) + }), + toReverseClient: (profile, client) => ({ + listRoots: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "roots/list", "roots") + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.ListRoots.payloadSchema, + McpSchema.ListRoots.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("roots/list", "Request is not representable by this protocol")) + ) + const { roots } = yield* client["roots/list"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("roots/list")) + ) + return new PublicMcpSchema.ListRootsResult({ roots }) + }), + createMessage: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "sampling/createMessage", "sampling") + const wireRequest = yield* McpProtocol.transcodeStrict( + PublicMcpSchema.CreateMessage.payloadSchema, + McpSchema.CreateMessage.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("sampling/createMessage", "Request is not representable by this protocol")) + ) + const result = yield* client["sampling/createMessage"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("sampling/createMessage")) + ) + return yield* McpProtocol.transcode( + McpSchema.CreateMessage.successSchema, + PublicMcpSchema.CreateMessage.successSchema, + result + ).pipe( + Effect.mapError(() => + unsupported("sampling/createMessage", "Response is not representable by the canonical model") + ) + ) + }), + elicit: () => + Effect.fail(unsupported("elicitation/create", "Elicitation was introduced after this protocol revision")) + }), + projectNotification: (notification) => + McpProtocol.makeNotificationProjector({ + supportsProgressMessage: true + }, notification), + normalizeCancellation: (payload) => + Schema.decodeUnknownEffect(McpSchema.CancelledNotification.payloadSchema)(payload).pipe( + Effect.map((request) => ({ + requestId: request.requestId, + reason: request.reason, + metadata: request._meta + })) + ) +}) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts new file mode 100644 index 0000000000..2cb939620b --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts @@ -0,0 +1,383 @@ +import * as Effect from "../../../Effect.ts" +import * as Base64 from "../../../encoding/Base64.ts" +import * as Match from "../../../Match.ts" +import * as Predicate from "../../../Predicate.ts" +import * as Schema from "../../../Schema.ts" +import * as Struct from "../../../Struct.ts" +import * as PublicMcpSchema from "../../McpSchema.ts" +import * as McpCore from "../mcpCore.ts" +import * as McpProtocol from "../mcpProtocol.ts" +import * as McpRuntime from "../mcpRuntime.ts" +import * as McpSchema from "../mcpSchema/v2025_06_18.ts" + +// Pings can arrive before initialize and do not require a client session. +const ClientRequestRpcs = McpSchema.ClientRequestRpcs.omit("ping").middleware( + PublicMcpSchema.McpServerClientMiddleware +).add(McpSchema.Ping) + +const ClientRpcs = ClientRequestRpcs.merge(McpSchema.ClientNotificationRpcs) + +const AdapterRpcs = ClientRpcs.omit("ping") +const isToolOutputSchema = ( + schema: PublicMcpSchema.ToolOutputJson | undefined +): schema is PublicMcpSchema.ToolJson => { + if (schema?.type !== "object") { + return false + } + + const isPropertiesSupported = schema.properties === undefined || + (Predicate.isReadonlyObject(schema.properties) && + Object.values(schema.properties).every(Predicate.isReadonlyObject)) + const isRequiredDefined = schema.required === undefined || + (Array.isArray(schema.required) && schema.required.every(Predicate.isString)) + + return isPropertiesSupported && isRequiredDefined +} + +const profileFromInitialize = ( + initialize: typeof McpSchema.Initialize.payloadSchema.Type +): McpCore.NegotiatedProtocolProfile => ({ + protocolVersion: McpSchema.protocolVersion, + clientCapabilities: PublicMcpSchema.ClientCapabilities.make(initialize.capabilities), + clientInfo: PublicMcpSchema.Implementation.make(initialize.clientInfo), + requestMetadata: initialize._meta +}) + +const unsupported = ( + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + reason: string +) => + new PublicMcpSchema.McpReverseOperationUnsupported({ + operation, + protocolVersion: McpSchema.protocolVersion, + reason + }) + +const requireCapability = ( + profile: McpCore.NegotiatedProtocolProfile, + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + capability: "roots" | "sampling" | "elicitation" +) => + Object.hasOwn(profile.clientCapabilities, capability) && + profile.clientCapabilities[capability] !== undefined + ? Effect.void + : Effect.fail(unsupported(operation, `Client did not advertise the ${capability} capability`)) + +const projectContent = Effect.fnUntraced(function*(content: PublicMcpSchema.ContentBlock) { + return Match.value(content).pipe( + Match.when({ type: Match.is("text", "resource_link") }, (content) => content), + Match.when({ type: Match.is("image", "audio") }, (content) => ({ + type: content.type, + mimeType: content.mimeType, + data: Base64.encode(content.data), + annotations: content.annotations, + _meta: content._meta + })), + Match.when({ type: "resource" }, (content) => { + const resource = content.resource + if ("text" in resource) { + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + _meta: resource._meta, + text: resource.text + }, + annotations: content.annotations, + _meta: content._meta + }) + } + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + _meta: resource._meta, + blob: Base64.encode(resource.blob) + }, + annotations: content.annotations, + _meta: content._meta + }) + }), + Match.exhaustive + ) +}) + +const projectResourceContents = ( + content: PublicMcpSchema.TextResourceContents | PublicMcpSchema.BlobResourceContents +): typeof McpSchema.ResourceContents.Type => + "text" in content + ? { + uri: content.uri, + mimeType: content.mimeType, + _meta: content._meta, + text: content.text + } + : { + uri: content.uri, + mimeType: content.mimeType, + _meta: content._meta, + blob: Base64.encode(content.blob) + } + +const projectStructuredContent = ( + content: Schema.Json | undefined +): Schema.JsonObject | undefined => content === undefined || isJsonObject(content) ? content : undefined + +const isJsonObject = (value: Schema.Json): value is Schema.JsonObject => Predicate.isReadonlyObject(value) + +/** + * @internal + */ +export const protocol = McpProtocol.make({ + protocolVersion: McpSchema.protocolVersion, + runtime: McpRuntime.stateful({ + jsonRpc: { acceptsBatches: false }, + http: { requiresVersionHeader: true } + }), + clientRpcs: ClientRpcs, + clientNotificationRpcs: McpSchema.ClientNotificationRpcs, + serverRequestRpcs: McpSchema.ServerRequestRpcs, + serverNotificationRpcs: McpSchema.ServerNotificationRpcs, + handlerRpcs: AdapterRpcs, + makeHandlers: (core, lifecycle) => + AdapterRpcs.of({ + initialize: (request, { client }) => + lifecycle.initialize(McpSchema.protocolVersion, profileFromInitialize(request), client.id).pipe( + Effect.map((result) => + McpSchema.InitializeResult.make({ + protocolVersion: McpSchema.protocolVersion, + capabilities: ({ + experimental: result.capabilities.experimental, + logging: result.capabilities.logging ? {} : undefined, + completions: result.capabilities.completions ? {} : undefined, + prompts: result.capabilities.prompts, + resources: result.capabilities.resources, + tools: result.capabilities.tools + }), + serverInfo: result.serverInfo, + instructions: result.instructions + }) + ) + ), + "logging/setLevel": ({ level }, { client, headers }) => + lifecycle.setLogLevel(level, client.id, headers).pipe(Effect.as({})), + "notifications/cancelled": () => Effect.void, + "notifications/initialized": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.Initialized(), client.id, headers), + "notifications/progress": (progress, { client, headers }) => + lifecycle.clientNotification( + McpCore.ClientNotification.Progress({ + progressToken: progress.progressToken, + progress: progress.progress, + total: progress.total, + message: progress.message, + metadata: progress._meta + }), + client.id, + headers + ), + "notifications/roots/list_changed": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.RootsChanged(), client.id, headers), + "resources/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => core.resources.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((resources) => McpSchema.ListResourcesResult.make({ resources })) + ), + "resources/templates/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => + core.resources.listTemplates(McpProtocol.profileFromClient(request)) + ).pipe( + Effect.map((resourceTemplates) => McpSchema.ListResourceTemplatesResult.make({ resourceTemplates })) + ), + "resources/read": Effect.fnUntraced(function*({ uri }) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.resources.read(uri, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + return McpSchema.ReadResourceResult.make({ + contents: result.contents.map(projectResourceContents), + _meta: result._meta + }) + }), + "resources/subscribe": ({ uri }, { client, headers }) => + lifecycle.subscribe(uri, client.id, headers).pipe(Effect.as({})), + "resources/unsubscribe": ({ uri }, { client, headers }) => + lifecycle.unsubscribe(uri, client.id, headers).pipe(Effect.as({})), + "prompts/list": (_pageRequest) => + PublicMcpSchema.McpServerClient.use((request) => core.prompts.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((prompts) => McpSchema.ListPromptsResult.make({ prompts })) + ), + "prompts/get": Effect.fnUntraced(function*({ arguments: args, name }) { + const request = yield* PublicMcpSchema.McpServerClient + const outcome = yield* core.prompts.get(name, args ?? {}, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + if (outcome._tag === "InputRequired") { + return yield* McpProtocol.ProtocolError.fromFeature( + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: "prompt input requirements" + }) + ) + } + const result = outcome.value + const messages = yield* Effect.forEach(result.messages, (message) => + projectContent(message.content).pipe( + Effect.map((content) => ({ role: message.role, content })), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + )) + return McpSchema.GetPromptResult.make({ + description: result.description, + messages, + _meta: result._meta + }) + }), + "completion/complete": Effect.fnUntraced(function*(completeRequest) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.completions.complete({ + reference: completeRequest.ref.type === "ref/prompt" + ? { + type: "prompt", + name: completeRequest.ref.name, + title: completeRequest.ref.title + } + : { type: "resourceTemplate", uriTemplate: completeRequest.ref.uri }, + argument: completeRequest.argument, + context: completeRequest.context?.arguments === undefined + ? undefined + : { arguments: completeRequest.context.arguments }, + metadata: completeRequest._meta + }, McpProtocol.invocationFromClient(request)).pipe(Effect.mapError(McpProtocol.ProtocolError.fromFeature)) + return McpSchema.CompleteResult.make({ + completion: { + values: Array.from(result.values), + total: result.total, + hasMore: result.hasMore + }, + _meta: result.metadata + }) + }), + "tools/list": Effect.fnUntraced(function*() { + const request = yield* PublicMcpSchema.McpServerClient + const tools = yield* core.tools.list(McpProtocol.profileFromClient(request)) + return McpSchema.ListToolsResult.make({ + tools: tools.map((tool) => + McpSchema.Tool.make({ + name: tool.name, + title: tool.title, + description: tool.description, + inputSchema: tool.inputSchema, + outputSchema: isToolOutputSchema(tool.outputSchema) + ? tool.outputSchema + : undefined, + annotations: tool.annotations === undefined + ? undefined + : McpSchema.ToolAnnotations.make({ + title: tool.annotations.title, + readOnlyHint: tool.annotations.readOnlyHint, + destructiveHint: tool.annotations.destructiveHint, + idempotentHint: tool.annotations.idempotentHint, + openWorldHint: tool.annotations.openWorldHint + }), + _meta: tool._meta + }) + ) + }) + }), + "tools/call": Effect.fnUntraced(function*(call) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.tools.call( + { ...call, arguments: call.arguments ?? {} }, + McpProtocol.invocationFromClient(request) + ).pipe( + Effect.flatMap((outcome) => McpProtocol.requireCompleteOperation(McpSchema.protocolVersion, outcome)), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const content = yield* Effect.forEach(McpProtocol.unwrapStringStructuredContent(result), projectContent).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const structuredContent = projectStructuredContent(result.structuredContent) + return McpSchema.CallToolResult.make({ + content, + structuredContent, + isError: result.isError, + _meta: result._meta + }) + }) + }), + toReverseClient: (profile, client) => ({ + listRoots: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "roots/list", "roots") + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.ListRoots.payloadSchema, + McpSchema.ListRoots.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("roots/list", "Request is not representable by this protocol")) + ) + const { roots } = yield* client["roots/list"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("roots/list")) + ) + return new PublicMcpSchema.ListRootsResult({ roots }) + }), + createMessage: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "sampling/createMessage", "sampling") + const wireRequest = yield* McpProtocol.transcodeStrict( + PublicMcpSchema.CreateMessage.payloadSchema, + McpSchema.CreateMessage.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("sampling/createMessage", "Request is not representable by this protocol")) + ) + const result = yield* client["sampling/createMessage"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("sampling/createMessage")) + ) + return yield* McpProtocol.transcode( + McpSchema.CreateMessage.successSchema, + PublicMcpSchema.CreateMessage.successSchema, + result + ).pipe( + Effect.mapError(() => + unsupported("sampling/createMessage", "Response is not representable by the canonical model") + ) + ) + }), + elicit: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "elicitation/create", "elicitation") + const projected = request.mode === "form" + ? Struct.omit(request, ["mode"]) + : request + const wireRequest = yield* Schema.decodeUnknownEffect( + McpSchema.Elicit.payloadSchema, + { onExcessProperty: "error" } + )(projected).pipe( + Effect.mapError(() => unsupported("elicitation/create", "Request is not representable by this protocol")) + ) + const result = yield* client["elicitation/create"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("elicitation/create")) + ) + return yield* McpProtocol.transcode( + McpSchema.Elicit.successSchema, + PublicMcpSchema.Elicit.successSchema, + result + ).pipe( + Effect.mapError(() => unsupported("elicitation/create", "Response is not representable by the canonical model")) + ) + }) + }), + projectNotification: (notification) => + McpProtocol.makeNotificationProjector({ + supportsProgressMessage: true + }, notification), + normalizeCancellation: (payload) => + Schema.decodeUnknownEffect(McpSchema.CancelledNotification.payloadSchema)(payload).pipe( + Effect.map((request) => ({ + requestId: request.requestId, + reason: request.reason, + metadata: request._meta + })) + ) +}) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts new file mode 100644 index 0000000000..facfd55e58 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts @@ -0,0 +1,436 @@ +import * as Effect from "../../../Effect.ts" +import * as Base64 from "../../../encoding/Base64.ts" +import * as Match from "../../../Match.ts" +import * as Predicate from "../../../Predicate.ts" +import * as Schema from "../../../Schema.ts" +import * as PublicMcpSchema from "../../McpSchema.ts" +import * as McpCore from "../mcpCore.ts" +import * as McpProtocol from "../mcpProtocol.ts" +import * as McpRuntime from "../mcpRuntime.ts" +import * as McpSchema from "../mcpSchema/v2025_11_25.ts" + +// Pings can arrive before initialize and do not require a client session. +const ClientRequestRpcs = McpSchema.ClientRequestRpcs.omit("ping").middleware( + PublicMcpSchema.McpServerClientMiddleware +).add(McpSchema.Ping) + +const ClientRpcs = ClientRequestRpcs.merge(McpSchema.ClientNotificationRpcs) + +const AdapterRpcs = ClientRpcs.omit("ping") +const isToolOutputSchema = ( + schema: PublicMcpSchema.ToolOutputJson | undefined +): schema is PublicMcpSchema.ToolJson => { + if (schema?.type !== "object") { + return false + } + + const isPropertiesSupported = schema.properties === undefined || + (Predicate.isReadonlyObject(schema.properties) && + Object.values(schema.properties).every(Predicate.isReadonlyObject)) + const isRequiredDefined = schema.required === undefined || + (Array.isArray(schema.required) && schema.required.every(Predicate.isString)) + + return isPropertiesSupported && isRequiredDefined +} + +const unsupported = ( + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + reason: string +) => + new PublicMcpSchema.McpReverseOperationUnsupported({ + operation, + protocolVersion: McpSchema.protocolVersion, + reason + }) + +const requireCapability = ( + profile: McpCore.NegotiatedProtocolProfile, + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"], + capability: "roots" | "sampling" | "elicitation" +) => + Object.hasOwn(profile.clientCapabilities, capability) && + profile.clientCapabilities[capability] !== undefined + ? Effect.void + : Effect.fail(unsupported(operation, `Client did not advertise the ${capability} capability`)) + +const resultRequiresSamplingTools = (result: typeof McpSchema.CreateMessage.successSchema.Type): boolean => + result.stopReason === "toolUse" || + ("type" in result.content + ? result.content.type === "tool_use" || result.content.type === "tool_result" + : result.content.some((block) => block.type === "tool_use" || block.type === "tool_result")) + +const hasElicitationModeCapability = ( + profile: McpCore.NegotiatedProtocolProfile, + mode: "form" | "url" +): boolean => { + const elicitation = profile.clientCapabilities.elicitation + if (elicitation === undefined) return false + return mode === "form" + ? elicitation.form !== undefined || elicitation.url === undefined + : elicitation.url !== undefined +} + +const projectContent = Effect.fnUntraced(function*(content: PublicMcpSchema.ContentBlock) { + return Match.value(content).pipe( + Match.when({ type: Match.is("text", "resource_link") }, (content) => content), + Match.when({ type: Match.is("image", "audio") }, (content) => ({ + type: content.type, + mimeType: content.mimeType, + data: Base64.encode(content.data), + annotations: content.annotations, + _meta: content._meta + })), + Match.when({ type: "resource" }, (content) => { + const resource = content.resource + if ("text" in resource) { + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + _meta: resource._meta, + text: resource.text + }, + annotations: content.annotations, + _meta: content._meta + }) + } + return McpSchema.EmbeddedResource.make({ + type: "resource", + resource: { + uri: resource.uri, + mimeType: resource.mimeType, + _meta: resource._meta, + blob: Base64.encode(resource.blob) + }, + annotations: content.annotations, + _meta: content._meta + }) + }), + Match.exhaustive + ) +}) + +const projectStructuredContent = ( + content: Schema.Json | undefined +): Schema.JsonObject | undefined => content === undefined || isJsonObject(content) ? content : undefined + +const isJsonObject = (value: Schema.Json): value is Schema.JsonObject => Predicate.isReadonlyObject(value) + +/** + * @internal + */ +export const protocol = McpProtocol.make({ + protocolVersion: McpSchema.protocolVersion, + runtime: McpRuntime.stateful({ + jsonRpc: { acceptsBatches: false }, + http: { requiresVersionHeader: true } + }), + clientRpcs: ClientRpcs, + clientNotificationRpcs: McpSchema.ClientNotificationRpcs, + serverRequestRpcs: McpSchema.ServerRequestRpcs, + serverNotificationRpcs: McpSchema.ServerNotificationRpcs, + handlerRpcs: AdapterRpcs, + makeHandlers: (core, lifecycle) => + AdapterRpcs.of({ + initialize: (request, { client }) => + lifecycle.initialize(McpSchema.protocolVersion, { + protocolVersion: McpSchema.protocolVersion, + clientCapabilities: PublicMcpSchema.ClientCapabilities.make(request.capabilities), + clientInfo: PublicMcpSchema.Implementation.make(request.clientInfo), + requestMetadata: request._meta + }, client.id).pipe( + Effect.map((result) => + McpSchema.InitializeResult.make({ + protocolVersion: McpSchema.protocolVersion, + capabilities: ({ + experimental: result.capabilities.experimental, + logging: result.capabilities.logging ? {} : undefined, + completions: result.capabilities.completions ? {} : undefined, + prompts: result.capabilities.prompts, + resources: result.capabilities.resources, + tools: result.capabilities.tools + }), + serverInfo: result.serverInfo, + instructions: result.instructions + }) + ) + ), + "logging/setLevel": ({ level }, { client, headers }) => + lifecycle.setLogLevel(level, client.id, headers).pipe(Effect.as({})), + "notifications/cancelled": () => Effect.void, + "notifications/initialized": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.Initialized(), client.id, headers), + "notifications/progress": (progress, { client, headers }) => + lifecycle.clientNotification( + McpCore.ClientNotification.Progress({ + progressToken: progress.progressToken, + progress: progress.progress, + total: progress.total, + message: progress.message, + metadata: progress._meta + }), + client.id, + headers + ), + "notifications/roots/list_changed": (_, { client, headers }) => + lifecycle.clientNotification(McpCore.ClientNotification.RootsChanged(), client.id, headers), + "resources/list": () => + PublicMcpSchema.McpServerClient.use((request) => core.resources.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((resources) => + McpSchema.ListResourcesResult.make({ + resources: resources.map((resource) => McpSchema.Resource.make(resource)) + }) + ) + ), + "resources/templates/list": () => + PublicMcpSchema.McpServerClient.use((request) => + core.resources.listTemplates(McpProtocol.profileFromClient(request)) + ).pipe( + Effect.map((resourceTemplates) => + McpSchema.ListResourceTemplatesResult.make({ + resourceTemplates: resourceTemplates.map((resourceTemplate) => + McpSchema.ResourceTemplate.make(resourceTemplate) + ) + }) + ) + ), + "resources/read": Effect.fnUntraced(function*({ uri }) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.resources.read(uri, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + return McpSchema.ReadResourceResult.make({ + contents: result.contents.map((content) => + "text" in content + ? { + uri: content.uri, + mimeType: content.mimeType, + _meta: content._meta, + text: content.text + } + : { + uri: content.uri, + mimeType: content.mimeType, + _meta: content._meta, + blob: Base64.encode(content.blob) + } + ), + _meta: result._meta + }) + }), + "resources/subscribe": ({ uri }, { client, headers }) => + lifecycle.subscribe(uri, client.id, headers).pipe(Effect.as({})), + "resources/unsubscribe": ({ uri }, { client, headers }) => + lifecycle.unsubscribe(uri, client.id, headers).pipe(Effect.as({})), + "prompts/list": () => + PublicMcpSchema.McpServerClient.use((request) => core.prompts.list(McpProtocol.profileFromClient(request))) + .pipe( + Effect.map((prompts) => + McpSchema.ListPromptsResult.make({ + prompts: prompts.map((prompt) => McpSchema.Prompt.make(prompt)) + }) + ) + ), + "prompts/get": Effect.fnUntraced(function*({ arguments: args, name }) { + const request = yield* PublicMcpSchema.McpServerClient + const outcome = yield* core.prompts.get(name, args ?? {}, McpProtocol.invocationFromClient(request)).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromFeature) + ) + if (outcome._tag === "InputRequired") { + return yield* McpProtocol.ProtocolError.fromFeature( + new McpCore.UnsupportedByProtocol({ + protocolVersion: McpSchema.protocolVersion, + feature: "prompt input requirements" + }) + ) + } + const result = outcome.value + const messages = yield* Effect.forEach(result.messages, (message) => + projectContent(message.content).pipe( + Effect.map((content) => ({ role: message.role, content })), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + )) + return McpSchema.GetPromptResult.make({ + description: result.description, + messages, + _meta: result._meta + }) + }), + "completion/complete": Effect.fnUntraced(function*(completeRequest) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.completions.complete({ + reference: completeRequest.ref.type === "ref/prompt" + ? { + type: "prompt", + name: completeRequest.ref.name, + title: completeRequest.ref.title + } + : { type: "resourceTemplate", uriTemplate: completeRequest.ref.uri }, + argument: completeRequest.argument, + context: completeRequest.context?.arguments === undefined + ? undefined + : { arguments: completeRequest.context.arguments }, + metadata: completeRequest._meta + }, McpProtocol.invocationFromClient(request)).pipe(Effect.mapError(McpProtocol.ProtocolError.fromFeature)) + return McpSchema.CompleteResult.make({ + completion: { + values: Array.from(result.values), + total: result.total, + hasMore: result.hasMore + }, + _meta: result.metadata + }) + }), + "tools/list": Effect.fnUntraced(function*() { + const request = yield* PublicMcpSchema.McpServerClient + const tools = yield* core.tools.list(McpProtocol.profileFromClient(request)) + return McpSchema.ListToolsResult.make({ + tools: tools.map((tool) => + McpSchema.Tool.make({ + name: tool.name, + title: tool.title, + description: tool.description, + inputSchema: tool.inputSchema, + outputSchema: isToolOutputSchema(tool.outputSchema) + ? tool.outputSchema + : undefined, + icons: tool.icons, + annotations: tool.annotations === undefined + ? undefined + : McpSchema.ToolAnnotations.make({ + title: tool.annotations.title, + readOnlyHint: tool.annotations.readOnlyHint, + destructiveHint: tool.annotations.destructiveHint, + idempotentHint: tool.annotations.idempotentHint, + openWorldHint: tool.annotations.openWorldHint + }), + _meta: tool._meta + }) + ) + }) + }), + "tools/call": Effect.fnUntraced(function*(call) { + const request = yield* PublicMcpSchema.McpServerClient + const result = yield* core.tools.call( + { ...call, arguments: call.arguments ?? {} }, + McpProtocol.invocationFromClient(request) + ).pipe( + Effect.flatMap((outcome) => McpProtocol.requireCompleteOperation(McpSchema.protocolVersion, outcome)), + Effect.catchTag(["InvalidToolInput", "ToolExecutionError"], (error) => + Effect.succeed(PublicMcpSchema.CallToolResult.make({ + content: [PublicMcpSchema.TextContent.make({ + type: "text", + text: error.message + })], + isError: true + }))), + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const content = yield* Effect.forEach(McpProtocol.unwrapStringStructuredContent(result), projectContent).pipe( + Effect.mapError(McpProtocol.ProtocolError.fromTool) + ) + const structuredContent = projectStructuredContent(result.structuredContent) + return McpSchema.CallToolResult.make({ + content, + structuredContent, + isError: result.isError, + _meta: result._meta + }) + }) + }), + toReverseClient: (profile, client) => ({ + listRoots: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "roots/list", "roots") + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.ListRoots.payloadSchema, + McpSchema.ListRoots.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("roots/list", "Request is not representable by this protocol")) + ) + const { roots } = yield* client["roots/list"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("roots/list")) + ) + return new PublicMcpSchema.ListRootsResult({ roots }) + }), + createMessage: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "sampling/createMessage", "sampling") + if ( + McpProtocol.samplingRequestRequiresTools(request) && profile.clientCapabilities.sampling?.tools == undefined + ) { + return yield* unsupported("sampling/createMessage", "Client did not advertise the sampling.tools capability") + } + if ( + (request.includeContext === "thisServer" || request.includeContext === "allServers") && + profile.clientCapabilities.sampling?.context == undefined + ) { + return yield* unsupported("sampling/createMessage", "Client did not advertise the sampling.context capability") + } + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.CreateMessage.payloadSchema, + McpSchema.CreateMessage.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("sampling/createMessage", "Request is not representable by this protocol")) + ) + const result = yield* client["sampling/createMessage"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("sampling/createMessage")) + ) + if (resultRequiresSamplingTools(result) && profile.clientCapabilities.sampling?.tools == undefined) { + return yield* unsupported("sampling/createMessage", "Client did not advertise the sampling.tools capability") + } + return yield* McpProtocol.transcode( + McpSchema.CreateMessage.successSchema, + PublicMcpSchema.CreateMessage.successSchema, + result + ).pipe( + Effect.mapError(() => + unsupported("sampling/createMessage", "Response is not representable by the canonical model") + ) + ) + }), + elicit: Effect.fnUntraced(function*(request) { + yield* requireCapability(profile, "elicitation/create", "elicitation") + const mode = request.mode === "url" ? "url" : "form" + if (!hasElicitationModeCapability(profile, mode)) { + return yield* unsupported("elicitation/create", `Client did not advertise the elicitation.${mode} capability`) + } + const wireRequest = yield* McpProtocol.transcode( + PublicMcpSchema.Elicit.payloadSchema, + McpSchema.Elicit.payloadSchema, + request + ).pipe( + Effect.mapError(() => unsupported("elicitation/create", "Request is not representable by this protocol")) + ) + const result = yield* client["elicitation/create"](wireRequest).pipe( + Effect.mapError(McpProtocol.reverseError("elicitation/create")) + ) + return yield* McpProtocol.transcode( + McpSchema.Elicit.successSchema, + PublicMcpSchema.Elicit.successSchema, + result + ).pipe( + Effect.mapError(() => unsupported("elicitation/create", "Response is not representable by the canonical model")) + ) + }) + }), + projectNotification: (notification) => + notification._tag === "ElicitationComplete" + ? Effect.succeed({ + tag: "notifications/elicitation/complete", + payload: { elicitationId: notification.elicitationId } + }) + : McpProtocol.makeNotificationProjector({ + supportsProgressMessage: true + }, notification), + normalizeCancellation: (payload) => + Schema.decodeUnknownEffect(McpSchema.CancelledNotification.payloadSchema)(payload).pipe( + Effect.map((request) => ({ + requestId: request.requestId, + reason: request.reason, + metadata: request._meta + })) + ) +}) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts new file mode 100644 index 0000000000..c1cce20905 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts @@ -0,0 +1,710 @@ +/** + * MCP v2026-07-28 wire projections. + * + * @internal + */ +import * as Deferred from "../../../Deferred.ts" +import * as Effect from "../../../Effect.ts" +import * as Base64 from "../../../encoding/Base64.ts" +import { appendPreResponseHandlerUnsafe } from "../../../http/HttpEffect.ts" +import * as HttpServerRequest from "../../../http/HttpServerRequest.ts" +import * as HttpServerResponse from "../../../http/HttpServerResponse.ts" +import * as Match from "../../../Match.ts" +import * as Option from "../../../Option.ts" +import * as Predicate from "../../../Predicate.ts" +import * as PubSub from "../../../PubSub.ts" +import * as Queue from "../../../Queue.ts" +import * as Record from "../../../Record.ts" +import type * as Rpc from "../../../rpc/Rpc.ts" +import type * as RpcMessage from "../../../rpc/RpcMessage.ts" +import * as Schema from "../../../Schema.ts" +import * as Scope from "../../../Scope.ts" +import type * as Types from "../../../Types.ts" +import type * as PublicMcpProtocol from "../../McpProtocol.ts" +import * as PublicMcpSchema from "../../McpSchema.ts" +import * as McpCore from "../mcpCore.ts" +import * as McpProtocol from "../mcpProtocol.ts" +import * as McpSchema from "../mcpSchema/v2026_07_28.ts" + +const InputResponses = Schema.Record(Schema.String, Schema.JsonObject) +const decodeRequestMetadata = Schema.decodeUnknownEffect(McpSchema.RequestMetaObject) +const decodeCancellation = Schema.decodeUnknownEffect(McpSchema.CancelledNotification.payloadSchema) +const isJson = Schema.is(Schema.Json) +const encodeImplementation = Schema.encodeEffect(McpSchema.Implementation) +const MAX_PENDING_SUBSCRIPTION_NOTIFICATIONS = 64 + +type ObjectWithUndefined = Readonly> + +const omitUndefined = (value: ObjectWithUndefined): Record => { + const result: Record = {} + for (const key in value) { + if (value[key] !== undefined) { + result[key] = value[key] + } + } + return result +} + +const parameterHeaderMatches = (header: string | undefined, argument: unknown): boolean => { + if (Predicate.isNullish(argument)) { + return header === undefined + } + if (header === undefined) { + return false + } + const decoded = McpProtocol.decodeRoutingHeader(header) + if (decoded === undefined) { + return false + } + return Match.value(argument).pipe( + Match.when(Match.string, (argument) => decoded === argument), + Match.when(Match.boolean, (argument) => decoded === String(argument)), + Match.when( + Match.number, + (argument) => Number.isSafeInteger(argument) && decoded.trim().length > 0 && Number(decoded) === argument + ), + Match.orElse(() => false) + ) +} + +/** + * @internal + */ +export interface StatelessRequestProfile { + readonly protocolVersion: typeof McpSchema.protocolVersion + readonly clientCapabilities: typeof McpSchema.ClientCapabilities.Type + readonly clientInfo?: typeof McpSchema.Implementation.Type | undefined + readonly requestMetadata: typeof McpSchema.RequestMetaObject.Type +} + +/** + * @internal + */ +export const profileFromRequestMetadata = Effect.fnUntraced(function*(metadata: unknown) { + const requestMetadata = yield* decodeRequestMetadata(metadata) + return { + protocolVersion: McpSchema.protocolVersion, + clientCapabilities: requestMetadata["io.modelcontextprotocol/clientCapabilities"], + clientInfo: requestMetadata["io.modelcontextprotocol/clientInfo"], + requestMetadata + } satisfies StatelessRequestProfile +}) + +const resultMetadata = ( + value: ObjectWithUndefined, + serverInfo: Schema.JsonObject +): Schema.JsonObject => { + const metadata: unknown = value._meta + return { + ...(Predicate.isReadonlyObject(metadata) ? metadata : {}), + "io.modelcontextprotocol/serverInfo": serverInfo + } +} + +const projectInputRequired = ( + outcome: McpCore.InputRequiredFields, + serverInfo: Schema.JsonObject +) => + omitUndefined({ + _meta: { "io.modelcontextprotocol/serverInfo": serverInfo }, + resultType: "input_required", + inputRequests: outcome.inputRequests, + requestState: outcome.requestState + }) + +const decodeCallToolOutcome = Schema.decodeUnknownEffect(Schema.Union([ + McpSchema.CallToolResult, + McpSchema.InputRequiredResult +])) + +/** + * @internal + */ +export const projectCallToolOutcome = Effect.fnUntraced(function*( + outcome: McpCore.OperationOutcome, + serverInfo: typeof McpSchema.Implementation.Type +) { + const encodedServerInfo = yield* encodeImplementation(serverInfo) + if (outcome._tag === "Complete") { + return yield* decodeCallToolOutcome({ + ...omitUndefined(outcome.value), + _meta: resultMetadata(outcome.value, encodedServerInfo), + resultType: "complete" + }) + } + return yield* decodeCallToolOutcome(projectInputRequired(outcome, encodedServerInfo)) +}) + +const requiredCapabilitiesForInputRequests = ( + inputRequests: McpCore.InputRequiredFields["inputRequests"], + capabilities: McpCore.NegotiatedProtocolProfile["clientCapabilities"] +): Record => { + const required: Record> = {} + for (const request of Object.values(inputRequests ?? {})) { + Match.value(request).pipe( + Match.when({ method: "roots/list" }, () => { + if (capabilities.roots === undefined) required.roots = {} + }), + Match.when({ method: "sampling/createMessage" }, (request) => { + const sampling = required.sampling ?? {} + if (McpProtocol.samplingRequestRequiresTools(request.params) && capabilities.sampling?.tools === undefined) { + sampling.tools = {} + } + if ( + (request.params.includeContext === "thisServer" || request.params.includeContext === "allServers") && + capabilities.sampling?.context === undefined + ) { + sampling.context = {} + } + if (capabilities.sampling === undefined || Object.keys(sampling).length > 0) required.sampling = sampling + }), + Match.when({ method: "elicitation/create" }, (request) => { + const mode = request.params.mode === "url" ? "url" : "form" + const elicitation = capabilities.elicitation + const supportsMode = elicitation !== undefined && (mode === "url" + ? elicitation.url !== undefined + : elicitation.form !== undefined || Object.keys(elicitation).length === 0) + if (!supportsMode) (required.elicitation ??= {})[mode] = {} + }), + Match.exhaustive + ) + } + return required +} + +const projectResourceContents = ( + resource: PublicMcpSchema.TextResourceContents | PublicMcpSchema.BlobResourceContents +) => + omitUndefined({ + uri: resource.uri, + mimeType: resource.mimeType, + _meta: resource._meta, + ...("text" in resource ? { text: resource.text } : { blob: Base64.encode(resource.blob) }) + }) + +const projectContent = Match.type().pipe( + Match.when({ type: Match.is("text", "resource_link") }, (content) => content), + Match.when({ type: Match.is("image", "audio") }, (content) => + omitUndefined({ + type: content.type, + mimeType: content.mimeType, + data: Base64.encode(content.data), + annotations: content.annotations, + _meta: content._meta + })), + Match.when({ type: "resource" }, (content) => + omitUndefined({ + type: "resource", + resource: projectResourceContents(content.resource), + annotations: content.annotations, + _meta: content._meta + })), + Match.exhaustive +) + +const projectResource = (resource: PublicMcpSchema.Resource) => McpSchema.Resource.make(resource) +const projectResourceTemplate = (resourceTemplate: PublicMcpSchema.ResourceTemplate) => + McpSchema.ResourceTemplate.make(resourceTemplate) + +const projectPrompt = (prompt: PublicMcpSchema.Prompt) => + McpProtocol.transcode(PublicMcpSchema.Prompt, McpSchema.Prompt, prompt) + +const projectTool = (tool: PublicMcpSchema.Tool) => McpProtocol.transcode(PublicMcpSchema.Tool, McpSchema.Tool, tool) + +const privateStaleCache = { + ttlMs: 0, + cacheScope: "private" +} satisfies { readonly ttlMs: number; readonly cacheScope: "private" } + +const projectCompleteResult = Effect.fnUntraced(function*( + value: ObjectWithUndefined, + serverInfo: typeof McpSchema.Implementation.Type +) { + const encodedServerInfo = yield* encodeImplementation(serverInfo) + return { + ...omitUndefined(value), + _meta: resultMetadata(value, encodedServerInfo), + resultType: "complete" + } +}) + +type ProtocolError = + | McpCore.ResourceNotFound + | McpCore.PromptNotFound + | McpCore.ToolError + | McpCore.UnsupportedByProtocol + | McpProtocol.ProtocolError + | PublicMcpSchema.McpError + | Schema.SchemaError + +const matchedProtocolError = Match.type().pipe( + Match.tag("ResourceNotFound", (error) => + new McpProtocol.ProtocolError({ + code: McpSchema.INVALID_PARAMS, + message: `Resource '${error.uri}' not found`, + data: { uri: error.uri } + })), + Match.tag( + "ToolNotFound", + "InvalidToolInput", + "InvalidToolContinuation", + "ToolExecutionError", + "UnsupportedByProtocol", + McpProtocol.ProtocolError.fromTool + ), + Match.orElse(McpProtocol.ProtocolError.fromFeature) +) + +const projectError = (error: ProtocolError): McpSchema.McpError => { + const protocolError = matchedProtocolError(error) + return McpSchema.McpError.make({ + code: protocolError.code, + message: protocolError.message, + ...(isJson(protocolError.data) ? { data: protocolError.data } : {}) + }) +} + +const failWithHttpStatus = Effect.fnUntraced(function*(status: number, error: McpProtocol.ProtocolError) { + const request = yield* Effect.serviceOption(HttpServerRequest.HttpServerRequest) + if (Option.isSome(request)) { + appendPreResponseHandlerUnsafe( + request.value, + (_request, response) => Effect.succeed(HttpServerResponse.setStatus(response, status)) + ) + } + return yield* error +}) + +/** + * @internal + */ +export const normalizeCancellation = (payload: unknown) => + decodeCancellation(payload).pipe( + Effect.map((request) => ({ + requestId: request.requestId, + reason: request.reason, + metadata: request._meta + })) + ) + +const unsupported = ( + operation: PublicMcpSchema.McpReverseOperationUnsupported["operation"] +): PublicMcpSchema.McpReverseOperationUnsupported => + new PublicMcpSchema.McpReverseOperationUnsupported({ + operation, + protocolVersion: McpSchema.protocolVersion, + reason: "MCP 2026-07-28 carries server input requests in multi round-trip results" + }) + +/** + * @internal + */ +export const handlerRpcs = McpSchema.ClientRequestRpcs.merge(McpSchema.ClientNotificationRpcs) + +/** + * @internal + */ +export const makeHandlers = ( + core: McpCore.McpCore, + _lifecycle: McpProtocol.LifecycleRuntime | undefined, + context: McpProtocol.HandlerInstallationContext +) => { + const decodeInputResponses = Schema.decodeUnknownEffect(InputResponses) + const decodeDiscoverResult = Schema.decodeUnknownEffect(McpSchema.DiscoverResult) + const decodeListResourcesResult = Schema.decodeUnknownEffect(McpSchema.ListResourcesResult) + const decodeListResourceTemplatesResult = Schema.decodeUnknownEffect(McpSchema.ListResourceTemplatesResult) + const decodeReadResourceResult = Schema.decodeUnknownEffect(McpSchema.ReadResourceResult) + const decodeListPromptsResult = Schema.decodeUnknownEffect(McpSchema.ListPromptsResult) + const decodeGetPromptResult = Schema.decodeUnknownEffect(McpSchema.GetPromptResult) + const decodePromptOutcome = Schema.decodeUnknownEffect(Schema.Union([ + McpSchema.GetPromptResult, + McpSchema.InputRequiredResult + ])) + const decodeCompleteResult = Schema.decodeUnknownEffect(McpSchema.CompleteResult) + const decodeListToolsResult = Schema.decodeUnknownEffect(McpSchema.ListToolsResult) + const sendNotification = context.sendNotification + const supportsSubscriptions = sendNotification !== undefined + // Older adapters accept arbitrary JSON; this binding requires extension settings objects. + const extensions = context.serverInfo.extensions === undefined ? undefined : Record.filter( + context.serverInfo.extensions, + (value): value is Schema.JsonObject => Predicate.isReadonlyObject(value) + ) + const getDiscovery = Effect.map(context.registrationPresence, (presence) => { + const capabilities: Types.Mutable = { completions: {}, logging: {} } + if (extensions !== undefined) capabilities.extensions = extensions + if (presence.tools) capabilities.tools = { listChanged: supportsSubscriptions } + if (presence.resources) { + capabilities.resources = { listChanged: supportsSubscriptions, subscribe: supportsSubscriptions } + } + if (presence.prompts) capabilities.prompts = { listChanged: supportsSubscriptions } + return { supportedVersions: context.supportedVersions, capabilities, serverInfo: context.serverInfo } + }) + const getInvocation = PublicMcpSchema.McpRequestContext.useSync( + McpProtocol.invocationFromRequestContext + ) + const getInputInvocation = Effect.fnUntraced(function*( + request: Pick + ) { + const context = yield* PublicMcpSchema.McpRequestContext + const inputResponses = request.inputResponses === undefined + ? undefined + // The RPC payload already validated the dated response union; this decode only erases it into canonical JSON. + : yield* decodeInputResponses(request.inputResponses).pipe(Effect.orDie) + return McpProtocol.invocationFromRequestContext(PublicMcpSchema.McpRequestContext.of({ + ...context, + inputResponses, + requestState: request.requestState + })) + }) + const validateInputRequestCapabilities = Effect.fnUntraced(function*( + inputRequired: McpCore.InputRequiredFields, + capabilities: McpCore.NegotiatedProtocolProfile["clientCapabilities"] + ) { + const requiredCapabilities = requiredCapabilitiesForInputRequests(inputRequired.inputRequests, capabilities) + if (Object.keys(requiredCapabilities).length === 0) { + return + } + return yield* failWithHttpStatus( + 400, + new McpProtocol.ProtocolError({ + code: McpSchema.MISSING_REQUIRED_CLIENT_CAPABILITY, + message: "The request requires client capabilities that were not declared", + data: { requiredCapabilities } + }) + ) + }) + return ({ + "subscriptions/listen": Effect.fnUntraced(function*( + request: typeof McpSchema.SubscriptionsListen.payloadSchema.Type, + { client, requestId }: { readonly client: Rpc.ServerClient; readonly requestId: RpcMessage.RequestId } + ) { + if (sendNotification === undefined) { + return yield* new McpProtocol.ProtocolError({ + code: McpSchema.METHOD_NOT_FOUND, + message: "Method not found: subscriptions/listen" + }) + } + const presence = yield* context.registrationPresence + const eventsScope = yield* Scope.fork(yield* Effect.scope) + const events = yield* Scope.provide(context.subscribeServerNotifications, eventsScope) + const honored = { + ...(presence.tools && request.notifications.toolsListChanged === true + ? { toolsListChanged: true } + : {}), + ...(presence.prompts && request.notifications.promptsListChanged === true + ? { promptsListChanged: true } + : {}), + ...(presence.resources && request.notifications.resourcesListChanged === true + ? { resourcesListChanged: true } + : {}), + ...(presence.resources && request.notifications.resourceSubscriptions !== undefined + ? { resourceSubscriptions: request.notifications.resourceSubscriptions } + : {}) + } + const subscriptionMetadata = { "io.modelcontextprotocol/subscriptionId": requestId } + const resourceSubscriptions = new Set(honored.resourceSubscriptions) + const pending = yield* Queue.dropping( + MAX_PENDING_SUBSCRIPTION_NOTIFICATIONS + ) + const overflowed = yield* Deferred.make() + yield* Effect.gen(function*() { + while (true) { + const event = yield* PubSub.take(events) + if (event.targetClientId !== undefined && event.targetClientId !== client.id) { + continue + } + const notification = event.notification + const rpc: Rpc.AnyWithProps | undefined = Match.value(notification).pipe( + Match.tags({ + ToolsChanged: () => honored.toolsListChanged === true ? McpSchema.ToolListChangedNotification : undefined, + PromptsChanged: () => + honored.promptsListChanged === true ? McpSchema.PromptListChangedNotification : undefined, + ResourcesChanged: () => + honored.resourcesListChanged === true ? McpSchema.ResourceListChangedNotification : undefined, + ResourceUpdated: (notification) => + resourceSubscriptions.has(notification.uri) ? McpSchema.ResourceUpdatedNotification : undefined + }), + Match.exhaustive + ) + if (rpc === undefined) { + continue + } + const projected = { + tag: rpc._tag, + payload: rpc.payloadSchema.make({ + _meta: { ...notification.metadata, ...subscriptionMetadata }, + ...(notification._tag === "ResourceUpdated" ? { uri: notification.uri } : {}) + }) + } + if (!(yield* Queue.offer(pending, projected))) { + break + } + } + }).pipe( + // Release the upstream listener on overflow even while transport writes remain blocked. + Scope.use(eventsScope), + Effect.andThen(Effect.suspend(() => context.markSubscriptionCancelled?.(client.id, requestId) ?? Effect.void)), + Effect.andThen(Deferred.succeed(overflowed, undefined)), + Effect.forkScoped + ) + yield* sendNotification(McpSchema.protocolVersion, client.id, { + tag: McpSchema.SubscriptionsAcknowledgedNotification._tag, + payload: McpSchema.SubscriptionsAcknowledgedNotification.payloadSchema.make({ + _meta: subscriptionMetadata, + notifications: honored + }) + }) + const deliver = Effect.forever( + Queue.take(pending).pipe( + Effect.flatMap((notification) => sendNotification(McpSchema.protocolVersion, client.id, notification)) + ) + ) + yield* Effect.race(deliver, Deferred.await(overflowed)) + yield* context.terminateSubscription?.( + McpSchema.protocolVersion, + client.id, + requestId, + "Pending notification limit exceeded" + ) ?? Effect.void + const encodedServerInfo = yield* encodeImplementation(context.serverInfo) + return McpSchema.SubscriptionsListenResult.make({ + _meta: { + ...subscriptionMetadata, + "io.modelcontextprotocol/serverInfo": encodedServerInfo + }, + resultType: "complete" + }) + }, Effect.mapError(projectError)), + "server/discover": Effect.fnUntraced(function*( + _request: typeof McpSchema.Discover.payloadSchema.Type + ) { + const discovery = yield* getDiscovery + const result = yield* projectCompleteResult({ + ...privateStaleCache, + supportedVersions: Array.from(discovery.supportedVersions), + capabilities: discovery.capabilities, + instructions: context.serverInfo.instructions + }, context.serverInfo) + return yield* decodeDiscoverResult(result) + }, Effect.mapError(projectError)), + "resources/list": Effect.fnUntraced(function*( + _request: typeof McpSchema.ListResources.payloadSchema.Type + ) { + const invocation = yield* getInvocation + const resources = yield* core.resources.list(invocation.protocol) + const result = yield* projectCompleteResult({ + ...privateStaleCache, + resources: resources.map(projectResource) + }, context.serverInfo) + return yield* decodeListResourcesResult(result) + }, Effect.mapError(projectError)), + "resources/templates/list": Effect.fnUntraced(function*( + _request: typeof McpSchema.ListResourceTemplates.payloadSchema.Type + ) { + const invocation = yield* getInvocation + const resourceTemplates = yield* core.resources.listTemplates(invocation.protocol) + const result = yield* projectCompleteResult({ + ...privateStaleCache, + resourceTemplates: resourceTemplates.map(projectResourceTemplate) + }, context.serverInfo) + return yield* decodeListResourceTemplatesResult(result) + }, Effect.mapError(projectError)), + "resources/read": Effect.fnUntraced(function*(request: typeof McpSchema.ReadResource.payloadSchema.Type) { + const invocation = yield* getInputInvocation(request) + const read = yield* core.resources.read(request.uri, invocation) + const contents = read.contents.map(projectResourceContents) + const result = yield* projectCompleteResult({ + ...privateStaleCache, + contents, + _meta: read._meta + }, context.serverInfo) + return yield* decodeReadResourceResult(result) + }, Effect.mapError(projectError)), + "prompts/list": Effect.fnUntraced(function*( + _request: typeof McpSchema.ListPrompts.payloadSchema.Type + ) { + const presence = yield* context.registrationPresence + if (!presence.prompts) { + return yield* failWithHttpStatus( + 404, + new McpProtocol.ProtocolError({ + code: PublicMcpSchema.METHOD_NOT_FOUND_ERROR_CODE, + message: "Method not found" + }) + ) + } + const invocation = yield* getInvocation + const prompts = yield* core.prompts.list(invocation.protocol) + const projectedPrompts = yield* Effect.forEach(prompts, projectPrompt) + const result = yield* projectCompleteResult({ + ...privateStaleCache, + prompts: projectedPrompts + }, context.serverInfo) + return yield* decodeListPromptsResult(result) + }, Effect.mapError(projectError)), + "prompts/get": Effect.fnUntraced(function*(request: typeof McpSchema.GetPrompt.payloadSchema.Type) { + const invocation = yield* getInputInvocation(request) + const outcome = yield* core.prompts.get(request.name, request.arguments ?? {}, invocation) + if (outcome._tag === "InputRequired") { + yield* validateInputRequestCapabilities(outcome, invocation.protocol.clientCapabilities) + const encodedServerInfo = yield* encodeImplementation(context.serverInfo) + return yield* decodePromptOutcome(projectInputRequired(outcome, encodedServerInfo)) + } + const prompt = outcome.value + const messages = prompt.messages.map((message) => ({ + role: message.role, + content: projectContent(message.content) + })) + const result = yield* projectCompleteResult({ + description: prompt.description, + messages, + _meta: prompt._meta + }, context.serverInfo) + return yield* decodeGetPromptResult(result) + }, Effect.mapError(projectError)), + "completion/complete": Effect.fnUntraced(function*( + request: typeof McpSchema.Complete.payloadSchema.Type + ) { + const invocation = yield* getInvocation + const completion = yield* core.completions.complete({ + reference: request.ref.type === "ref/prompt" + ? { type: "prompt", name: request.ref.name, title: request.ref.title } + : { type: "resourceTemplate", uriTemplate: request.ref.uri }, + argument: request.argument, + context: request.context?.arguments === undefined + ? undefined + : { arguments: request.context.arguments }, + metadata: request._meta + }, invocation) + const result = yield* projectCompleteResult({ + completion: omitUndefined({ + values: Array.from(completion.values), + total: completion.total, + hasMore: completion.hasMore + }), + _meta: completion.metadata + }, context.serverInfo) + return yield* decodeCompleteResult(result) + }, Effect.mapError(projectError)), + "tools/list": Effect.fnUntraced(function*( + _request: typeof McpSchema.ListTools.payloadSchema.Type + ) { + const invocation = yield* getInvocation + const tools = yield* core.tools.list(invocation.protocol) + const projectedTools = yield* Effect.forEach(tools, projectTool) + const result = yield* projectCompleteResult({ + ...privateStaleCache, + tools: projectedTools + }, context.serverInfo) + return yield* decodeListToolsResult(result) + }, Effect.mapError(projectError)), + "tools/call": Effect.fnUntraced(function*(request: typeof McpSchema.CallTool.payloadSchema.Type) { + const invocation = yield* getInputInvocation(request) + const tool = (yield* core.tools.list(invocation.protocol)).find((tool) => tool.name === request.name) + const httpRequest = yield* Effect.serviceOption(HttpServerRequest.HttpServerRequest) + + if (tool !== undefined && Option.isSome(httpRequest)) { + const pending: Array<{ readonly schema: unknown; readonly argument: unknown; readonly path: string }> = [ + { schema: tool.inputSchema, argument: request.arguments, path: "" } + ] + // Only properties paths may mirror headers; composition, arrays, and references do not qualify. + while (pending.length > 0) { + const { schema, argument, path } = pending.pop()! + if (!Predicate.isReadonlyObject(schema)) continue + const annotation = schema["x-mcp-header"] + if (typeof annotation === "string") { + const headerName = `mcp-param-${annotation.toLowerCase()}` + if (!parameterHeaderMatches(httpRequest.value.headers[headerName], argument)) { + return yield* failWithHttpStatus( + 400, + new McpProtocol.ProtocolError({ + code: PublicMcpSchema.HEADER_MISMATCH_ERROR_CODE, + message: `${headerName} does not match argument '${path}'` + }) + ) + } + } + if (Predicate.isReadonlyObject(schema.properties)) { + for (const [name, property] of Object.entries(schema.properties)) { + pending.push({ + schema: property, + argument: Predicate.isReadonlyObject(argument) && Object.hasOwn(argument, name) + ? argument[name] + : undefined, + path: path === "" ? name : `${path}.${name}` + }) + } + } + } + } + + const outcome = yield* core.tools.call({ name: request.name, arguments: request.arguments ?? {} }, invocation) + .pipe( + Effect.catchTag( + ["ToolExecutionError", "InvalidToolInput"], + (error) => + Effect.succeed(McpCore.OperationOutcome.Complete(PublicMcpSchema.CallToolResult.make({ + content: [PublicMcpSchema.TextContent.make({ type: "text", text: error.message })], + isError: true + }))) + ) + ) + if (outcome._tag === "InputRequired") { + yield* validateInputRequestCapabilities(outcome, invocation.protocol.clientCapabilities) + return yield* projectCallToolOutcome(outcome, context.serverInfo) + } + const toolResult = outcome.value + const content = toolResult.content.map(projectContent) + return yield* projectCallToolOutcome( + McpCore.OperationOutcome.Complete({ + content, + structuredContent: toolResult.structuredContent, + isError: toolResult.isError, + _meta: toolResult._meta + }), + context.serverInfo + ) + }, Effect.mapError(projectError)), + "notifications/cancelled": Effect.fnUntraced(function*( + _request: typeof McpSchema.CancelledNotification.payloadSchema.Type + ) { + return yield* Effect.void + }) + }) +} + +const runtime = { + _tag: "Stateless", + transport: { + jsonRpc: { acceptsBatches: false }, + http: {} + }, + profileFromRequestMetadata +} as const + +/** + * @internal + */ +export const protocol = McpProtocol.make({ + protocolVersion: McpSchema.protocolVersion, + runtime, + clientRpcs: McpSchema.ClientRpcs, + clientNotificationRpcs: McpSchema.ClientNotificationRpcs, + serverRequestRpcs: McpSchema.ServerRequestRpcs, + serverNotificationRpcs: McpSchema.ServerNotificationRpcs, + handlerRpcs, + makeHandlers, + toReverseClient: () => ({ + listRoots: () => Effect.fail(unsupported("roots/list")), + createMessage: () => Effect.fail(unsupported("sampling/createMessage")), + elicit: () => Effect.fail(unsupported("elicitation/create")) + }), + projectNotification: (notification) => + McpProtocol.isSubscriptionServerNotification(notification) + // @effect-diagnostics-next-line effectSucceedWithVoid:off the projector requires the value `undefined` + ? Effect.succeed(undefined) + : McpProtocol.makeNotificationProjector({ + supportsProgressMessage: true + }, notification), + normalizeCancellation +}) diff --git a/repos/effect/packages/effect/src/unstable/ai/internal/mcpProtocolRegistry.ts b/repos/effect/packages/effect/src/ai/internal/mcpProtocolRegistry.ts similarity index 85% rename from repos/effect/packages/effect/src/unstable/ai/internal/mcpProtocolRegistry.ts rename to repos/effect/packages/effect/src/ai/internal/mcpProtocolRegistry.ts index 0f6762d145..b992876e45 100644 --- a/repos/effect/packages/effect/src/unstable/ai/internal/mcpProtocolRegistry.ts +++ b/repos/effect/packages/effect/src/ai/internal/mcpProtocolRegistry.ts @@ -1,6 +1,6 @@ -import type { NonEmptyReadonlyArray } from "../../../Array.ts" -import * as Cause from "../../../Cause.ts" -import * as Effect from "../../../Effect.ts" +import type { NonEmptyReadonlyArray } from "../../Array.ts" +import * as Cause from "../../Cause.ts" +import * as Effect from "../../Effect.ts" import type * as Rpc from "../../rpc/Rpc.ts" import type * as RpcGroup from "../../rpc/RpcGroup.ts" import type * as RpcMessage from "../../rpc/RpcMessage.ts" @@ -13,26 +13,32 @@ const prefix = (protocol: { readonly protocolVersion: string }): string => `@effect/mcp/${encodeURIComponent(protocol.protocolVersion)}/` -/** @internal */ +/** + * @internal + */ export interface ProtocolRegistry< Protocol extends McpProtocol.AnyProtocolAdapter = McpProtocol.AnyProtocolAdapter > { readonly protocols: NonEmptyReadonlyArray readonly clientRpcs: AnyRpcGroup readonly select: (offeredVersion: string) => Protocol + readonly protocolForInternalTag: (tag: string) => Protocol readonly routeClientRequest: ( protocol: Protocol, request: RpcMessage.RequestEncoded ) => RpcMessage.RequestEncoded readonly handlerTarget: ( - contextMap: Map + contextMap: Map, + context: McpProtocolInternal.HandlerInstallationContext ) => McpProtocolInternal.HandlerInstallationTarget } // NOTE: Protocol selection and request namespacing happen before an adapter's // payload codec decodes the request. Canonical McpSchema value reuse must not // introduce a shared permissive decode-first path. -/** @internal */ +/** + * @internal + */ export const make = Effect.fnUntraced(function*< const Protocols extends NonEmptyReadonlyArray >( @@ -65,6 +71,8 @@ export const make = Effect.fnUntraced(function*< protocols: snapshot, clientRpcs, select: (offeredVersion: string) => byVersion.get(offeredVersion) ?? snapshot[0], + protocolForInternalTag: (tag: string) => + snapshot.find((protocol) => tag.startsWith(prefix(protocol))) ?? snapshot[0], routeClientRequest: ( protocol: Protocol, request: RpcMessage.RequestEncoded @@ -72,7 +80,8 @@ export const make = Effect.fnUntraced(function*< ...request, tag: `${prefix(protocol)}${request.tag}` }), - handlerTarget: (contextMap: Map): McpProtocolInternal.HandlerInstallationTarget => ({ + handlerTarget: (contextMap, context): McpProtocolInternal.HandlerInstallationTarget => ({ + context, install: Effect.fnUntraced(function*< Rpcs extends Rpc.Any, Handlers extends RpcGroup.HandlersFrom diff --git a/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts b/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts new file mode 100644 index 0000000000..b388d17805 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpRuntime.ts @@ -0,0 +1,572 @@ +/** + * Runtime descriptors for MCP protocol adapters. + * + * @internal + */ +import type { NonEmptyReadonlyArray } from "../../Array.ts" +import * as Cause from "../../Cause.ts" +import * as Context from "../../Context.ts" +import * as Effect from "../../Effect.ts" +import type * as Headers from "../../http/Headers.ts" +import { appendPreResponseHandlerUnsafe } from "../../http/HttpEffect.ts" +import * as HttpServerRequest from "../../http/HttpServerRequest.ts" +import * as HttpServerResponse from "../../http/HttpServerResponse.ts" +import * as Layer from "../../Layer.ts" +import type * as LogLevel from "../../LogLevel.ts" +import * as Predicate from "../../Predicate.ts" +import * as Result from "../../Result.ts" +import * as RpcGroup from "../../rpc/RpcGroup.ts" +import type * as RpcMessage from "../../rpc/RpcMessage.ts" +import type * as PublicMcpProtocol from "../McpProtocol.ts" +import * as PublicMcpSchema from "../McpSchema.ts" +import type * as McpCore from "./mcpCore.ts" +import * as McpProtocol from "./mcpProtocol.ts" +import * as McpProtocolRegistry from "./mcpProtocolRegistry.ts" +import * as McpStatefulRuntime from "./mcpStatefulRuntime.ts" + +const MCP_SESSION_ID_HEADER = "mcp-session-id" +const MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version" +const MCP_METHOD_HEADER = "mcp-method" +const MCP_NAME_HEADER = "mcp-name" +const PROTOCOL_VERSION_METADATA_KEY = "io.modelcontextprotocol/protocolVersion" +const CLIENT_CAPABILITIES_METADATA_KEY = "io.modelcontextprotocol/clientCapabilities" +const UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE = -32022 + +const asRecord = (input: unknown): Record | undefined => + typeof input === "object" && input !== null ? input as Record : undefined + +const protocolVersionClaim = (input: unknown): { readonly present: boolean; readonly value: unknown } => { + const metadata = asRecord(input) + return metadata !== undefined && PROTOCOL_VERSION_METADATA_KEY in metadata + ? { present: true, value: metadata[PROTOCOL_VERSION_METADATA_KEY] } + : { present: false, value: undefined } +} + +/** + * @internal + */ +export const hasRequestProtocolVersion = (input: unknown): boolean => + protocolVersionClaim(asRecord(asRecord(input)?.params)?._meta).present + +const routingNameKey = (method: string): "name" | "uri" | undefined => { + switch (method) { + case "tools/call": + case "prompts/get": + return "name" + case "resources/read": + return "uri" + default: + return undefined + } +} + +const headerMismatch = (message: string): HttpProtocolSelection => ({ + _tag: "Rejected", + status: 400, + error: { code: PublicMcpSchema.HEADER_MISMATCH_ERROR_CODE, message } +}) + +const PingRpcs = RpcGroup.make(PublicMcpSchema.Ping) + +/** + * @internal + */ +export interface RequestBinding { + readonly initializePayload: typeof PublicMcpSchema.Initialize.payloadSchema.Type + readonly negotiatedProfile: McpCore.NegotiatedProtocolProfile + readonly protocol: PublicMcpProtocol.AnyProtocolAdapter +} + +/** + * @internal + */ +export interface PreparedRequest { + readonly protocol: PublicMcpProtocol.AnyProtocolAdapter + readonly binding?: RequestBinding | undefined + readonly profile?: McpCore.NegotiatedProtocolProfile | undefined + readonly requestContext?: PublicMcpSchema.McpRequestContext["Service"] | undefined +} + +type HttpProtocolSelection = + | { + readonly _tag: "Accepted" + readonly binding: RequestBinding | undefined + readonly protocol?: PublicMcpProtocol.AnyProtocolAdapter | undefined + } + | { + readonly _tag: "Rejected" + readonly status: 400 | 404 + readonly error?: { + readonly code: number + readonly message: string + readonly data?: unknown + } | undefined + } + +/** + * @internal + */ +export type HttpAdmission = + | { + readonly _tag: "Accepted" + readonly acknowledge: boolean + readonly isSubscription: boolean + } + | { + readonly _tag: "Rejected" + readonly response: HttpServerResponse.HttpServerResponse + } + +/** + * @internal + */ +export interface HandlerInstallationOptions { + readonly core: McpCore.McpCore + readonly subscribeServerNotifications: McpProtocol.HandlerInstallationContext["subscribeServerNotifications"] + readonly sendNotification?: NonNullable + readonly markSubscriptionCancelled?: NonNullable< + McpProtocol.HandlerInstallationContext["markSubscriptionCancelled"] + > + readonly terminateSubscription?: NonNullable + readonly defaultLogLevel: LogLevel.LogLevel + readonly serverInfo: { + readonly name: string + readonly version: string + readonly instructions?: string | undefined + readonly description?: string | undefined + readonly websiteUrl?: string | undefined + readonly icons?: ReadonlyArray | undefined + readonly extensions?: NonNullable | undefined + } +} + +/** + * @internal + */ +export const stateful = ( + transport: PublicMcpProtocol.TransportPolicy +): PublicMcpProtocol.StatefulRuntimeDescriptor => ({ + _tag: "Stateful", + transport +}) + +/** + * @internal + */ +export interface ServerRuntimeShape { + readonly protocols: NonEmptyReadonlyArray + readonly clientRpcs: McpProtocolRegistry.ProtocolRegistry["clientRpcs"] + readonly selectProtocol: (offeredVersion: string) => PublicMcpProtocol.AnyProtocolAdapter + readonly protocolForInternalTag: (tag: string) => PublicMcpProtocol.AnyProtocolAdapter + readonly routeClientRequest: ( + protocol: PublicMcpProtocol.AnyProtocolAdapter, + request: RpcMessage.RequestEncoded + ) => RpcMessage.RequestEncoded + readonly prepareRequest: ( + clientId: number, + headers: Headers.Headers, + request: RpcMessage.RequestEncoded + ) => Effect.Effect + readonly resolveRequest: (clientId: number, headers: Headers.Headers) => RequestBinding | undefined + readonly admitHttp: (headers: Headers.Headers, input: Result.Result) => HttpAdmission + readonly effectLogLevel: ( + clientId: number, + headers: Headers.Headers, + fallback: LogLevel.LogLevel + ) => LogLevel.LogLevel + readonly disconnect: (clientId: number) => void + readonly deliveryClientIds: () => Iterable + readonly canDeliver: ( + clientId: number, + headers: Headers.Headers, + notification: McpCore.ServerNotification, + fallbackLogLevel: LogLevel.LogLevel + ) => boolean + readonly installHandlers: ( + options: HandlerInstallationOptions + ) => Effect.Effect, never, unknown> +} + +/** + * @internal + */ +export class ServerRuntime extends Context.Service()( + "effect/ai/McpRuntime/ServerRuntime" +) {} + +/** + * @internal + */ +export const selectStatefulProtocol = ( + protocols: ReadonlyArray, + offeredVersion: unknown +): Protocol | undefined => + protocols.find((protocol) => protocol.runtime._tag === "Stateful" && protocol.protocolVersion === offeredVersion) ?? + protocols.find((protocol) => protocol.runtime._tag === "Stateful") + +/** + * @internal + */ +export const make = Effect.fnUntraced(function*( + protocols: NonEmptyReadonlyArray +) { + const statefulProtocol = protocols.find((protocol) => protocol.runtime._tag === "Stateful") + const stateful = statefulProtocol === undefined ? undefined : McpStatefulRuntime.make() + const protocolVersions = protocols.map((protocol) => protocol.protocolVersion) + let statelessDescriptor: PublicMcpProtocol.StatelessRuntimeDescriptor | undefined + let statelessProtocol: PublicMcpProtocol.AnyProtocolAdapter | undefined + for (const protocol of protocols) { + if (protocol.runtime._tag !== "Stateless") { + continue + } + if (statelessDescriptor !== undefined) { + return yield* new Cause.IllegalArgumentError( + "MCP runtime supports at most one stateless protocol" + ) + } + statelessDescriptor = protocol.runtime + statelessProtocol = protocol + } + const registry = yield* McpProtocolRegistry.make(protocols) + const selectHttpProtocol = (headers: Headers.Headers, input: unknown): HttpProtocolSelection => { + const protocolVersion = headers[MCP_PROTOCOL_VERSION_HEADER] + const sessionId = headers[MCP_SESSION_ID_HEADER] + const inputRecord = asRecord(input) + const metadata = asRecord(asRecord(inputRecord?.params)?._meta) + const claim = protocolVersionClaim(metadata) + const id = inputRecord?.id + const isInitialize = inputRecord?.jsonrpc === "2.0" && inputRecord.method === "initialize" && + (typeof id === "string" || typeof id === "number") + const isCancellationNotification = inputRecord?.jsonrpc === "2.0" && + inputRecord.method === "notifications/cancelled" && id === undefined + const isStatelessRequest = claim.present || ((!isInitialize || stateful === undefined) && + statelessProtocol !== undefined && + (protocolVersion === statelessProtocol.protocolVersion || + (stateful === undefined && inputRecord?.jsonrpc === "2.0" && + typeof inputRecord.method === "string" && (typeof id === "string" || typeof id === "number")))) + if (isStatelessRequest) { + if (protocolVersion === undefined) { + return headerMismatch("MCP-Protocol-Version header is required") + } + if (!isCancellationNotification && (metadata === undefined || typeof claim.value !== "string")) { + return { + _tag: "Rejected", + status: 400, + error: { + code: PublicMcpSchema.INVALID_PARAMS_ERROR_CODE, + message: "Required request metadata is missing" + } + } + } + if ((!isCancellationNotification || claim.present) && claim.value !== protocolVersion) { + return headerMismatch("MCP-Protocol-Version header does not match request metadata") + } + if (!isCancellationNotification && asRecord(metadata?.[CLIENT_CAPABILITIES_METADATA_KEY]) === undefined) { + return { + _tag: "Rejected", + status: 400, + error: { + code: PublicMcpSchema.INVALID_PARAMS_ERROR_CODE, + message: `${CLIENT_CAPABILITIES_METADATA_KEY} request metadata is required` + } + } + } + if (statelessProtocol === undefined || protocolVersion !== statelessProtocol.protocolVersion) { + return { + _tag: "Rejected", + status: 400, + error: { + code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, + message: `Unsupported protocol version '${protocolVersion}'`, + data: { + supported: protocolVersions, + requested: protocolVersion + } + } + } + } + const method = inputRecord?.method + if (typeof method !== "string" || headers[MCP_METHOD_HEADER] !== method) { + return headerMismatch("Mcp-Method header does not match request method") + } + const nameKey = routingNameKey(method) + if (nameKey !== undefined) { + const name = asRecord(inputRecord?.params)?.[nameKey] + const header = headers[MCP_NAME_HEADER] + if (typeof name !== "string" || header === undefined || McpProtocol.decodeRoutingHeader(header) !== name) { + return headerMismatch("Mcp-Name header does not match request parameters") + } + } + return { _tag: "Accepted", binding: undefined, protocol: statelessProtocol } + } + const binding = sessionId === undefined ? undefined : stateful?.resolveSessionId(sessionId) + if (sessionId !== undefined && binding === undefined) { + return { _tag: "Rejected", status: 404 } + } + if ( + !isInitialize && + protocolVersion !== undefined && + !registry.protocols.some((protocol) => protocol.protocolVersion === protocolVersion) + ) { + return { _tag: "Rejected", status: 400 } + } + if ( + !isInitialize && + binding?.protocol.runtime.transport.http.requiresVersionHeader === true && + protocolVersion !== binding.protocol.protocolVersion + ) { + return { _tag: "Rejected", status: 400 } + } + return { _tag: "Accepted", binding, protocol: binding?.protocol } + } + return ServerRuntime.of({ + protocols: registry.protocols, + clientRpcs: registry.clientRpcs, + selectProtocol: registry.select, + protocolForInternalTag: registry.protocolForInternalTag, + routeClientRequest: registry.routeClientRequest, + prepareRequest: Effect.fnUntraced(function*(clientId, headers, request) { + const metadata = asRecord(request.payload)?._meta + const claim = protocolVersionClaim(metadata) + const requestedVersion = typeof claim.value === "string" ? claim.value : undefined + const binding = claim.present ? undefined : stateful?.resolve(clientId, headers) + let protocol: PublicMcpProtocol.AnyProtocolAdapter + if (claim.present) { + protocol = registry.protocols.find((protocol) => protocol.protocolVersion === requestedVersion) ?? + statelessProtocol ?? registry.protocols[0] + } else if (binding !== undefined) { + protocol = binding.protocol + } else if (request.tag === "initialize") { + const offeredVersion = (request.payload as any)?.protocolVersion + const selected = selectStatefulProtocol(registry.protocols, offeredVersion) + if (selected === undefined) { + return yield* new McpProtocol.ProtocolError({ + code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, + message: `initialize is not supported by the configured MCP protocols (requested '${offeredVersion}')` + }) + } + protocol = selected + } else if (request.tag === "ping") { + // An unversioned ping may precede initialize, even with a stateless adapter first. + protocol = selectStatefulProtocol(registry.protocols, undefined) ?? registry.protocols[0] + } else { + protocol = registry.protocols[0] + } + if (protocol.runtime._tag === "Stateful") { + return { protocol, binding } + } + if (request.isNotification && request.tag === "notifications/cancelled") { + return { protocol } + } + if (statelessDescriptor === undefined) { + return yield* Effect.die("MCP stateless runtime invariant failed") + } + if (requestedVersion !== undefined && requestedVersion !== protocol.protocolVersion) { + return yield* new McpProtocol.ProtocolError({ + code: UNSUPPORTED_PROTOCOL_VERSION_ERROR_CODE, + message: `Unsupported protocol version '${requestedVersion}'`, + data: { supported: protocolVersions, requested: requestedVersion } + }) + } + const decodedProfile = yield* statelessDescriptor.profileFromRequestMetadata(metadata) + const profile: McpCore.NegotiatedProtocolProfile = { + protocolVersion: decodedProfile.protocolVersion, + clientCapabilities: decodedProfile.clientCapabilities, + clientInfo: decodedProfile.clientInfo, + requestMetadata: decodedProfile.requestMetadata + } + const requestContext = PublicMcpSchema.McpRequestContext.of({ + clientId, + protocolVersion: profile.protocolVersion, + clientCapabilities: profile.clientCapabilities, + clientInfo: profile.clientInfo, + requestMetadata: profile.requestMetadata + }) + return { protocol, profile, requestContext } + }), + resolveRequest: (clientId, headers) => stateful?.resolve(clientId, headers), + admitHttp: (headers, parsed) => { + const reject = (status: number, error?: unknown, id: string | number | null = null): HttpAdmission => ({ + _tag: "Rejected", + response: error === undefined + ? HttpServerResponse.empty({ status }) + : HttpServerResponse.jsonUnsafe({ jsonrpc: "2.0", id, error }, { status }) + }) + if (Result.isFailure(parsed)) { + const version = headers[MCP_PROTOCOL_VERSION_HEADER] + if (version !== undefined && !registry.protocols.some((protocol) => protocol.protocolVersion === version)) { + return reject(400) + } + const admission = selectHttpProtocol(headers, undefined) + return admission._tag === "Rejected" && admission.error === undefined + ? reject(admission.status) + : reject(200, new PublicMcpSchema.ParseError({ message: "Parse error" })) + } + const input = parsed.success + if (Array.isArray(input)) { + if (input.length === 0) { + return reject(400, new PublicMcpSchema.InvalidRequest({ message: "Invalid Request" })) + } + const admission = selectHttpProtocol(headers, input) + if ( + admission._tag === "Rejected" || + input.some(hasRequestProtocolVersion) || + input.some((message) => Predicate.hasProperty(message, "method") && message.method === "initialize") || + admission.binding?.protocol.runtime.transport.jsonRpc.acceptsBatches !== true + ) { + return reject(400) + } + return { + _tag: "Accepted", + acknowledge: !input.some((message) => + Predicate.hasProperty(message, "method") && Predicate.hasProperty(message, "id") + ), + isSubscription: false + } + } + const hasId = Predicate.hasProperty(input, "id") + const id = hasId && (typeof input.id === "string" || typeof input.id === "number") ? input.id : null + const isJsonRpc = Predicate.hasProperty(input, "jsonrpc") && input.jsonrpc === "2.0" + const hasValidRequestId = !hasId || typeof input.id === "string" || typeof input.id === "number" + const isRequest = isJsonRpc && hasValidRequestId && + Predicate.hasProperty(input, "method") && typeof input.method === "string" + const hasValidResponseId = hasId && + (typeof input.id === "string" || typeof input.id === "number" || input.id === null) + const hasResult = Predicate.hasProperty(input, "result") + const hasError = Predicate.hasProperty(input, "error") + const isResponse = isJsonRpc && hasValidResponseId && hasResult !== hasError + const admission = selectHttpProtocol(headers, input) + if (admission._tag === "Rejected") { + return reject(admission.status, admission.error, id) + } + if (!isRequest && !isResponse) { + return reject(200, new PublicMcpSchema.InvalidRequest({ message: "Invalid Request" }), id) + } + const isInitialize = Predicate.hasProperty(input, "method") && input.method === "initialize" + const hasSession = headers[MCP_SESSION_ID_HEADER] !== undefined + if (isInitialize ? hasSession : !hasSession && admission.protocol?.runtime._tag !== "Stateless") { + return reject(400) + } + if ( + isRequest && admission.protocol?.runtime._tag === "Stateless" && + !((admission.protocol as unknown as McpProtocol.ProtocolAdapter).handlerRpcs?.requests.has( + input.method as string + ) ?? + admission.protocol.clientRpcs.requests.has(input.method as string)) + ) { + return reject(404, new PublicMcpSchema.MethodNotFound({ message: `Method not found: ${input.method}` }), id) + } + return { + _tag: "Accepted", + acknowledge: !isRequest || !hasId, + isSubscription: Predicate.hasProperty(input, "method") && input.method === "subscriptions/listen" + } + }, + effectLogLevel: (clientId, headers, fallback) => stateful?.effectLogLevel(clientId, headers, fallback) ?? fallback, + disconnect: (clientId) => stateful?.disconnect(clientId), + deliveryClientIds: () => stateful?.initializedClientIds() ?? [], + canDeliver: (clientId, headers, notification, fallback) => + stateful?.canDeliver(clientId, headers, notification, fallback) ?? true, + installHandlers: Effect.fnUntraced(function*(options) { + const contextMap = new Map() + const installationContext: McpProtocol.HandlerInstallationContext = { + subscribeServerNotifications: options.subscribeServerNotifications, + ...(options.sendNotification === undefined ? {} : { sendNotification: options.sendNotification }), + ...(options.markSubscriptionCancelled === undefined + ? {} + : { markSubscriptionCancelled: options.markSubscriptionCancelled }), + ...(options.terminateSubscription === undefined + ? {} + : { terminateSubscription: options.terminateSubscription }), + supportedVersions: protocolVersions, + serverInfo: options.serverInfo, + registrationPresence: options.core.registrationPresence + } + const handlerTarget = registry.handlerTarget(contextMap, installationContext) + for (const protocol of registry.protocols) { + if (protocol.runtime._tag === "Stateless") { + yield* protocol.installHandlers(options.core, undefined, handlerTarget) + continue + } + if (stateful === undefined) { + return yield* Effect.die("MCP sessionful runtime invariant failed") + } + yield* handlerTarget.install(protocol, PingRpcs, PingRpcs.of({ ping: () => Effect.succeed({}) })) + const lifecycle: McpProtocol.LifecycleRuntime = { + initialize: Effect.fnUntraced(function*(protocolVersion, profile, clientId) { + const presence = yield* options.core.registrationPresence + return yield* Effect.withFiber((fiber) => { + const httpRequest = Context.getOrUndefined(fiber.context, HttpServerRequest.HttpServerRequest) + const capabilities: McpCore.CanonicalServerCapabilities = { + completions: true, + logging: true, + ...(presence.tools ? { tools: { listChanged: true } } : {}), + ...(presence.resources + ? { resources: { listChanged: true, subscribe: httpRequest === undefined } } + : {}), + ...(presence.prompts ? { prompts: { listChanged: true } } : {}), + ...(options.serverInfo.extensions ? { extensions: options.serverInfo.extensions } : {}) + } + const initializePayload = PublicMcpSchema.Initialize.payloadSchema.make({ + protocolVersion, + capabilities: profile.clientCapabilities, + clientInfo: profile.clientInfo!, + _meta: profile.requestMetadata + }) + const registration: McpStatefulRuntime.Registration = { + initializePayload, + negotiatedProfile: profile, + protocol: protocol as PublicMcpProtocol.ProtocolAdapter, + supportsResourceSubscriptions: httpRequest === undefined && + capabilities.resources?.subscribe === true, + logLevel: options.defaultLogLevel + } + if (httpRequest !== undefined) { + const sessionId = crypto.randomUUID() + stateful.registerHttp(sessionId, registration) + appendPreResponseHandlerUnsafe( + httpRequest, + (_request, response) => + Effect.succeed(HttpServerResponse.setHeaders(response, { + [MCP_SESSION_ID_HEADER]: sessionId, + [MCP_PROTOCOL_VERSION_HEADER]: protocol.protocolVersion + })) + ) + } else { + stateful.registerConnection(clientId, registration) + } + return Effect.succeed({ + capabilities, + instructions: options.serverInfo.instructions, + serverInfo: PublicMcpSchema.Implementation.make({ + name: options.serverInfo.name, + version: options.serverInfo.version, + description: options.serverInfo.description, + websiteUrl: options.serverInfo.websiteUrl, + icons: options.serverInfo.icons + }) + }) + }) + }), + setLogLevel: stateful.setLogLevel, + subscribe: stateful.subscribe, + unsubscribe: stateful.unsubscribe, + clientNotification: Effect.fnUntraced(function*(notification, clientId) { + if (notification._tag === "Initialized") { + stateful.markInitialized(clientId) + } + }) + } + yield* protocol.installHandlers(options.core, lifecycle, handlerTarget) + } + return Context.makeUnsafe(contextMap) + }) + }) +}) + +/** + * @internal + */ +export const layer = ( + protocols: NonEmptyReadonlyArray +): Layer.Layer => Layer.effect(ServerRuntime)(make(protocols)) diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts new file mode 100644 index 0000000000..62c1bf9b8a --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2024_11_05.ts @@ -0,0 +1,753 @@ +/** + * Exact MCP v2024-11-05 wire schemas. + * + * Transport topology is intentionally not represented here. This module owns + * the dated JSON-RPC method payloads and results only. + * + * @internal + */ +import * as Option from "../../../Option.ts" +import * as Rpc from "../../../rpc/Rpc.ts" +import * as RpcGroup from "../../../rpc/RpcGroup.ts" +import * as Schema from "../../../Schema.ts" +import * as SchemaGetter from "../../../SchemaGetter.ts" + +/** + * @internal + */ +export const protocolVersion = "2024-11-05" + +/** + * @internal + */ +export const optional = ( + schema: S +): Schema.decodeTo, Schema.optionalKey> => + Schema.optionalKey(schema).pipe( + Schema.decodeTo(Schema.optional(schema), { + decode: SchemaGetter.passthrough(), + encode: SchemaGetter.transformOptional(Option.flatMap(Option.fromUndefinedOr)) + }) + ) +const JsonObject = Schema.JsonObject + +/** + * @internal + */ +export const RequestId = Schema.Union([Schema.String, Schema.Finite]) +/** + * @internal + */ +export const ProgressToken = Schema.Union([Schema.String, Schema.Finite]) +/** + * @internal + */ +export const Role = Schema.Literals(["user", "assistant"]) +/** + * @internal + */ +export const LoggingLevel = Schema.Literals([ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" +]) + +/** + * @internal + */ +export const RequestMeta = Schema.Struct({ + _meta: optional(Schema.StructWithRest( + Schema.Struct({ + progressToken: optional(ProgressToken) + }), + [Schema.JsonObject] + )) +}) + +/** + * @internal + */ +export const NotificationMeta = Schema.Struct({ + _meta: optional(JsonObject) +}) + +/** + * @internal + */ +export const ResultMeta = Schema.Struct({ + _meta: optional(JsonObject) +}) + +/** + * @internal + */ +export const PaginatedRequest = Schema.Struct({ + ...RequestMeta.fields, + cursor: optional(Schema.String) +}) + +/** + * @internal + */ +export const PaginatedResult = Schema.Struct({ + ...ResultMeta.fields, + nextCursor: optional(Schema.String) +}) + +/** + * @internal + */ +export const Implementation = Schema.Struct({ + name: Schema.String, + version: Schema.String +}) + +/** + * @internal + */ +export const ClientCapabilities = Schema.Struct({ + experimental: optional(Schema.Record(Schema.String, JsonObject)), + roots: optional(Schema.Struct({ + listChanged: optional(Schema.Boolean) + })), + sampling: optional(JsonObject) +}) + +/** + * @internal + */ +export const ServerCapabilities = Schema.Struct({ + experimental: optional(Schema.Record(Schema.String, JsonObject)), + logging: optional(JsonObject), + prompts: optional(Schema.Struct({ + listChanged: optional(Schema.Boolean) + })), + resources: optional(Schema.Struct({ + subscribe: optional(Schema.Boolean), + listChanged: optional(Schema.Boolean) + })), + tools: optional(Schema.Struct({ + listChanged: optional(Schema.Boolean) + })) +}) + +/** + * @internal + */ +export const McpError = Schema.Struct({ + code: Schema.Int, + message: Schema.String, + data: optional(Schema.Any) +}) +/** + * @internal + */ +export type McpError = typeof McpError.Type + +/** + * @internal + */ +export const Annotation = Schema.Struct({ + audience: optional(Schema.Array(Role)), + priority: optional(Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))) +}) + +/** + * @internal + */ +export const TextResourceContents = Schema.Struct({ + uri: Schema.String, + mimeType: optional(Schema.String), + text: Schema.String +}) + +/** + * @internal + */ +export const BlobResourceContents = Schema.Struct({ + uri: Schema.String, + mimeType: optional(Schema.String), + blob: Schema.String +}) + +/** + * @internal + */ +export const ResourceContents = Schema.Union([ + TextResourceContents, + BlobResourceContents +]) + +/** + * @internal + */ +export const TextContent = Schema.Struct({ + type: Schema.Literal("text"), + text: Schema.String, + annotations: optional(Annotation) +}) + +/** + * @internal + */ +export const ImageContent = Schema.Struct({ + type: Schema.Literal("image"), + data: Schema.String, + mimeType: Schema.String, + annotations: optional(Annotation) +}) + +/** + * @internal + */ +export const EmbeddedResource = Schema.Struct({ + type: Schema.Literal("resource"), + resource: ResourceContents, + annotations: optional(Annotation) +}) + +/** + * @internal + */ +export const PromptOrToolContent = Schema.Union([ + TextContent, + ImageContent, + EmbeddedResource +]) + +/** + * @internal + */ +export const SamplingContent = Schema.Union([ + TextContent, + ImageContent +]) + +/** + * @internal + */ +export const Resource = Schema.Struct({ + uri: Schema.String, + name: Schema.String, + description: optional(Schema.String), + mimeType: optional(Schema.String), + size: optional(Schema.Finite), + annotations: optional(Annotation) +}) + +/** + * @internal + */ +export const ResourceTemplate = Schema.Struct({ + uriTemplate: Schema.String, + name: Schema.String, + description: optional(Schema.String), + mimeType: optional(Schema.String), + annotations: optional(Annotation) +}) + +/** + * @internal + */ +export const PromptArgument = Schema.Struct({ + name: Schema.String, + description: optional(Schema.String), + required: optional(Schema.Boolean) +}) + +/** + * @internal + */ +export const Prompt = Schema.Struct({ + name: Schema.String, + description: optional(Schema.String), + arguments: optional(Schema.Array(PromptArgument)) +}) + +/** + * @internal + */ +export const PromptMessage = Schema.Struct({ + role: Role, + content: PromptOrToolContent +}) + +/** + * @internal + */ +export const ToolInputSchema = Schema.Struct({ + type: Schema.Literal("object"), + properties: optional(Schema.Record(Schema.String, JsonObject)), + required: optional(Schema.Array(Schema.String)) +}) + +/** + * @internal + */ +export const Tool = Schema.Struct({ + name: Schema.String, + description: optional(Schema.String), + inputSchema: ToolInputSchema +}) + +/** + * @internal + */ +export const ModelHint = Schema.Struct({ + name: optional(Schema.String) +}) + +/** + * @internal + */ +export const ModelPreferences = Schema.Struct({ + hints: optional(Schema.Array(ModelHint)), + costPriority: optional(Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))), + speedPriority: optional(Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))), + intelligencePriority: optional(Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))) +}) + +/** + * @internal + */ +export const SamplingMessage = Schema.Struct({ + role: Role, + content: SamplingContent +}) + +/** + * @internal + */ +export const ResourceReference = Schema.Struct({ + type: Schema.Literal("ref/resource"), + uri: Schema.String +}) + +/** + * @internal + */ +export const PromptReference = Schema.Struct({ + type: Schema.Literal("ref/prompt"), + name: Schema.String +}) + +/** + * @internal + */ +export const Root = Schema.Struct({ + uri: Schema.String, + name: optional(Schema.String) +}) + +/** + * @internal + */ +export const InitializeResult = Schema.Struct({ + ...ResultMeta.fields, + protocolVersion: Schema.String, + capabilities: ServerCapabilities, + serverInfo: Implementation, + instructions: optional(Schema.String) +}) + +/** + * @internal + */ +export const ListResourcesResult = Schema.Struct({ + ...PaginatedResult.fields, + resources: Schema.Array(Resource) +}) + +/** + * @internal + */ +export const ListResourceTemplatesResult = Schema.Struct({ + ...PaginatedResult.fields, + resourceTemplates: Schema.Array(ResourceTemplate) +}) + +/** + * @internal + */ +export const ReadResourceResult = Schema.Struct({ + ...ResultMeta.fields, + contents: Schema.Array(ResourceContents) +}) + +/** + * @internal + */ +export const ListPromptsResult = Schema.Struct({ + ...PaginatedResult.fields, + prompts: Schema.Array(Prompt) +}) + +/** + * @internal + */ +export const GetPromptResult = Schema.Struct({ + ...ResultMeta.fields, + description: optional(Schema.String), + messages: Schema.Array(PromptMessage) +}) + +/** + * @internal + */ +export const ListToolsResult = Schema.Struct({ + ...PaginatedResult.fields, + tools: Schema.Array(Tool) +}) + +/** + * @internal + */ +export const CallToolResult = Schema.Struct({ + ...ResultMeta.fields, + content: Schema.Array(PromptOrToolContent), + isError: optional(Schema.Boolean) +}) + +/** + * @internal + */ +export const CreateMessageResult = Schema.Struct({ + ...ResultMeta.fields, + role: Role, + content: SamplingContent, + model: Schema.String, + stopReason: optional(Schema.String) +}) + +/** + * @internal + */ +export const CompleteResult = Schema.Struct({ + ...ResultMeta.fields, + completion: Schema.Struct({ + values: Schema.Array(Schema.String), + total: optional(Schema.Finite), + hasMore: optional(Schema.Boolean) + }) +}) + +/** + * @internal + */ +export const ListRootsResult = Schema.Struct({ + ...ResultMeta.fields, + roots: Schema.Array(Root) +}) + +/** + * @internal + */ +export class Ping extends Rpc.make("ping", { + success: ResultMeta, + error: McpError, + payload: Schema.UndefinedOr(RequestMeta) +}) {} + +/** + * @internal + */ +export class Initialize extends Rpc.make("initialize", { + success: InitializeResult, + error: McpError, + payload: { + ...RequestMeta.fields, + protocolVersion: Schema.String, + capabilities: ClientCapabilities, + clientInfo: Implementation + } +}) {} + +/** + * @internal + */ +export class Complete extends Rpc.make("completion/complete", { + success: CompleteResult, + error: McpError, + payload: { + ...RequestMeta.fields, + ref: Schema.Union([PromptReference, ResourceReference]), + argument: Schema.Struct({ + name: Schema.String, + value: Schema.String + }) + } +}) {} + +/** + * @internal + */ +export class SetLevel extends Rpc.make("logging/setLevel", { + success: ResultMeta, + error: McpError, + payload: { + ...RequestMeta.fields, + level: LoggingLevel + } +}) {} + +/** + * @internal + */ +export class GetPrompt extends Rpc.make("prompts/get", { + success: GetPromptResult, + error: McpError, + payload: { + ...RequestMeta.fields, + name: Schema.String, + arguments: optional(Schema.Record(Schema.String, Schema.String)) + } +}) {} + +/** + * @internal + */ +export class ListPrompts extends Rpc.make("prompts/list", { + success: ListPromptsResult, + error: McpError, + payload: Schema.UndefinedOr(PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ListResources extends Rpc.make("resources/list", { + success: ListResourcesResult, + error: McpError, + payload: Schema.UndefinedOr(PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ListResourceTemplates extends Rpc.make("resources/templates/list", { + success: ListResourceTemplatesResult, + error: McpError, + payload: Schema.UndefinedOr(PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ReadResource extends Rpc.make("resources/read", { + success: ReadResourceResult, + error: McpError, + payload: { + ...RequestMeta.fields, + uri: Schema.String + } +}) {} + +/** + * @internal + */ +export class Subscribe extends Rpc.make("resources/subscribe", { + success: ResultMeta, + error: McpError, + payload: { + ...RequestMeta.fields, + uri: Schema.String + } +}) {} + +/** + * @internal + */ +export class Unsubscribe extends Rpc.make("resources/unsubscribe", { + success: ResultMeta, + error: McpError, + payload: { + ...RequestMeta.fields, + uri: Schema.String + } +}) {} + +/** + * @internal + */ +export class CallTool extends Rpc.make("tools/call", { + success: CallToolResult, + error: McpError, + payload: { + ...RequestMeta.fields, + name: Schema.String, + arguments: optional(JsonObject) + } +}) {} + +/** + * @internal + */ +export class ListTools extends Rpc.make("tools/list", { + success: ListToolsResult, + error: McpError, + payload: Schema.UndefinedOr(PaginatedRequest) +}) {} + +/** + * @internal + */ +export class CreateMessage extends Rpc.make("sampling/createMessage", { + success: CreateMessageResult, + error: McpError, + payload: { + ...RequestMeta.fields, + messages: Schema.Array(SamplingMessage), + modelPreferences: optional(ModelPreferences), + systemPrompt: optional(Schema.String), + includeContext: optional(Schema.Literals(["none", "thisServer", "allServers"])), + temperature: optional(Schema.Finite), + maxTokens: Schema.Finite, + stopSequences: optional(Schema.Array(Schema.String)), + metadata: optional(JsonObject) + } +}) {} + +/** + * @internal + */ +export class ListRoots extends Rpc.make("roots/list", { + success: ListRootsResult, + error: McpError, + payload: Schema.UndefinedOr(RequestMeta) +}) {} + +/** + * @internal + */ +export class CancelledNotification extends Rpc.make("notifications/cancelled", { + payload: { + ...NotificationMeta.fields, + requestId: RequestId, + reason: optional(Schema.String) + } +}) {} + +/** + * @internal + */ +export class ProgressNotification extends Rpc.make("notifications/progress", { + payload: { + ...NotificationMeta.fields, + progressToken: ProgressToken, + progress: Schema.Finite, + total: optional(Schema.Finite) + } +}) {} + +/** + * @internal + */ +export class InitializedNotification extends Rpc.make("notifications/initialized", { + payload: Schema.UndefinedOr(NotificationMeta) +}) {} + +/** + * @internal + */ +export class RootsListChangedNotification extends Rpc.make("notifications/roots/list_changed", { + payload: Schema.UndefinedOr(NotificationMeta) +}) {} + +/** + * @internal + */ +export class LoggingMessageNotification extends Rpc.make("notifications/message", { + payload: { + ...NotificationMeta.fields, + level: LoggingLevel, + logger: optional(Schema.String), + data: Schema.Any + } +}) {} + +/** + * @internal + */ +export class ResourceUpdatedNotification extends Rpc.make("notifications/resources/updated", { + payload: { + ...NotificationMeta.fields, + uri: Schema.String + } +}) {} + +/** + * @internal + */ +export class ResourceListChangedNotification extends Rpc.make("notifications/resources/list_changed", { + payload: Schema.UndefinedOr(NotificationMeta) +}) {} + +/** + * @internal + */ +export class ToolListChangedNotification extends Rpc.make("notifications/tools/list_changed", { + payload: Schema.UndefinedOr(NotificationMeta) +}) {} + +/** + * @internal + */ +export class PromptListChangedNotification extends Rpc.make("notifications/prompts/list_changed", { + payload: Schema.UndefinedOr(NotificationMeta) +}) {} + +/** + * @internal + */ +export class ClientRequestRpcs extends RpcGroup.make( + Ping, + Initialize, + Complete, + SetLevel, + GetPrompt, + ListPrompts, + ListResources, + ListResourceTemplates, + ReadResource, + Subscribe, + Unsubscribe, + CallTool, + ListTools +) {} + +/** + * @internal + */ +export class ClientNotificationRpcs extends RpcGroup.make( + CancelledNotification, + ProgressNotification, + InitializedNotification, + RootsListChangedNotification +) {} + +/** + * @internal + */ +export class ServerRequestRpcs extends RpcGroup.make( + Ping, + CreateMessage, + ListRoots +) {} + +/** + * @internal + */ +export class ServerNotificationRpcs extends RpcGroup.make( + CancelledNotification, + ProgressNotification, + LoggingMessageNotification, + ResourceUpdatedNotification, + ResourceListChangedNotification, + ToolListChangedNotification, + PromptListChangedNotification +) {} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts new file mode 100644 index 0000000000..a8b1587489 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_03_26.ts @@ -0,0 +1,274 @@ +/** + * Exact MCP v2025-03-26 wire schemas. + * + * This revision is expressed as a frozen delta from the exact 2024-11-05 + * schemas. Transport envelopes (including JSON-RPC batches) are owned by the + * transport codec rather than Effect RPC payload schemas. + * + * @internal + */ +import * as Rpc from "../../../rpc/Rpc.ts" +import * as RpcGroup from "../../../rpc/RpcGroup.ts" +import * as Schema from "../../../Schema.ts" +import * as Previous from "./v2024_11_05.ts" + +export * from "./v2024_11_05.ts" + +/** + * @internal + */ +export const protocolVersion = "2025-03-26" + +const optional = Previous.optional + +/** + * @internal + */ +export const ServerCapabilities = Schema.Struct({ + ...Previous.ServerCapabilities.fields, + completions: optional(Schema.Struct({})) +}) + +/** + * @internal + */ +export const AudioContent = Schema.Struct({ + type: Schema.Literal("audio"), + data: Schema.String, + mimeType: Schema.String, + annotations: optional(Previous.Annotation) +}) + +/** + * @internal + */ +export const PromptOrToolContent = Schema.Union([ + Previous.TextContent, + Previous.ImageContent, + AudioContent, + Previous.EmbeddedResource +]) + +/** + * @internal + */ +export const SamplingContent = Schema.Union([ + Previous.TextContent, + Previous.ImageContent, + AudioContent +]) + +/** + * @internal + */ +export const PromptMessage = Schema.Struct({ + role: Previous.Role, + content: PromptOrToolContent +}) + +/** + * @internal + */ +export const SamplingMessage = Schema.Struct({ + role: Previous.Role, + content: SamplingContent +}) + +/** + * @internal + */ +export const ToolAnnotations = Schema.Struct({ + title: optional(Schema.String), + readOnlyHint: optional(Schema.Boolean), + destructiveHint: optional(Schema.Boolean), + idempotentHint: optional(Schema.Boolean), + openWorldHint: optional(Schema.Boolean) +}) + +/** + * @internal + */ +export const Tool = Schema.Struct({ + ...Previous.Tool.fields, + annotations: optional(ToolAnnotations) +}) + +/** + * @internal + */ +export const InitializeResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + protocolVersion: Schema.String, + capabilities: ServerCapabilities, + serverInfo: Previous.Implementation, + instructions: optional(Schema.String) +}) + +/** + * @internal + */ +export const GetPromptResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + description: optional(Schema.String), + messages: Schema.Array(PromptMessage) +}) + +/** + * @internal + */ +export const ListToolsResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + tools: Schema.Array(Tool) +}) + +/** + * @internal + */ +export const CallToolResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + content: Schema.Array(PromptOrToolContent), + isError: optional(Schema.Boolean) +}) + +/** + * @internal + */ +export const CreateMessageResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + role: Previous.Role, + content: SamplingContent, + model: Schema.String, + stopReason: optional(Schema.String) +}) + +/** + * @internal + */ +export class Initialize extends Rpc.make("initialize", { + success: InitializeResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + protocolVersion: Schema.String, + capabilities: Previous.ClientCapabilities, + clientInfo: Previous.Implementation + } +}) {} + +/** + * @internal + */ +export class GetPrompt extends Rpc.make("prompts/get", { + success: GetPromptResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + name: Schema.String, + arguments: optional(Schema.Record(Schema.String, Schema.String)) + } +}) {} + +/** + * @internal + */ +export class ListTools extends Rpc.make("tools/list", { + success: ListToolsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class CallTool extends Rpc.make("tools/call", { + success: CallToolResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + name: Schema.String, + arguments: optional(Schema.JsonObject) + } +}) {} + +/** + * @internal + */ +export class CreateMessage extends Rpc.make("sampling/createMessage", { + success: CreateMessageResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + messages: Schema.Array(SamplingMessage), + modelPreferences: optional(Previous.ModelPreferences), + systemPrompt: optional(Schema.String), + includeContext: optional(Schema.Literals(["none", "thisServer", "allServers"])), + temperature: optional(Schema.Finite), + maxTokens: Schema.Finite, + stopSequences: optional(Schema.Array(Schema.String)), + metadata: optional(Schema.JsonObject) + } +}) {} + +/** + * @internal + */ +export class ProgressNotification extends Rpc.make("notifications/progress", { + payload: { + ...Previous.NotificationMeta.fields, + progressToken: Previous.ProgressToken, + progress: Schema.Finite, + total: optional(Schema.Finite), + message: optional(Schema.String) + } +}) {} + +/** + * @internal + */ +export class ClientRequestRpcs extends RpcGroup.make( + Previous.Ping, + Initialize, + Previous.Complete, + Previous.SetLevel, + GetPrompt, + Previous.ListPrompts, + Previous.ListResources, + Previous.ListResourceTemplates, + Previous.ReadResource, + Previous.Subscribe, + Previous.Unsubscribe, + CallTool, + ListTools +) {} + +/** + * @internal + */ +export class ClientNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + ProgressNotification, + Previous.InitializedNotification, + Previous.RootsListChangedNotification +) {} + +/** + * @internal + */ +export class ServerRequestRpcs extends RpcGroup.make( + Previous.Ping, + CreateMessage, + Previous.ListRoots +) {} + +/** + * @internal + */ +export class ServerNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + ProgressNotification, + Previous.LoggingMessageNotification, + Previous.ResourceUpdatedNotification, + Previous.ResourceListChangedNotification, + Previous.ToolListChangedNotification, + Previous.PromptListChangedNotification +) {} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts new file mode 100644 index 0000000000..6fa204820e --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_06_18.ts @@ -0,0 +1,552 @@ +/** + * Exact MCP v2025-06-18 wire schemas. + * + * This module is independent of the public compatibility-oriented McpSchema + * surface and is frozen as a dated delta from v2025-03-26. + * + * @internal + */ +import * as Rpc from "../../../rpc/Rpc.ts" +import * as RpcGroup from "../../../rpc/RpcGroup.ts" +import * as Schema from "../../../Schema.ts" +import * as Previous from "./v2025_03_26.ts" + +export * from "./v2025_03_26.ts" + +/** + * @internal + */ +export const protocolVersion = "2025-06-18" + +const optional = Previous.optional +const JsonObject = Schema.JsonObject +const Meta = optional(JsonObject) + +/** + * @internal + */ +export const Implementation = Schema.Struct({ + name: Schema.String, + title: optional(Schema.String), + version: Schema.String +}) + +/** + * @internal + */ +export const ClientCapabilities = Schema.Struct({ + ...Previous.ClientCapabilities.fields, + elicitation: optional(Schema.Struct({})) +}) + +/** + * @internal + */ +export const Annotations = Schema.Struct({ + audience: optional(Schema.Array(Previous.Role)), + priority: optional(Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))) +}) + +/** + * @internal + */ +export const Resource = Schema.Struct({ + ...Previous.Resource.fields, + title: optional(Schema.String), + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const ResourceTemplate = Schema.Struct({ + ...Previous.ResourceTemplate.fields, + title: optional(Schema.String), + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const TextResourceContents = Schema.Struct({ + ...Previous.TextResourceContents.fields, + _meta: Meta +}) + +/** + * @internal + */ +export const BlobResourceContents = Schema.Struct({ + ...Previous.BlobResourceContents.fields, + _meta: Meta +}) + +/** + * @internal + */ +export const ResourceContents = Schema.Union([TextResourceContents, BlobResourceContents]) + +/** + * @internal + */ +export const PromptArgument = Schema.Struct({ + ...Previous.PromptArgument.fields, + title: optional(Schema.String) +}) + +/** + * @internal + */ +export const Prompt = Schema.Struct({ + ...Previous.Prompt.fields, + title: optional(Schema.String), + arguments: optional(Schema.Array(PromptArgument)), + _meta: Meta +}) + +/** + * @internal + */ +export const EmbeddedResource = Schema.Struct({ + type: Schema.Literal("resource"), + resource: ResourceContents, + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const ResourceLink = Schema.Struct({ + ...Resource.fields, + type: Schema.Literal("resource_link") +}) + +/** + * @internal + */ +export const TextContent = Schema.Struct({ + ...Previous.TextContent.fields, + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const ImageContent = Schema.Struct({ + ...Previous.ImageContent.fields, + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const AudioContent = Schema.Struct({ + ...Previous.AudioContent.fields, + annotations: optional(Annotations), + _meta: Meta +}) + +/** + * @internal + */ +export const ContentBlock = Schema.Union([ + TextContent, + ImageContent, + AudioContent, + EmbeddedResource, + ResourceLink +]) + +/** + * @internal + */ +export const SamplingContent = Schema.Union([TextContent, ImageContent, AudioContent]) + +/** + * @internal + */ +export const PromptMessage = Schema.Struct({ + role: Previous.Role, + content: ContentBlock +}) + +/** + * @internal + */ +export const SamplingMessage = Schema.Struct({ + role: Previous.Role, + content: SamplingContent +}) + +/** + * @internal + */ +export const ServerCapabilities = Schema.Struct({ + ...Previous.ServerCapabilities.fields +}) + +/** + * @internal + */ +export const InitializeResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + protocolVersion: Schema.String, + capabilities: ServerCapabilities, + serverInfo: Implementation, + instructions: optional(Schema.String) +}) + +/** + * @internal + */ +export class Initialize extends Rpc.make("initialize", { + success: InitializeResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + protocolVersion: Schema.String, + capabilities: ClientCapabilities, + clientInfo: Implementation + } +}) {} + +const ToolJsonSchema = Schema.StructWithRest( + Schema.Struct({ + type: Schema.Literal("object"), + properties: optional(Schema.Record(Schema.String, JsonObject)), + required: optional(Schema.Array(Schema.String)) + }), + [Schema.JsonObject] +) + +/** + * @internal + */ +export const Tool = Schema.Struct({ + name: Schema.String, + title: optional(Schema.String), + description: optional(Schema.String), + inputSchema: ToolJsonSchema, + outputSchema: optional(ToolJsonSchema), + annotations: optional(Previous.ToolAnnotations), + _meta: Meta +}) + +/** + * @internal + */ +export const CallToolResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + content: Schema.Array(ContentBlock), + structuredContent: optional(JsonObject), + isError: optional(Schema.Boolean) +}) + +/** + * @internal + */ +export const CreateMessageResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + role: Previous.Role, + content: SamplingContent, + model: Schema.String, + stopReason: optional(Schema.String) +}) + +/** + * @internal + */ +export class CreateMessage extends Rpc.make("sampling/createMessage", { + success: CreateMessageResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + messages: Schema.Array(SamplingMessage), + modelPreferences: optional(Previous.ModelPreferences), + systemPrompt: optional(Schema.String), + includeContext: optional(Schema.Literals(["none", "thisServer", "allServers"])), + temperature: optional(Schema.Finite), + maxTokens: Schema.Finite, + stopSequences: optional(Schema.Array(Schema.String)), + metadata: optional(JsonObject) + } +}) {} + +/** + * @internal + */ +export const PromptReference = Schema.Struct({ + ...Previous.PromptReference.fields, + title: optional(Schema.String) +}) + +/** + * @internal + */ +export const ResourceTemplateReference = Schema.Struct({ + type: Schema.Literal("ref/resource"), + uri: Schema.String +}) + +/** + * @internal + */ +export const CompleteResult = Previous.CompleteResult + +/** + * @internal + */ +export class Complete extends Rpc.make("completion/complete", { + success: CompleteResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + ref: Schema.Union([PromptReference, ResourceTemplateReference]), + argument: Schema.Struct({ + name: Schema.String, + value: Schema.String + }), + context: optional(Schema.Struct({ + arguments: optional(Schema.Record(Schema.String, Schema.String)) + })) + } +}) {} + +/** + * @internal + */ +export const ListResourcesResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + resources: Schema.Array(Resource) +}) + +/** + * @internal + */ +export const ListResourceTemplatesResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + resourceTemplates: Schema.Array(ResourceTemplate) +}) + +/** + * @internal + */ +export const ReadResourceResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + contents: Schema.Array(ResourceContents) +}) + +/** + * @internal + */ +export const ListPromptsResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + prompts: Schema.Array(Prompt) +}) + +/** + * @internal + */ +export const GetPromptResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + description: optional(Schema.String), + messages: Schema.Array(PromptMessage) +}) + +/** + * @internal + */ +export const ListToolsResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + tools: Schema.Array(Tool) +}) + +/** + * @internal + */ +export class ListResources extends Rpc.make("resources/list", { + success: ListResourcesResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ListResourceTemplates extends Rpc.make("resources/templates/list", { + success: ListResourceTemplatesResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ReadResource extends Rpc.make("resources/read", { + success: ReadResourceResult, + error: Previous.McpError, + payload: { ...Previous.RequestMeta.fields, uri: Schema.String } +}) {} + +/** + * @internal + */ +export class ListPrompts extends Rpc.make("prompts/list", { + success: ListPromptsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class GetPrompt extends Rpc.make("prompts/get", { + success: GetPromptResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + name: Schema.String, + arguments: optional(Schema.Record(Schema.String, Schema.String)) + } +}) {} + +/** + * @internal + */ +export class ListTools extends Rpc.make("tools/list", { + success: ListToolsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class CallTool extends Rpc.make("tools/call", { + success: CallToolResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + name: Schema.String, + arguments: optional(JsonObject) + } +}) {} + +/** + * @internal + */ +export const ElicitResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + action: Schema.Literals(["accept", "decline", "cancel"]), + content: optional(Schema.Record( + Schema.String, + Schema.Union([Schema.String, Schema.Finite, Schema.Boolean]) + )) +}) + +const StringSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + minLength: optional(Schema.Int), + maxLength: optional(Schema.Int), + format: optional(Schema.Literals(["email", "uri", "date", "date-time"])) +}) +const NumberSchema = Schema.Struct({ + type: Schema.Literals(["number", "integer"]), + title: optional(Schema.String), + description: optional(Schema.String), + minimum: optional(Schema.Finite), + maximum: optional(Schema.Finite) +}) +const BooleanSchema = Schema.Struct({ + type: Schema.Literal("boolean"), + title: optional(Schema.String), + description: optional(Schema.String), + default: optional(Schema.Boolean) +}) +const EnumSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + enum: Schema.Array(Schema.String), + enumNames: optional(Schema.Array(Schema.String)) +}) +const RequestedSchema = Schema.Struct({ + type: Schema.Literal("object"), + properties: Schema.Record( + Schema.String, + Schema.Union([StringSchema, NumberSchema, BooleanSchema, EnumSchema]) + ), + required: optional(Schema.Array(Schema.String)) +}) + +/** + * @internal + */ +export class Elicit extends Rpc.make("elicitation/create", { + success: ElicitResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + message: Schema.String, + requestedSchema: RequestedSchema + } +}) {} + +/** + * @internal + */ +export class ClientRequestRpcs extends RpcGroup.make( + Previous.Ping, + Initialize, + Complete, + Previous.SetLevel, + GetPrompt, + ListPrompts, + ListResources, + ListResourceTemplates, + ReadResource, + Previous.Subscribe, + Previous.Unsubscribe, + CallTool, + ListTools +) {} + +/** + * @internal + */ +export class ClientNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + Previous.ProgressNotification, + Previous.InitializedNotification, + Previous.RootsListChangedNotification +) {} + +/** + * @internal + */ +export class ServerRequestRpcs extends RpcGroup.make( + Previous.Ping, + CreateMessage, + Previous.ListRoots, + Elicit +) {} + +/** + * @internal + */ +export class ServerNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + Previous.ProgressNotification, + Previous.LoggingMessageNotification, + Previous.ResourceUpdatedNotification, + Previous.ResourceListChangedNotification, + Previous.ToolListChangedNotification, + Previous.PromptListChangedNotification +) {} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts new file mode 100644 index 0000000000..dcaf301167 --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2025_11_25.ts @@ -0,0 +1,651 @@ +/** + * Supported non-Task MCP v2025-11-25 wire schemas. + * + * This module is a dated delta from v2025-06-18. Experimental Tasks are not + * part of the supported vocabulary for this adapter. + * + * @internal + */ +import * as Rpc from "../../../rpc/Rpc.ts" +import * as RpcGroup from "../../../rpc/RpcGroup.ts" +import * as Schema from "../../../Schema.ts" +import * as Previous from "./v2025_06_18.ts" + +export * from "./v2025_06_18.ts" + +/** + * @internal + */ +export const protocolVersion = "2025-11-25" + +const optional = Previous.optional +const JsonObject = Schema.JsonObject +const Meta = optional(JsonObject) + +/** + * @internal + */ +export const Icon = Schema.Struct({ + src: Schema.String, + mimeType: optional(Schema.String), + sizes: optional(Schema.Array(Schema.String)), + theme: optional(Schema.Literals(["light", "dark"])) +}) + +/** + * @internal + */ +export const Implementation = Schema.Struct({ + ...Previous.Implementation.fields, + description: optional(Schema.String), + websiteUrl: optional(Schema.String), + icons: optional(Schema.Array(Icon)) +}) + +/** + * @internal + */ +export const ClientCapabilities = Schema.Struct({ + ...Previous.ClientCapabilities.fields, + sampling: optional(Schema.Struct({ + context: optional(JsonObject), + tools: optional(JsonObject) + })), + elicitation: optional(Schema.Struct({ + form: optional(JsonObject), + url: optional(JsonObject) + })) +}) + +/** + * @internal + */ +export const Annotations = Schema.Struct({ + ...Previous.Annotations.fields, + lastModified: optional(Schema.String) +}) + +/** + * @internal + */ +export const Resource = Schema.Struct({ + ...Previous.Resource.fields, + annotations: optional(Annotations), + icons: optional(Schema.Array(Icon)) +}) + +/** + * @internal + */ +export const ResourceTemplate = Schema.Struct({ + ...Previous.ResourceTemplate.fields, + annotations: optional(Annotations), + icons: optional(Schema.Array(Icon)) +}) + +/** + * @internal + */ +export const Prompt = Schema.Struct({ + ...Previous.Prompt.fields, + icons: optional(Schema.Array(Icon)) +}) + +/** + * @internal + */ +export const TextContent = Schema.Struct({ + ...Previous.TextContent.fields, + annotations: optional(Annotations) +}) + +/** + * @internal + */ +export const ImageContent = Schema.Struct({ + ...Previous.ImageContent.fields, + annotations: optional(Annotations) +}) + +/** + * @internal + */ +export const AudioContent = Schema.Struct({ + ...Previous.AudioContent.fields, + annotations: optional(Annotations) +}) + +/** + * @internal + */ +export const EmbeddedResource = Schema.Struct({ + ...Previous.EmbeddedResource.fields, + annotations: optional(Annotations) +}) + +/** + * @internal + */ +export const ResourceLink = Schema.Struct({ + ...Resource.fields, + type: Schema.Literal("resource_link") +}) + +/** + * @internal + */ +export const ContentBlock = Schema.Union([ + TextContent, + ImageContent, + AudioContent, + ResourceLink, + EmbeddedResource +]) + +/** + * @internal + */ +export const PromptMessage = Schema.Struct({ + role: Previous.Role, + content: ContentBlock +}) + +/** + * @internal + */ +export const Tool = Schema.Struct({ + ...Previous.Tool.fields, + icons: optional(Schema.Array(Icon)) +}) + +/** + * @internal + */ +export const CallToolResult = Schema.Struct({ + ...Previous.CallToolResult.fields, + content: Schema.Array(ContentBlock) +}) + +/** + * @internal + */ +export class CallTool extends Rpc.make("tools/call", { + success: CallToolResult, + error: Previous.McpError, + payload: Previous.CallTool.payloadSchema +}) {} + +/** + * @internal + */ +export const ToolUseContent = Schema.Struct({ + type: Schema.Literal("tool_use"), + id: Schema.String, + name: Schema.String, + input: JsonObject, + _meta: Meta +}) + +/** + * @internal + */ +export const ToolResultContent = Schema.Struct({ + type: Schema.Literal("tool_result"), + toolUseId: Schema.String, + content: Schema.Array(ContentBlock), + structuredContent: optional(JsonObject), + isError: optional(Schema.Boolean), + _meta: Meta +}) + +/** + * @internal + */ +export const SamplingMessageContentBlock = Schema.Union([ + TextContent, + ImageContent, + AudioContent, + ToolUseContent, + ToolResultContent +]) + +/** + * @internal + */ +export const SamplingMessage = Schema.Struct({ + role: Previous.Role, + content: Schema.Union([ + SamplingMessageContentBlock, + Schema.Array(SamplingMessageContentBlock) + ]), + _meta: Meta +}) + +/** + * @internal + */ +export const ToolChoice = Schema.Struct({ + mode: optional(Schema.Literals(["auto", "required", "none"])) +}) + +/** + * @internal + */ +export const CreateMessageResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + ...SamplingMessage.fields, + model: Schema.String, + stopReason: optional(Schema.String) +}) + +/** + * @internal + */ +export class CreateMessage extends Rpc.make("sampling/createMessage", { + success: CreateMessageResult, + error: Previous.McpError, + payload: { + ...Previous.CreateMessage.payloadSchema.fields, + messages: Schema.Array(SamplingMessage), + maxTokens: Schema.Int, + tools: optional(Schema.Array(Tool)), + toolChoice: optional(ToolChoice) + } +}) {} + +/** + * @internal + */ +export const Root = Schema.Struct({ + ...Previous.Root.fields, + _meta: Meta +}) + +/** + * @internal + */ +export const ListRootsResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + roots: Schema.Array(Root) +}) + +/** + * @internal + */ +export class ListRoots extends Rpc.make("roots/list", { + success: ListRootsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.RequestMeta) +}) {} + +/** + * @internal + */ +export const StringSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + minLength: optional(Schema.Int), + maxLength: optional(Schema.Int), + format: optional(Schema.Literals(["email", "uri", "date", "date-time"])), + default: optional(Schema.String) +}) + +/** + * @internal + */ +export const NumberSchema = Schema.Struct({ + type: Schema.Literals(["number", "integer"]), + title: optional(Schema.String), + description: optional(Schema.String), + minimum: optional(Schema.Finite), + maximum: optional(Schema.Finite), + default: optional(Schema.Finite) +}) + +/** + * @internal + */ +export const BooleanSchema = Schema.Struct({ + type: Schema.Literal("boolean"), + title: optional(Schema.String), + description: optional(Schema.String), + default: optional(Schema.Boolean) +}) + +const EnumOption = Schema.Struct({ + const: Schema.String, + title: Schema.String +}) + +/** + * @internal + */ +export const UntitledSingleSelectEnumSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + enum: Schema.Array(Schema.String), + default: optional(Schema.String) +}) + +/** + * @internal + */ +export const TitledSingleSelectEnumSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + oneOf: Schema.Array(EnumOption), + default: optional(Schema.String) +}) + +/** + * @internal + */ +export const SingleSelectEnumSchema = Schema.Union([ + UntitledSingleSelectEnumSchema, + TitledSingleSelectEnumSchema +]) + +/** + * @internal + */ +export const UntitledMultiSelectEnumSchema = Schema.Struct({ + type: Schema.Literal("array"), + title: optional(Schema.String), + description: optional(Schema.String), + minItems: optional(Schema.Int), + maxItems: optional(Schema.Int), + items: Schema.Struct({ + type: Schema.Literal("string"), + enum: Schema.Array(Schema.String) + }), + default: optional(Schema.Array(Schema.String)) +}) + +/** + * @internal + */ +export const TitledMultiSelectEnumSchema = Schema.Struct({ + type: Schema.Literal("array"), + title: optional(Schema.String), + description: optional(Schema.String), + minItems: optional(Schema.Int), + maxItems: optional(Schema.Int), + items: Schema.Struct({ + anyOf: Schema.Array(EnumOption) + }), + default: optional(Schema.Array(Schema.String)) +}) + +/** + * @internal + */ +export const MultiSelectEnumSchema = Schema.Union([ + UntitledMultiSelectEnumSchema, + TitledMultiSelectEnumSchema +]) + +/** + * @internal + */ +export const LegacyTitledEnumSchema = Schema.Struct({ + type: Schema.Literal("string"), + title: optional(Schema.String), + description: optional(Schema.String), + enum: Schema.Array(Schema.String), + enumNames: optional(Schema.Array(Schema.String)), + default: optional(Schema.String) +}) + +/** + * @internal + */ +export const EnumSchema = Schema.Union([ + LegacyTitledEnumSchema, + SingleSelectEnumSchema, + MultiSelectEnumSchema +]) + +/** + * @internal + */ +export const PrimitiveSchemaDefinition = Schema.Union([ + EnumSchema, + StringSchema, + NumberSchema, + BooleanSchema +]) + +/** + * @internal + */ +export const RequestedSchema = Schema.Struct({ + $schema: optional(Schema.String), + type: Schema.Literal("object"), + properties: Schema.Record(Schema.String, PrimitiveSchemaDefinition), + required: optional(Schema.Array(Schema.String)) +}) + +/** + * @internal + */ +export const ElicitRequestFormParams = Schema.Struct({ + ...Previous.RequestMeta.fields, + mode: optional(Schema.Literal("form")), + message: Schema.String, + requestedSchema: RequestedSchema +}) + +/** + * @internal + */ +export const ElicitRequestURLParams = Schema.Struct({ + ...Previous.RequestMeta.fields, + mode: Schema.Literal("url"), + message: Schema.String, + elicitationId: Schema.String, + url: Schema.String +}) + +/** + * @internal + */ +export const ElicitRequestParams = Schema.Union([ + ElicitRequestFormParams, + ElicitRequestURLParams +]) + +/** + * @internal + */ +export const ElicitResult = Schema.Struct({ + ...Previous.ResultMeta.fields, + action: Schema.Literals(["accept", "decline", "cancel"]), + content: optional(Schema.Record( + Schema.String, + Schema.Union([Schema.String, Schema.Finite, Schema.Boolean, Schema.Array(Schema.String)]) + )) +}) + +/** + * @internal + */ +export class Elicit extends Rpc.make("elicitation/create", { + success: ElicitResult, + error: Previous.McpError, + payload: ElicitRequestParams +}) {} + +/** + * @internal + */ +export class ElicitationCompleteNotification extends Rpc.make("notifications/elicitation/complete", { + payload: { + elicitationId: Schema.String + } +}) {} + +/** + * @internal + */ +export const InitializeResult = Schema.Struct({ + ...Previous.InitializeResult.fields, + serverInfo: Implementation +}) + +/** + * @internal + */ +export class Initialize extends Rpc.make("initialize", { + success: InitializeResult, + error: Previous.McpError, + payload: { + ...Previous.Initialize.payloadSchema.fields, + capabilities: ClientCapabilities, + clientInfo: Implementation + } +}) {} + +/** + * @internal + */ +export const ListResourcesResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + resources: Schema.Array(Resource) +}) + +/** + * @internal + */ +export const ListResourceTemplatesResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + resourceTemplates: Schema.Array(ResourceTemplate) +}) + +/** + * @internal + */ +export const ListPromptsResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + prompts: Schema.Array(Prompt) +}) + +/** + * @internal + */ +export const GetPromptResult = Schema.Struct({ + ...Previous.GetPromptResult.fields, + messages: Schema.Array(PromptMessage) +}) + +/** + * @internal + */ +export const ListToolsResult = Schema.Struct({ + ...Previous.PaginatedResult.fields, + tools: Schema.Array(Tool) +}) + +/** + * @internal + */ +export class ListResources extends Rpc.make("resources/list", { + success: ListResourcesResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ListResourceTemplates extends Rpc.make("resources/templates/list", { + success: ListResourceTemplatesResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ListPrompts extends Rpc.make("prompts/list", { + success: ListPromptsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class GetPrompt extends Rpc.make("prompts/get", { + success: GetPromptResult, + error: Previous.McpError, + payload: { + ...Previous.RequestMeta.fields, + name: Schema.String, + arguments: optional(Schema.Record(Schema.String, Schema.String)) + } +}) {} + +/** + * @internal + */ +export class ListTools extends Rpc.make("tools/list", { + success: ListToolsResult, + error: Previous.McpError, + payload: Schema.UndefinedOr(Previous.PaginatedRequest) +}) {} + +/** + * @internal + */ +export class ClientRequestRpcs extends RpcGroup.make( + Previous.Ping, + Initialize, + Previous.Complete, + Previous.SetLevel, + GetPrompt, + ListPrompts, + ListResources, + ListResourceTemplates, + Previous.ReadResource, + Previous.Subscribe, + Previous.Unsubscribe, + CallTool, + ListTools +) {} + +/** + * @internal + */ +export class ClientNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + Previous.ProgressNotification, + Previous.InitializedNotification, + Previous.RootsListChangedNotification +) {} + +/** + * @internal + */ +export class ServerRequestRpcs extends RpcGroup.make( + Previous.Ping, + CreateMessage, + ListRoots, + Elicit +) {} + +/** + * @internal + */ +export class ServerNotificationRpcs extends RpcGroup.make( + Previous.CancelledNotification, + Previous.ProgressNotification, + Previous.LoggingMessageNotification, + Previous.ResourceUpdatedNotification, + Previous.ResourceListChangedNotification, + Previous.ToolListChangedNotification, + Previous.PromptListChangedNotification, + ElicitationCompleteNotification +) {} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts new file mode 100644 index 0000000000..b38c929e3c --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpSchema/v2026_07_28.ts @@ -0,0 +1,857 @@ +/** + * MCP v2026-07-28 wire schemas. + * + * @internal + */ +import * as Rpc from "../../../rpc/Rpc.ts" +import * as RpcGroup from "../../../rpc/RpcGroup.ts" +import * as Schema from "../../../Schema.ts" +import * as Previous from "./v2025_11_25.ts" + +export * from "./v2025_11_25.ts" + +/** + * @internal + */ +export const protocolVersion = "2026-07-28" + +const optional = Previous.optional +const Meta = optional(Schema.JsonObject) + +/** + * @internal + */ +export const RequestId = Schema.Union([Schema.String, Schema.Int]) +/** + * @internal + */ +export const ProgressToken = RequestId +/** + * @internal + */ +export const Role = Previous.Role +/** + * @internal + */ +export const LoggingLevel = Previous.LoggingLevel +/** + * @internal + */ +export const Implementation = Previous.Implementation + +/** + * @internal + */ +export const ClientCapabilities = Schema.StructWithRest( + Schema.Struct({ + experimental: optional(Schema.Record(Schema.String, Schema.JsonObject)), + roots: optional(Schema.Struct({})), + sampling: optional(Schema.Struct({ + context: optional(Schema.JsonObject), + tools: optional(Schema.JsonObject) + })), + elicitation: optional(Schema.Struct({ + form: optional(Schema.JsonObject), + url: optional(Schema.JsonObject) + })), + extensions: optional(Schema.Record(Schema.String, Schema.JsonObject)) + }), + [Schema.JsonObject] +) + +/** + * @internal + */ +export const ServerCapabilities = Schema.StructWithRest( + Schema.Struct({ + experimental: optional(Schema.Record(Schema.String, Schema.JsonObject)), + logging: optional(Schema.JsonObject), + completions: optional(Schema.JsonObject), + prompts: optional(Schema.Struct({ listChanged: optional(Schema.Boolean) })), + resources: optional(Schema.Struct({ + subscribe: optional(Schema.Boolean), + listChanged: optional(Schema.Boolean) + })), + tools: optional(Schema.Struct({ listChanged: optional(Schema.Boolean) })), + extensions: optional(Schema.Record(Schema.String, Schema.JsonObject)) + }), + [Schema.JsonObject] +) + +/** + * @internal + */ +export const RequestMetaObject = Schema.StructWithRest( + Schema.Struct({ + progressToken: optional(ProgressToken), + "io.modelcontextprotocol/protocolVersion": Schema.String, + "io.modelcontextprotocol/clientInfo": optional(Implementation), + "io.modelcontextprotocol/clientCapabilities": ClientCapabilities, + "io.modelcontextprotocol/logLevel": optional(LoggingLevel) + }), + [Schema.JsonObject] +) + +/** + * @internal + */ +export const RequestParams = Schema.Struct({ _meta: RequestMetaObject }) + +/** + * @internal + */ +export const NotificationMetaObject = Schema.StructWithRest( + Schema.Struct({ + "io.modelcontextprotocol/subscriptionId": optional(RequestId) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export const NotificationParams = Schema.Struct({ _meta: optional(NotificationMetaObject) }) + +/** + * @internal + */ +export const ResultMetaObject = Schema.StructWithRest( + Schema.Struct({ + "io.modelcontextprotocol/serverInfo": Implementation + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export const ResultMeta = { + _meta: ResultMetaObject, + resultType: Schema.Literal("complete") +} +/** + * @internal + */ +export const McpError = Schema.Struct({ + code: Schema.Int, + message: Schema.String, + data: optional(Schema.Json) +}) +/** + * @internal + */ +export type McpError = typeof McpError.Type + +/** + * @internal + */ +export const METHOD_NOT_FOUND = -32601 +/** + * @internal + */ +export const INVALID_PARAMS = -32602 +/** + * @internal + */ +export const MISSING_REQUIRED_CLIENT_CAPABILITY = -32021 + +/** + * @internal + */ +export const Annotations = Previous.Annotations + +/** + * @internal + */ +export const Resource = Schema.Struct({ + ...Previous.Resource.fields, + size: optional(Schema.Int), + annotations: optional(Annotations) +}) + +/** + * @internal + */ +export const ResourceTemplate = Previous.ResourceTemplate +/** + * @internal + */ +export const ResourceContents = Previous.ResourceContents +/** + * @internal + */ +export const TextContent = Previous.TextContent +/** + * @internal + */ +export const ImageContent = Previous.ImageContent +/** + * @internal + */ +export const AudioContent = Previous.AudioContent +/** + * @internal + */ +export const ResourceLink = Schema.Struct({ ...Resource.fields, type: Schema.Literal("resource_link") }) +/** + * @internal + */ +export const EmbeddedResource = Previous.EmbeddedResource +/** + * @internal + */ +export const ContentBlock = Schema.Union([ + TextContent, + ImageContent, + AudioContent, + ResourceLink, + EmbeddedResource +]) + +/** + * @internal + */ +export const Prompt = Previous.Prompt +/** + * @internal + */ +export const PromptMessage = Schema.Struct({ role: Role, content: ContentBlock }) + +const ToolInputSchema = Schema.StructWithRest( + Schema.Struct({ + $schema: optional(Schema.String), + type: Schema.Literal("object") + }), + [Schema.JsonObject] +) +const ToolOutputSchema = Schema.StructWithRest( + Schema.Struct({ $schema: optional(Schema.String) }), + [Schema.JsonObject] +) +/** + * @internal + */ +export const ToolAnnotations = Previous.ToolAnnotations +/** + * @internal + */ +export const Tool = Schema.Struct({ + ...Previous.Tool.fields, + inputSchema: ToolInputSchema, + outputSchema: optional(ToolOutputSchema), + annotations: optional(ToolAnnotations) +}) + +/** + * @internal + */ +export const ToolUseContent = Previous.ToolUseContent +/** + * @internal + */ +export const ToolResultContent = Schema.Struct({ + type: Schema.Literal("tool_result"), + toolUseId: Schema.String, + content: Schema.Array(ContentBlock), + structuredContent: optional(Schema.Json), + isError: optional(Schema.Boolean), + _meta: Meta +}) +/** + * @internal + */ +export const SamplingMessageContentBlock = Schema.Union([ + TextContent, + ImageContent, + AudioContent, + ToolUseContent, + ToolResultContent +]) +/** + * @internal + */ +export const SamplingMessage = Schema.Struct({ + role: Role, + content: Schema.Union([SamplingMessageContentBlock, Schema.Array(SamplingMessageContentBlock)]), + _meta: Meta +}) +/** + * @internal + */ +export const ModelHint = Schema.StructWithRest( + Schema.Struct({ name: optional(Schema.String) }), + [Schema.JsonObject] +) +const ModelPriority = Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 })) +/** + * @internal + */ +export const ModelPreferences = Schema.Struct({ + ...Previous.ModelPreferences.fields, + hints: optional(Schema.Array(ModelHint)), + costPriority: optional(ModelPriority), + speedPriority: optional(ModelPriority), + intelligencePriority: optional(ModelPriority) +}) +/** + * @internal + */ +export const ToolChoice = Previous.ToolChoice + +/** + * @internal + */ +export const CreateMessageRequest = Schema.Struct({ + method: Schema.Literal("sampling/createMessage"), + params: Schema.Struct({ + messages: Schema.Array(SamplingMessage), + modelPreferences: optional(ModelPreferences), + systemPrompt: optional(Schema.String), + includeContext: optional(Schema.Literals(["none", "thisServer", "allServers"])), + temperature: optional(Schema.Finite), + maxTokens: Schema.Int, + stopSequences: optional(Schema.Array(Schema.String)), + metadata: optional(Schema.JsonObject), + tools: optional(Schema.Array(Tool)), + toolChoice: optional(ToolChoice) + }) +}) +/** + * @internal + */ +export const CreateMessageResult = Schema.Struct({ + ...SamplingMessage.fields, + model: Schema.String, + stopReason: optional(Schema.String) +}) + +/** + * @internal + */ +export const Root = Previous.Root +/** + * @internal + */ +export const ListRootsRequest = Schema.Struct({ + method: Schema.Literal("roots/list"), + params: optional(Schema.Struct({ _meta: Meta })) +}) +/** + * @internal + */ +export const ListRootsResult = Schema.Struct({ roots: Schema.Array(Root) }) + +/** + * @internal + */ +export const StringSchema = Previous.StringSchema +/** + * @internal + */ +export const NumberSchema = Previous.NumberSchema +/** + * @internal + */ +export const BooleanSchema = Previous.BooleanSchema +/** + * @internal + */ +export const SingleSelectEnumSchema = Previous.SingleSelectEnumSchema +/** + * @internal + */ +export const MultiSelectEnumSchema = Previous.MultiSelectEnumSchema +/** + * @internal + */ +export const LegacyTitledEnumSchema = Previous.LegacyTitledEnumSchema +/** + * @internal + */ +export const EnumSchema = Schema.Union([ + LegacyTitledEnumSchema, + SingleSelectEnumSchema, + MultiSelectEnumSchema +]) +/** + * @internal + */ +export const PrimitiveSchemaDefinition = Schema.Union([ + EnumSchema, + StringSchema, + NumberSchema, + BooleanSchema +]) +/** + * @internal + */ +export const RequestedSchema = Schema.Struct({ + $schema: optional(Schema.String), + type: Schema.Literal("object"), + properties: Schema.Record(Schema.String, PrimitiveSchemaDefinition), + required: optional(Schema.Array(Schema.String)) +}) +/** + * @internal + */ +export const ElicitRequestFormParams = Schema.Struct({ + mode: optional(Schema.Literal("form")), + message: Schema.String, + requestedSchema: RequestedSchema +}) +/** + * @internal + */ +export const ElicitRequestURLParams = Schema.Struct({ + mode: Schema.Literal("url"), + message: Schema.String, + url: Schema.String +}) +/** + * @internal + */ +export const ElicitRequestParams = Schema.Union([ElicitRequestFormParams, ElicitRequestURLParams]) +/** + * @internal + */ +export const ElicitRequest = Schema.Struct({ + method: Schema.Literal("elicitation/create"), + params: ElicitRequestParams +}) +/** + * @internal + */ +export const ElicitResult = Schema.Struct({ + action: Schema.Literals(["accept", "decline", "cancel"]), + content: optional(Schema.Record( + Schema.String, + Schema.Union([Schema.String, Schema.Finite, Schema.Boolean, Schema.Array(Schema.String)]) + )) +}) + +/** + * @internal + */ +export const InputRequest = Schema.Union([CreateMessageRequest, ListRootsRequest, ElicitRequest]) +/** + * @internal + */ +export const InputResponse = Schema.Union([CreateMessageResult, ListRootsResult, ElicitResult]) +/** + * @internal + */ +export const InputRequests = Schema.Record(Schema.String, InputRequest) +/** + * @internal + */ +export const InputResponses = Schema.Record(Schema.String, InputResponse) +const InputRequiredResultMeta = { + _meta: ResultMetaObject, + resultType: Schema.Literal("input_required") +} +/** + * @internal + */ +export const InputRequiredResult = Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + ...InputRequiredResultMeta, + inputRequests: InputRequests, + requestState: optional(Schema.String) + }), + [Schema.JsonObject] + ), + Schema.StructWithRest( + Schema.Struct({ + ...InputRequiredResultMeta, + inputRequests: optional(InputRequests), + requestState: Schema.String + }), + [Schema.JsonObject] + ) +]) +/** + * @internal + */ +export const InputResponseRequestParams = { + ...RequestParams.fields, + inputResponses: optional(InputResponses), + requestState: optional(Schema.String) +} + +/** + * @internal + */ +export const CacheableResult = { + ...ResultMeta, + ttlMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)), + cacheScope: Schema.Literals(["public", "private"]) +} +/** + * @internal + */ +export const PaginatedRequestParams = { + ...RequestParams.fields, + cursor: optional(Schema.String) +} +/** + * @internal + */ +export const PaginatedResult = { + ...ResultMeta, + nextCursor: optional(Schema.String) +} + +/** + * @internal + */ +export const DiscoverResult = Schema.StructWithRest( + Schema.Struct({ + ...CacheableResult, + supportedVersions: Schema.Array(Schema.String), + capabilities: ServerCapabilities, + instructions: optional(Schema.String) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class Discover extends Rpc.make("server/discover", { + success: DiscoverResult, + error: McpError, + payload: RequestParams +}) {} + +/** + * @internal + */ +export const ListResourcesResult = Schema.StructWithRest( + Schema.Struct({ + ...PaginatedResult, + ...CacheableResult, + resources: Schema.Array(Resource) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class ListResources extends Rpc.make("resources/list", { + success: ListResourcesResult, + error: McpError, + payload: PaginatedRequestParams +}) {} + +/** + * @internal + */ +export const ListResourceTemplatesResult = Schema.StructWithRest( + Schema.Struct({ + ...PaginatedResult, + ...CacheableResult, + resourceTemplates: Schema.Array(ResourceTemplate) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class ListResourceTemplates extends Rpc.make("resources/templates/list", { + success: ListResourceTemplatesResult, + error: McpError, + payload: PaginatedRequestParams +}) {} + +/** + * @internal + */ +export const ReadResourceResult = Schema.StructWithRest( + Schema.Struct({ + ...CacheableResult, + contents: Schema.Array(ResourceContents) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class ReadResource extends Rpc.make("resources/read", { + success: Schema.Union([ReadResourceResult, InputRequiredResult]), + error: McpError, + payload: { ...InputResponseRequestParams, uri: Schema.String } +}) {} + +/** + * @internal + */ +export const ListPromptsResult = Schema.StructWithRest( + Schema.Struct({ + ...PaginatedResult, + ...CacheableResult, + prompts: Schema.Array(Prompt) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class ListPrompts extends Rpc.make("prompts/list", { + success: ListPromptsResult, + error: McpError, + payload: PaginatedRequestParams +}) {} + +/** + * @internal + */ +export const GetPromptResult = Schema.StructWithRest( + Schema.Struct({ + ...ResultMeta, + description: optional(Schema.String), + messages: Schema.Array(PromptMessage) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class GetPrompt extends Rpc.make("prompts/get", { + success: Schema.Union([GetPromptResult, InputRequiredResult]), + error: McpError, + payload: { + ...InputResponseRequestParams, + name: Schema.String, + arguments: optional(Schema.Record(Schema.String, Schema.String)) + } +}) {} + +/** + * @internal + */ +export const ListToolsResult = Schema.StructWithRest( + Schema.Struct({ + ...PaginatedResult, + ...CacheableResult, + tools: Schema.Array(Tool) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class ListTools extends Rpc.make("tools/list", { + success: ListToolsResult, + error: McpError, + payload: PaginatedRequestParams +}) {} + +/** + * @internal + */ +export const CallToolResult = Schema.StructWithRest( + Schema.Struct({ + ...ResultMeta, + content: Schema.Array(ContentBlock), + structuredContent: optional(Schema.Json), + isError: optional(Schema.Boolean) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class CallTool extends Rpc.make("tools/call", { + success: Schema.Union([CallToolResult, InputRequiredResult]), + error: McpError, + payload: { + ...InputResponseRequestParams, + name: Schema.String, + arguments: optional(Schema.JsonObject) + } +}) {} + +/** + * @internal + */ +export const PromptReference = Previous.PromptReference +/** + * @internal + */ +export const ResourceTemplateReference = Previous.ResourceTemplateReference +/** + * @internal + */ +export const CompleteResult = Schema.StructWithRest( + Schema.Struct({ + ...ResultMeta, + completion: Schema.Struct({ + values: Schema.Array(Schema.String).check(Schema.isMaxLength(100)), + total: optional(Schema.Int), + hasMore: optional(Schema.Boolean) + }) + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class Complete extends Rpc.make("completion/complete", { + success: CompleteResult, + error: McpError, + payload: { + ...RequestParams.fields, + ref: Schema.Union([PromptReference, ResourceTemplateReference]), + argument: Schema.Struct({ name: Schema.String, value: Schema.String }), + context: optional(Schema.Struct({ + arguments: optional(Schema.Record(Schema.String, Schema.String)) + })) + } +}) {} + +/** + * @internal + */ +export const SubscriptionFilter = Schema.Struct({ + toolsListChanged: optional(Schema.Boolean), + promptsListChanged: optional(Schema.Boolean), + resourcesListChanged: optional(Schema.Boolean), + resourceSubscriptions: optional(Schema.Array(Schema.String)) +}) +/** + * @internal + */ +export const SubscriptionsListenResultMetaObject = Schema.StructWithRest( + Schema.Struct({ + "io.modelcontextprotocol/serverInfo": Implementation, + "io.modelcontextprotocol/subscriptionId": RequestId + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export const SubscriptionsListenResult = Schema.StructWithRest( + Schema.Struct({ + _meta: SubscriptionsListenResultMetaObject, + resultType: Schema.Literal("complete") + }), + [Schema.JsonObject] +) +/** + * @internal + */ +export class SubscriptionsListen extends Rpc.make("subscriptions/listen", { + success: SubscriptionsListenResult, + error: McpError, + payload: { ...RequestParams.fields, notifications: SubscriptionFilter } +}) {} + +/** + * @internal + */ +export class CancelledNotification extends Rpc.make("notifications/cancelled", { + payload: { + ...NotificationParams.fields, + requestId: RequestId, + reason: optional(Schema.String) + } +}) {} +/** + * @internal + */ +export class ProgressNotification extends Rpc.make("notifications/progress", { + payload: { + ...NotificationParams.fields, + progressToken: ProgressToken, + progress: Schema.Finite, + total: optional(Schema.Finite), + message: optional(Schema.String) + } +}) {} +/** + * @internal + */ +export class LoggingMessageNotification extends Rpc.make("notifications/message", { + payload: { + ...NotificationParams.fields, + level: LoggingLevel, + logger: optional(Schema.String), + data: Schema.Json + } +}) {} +/** + * @internal + */ +export class ResourceUpdatedNotification extends Rpc.make("notifications/resources/updated", { + payload: { ...NotificationParams.fields, uri: Schema.String } +}) {} +/** + * @internal + */ +export class ResourceListChangedNotification extends Rpc.make("notifications/resources/list_changed", { + payload: Schema.UndefinedOr(NotificationParams) +}) {} +/** + * @internal + */ +export class ToolListChangedNotification extends Rpc.make("notifications/tools/list_changed", { + payload: Schema.UndefinedOr(NotificationParams) +}) {} +/** + * @internal + */ +export class PromptListChangedNotification extends Rpc.make("notifications/prompts/list_changed", { + payload: Schema.UndefinedOr(NotificationParams) +}) {} +/** + * @internal + */ +export class SubscriptionsAcknowledgedNotification extends Rpc.make( + "notifications/subscriptions/acknowledged", + { payload: { ...NotificationParams.fields, notifications: SubscriptionFilter } } +) {} + +/** + * @internal + */ +export class ClientRequestRpcs extends RpcGroup.make( + Discover, + Complete, + GetPrompt, + ListPrompts, + ListResources, + ListResourceTemplates, + ReadResource, + SubscriptionsListen, + CallTool, + ListTools +) {} + +/** + * @internal + */ +export class ClientNotificationRpcs extends RpcGroup.make(CancelledNotification) {} +/** + * @internal + */ +export class ClientRpcs extends ClientRequestRpcs.merge(ClientNotificationRpcs) {} + +// In v2026-07-28 these requests are embedded in InputRequiredResult rather than +// being sent as independent JSON-RPC requests. +/** + * @internal + */ +export class ServerRequestRpcs extends RpcGroup.make() {} + +/** + * @internal + */ +export class ServerNotificationRpcs extends RpcGroup.make( + CancelledNotification, + ProgressNotification, + LoggingMessageNotification, + ResourceUpdatedNotification, + ResourceListChangedNotification, + ToolListChangedNotification, + PromptListChangedNotification, + SubscriptionsAcknowledgedNotification +) {} diff --git a/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts b/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts new file mode 100644 index 0000000000..19c41ab07b --- /dev/null +++ b/repos/effect/packages/effect/src/ai/internal/mcpStatefulRuntime.ts @@ -0,0 +1,180 @@ +/** + * Stateful lifecycle storage for MCP revisions before v2026-07-28. + * + * @internal + */ +import * as Effect from "../../Effect.ts" +import type * as Headers from "../../http/Headers.ts" +import * as LogLevel from "../../LogLevel.ts" +import type * as PublicMcpProtocol from "../McpProtocol.ts" +import * as PublicMcpSchema from "../McpSchema.ts" +import type * as McpCore from "./mcpCore.ts" +import * as McpProtocol from "./mcpProtocol.ts" + +const MCP_SESSION_ID_HEADER = "mcp-session-id" + +type SessionLogLevel = + | { readonly _tag: "Effect"; readonly level: LogLevel.LogLevel } + | { readonly _tag: "Mcp"; readonly level: PublicMcpSchema.LoggingLevel } + +/** + * @internal + */ +export interface Binding { + readonly initializePayload: typeof PublicMcpSchema.Initialize.payloadSchema.Type + readonly negotiatedProfile: McpCore.NegotiatedProtocolProfile + readonly protocol: PublicMcpProtocol.ProtocolAdapter +} + +interface Session extends Binding { + readonly resourceSubscriptions: Set | undefined + logLevel: SessionLogLevel +} + +/** + * @internal + */ +export interface Registration extends Binding { + readonly supportsResourceSubscriptions: boolean + readonly logLevel: LogLevel.LogLevel +} + +/** + * @internal + */ +export interface StatefulRuntime { + readonly registerHttp: (sessionId: string, registration: Registration) => Binding + readonly registerConnection: (clientId: number, registration: Registration) => Binding + readonly resolve: (clientId: number, headers: Headers.Headers) => Binding | undefined + readonly resolveSessionId: (sessionId: string) => Binding | undefined + readonly setLogLevel: ( + level: PublicMcpSchema.LoggingLevel, + clientId: number, + headers: Headers.Headers + ) => Effect.Effect + readonly subscribe: ( + uri: string, + clientId: number, + headers: Headers.Headers + ) => Effect.Effect + readonly unsubscribe: ( + uri: string, + clientId: number, + headers: Headers.Headers + ) => Effect.Effect + readonly canDeliver: ( + clientId: number, + headers: Headers.Headers, + notification: McpCore.ServerNotification, + fallbackLogLevel: LogLevel.LogLevel + ) => boolean + readonly effectLogLevel: ( + clientId: number, + headers: Headers.Headers, + fallback: LogLevel.LogLevel + ) => LogLevel.LogLevel + readonly markInitialized: (clientId: number) => void + readonly initializedClientIds: () => Iterable + readonly disconnect: (clientId: number) => void +} + +const makeSession = (registration: Registration): Session => ({ + initializePayload: registration.initializePayload, + negotiatedProfile: registration.negotiatedProfile, + protocol: registration.protocol, + resourceSubscriptions: registration.supportsResourceSubscriptions ? new Set() : undefined, + logLevel: { _tag: "Effect", level: registration.logLevel } +}) + +/** + * @internal + */ +export const make = (): StatefulRuntime => { + const bySessionId = new Map() + const byClientId = new Map() + const initializedClientIds = new Set() + + const resolveSession = (clientId: number, headers: Headers.Headers): Session | undefined => { + const sessionId = headers[MCP_SESSION_ID_HEADER] + return sessionId === undefined ? byClientId.get(clientId) : bySessionId.get(sessionId) + } + + const effectLogLevel = ( + clientId: number, + headers: Headers.Headers, + fallback: LogLevel.LogLevel + ): LogLevel.LogLevel => { + const session = resolveSession(clientId, headers) + return session?.logLevel._tag === "Mcp" + ? McpProtocol.mcpLogLevels[session.logLevel.level].effect + : session?.logLevel.level ?? fallback + } + + return { + registerHttp: (sessionId, registration) => { + const session = makeSession(registration) + bySessionId.set(sessionId, session) + return session + }, + registerConnection: (clientId, registration) => { + const session = makeSession(registration) + byClientId.set(clientId, session) + return session + }, + resolve: resolveSession, + resolveSessionId: (sessionId) => bySessionId.get(sessionId), + setLogLevel: (level, clientId, headers) => + Effect.sync(() => { + const session = resolveSession(clientId, headers) + if (session !== undefined) { + session.logLevel = { _tag: "Mcp", level } + } + }), + subscribe: (uri, clientId, headers) => { + const subscriptions = resolveSession(clientId, headers)?.resourceSubscriptions + if (subscriptions === undefined) { + return Effect.fail( + new McpProtocol.ProtocolError({ + code: PublicMcpSchema.METHOD_NOT_FOUND_ERROR_CODE, + message: "Resource subscriptions are not supported" + }) + ) + } + return Effect.sync(() => subscriptions.add(uri)).pipe(Effect.asVoid) + }, + unsubscribe: (uri, clientId, headers) => { + const subscriptions = resolveSession(clientId, headers)?.resourceSubscriptions + if (subscriptions === undefined) { + return Effect.fail( + new McpProtocol.ProtocolError({ + code: PublicMcpSchema.METHOD_NOT_FOUND_ERROR_CODE, + message: "Resource subscriptions are not supported" + }) + ) + } + return Effect.sync(() => subscriptions.delete(uri)).pipe(Effect.asVoid) + }, + effectLogLevel, + canDeliver: (clientId, headers, notification, fallbackLogLevel) => { + const session = resolveSession(clientId, headers) + if (notification._tag === "LoggingMessage") { + const minimum = session?.logLevel + return minimum?._tag === "Mcp" + ? McpProtocol.mcpLogLevels[notification.level].order >= McpProtocol.mcpLogLevels[minimum.level].order + : LogLevel.isGreaterThanOrEqualTo( + McpProtocol.mcpLogLevels[notification.level].effect, + minimum?.level ?? fallbackLogLevel + ) + } + return notification._tag !== "ResourceUpdated" || session?.resourceSubscriptions?.has(notification.uri) === true + }, + markInitialized: (clientId) => { + initializedClientIds.add(clientId) + }, + initializedClientIds: () => initializedClientIds.values(), + disconnect: (clientId) => { + byClientId.delete(clientId) + initializedClientIds.delete(clientId) + } + } +} diff --git a/repos/effect/packages/effect/src/unstable/ai/internal/structured-output.ts b/repos/effect/packages/effect/src/ai/internal/structured-output.ts similarity index 96% rename from repos/effect/packages/effect/src/unstable/ai/internal/structured-output.ts rename to repos/effect/packages/effect/src/ai/internal/structured-output.ts index 984e209e31..400bb2cf1b 100644 --- a/repos/effect/packages/effect/src/unstable/ai/internal/structured-output.ts +++ b/repos/effect/packages/effect/src/ai/internal/structured-output.ts @@ -1,11 +1,11 @@ -import * as Arr from "../../../Array.ts" -import * as InternalRecord from "../../../internal/record.ts" -import * as JsonSchema from "../../../JsonSchema.ts" -import * as Option from "../../../Option.ts" -import * as Predicate from "../../../Predicate.ts" -import * as Schema from "../../../Schema.ts" -import * as SchemaAST from "../../../SchemaAST.ts" -import * as SchemaTransformation from "../../../SchemaTransformation.ts" +import * as Arr from "../../Array.ts" +import * as InternalRecord from "../../internal/record.ts" +import * as JsonSchema from "../../JsonSchema.ts" +import * as Option from "../../Option.ts" +import * as Predicate from "../../Predicate.ts" +import * as Schema from "../../Schema.ts" +import * as SchemaAST from "../../SchemaAST.ts" +import * as SchemaTransformation from "../../SchemaTransformation.ts" import * as Tool from "../Tool.ts" const REST_PROPERTY_NAME = "__rest__" @@ -18,7 +18,9 @@ const TUPLE_DESCRIPTION = "Tuple encoded as an object with numeric string keys ('0', '1', ...). If present, '__rest__' contains remaining elements" const TUPLE_TAIL_DESCRIPTION = `${TUPLE_DESCRIPTION}. Post-rest elements use '__tail_0__', '__tail_1__', and so on` -/** @internal */ +/** + * @internal + */ export function resolveReference($ref: string, definitions: JsonSchema.Definitions): JsonSchema.JsonSchema { const key = JsonSchema.getReferenceKey($ref) if (key === undefined) { @@ -30,7 +32,9 @@ export function resolveReference($ref: string, definitions: JsonSchema.Definitio return definitions[key] } -/** @internal */ +/** + * @internal + */ export function resolveTopLevelReference( document: JsonSchema.Document<"draft-2020-12"> ): JsonSchema.Document<"draft-2020-12"> { @@ -41,7 +45,9 @@ export function resolveTopLevelReference( } } -/** @internal */ +/** + * @internal + */ export function toCodec( schema: Schema.ConstraintCodec ): Schema.ConstraintCodec { @@ -352,7 +358,7 @@ function recordCheck(check: SchemaAST.Check): Array> { return typeof payload.minProperties === "number" ? [withoutDescription(Schema.isMinLength(payload.minProperties))] : [] - case "effect/schema/isPropertiesLengthBetween": + case "effect/schema/isBetweenProperties": return typeof payload.minimum === "number" ? [withoutDescription(Schema.isMinLength(payload.minimum))] : [] @@ -408,7 +414,9 @@ function appendAnnotationDescription( type JsonSchemaVisitor = (schema: JsonSchema.JsonSchema) => JsonSchema.JsonSchema -/** @internal */ +/** + * @internal + */ export function walkJsonSchema( schema: JsonSchema.JsonSchema, visitor: JsonSchemaVisitor @@ -460,7 +468,9 @@ export function walkJsonSchema( return visitor(out) } -/** @internal */ +/** + * @internal + */ export function appendDescription(schema: JsonSchema.JsonSchema, description: string): void { if (typeof schema.description === "string") { const descriptions = schema.description.split(" and ") @@ -470,7 +480,9 @@ export function appendDescription(schema: JsonSchema.JsonSchema, description: st } } -/** @internal */ +/** + * @internal + */ export function isJsonSchema(input: unknown): input is JsonSchema.JsonSchema { return typeof input === "object" && input !== null && !Array.isArray(input) } diff --git a/repos/effect/packages/effect/src/unstable/cli/Argument.ts b/repos/effect/packages/effect/src/cli/Argument.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/cli/Argument.ts rename to repos/effect/packages/effect/src/cli/Argument.ts index 47dd9b1d23..1d46bbd4c1 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Argument.ts +++ b/repos/effect/packages/effect/src/cli/Argument.ts @@ -7,15 +7,16 @@ * variadic arguments, schema validation, transformations, defaults, config * fallbacks, and prompts for missing values. * + * @stability unstable * @since 4.0.0 */ -import type * as Config from "../../Config.ts" -import type * as Effect from "../../Effect.ts" -import { dual, type LazyArg } from "../../Function.ts" -import type * as Option from "../../Option.ts" -import type * as Redacted_ from "../../Redacted.ts" -import type * as Result from "../../Result.ts" -import type * as Schema from "../../Schema.ts" +import type * as Config from "../Config.ts" +import type * as Effect from "../Effect.ts" +import { dual, type LazyArg } from "../Function.ts" +import type * as Option from "../Option.ts" +import type * as Redacted_ from "../Redacted.ts" +import type * as Result from "../Result.ts" +import type * as Schema from "../Schema.ts" import type * as CliError from "./CliError.ts" import type { Environment } from "./Command.ts" import * as Param from "./Param.ts" @@ -32,6 +33,7 @@ import type * as Primitive from "./Primitive.ts" * * For booleans, use `Flag.Boolean` or `Argument.Literals` with "true" and "false". * + * @stability unstable * @category models * @since 4.0.0 */ @@ -47,12 +49,13 @@ export interface Argument extends Param.Param { * **Example** (Creating a string argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const filename = Argument.String("filename") * filename.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -64,12 +67,13 @@ export const String = (name: string): Argument => Param.String(Param.arg * **Example** (Creating an integer argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const count = Argument.Int("count") * count.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -81,13 +85,14 @@ export const Int = (name: string): Argument => Param.Int(Param.argumentK * **Example** (Creating file path arguments) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const inputFile = Argument.File("input", { mustExist: true }) // Must exist * const outputFile = Argument.File("output", { mustExist: false }) // Must not exist * const kinds = [inputFile.kind, outputFile.kind] // => ["argument", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -101,12 +106,13 @@ export const File = (name: string, options?: { * **Example** (Creating a directory path argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const workspace = Argument.Directory("workspace", { mustExist: true }) // Must exist * workspace.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -120,12 +126,13 @@ export const Directory = (name: string, options?: { * **Example** (Parsing a finite number) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const ratio = Argument.Finite("ratio") * ratio.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -137,12 +144,13 @@ export const Finite = (name: string): Argument => Param.Finite(Param.arg * **Example** (Creating a date argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const startDate = Argument.Date("start-date") * startDate.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -154,12 +162,13 @@ export const Date = (name: string): Argument => Param.Date(Para * **Example** (Creating a choice argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const environment = Argument.Literals("environment", ["dev", "staging", "prod"]) * environment.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -174,12 +183,13 @@ export const Literals = >( * **Example** (Creating a path argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const configPath = Argument.Path("config") * configPath.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -194,12 +204,13 @@ export const Path = (name: string, options?: { * **Example** (Creating a redacted argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const secret = Argument.Redacted("secret") * secret.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -211,12 +222,13 @@ export const Redacted = (name: string): Argument> => * **Example** (Reading file text) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const config = Argument.FileText("config-file") * config.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -234,12 +246,13 @@ export const FileText = (name: string): Argument => Param.FileText(Param * **Example** (Parsing file content) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const config = Argument.FileParse("config", { format: "json" }) * config.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -255,7 +268,7 @@ export const FileParse = ( * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const ConfigSchema = Schema.Struct({ * port: Schema.Number, @@ -266,6 +279,7 @@ export const FileParse = ( * config.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -281,12 +295,13 @@ export const FileSchema = ( * **Example** (Creating a sentinel argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const noArg = Argument.Never * noArg.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -302,12 +317,13 @@ export const Never: Argument = Param.Never(Param.argumentKind) * **Example** (Making an argument optional) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const optionalVersion = Argument.String("version").pipe(Argument.optional) * optionalVersion.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -319,7 +335,7 @@ export const optional = (arg: Argument): Argument> => Par * **Example** (Adding an argument description) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const filename = Argument.String("filename").pipe( * Argument.withDescription("The input file to process") @@ -327,6 +343,7 @@ export const optional = (arg: Argument): Argument> => Par * filename.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -341,12 +358,13 @@ export const withDescription: { * **Example** (Providing a default value) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const port = Argument.Int("port").pipe(Argument.withDefault(8080)) * port.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -367,7 +385,7 @@ export const withDefault: { * * ```ts import.meta.vitest * import { Config } from "effect" - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const repository = Argument.String("repository").pipe( * Argument.withFallbackConfig(Config.String("REPOSITORY")) @@ -375,6 +393,7 @@ export const withDefault: { * repository.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -389,7 +408,7 @@ export const withFallbackConfig: { * **Example** (Showing a fallback prompt) * * ```ts import.meta.vitest - * import { Argument, Prompt } from "effect/unstable/cli" + * import { Argument, Prompt } from "effect/cli" * * const filename = Argument.String("filename").pipe( * Argument.withFallbackPrompt(Prompt.String({ message: "Filename" })) @@ -397,6 +416,7 @@ export const withFallbackConfig: { * filename.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -411,7 +431,7 @@ export const withFallbackPrompt: { * **Example** (Accepting multiple values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * // Accept any number of files * const anyFiles = Argument.String("files").pipe(Argument.variadic) @@ -429,6 +449,7 @@ export const withFallbackPrompt: { * const kinds = [anyFiles.kind, atLeastOneFile.kind, limitedFiles.kind] // => ["argument", "argument", "argument"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -446,7 +467,7 @@ export const variadic: { * **Example** (Mapping parsed values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const port = Argument.Int("port").pipe( * Argument.map((p) => ({ port: p, url: `http://localhost:${p}` })) @@ -454,6 +475,7 @@ export const variadic: { * port.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -469,8 +491,8 @@ export const map: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Argument, CliError } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Argument, CliError } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -508,6 +530,7 @@ export const map: { * value // => "notes.txt" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -531,8 +554,8 @@ export const mapEffect: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Argument } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Argument } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -565,6 +588,7 @@ export const mapEffect: { * value // => { enabled: true } * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -586,12 +610,13 @@ export const mapTryCatch: { * **Example** (Requiring a minimum number of values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const files = Argument.String("files").pipe(Argument.atLeast(1)) * files.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -606,12 +631,13 @@ export const atLeast: { * **Example** (Limiting the maximum number of values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const files = Argument.String("files").pipe(Argument.atMost(5)) * files.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -626,12 +652,13 @@ export const atMost: { * **Example** (Requiring a range of values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const files = Argument.String("files").pipe(Argument.between(1, 5)) * files.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -647,7 +674,7 @@ export const between: { * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const input = Argument.String("input").pipe( * Argument.withSchema(Schema.NonEmptyString) @@ -655,6 +682,7 @@ export const between: { * input.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -673,7 +701,7 @@ export const withSchema: { * **Example** (Mapping choices to values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const logLevel = Argument.ChoiceWithValue("level", [ * ["debug", 0], @@ -684,6 +712,7 @@ export const withSchema: { * logLevel.kind // => "argument" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -707,7 +736,7 @@ export const ChoiceWithValue = "argument" * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -729,7 +759,7 @@ export const withMetavar: { * **Example** (Filtering parsed values) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const positiveInt = Argument.Int("count").pipe( * Argument.filter( @@ -740,6 +770,7 @@ export const withMetavar: { * positiveInt.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -760,7 +791,7 @@ export const filter: { * * ```ts import.meta.vitest * import { Option } from "effect" - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const positiveInt = Argument.Int("count").pipe( * Argument.filterMap( @@ -771,6 +802,7 @@ export const filter: { * positiveInt.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -789,7 +821,7 @@ export const filterMap: { * **Example** (Providing a fallback argument) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const value = Argument.Int("value").pipe( * Argument.orElse(() => Argument.String("value")) @@ -797,6 +829,7 @@ export const filterMap: { * value.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -811,7 +844,7 @@ export const orElse: { * **Example** (Returning which fallback succeeded) * * ```ts import.meta.vitest - * import { Argument } from "effect/unstable/cli" + * import { Argument } from "effect/cli" * * const source = Argument.File("source").pipe( * Argument.orElseResult(() => Argument.String("url")) @@ -820,6 +853,7 @@ export const orElse: { * source.kind // => "argument" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/CliConfig.ts b/repos/effect/packages/effect/src/cli/CliConfig.ts similarity index 87% rename from repos/effect/packages/effect/src/unstable/cli/CliConfig.ts rename to repos/effect/packages/effect/src/cli/CliConfig.ts index cab8e1687f..fac455cdc1 100644 --- a/repos/effect/packages/effect/src/unstable/cli/CliConfig.ts +++ b/repos/effect/packages/effect/src/cli/CliConfig.ts @@ -1,10 +1,11 @@ /** * Configuration for Effect CLI command execution. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Layer from "../../Layer.ts" +import * as Context from "../Context.ts" +import * as Layer from "../Layer.ts" import * as GlobalFlag from "./GlobalFlag.ts" /** @@ -16,22 +17,25 @@ import * as GlobalFlag from "./GlobalFlag.ts" * Use when you need to customize runner-wide CLI behavior, such as which * built-in global flags are available. * + * @stability unstable * @category services * @since 4.0.0 */ -export class CliConfig extends Context.Reference("effect/unstable/cli/CliConfig", { +export class CliConfig extends Context.Reference("effect/cli/CliConfig", { defaultValue: () => defaults }) {} /** * Types used by the `CliConfig` context reference. * + * @stability unstable * @since 4.0.0 */ export declare namespace CliConfig { /** * Configuration values used while running a CLI command. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -44,6 +48,7 @@ export declare namespace CliConfig { /** * Default CLI configuration containing every built-in global flag. * + * @stability unstable * @category defaults * @since 4.0.0 */ @@ -61,6 +66,7 @@ export const defaults: CliConfig.Service = { * * @see {@link layer} for providing configuration as a layer * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -78,6 +84,7 @@ export const make = (options?: Partial): CliConfig.Service => * * @see {@link make} for creating a configuration value directly * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/CliError.ts b/repos/effect/packages/effect/src/cli/CliError.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cli/CliError.ts rename to repos/effect/packages/effect/src/cli/CliError.ts index 9588423096..99afc264f5 100644 --- a/repos/effect/packages/effect/src/unstable/cli/CliError.ts +++ b/repos/effect/packages/effect/src/cli/CliError.ts @@ -8,13 +8,15 @@ * error classes with display messages, and the `NonShowHelpErrors` union used * when parse or validation errors should be shown with help output. * + * @stability unstable * @since 4.0.0 */ -import * as Predicate from "../../Predicate.ts" -import * as Runtime from "../../Runtime.ts" -import * as Schema from "../../Schema.ts" +import * as Predicate from "../Predicate.ts" +import * as Runtime from "../Runtime.ts" +import * as Schema from "../Schema.ts" /** + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -27,7 +29,7 @@ const TypeId = "~effect/cli/CliError" * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const error = new CliError.MissingOption({ option: "api-key" }) * const program = CliError.isCliError(error) @@ -37,6 +39,7 @@ const TypeId = "~effect/cli/CliError" * await Effect.runPromise(program) // => "Missing required flag: --api-key" * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -48,7 +51,7 @@ export const isCliError = (u: unknown): u is CliError => Predicate.hasProperty(u * **Example** (Handling CLI errors) * * ```ts import.meta.vitest - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const describe = (error: CliError.CliError): string => { * switch (error._tag) { @@ -68,6 +71,7 @@ export const isCliError = (u: unknown): u is CliError => Predicate.hasProperty(u * describe(new CliError.MissingOption({ option: "token" })) // => "Required flag missing: token" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -89,7 +93,7 @@ export type CliError = * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * // Creating an unrecognized option error * const unrecognizedError = new CliError.UnrecognizedOption({ @@ -112,6 +116,7 @@ export type CliError = * parseError._tag // => "UnrecognizedOption" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -125,6 +130,7 @@ export class UnrecognizedOption extends Schema.TaggedError( /** * Marks this value as a CLI parsing error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -132,6 +138,7 @@ export class UnrecognizedOption extends Schema.TaggedError( /** * Formats the unrecognized option with command context and suggestions. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -151,7 +158,7 @@ export class UnrecognizedOption extends Schema.TaggedError( * **Example** (Creating duplicate option errors) * * ```ts import.meta.vitest - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const duplicateError = new CliError.DuplicateOption({ * option: "--verbose", @@ -165,6 +172,7 @@ export class UnrecognizedOption extends Schema.TaggedError( * duplicateError.childCommand // => "deploy" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -178,6 +186,7 @@ export class DuplicateOption extends Schema.TaggedError( /** * Marks this value as a CLI configuration error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -185,6 +194,7 @@ export class DuplicateOption extends Schema.TaggedError( /** * Explains which parent and child commands define the duplicate option. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -200,7 +210,7 @@ export class DuplicateOption extends Schema.TaggedError( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const missingOptionError = new CliError.MissingOption({ * option: "api-key" @@ -222,6 +232,7 @@ export class DuplicateOption extends Schema.TaggedError( * validationError._tag // => "MissingOption" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -233,6 +244,7 @@ export class MissingOption extends Schema.TaggedError( /** * Marks this value as a missing CLI option error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -240,6 +252,7 @@ export class MissingOption extends Schema.TaggedError( /** * Formats the missing required flag for display. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -254,7 +267,7 @@ export class MissingOption extends Schema.TaggedError( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const missingArgError = new CliError.MissingArgument({ * argument: "target" @@ -275,6 +288,7 @@ export class MissingOption extends Schema.TaggedError( * parseError._tag // => "MissingArgument" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -286,6 +300,7 @@ export class MissingArgument extends Schema.TaggedError( /** * Marks this value as a missing CLI argument error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -293,6 +308,7 @@ export class MissingArgument extends Schema.TaggedError( /** * Formats the missing required positional argument for display. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -307,7 +323,7 @@ export class MissingArgument extends Schema.TaggedError( * **Example** (Reporting unexpected arguments) * * ```ts import.meta.vitest - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const error = new CliError.UnexpectedArgument({ * arguments: ["extra.txt"] @@ -316,6 +332,7 @@ export class MissingArgument extends Schema.TaggedError( * const details = [error._tag, error.arguments] // => ["UnexpectedArgument", ["extra.txt"]] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -327,6 +344,7 @@ export class UnexpectedArgument extends Schema.TaggedError( /** * Marks this value as an unexpected CLI argument error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -334,6 +352,7 @@ export class UnexpectedArgument extends Schema.TaggedError( /** * Formats the unexpected positional arguments for display. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -348,7 +367,7 @@ export class UnexpectedArgument extends Schema.TaggedError( * **Example** (Creating invalid value errors) * * ```ts import.meta.vitest - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const invalidValueError = new CliError.InvalidValue({ * option: "port", @@ -373,6 +392,7 @@ export class UnexpectedArgument extends Schema.TaggedError( * const details = [invalidArgError.kind, invalidArgError.option, invalidArgError.value] // => ["argument", "count", "abc"] * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -387,6 +407,7 @@ export class InvalidValue extends Schema.TaggedError( /** * Marks this value as an invalid CLI value error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -394,6 +415,7 @@ export class InvalidValue extends Schema.TaggedError( /** * Formats the invalid flag or argument value with the expected input. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -417,7 +439,7 @@ export class InvalidValue extends Schema.TaggedError( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * const unknownSubcommandError = new CliError.UnknownSubcommand({ * subcommand: "deplyo", // typo @@ -443,6 +465,7 @@ export class InvalidValue extends Schema.TaggedError( * parseError._tag // => "UnknownSubcommand" * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -456,6 +479,7 @@ export class UnknownSubcommand extends Schema.TaggedError( /** * Marks this value as an unknown CLI subcommand error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -463,6 +487,7 @@ export class UnknownSubcommand extends Schema.TaggedError( /** * Formats the unknown subcommand with parent command context and suggestions. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -488,7 +513,7 @@ export class UnknownSubcommand extends Schema.TaggedError( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliError } from "effect/unstable/cli" + * import { CliError } from "effect/cli" * * // Wrapping user errors * const userError = new CliError.UserError({ @@ -517,6 +542,7 @@ export class UnknownSubcommand extends Schema.TaggedError( * await Effect.runPromise(handleError(userError)) // => 1 * ``` * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -529,6 +555,7 @@ export class UserError extends Schema.TaggedError( /** * Marks this value as a user handler error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -537,6 +564,7 @@ export class UserError extends Schema.TaggedError( * Controls whether the runtime logger should report this error. The CLI * runner sets this to `false` after rendering the error itself. * + * @stability unstable * @since 4.0.0 */ override [Runtime.errorReported] = true @@ -544,6 +572,7 @@ export class UserError extends Schema.TaggedError( /** * Returns the explicit user-facing message or a safe fallback from `cause`. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -562,6 +591,7 @@ export class UserError extends Schema.TaggedError( * This excludes `ShowHelp` itself, allowing parse and validation errors to be * stored in `ShowHelp.errors` without nesting another help-control value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -596,6 +626,7 @@ export const NonShowHelpErrors: Schema.Union< * runner should display help along with the underlying parse or validation * failures. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -610,6 +641,7 @@ export type NonShowHelpErrors = typeof NonShowHelpErrors.Type * that should be shown with help text. When `errors` is non-empty, the runtime * exit code is `1`; otherwise it is `0`. * + * @stability unstable * @category errors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/CliOutput.ts b/repos/effect/packages/effect/src/cli/CliOutput.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/cli/CliOutput.ts rename to repos/effect/packages/effect/src/cli/CliOutput.ts index bdceb7bb76..96f7debdbe 100644 --- a/repos/effect/packages/effect/src/unstable/cli/CliOutput.ts +++ b/repos/effect/packages/effect/src/cli/CliOutput.ts @@ -7,12 +7,13 @@ * for custom formatters, and the default formatter with configurable color * support. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" +import * as Context from "../Context.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" import type * as CliError from "./CliError.ts" import type { HelpDoc } from "./HelpDoc.ts" @@ -24,7 +25,7 @@ import type { HelpDoc } from "./HelpDoc.ts" * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * // Create a custom formatter implementation * const customFormatter: CliOutput.Formatter = { @@ -46,6 +47,7 @@ import type { HelpDoc } from "./HelpDoc.ts" * await Effect.runPromise(program) // => "myapp (1.0.0)" * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -57,8 +59,8 @@ export interface Formatter { * * ```ts import.meta.vitest * import { Option as O } from "effect" - * import { CliOutput } from "effect/unstable/cli" - * import type { HelpDoc } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" + * import type { HelpDoc } from "effect/cli" * * const helpDoc: HelpDoc = { * usage: "myapp [options] ", @@ -88,6 +90,7 @@ export interface Formatter { * const sectionsPresent = [helpText.includes("DESCRIPTION"), helpText.includes("FLAGS")] // => [true, true] * ``` * + * @stability unstable * @since 4.0.0 */ readonly formatHelpDoc: (doc: HelpDoc) => string @@ -99,7 +102,7 @@ export interface Formatter { * * ```ts import.meta.vitest * import { Data } from "effect" - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * class InvalidOption extends Data.TaggedError("InvalidOption")<{ * readonly message: string @@ -111,6 +114,7 @@ export interface Formatter { * errorMessage // => "Unknown flag '--invalid'" * ``` * + * @stability unstable * @since 4.0.0 */ readonly formatCliError: (error: CliError.CliError) => string @@ -122,7 +126,7 @@ export interface Formatter { * * ```ts import.meta.vitest * import { Data } from "effect" - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * class ValidationError extends Data.TaggedError("ValidationError")<{ * readonly message: string @@ -140,6 +144,7 @@ export interface Formatter { * plainError // => "\nERROR\n Value must be positive" * ``` * + * @stability unstable * @since 4.0.0 */ readonly formatError: (error: CliError.CliError) => string @@ -150,7 +155,7 @@ export interface Formatter { * **Example** (Formatting version output) * * ```ts import.meta.vitest - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * const colorFormatter = CliOutput.defaultFormatter({ colors: true }) * const noColorFormatter = CliOutput.defaultFormatter({ colors: false }) @@ -165,6 +170,7 @@ export interface Formatter { * plainVersion // => "my-awesome-tool v1.2.3" * ``` * + * @stability unstable * @since 4.0.0 */ readonly formatVersion: (name: string, version: string) => string @@ -175,7 +181,7 @@ export interface Formatter { * **Example** (Formatting grouped errors) * * ```ts import.meta.vitest - * import { CliError, CliOutput } from "effect/unstable/cli" + * import { CliError, CliOutput } from "effect/cli" * * const formatter = CliOutput.defaultFormatter({ colors: false }) * @@ -192,6 +198,7 @@ export interface Formatter { * const optionsPresent = [output.includes("--foo"), output.includes("--required")] // => [true, true] * ``` * + * @stability unstable * @since 4.0.0 */ readonly formatErrors: (errors: ReadonlyArray) => string @@ -205,7 +212,7 @@ export interface Formatter { * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * // Access the formatter service * const program = Effect.gen(function*() { @@ -221,6 +228,7 @@ export interface Formatter { * )) // => "my-cli v2.1.0" * ``` * + * @stability unstable * @category services * @since 4.0.0 */ @@ -236,7 +244,7 @@ export const Formatter: Context.Reference = Context.Reference( * * ```ts import.meta.vitest * import { Effect } from "effect" - * import { CliOutput } from "effect/unstable/cli" + * import { CliOutput } from "effect/cli" * * // Create a custom formatter without colors * const noColorFormatter = CliOutput.defaultFormatter({ colors: false }) @@ -253,6 +261,7 @@ export const Formatter: Context.Reference = Context.Reference( * await Effect.runPromise(program) // => "my-cli v1.0.0" * ``` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -269,7 +278,7 @@ const escapeControlCharacters = (text: string): string => * **Example** (Creating default formatters) * * ```ts import.meta.vitest - * import { CliError, CliOutput } from "effect/unstable/cli" + * import { CliError, CliOutput } from "effect/cli" * * // Create a formatter without colors for tests or CI environments * const noColorFormatter = CliOutput.defaultFormatter({ colors: false }) @@ -292,6 +301,7 @@ const escapeControlCharacters = (text: string): string => * autoFormatter.formatVersion("my-tool", "1.2.3").includes("1.2.3") // => true * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/Command.ts b/repos/effect/packages/effect/src/cli/Command.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/cli/Command.ts rename to repos/effect/packages/effect/src/cli/Command.ts index e932f3bba8..62fa0f31ff 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Command.ts +++ b/repos/effect/packages/effect/src/cli/Command.ts @@ -8,25 +8,26 @@ * handle built-in help and version behavior, render help through `CliOutput`, * and execute the selected handler. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyArray, NonEmptyReadonlyArray } from "../../Array.ts" -import * as Console from "../../Console.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import type * as FileSystem from "../../FileSystem.ts" -import { dual } from "../../Function.ts" -import type * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import type * as Path from "../../Path.ts" -import * as Predicate from "../../Predicate.ts" -import * as References from "../../References.ts" -import * as Result from "../../Result.ts" -import * as Runtime from "../../Runtime.ts" -import * as Stdio from "../../Stdio.ts" -import * as Terminal from "../../Terminal.ts" -import type { Contravariant, Covariant, NoInfer, Simplify } from "../../Types.ts" +import type { NonEmptyArray, NonEmptyReadonlyArray } from "../Array.ts" +import * as Console from "../Console.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import type * as FileSystem from "../FileSystem.ts" +import { dual } from "../Function.ts" +import type * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import type * as Path from "../Path.ts" +import * as Predicate from "../Predicate.ts" import type { ChildProcessSpawner } from "../process/ChildProcessSpawner.ts" +import * as References from "../References.ts" +import * as Result from "../Result.ts" +import * as Runtime from "../Runtime.ts" +import * as Stdio from "../Stdio.ts" +import * as Terminal from "../Terminal.ts" +import type { Contravariant, Covariant, NoInfer, Simplify } from "../Types.ts" import * as CliConfig from "./CliConfig.ts" import * as CliError from "./CliError.ts" import * as CliOutput from "./CliOutput.ts" @@ -60,8 +61,8 @@ import * as Prompt from "./Prompt.ts" * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Argument, Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Argument, Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -114,6 +115,7 @@ import * as Prompt from "./Prompt.ts" * output // => ["Hello, Alice!"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -177,6 +179,7 @@ export interface Command ["flag", "flag"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -262,6 +268,7 @@ export declare namespace Command { * * Only flags are allowed here; arguments are intentionally excluded. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -275,6 +282,7 @@ export declare namespace Command { /** * Utilities for working with command configurations. * + * @stability unstable * @since 4.0.0 */ export namespace Config { @@ -289,8 +297,8 @@ export declare namespace Command { * **Example** (Inferring command input) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" - * import type { Command as CliCommand } from "effect/unstable/cli" + * import { Flag } from "effect/cli" + * import type { Command as CliCommand } from "effect/cli" * * const config = { * name: Flag.String("name"), @@ -316,6 +324,7 @@ export declare namespace Command { * inferred // => { name: "Alice", server: { host: "localhost", port: 8080 } } * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -326,6 +335,7 @@ export declare namespace Command { /** * Helper type utility for recursively inferring types from Config values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -338,6 +348,7 @@ export declare namespace Command { /** * Represents any Command regardless of its type parameters. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -359,6 +370,7 @@ export declare namespace Command { /** * A grouped set of subcommands used by `Command.withSubcommands`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -370,6 +382,7 @@ export declare namespace Command { /** * Entry type accepted by `Command.withSubcommands`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -385,6 +398,7 @@ export declare namespace Command { * stdio services for running commands, and child-process spawning for * process-related CLI features. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -393,6 +407,7 @@ export type Environment = FileSystem.FileSystem | Path.Path | Terminal.Terminal /** * A utility type to extract the error type from a `Command`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -408,6 +423,7 @@ export type Error = C extends Command< /** * A utility type to extract the required services type from a `Command`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -434,8 +450,8 @@ export type Services = C extends Command< * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -488,6 +504,7 @@ export type Services = C extends Command< * output // => ["Verbose: true", "Config: prod.json", "Target: staging"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -499,6 +516,7 @@ export interface CommandContext { /** * Represents the parsed tokens from command-line input before validation. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -520,6 +538,7 @@ export interface ParsedTokens { * This checks for the `Command` type-id property; it does not validate the full * command shape. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -543,8 +562,8 @@ export const isCommand = (u: unknown): u is Command.Any => Predicate.hasProperty * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Argument, Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Argument, Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -614,6 +633,7 @@ export const isCommand = (u: unknown): u is Command.Any => Predicate.hasProperty * output // => ["Starting deployment to staging", "Deployment completed successfully"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -654,8 +674,8 @@ export const make: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -695,6 +715,7 @@ export const make: { * output // => ["Hello, Alice!"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -778,8 +799,8 @@ const normalizeSubcommandEntries = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -831,6 +852,7 @@ const normalizeSubcommandEntries = ( * output // => ["Verbose mode enabled", "Cloning github.com/foo/bar"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -950,6 +972,7 @@ export const withSubcommands: { * handlers via `yield* parentCommand`. Shared flags are accepted both before * and after a selected subcommand name (npm-style). * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1049,6 +1072,7 @@ export const withSharedFlags: { * * Declared global flags apply to the command and all of its descendants. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1112,8 +1136,8 @@ type ExtractSubcommandContext> * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1148,6 +1172,7 @@ type ExtractSubcommandContext> * output // => ["Deploying to staging"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1173,6 +1198,7 @@ export const withDescription: { * shell completions. If no short description is provided, the full * `description` is used as a fallback. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1197,6 +1223,7 @@ export const withShortDescription: { * Aliases are accepted as alternate subcommand names during parsing and are * shown in help output as `name, alias`. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1225,7 +1252,7 @@ export const withAlias: { * **Example** (Unlisting a subcommand) * * ```ts import.meta.vitest - * import { Command } from "effect/unstable/cli" + * import { Command } from "effect/cli" * * // `experimental` still runs when invoked as `mycli experimental`, * // but it does not appear under SUBCOMMANDS in `mycli --help`. @@ -1240,6 +1267,7 @@ export const withAlias: { * root.subcommands[0].commands[0].unlisted // => true * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1267,6 +1295,7 @@ export const unlisted = ( * * @see {@link annotateMerge} for merging an existing annotation context * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1311,6 +1340,7 @@ export const annotate: { * * @see {@link annotate} for adding a single annotation without constructing a `Context` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1343,7 +1373,7 @@ export const annotateMerge: { * **Example** (Adding usage examples) * * ```ts import.meta.vitest - * import { Command } from "effect/unstable/cli" + * import { Command } from "effect/cli" * * const login = Command.make("login").pipe( * Command.withExamples([ @@ -1355,6 +1385,7 @@ export const annotateMerge: { * login.examples.map((example) => example.command) // => ["myapp login", "myapp login --token sbp_abc123"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1392,8 +1423,8 @@ const mapHandler = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, PlatformError, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1442,6 +1473,7 @@ const mapHandler = ( * output // => ["Using file system for local"] * ``` * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -1480,6 +1512,7 @@ export const provide: { * Use when a command handler needs a pure service implementation, optionally * derived from the parsed command input. * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -1520,6 +1553,7 @@ export const provideSync: { * @see {@link provide} for providing an already-available service * @see {@link provideEffectDiscard} for running an effect before the handler without providing a service * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -1550,6 +1584,7 @@ export const provideEffect: { * Allows for execution of an effect, which optionally depends on command-line * input to be created, prior to executing the handler of a command. * + * @stability unstable * @category providing services * @since 4.0.0 */ @@ -1585,8 +1620,8 @@ export const provideEffectDiscard: { * * ```ts import.meta.vitest * import { Console, Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1618,6 +1653,7 @@ export const provideEffectDiscard: { * await Effect.runPromise(program) // => ["app"] * ``` * + * @stability unstable * @category running * @since 4.0.0 */ @@ -1707,8 +1743,8 @@ const showUserError = (error: CliError.UserError): Effect.Effect => * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1748,6 +1784,7 @@ const showUserError = (error: CliError.UserError): Effect.Effect => * * @see {@link runWith} for running a command with an explicit argument array * + * @stability unstable * @category running * @since 4.0.0 */ @@ -1796,8 +1833,8 @@ export const run: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Command, Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Command, Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1838,6 +1875,7 @@ export const run: { * output // => ["Hello, Alice!", "Hello, Alice!"] * ``` * + * @stability unstable * @category running * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/Completions.ts b/repos/effect/packages/effect/src/cli/Completions.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cli/Completions.ts rename to repos/effect/packages/effect/src/cli/Completions.ts index e2b32b0226..0d564e3716 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Completions.ts +++ b/repos/effect/packages/effect/src/cli/Completions.ts @@ -4,6 +4,7 @@ * low-level script generation surface used by the unstable CLI package and by * the built-in completions global flag. * + * @stability unstable * @since 4.0.0 */ import * as Bash from "./internal/completions/bash.ts" @@ -13,6 +14,7 @@ import * as Zsh from "./internal/completions/zsh.ts" /** * Shell type used to generate completion scripts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -21,6 +23,7 @@ export type Shell = "bash" | "zsh" | "fish" /** * Describes a command for completion script generation. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -35,6 +38,7 @@ export interface CommandDescriptor { /** * Describes a command flag for completions. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -48,6 +52,7 @@ export interface FlagDescriptor { /** * Describes the supported flag value shapes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -63,6 +68,7 @@ export type FlagType = /** * Describes a positional argument for completions. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -77,6 +83,7 @@ export interface ArgumentDescriptor { /** * Describes the supported argument value shapes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -104,6 +111,7 @@ export type ArgumentType = * @see {@link Shell} for supported shell names * @see {@link CommandDescriptor} for the command shape used by completion generation * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/Flag.ts b/repos/effect/packages/effect/src/cli/Flag.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cli/Flag.ts rename to repos/effect/packages/effect/src/cli/Flag.ts index f5aa25bcc6..f18b0c447a 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Flag.ts +++ b/repos/effect/packages/effect/src/cli/Flag.ts @@ -8,15 +8,16 @@ * defaults, optional values, prompts, configuration fallbacks, validation, and * value transformations. * + * @stability unstable * @since 4.0.0 */ -import type * as Config from "../../Config.ts" -import type * as Effect from "../../Effect.ts" -import { dual, type LazyArg } from "../../Function.ts" -import type * as Option from "../../Option.ts" -import type * as Redacted_ from "../../Redacted.ts" -import type * as Result from "../../Result.ts" -import type * as Schema from "../../Schema.ts" +import type * as Config from "../Config.ts" +import type * as Effect from "../Effect.ts" +import { dual, type LazyArg } from "../Function.ts" +import type * as Option from "../Option.ts" +import type * as Redacted_ from "../Redacted.ts" +import type * as Result from "../Result.ts" +import type * as Schema from "../Schema.ts" import type * as CliError from "./CliError.ts" import type { Environment } from "./Command.ts" import * as Param from "./Param.ts" @@ -29,6 +30,7 @@ import type * as Primitive from "./Primitive.ts" /** * Represents a command-line flag. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -44,13 +46,14 @@ export interface Flag extends Param.Param {} * **Example** (Creating string flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const nameFlag = Flag.String("name") * // Usage: --name "John Doe" * nameFlag.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -62,7 +65,7 @@ export const String = (name: string): Flag => Param.String(Param.flagKin * **Example** (Creating boolean flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const verboseFlag = Flag.Boolean("verbose") * // Usage: --verbose (true) or --no-verbose (false) @@ -70,6 +73,7 @@ export const String = (name: string): Flag => Param.String(Param.flagKin * verboseFlag.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -81,13 +85,14 @@ export const Boolean = (name: string): Flag => Param.Boolean(Param.flag * **Example** (Creating integer flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const portFlag = Flag.Int("port") * // Usage: --port 8080 * portFlag.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -99,13 +104,14 @@ export const Int = (name: string): Flag => Param.Int(Param.flagKind, nam * **Example** (Creating float flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const rateFlag = Flag.Finite("rate") * // Usage: --rate 3.14 * rateFlag.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -117,13 +123,14 @@ export const Finite = (name: string): Flag => Param.Finite(Param.flagKin * **Example** (Creating date flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const startDateFlag = Flag.Date("start-date") * // Usage: --start-date 2023-12-25 * startDateFlag.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -136,7 +143,7 @@ export const Date = (name: string): Flag => Param.Date(Param.fl * **Example** (Creating flag choices with values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // simple enum like choice mapping directly to string union * const color = Flag.Literals("color", ["red", "green", "blue"]) @@ -150,6 +157,7 @@ export const Date = (name: string): Flag => Param.Date(Param.fl * const kinds = [color.kind, logLevel.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -163,6 +171,7 @@ export const ChoiceWithValue = >( * **Example** (Creating path flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Basic path flag * const pathFlag = Flag.Path("config-path") @@ -196,6 +205,7 @@ export const Literals = >( * const kinds = [pathFlag.kind, fileFlag.kind, dirFlag.kind] // => ["flag", "flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -211,7 +221,7 @@ export const Path = (name: string, options?: { * **Example** (Creating file flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Basic file flag * const inputFlag = Flag.File("input") @@ -223,6 +233,7 @@ export const Path = (name: string, options?: { * const kinds = [inputFlag.kind, configFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -236,7 +247,7 @@ export const File = (name: string, options?: { * **Example** (Creating directory flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Basic directory flag * const outputFlag = Flag.Directory("output") @@ -248,6 +259,7 @@ export const File = (name: string, options?: { * const kinds = [outputFlag.kind, sourceFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -268,8 +280,8 @@ export const Directory = (name: string, options?: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Redacted, Stdio, Terminal } from "effect" - * import { Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -301,6 +313,7 @@ export const Directory = (name: string, options?: { * await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer))) // => 6 * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -312,13 +325,14 @@ export const Redacted = (name: string): Flag> => Para * **Example** (Reading file text) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const config = Flag.FileText("config-file") * // --config-file ./app.json will read the file content * config.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -335,7 +349,7 @@ export const FileText = (name: string): Flag => Param.FileText(Param.fla * **Example** (Parsing file contents) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Will use the extension of the file passed on the command line to determine * // the parser to use @@ -346,6 +360,7 @@ export const FileText = (name: string): Flag => Param.FileText(Param.fla * const kinds = [config.kind, jsonConfig.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -362,7 +377,7 @@ export const FileParse = ( * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const ConfigSchema = Schema.Struct({ * port: Schema.Number, @@ -373,6 +388,7 @@ export const FileParse = ( * config.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -398,13 +414,14 @@ export const FileSchema = ( * **Example** (Parsing key-value pairs) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const env = Flag.KeyValuePair("env") * // --env FOO=bar --env BAZ=qux will parse to { FOO: "bar", BAZ: "qux" } * env.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -416,7 +433,7 @@ export const KeyValuePair = (name: string): Flag> => Para * **Example** (Creating sentinel flags) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const makeValueFlag = (includeValue: boolean) => * includeValue ? Flag.String("value") : Flag.Never @@ -425,6 +442,7 @@ export const KeyValuePair = (name: string): Flag> => Para * makeValueFlag(false) === Flag.Never // => true * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -440,7 +458,7 @@ export const Never: Flag = Param.Never(Param.flagKind) * **Example** (Adding flag aliases) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Flag can be used as both --verbose and -v * const verboseFlag = Flag.Boolean("verbose").pipe( @@ -455,6 +473,7 @@ export const Never: Flag = Param.Never(Param.flagKind) * const kinds = [verboseFlag.kind, helpFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category aliasing * @since 4.0.0 */ @@ -469,7 +488,7 @@ export const withAlias: { * **Example** (Adding help descriptions) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const portFlag = Flag.Int("port").pipe( * Flag.withDescription("The port number to listen on") @@ -481,6 +500,7 @@ export const withAlias: { * const kinds = [portFlag.kind, configFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -504,7 +524,7 @@ export const withDescription: { * **Example** (Setting metavars) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const databaseFlag = Flag.String("database-url").pipe( * Flag.withMetavar("URL"), @@ -519,6 +539,7 @@ export const withDescription: { * const kinds = [databaseFlag.kind, timeoutFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -540,7 +561,7 @@ export const withMetavar: { * **Example** (Hiding a flag from help) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Flag still parses --experimental-foo, but it does not appear in --help. * const experimental = Flag.Boolean("experimental-foo").pipe( @@ -549,6 +570,7 @@ export const withMetavar: { * experimental.kind // => "flag" * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -561,8 +583,8 @@ export const withHidden = (self: Flag): Flag => Param.withHidden(self) * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Option, Path, Stdio, Terminal } from "effect" - * import { Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -594,6 +616,7 @@ export const withHidden = (self: Flag): Flag => Param.withHidden(self) * await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer))) // => Option.some(4000) * ``` * + * @stability unstable * @category optionality * @since 4.0.0 */ @@ -605,7 +628,7 @@ export const optional = (param: Flag): Flag> => Param.opt * **Example** (Providing default values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const portFlag = Flag.Int("port").pipe( * Flag.withDefault(8080) @@ -619,6 +642,7 @@ export const optional = (param: Flag): Flag> => Param.opt * const kinds = [portFlag.kind, hostFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category optionality * @since 4.0.0 */ @@ -634,7 +658,7 @@ export const withDefault: { * * ```ts import.meta.vitest * import { Config } from "effect" - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const verbose = Flag.Boolean("verbose").pipe( * Flag.withFallbackConfig(Config.Boolean("VERBOSE")) @@ -642,6 +666,7 @@ export const withDefault: { * verbose.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -656,7 +681,7 @@ export const withFallbackConfig: { * **Example** (Falling back to prompts) * * ```ts import.meta.vitest - * import { Flag, Prompt } from "effect/unstable/cli" + * import { Flag, Prompt } from "effect/cli" * * const name = Flag.String("name").pipe( * Flag.withFallbackPrompt(Prompt.String({ message: "Name" })) @@ -664,6 +689,7 @@ export const withFallbackConfig: { * name.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -678,7 +704,7 @@ export const withFallbackPrompt: { * **Example** (Mapping parsed values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Convert string to uppercase * const nameFlag = Flag.String("name").pipe( @@ -692,6 +718,7 @@ export const withFallbackPrompt: { * const kinds = [nameFlag.kind, urlFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category mapping * @since 4.0.0 */ @@ -707,8 +734,8 @@ export const map: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -740,6 +767,7 @@ export const map: { * value // => "ALICE" * ``` * + * @stability unstable * @category mapping * @since 4.0.0 */ @@ -763,8 +791,8 @@ export const mapEffect: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -808,6 +836,7 @@ export const mapEffect: { * value // => { enabled: true } * ``` * + * @stability unstable * @category mapping * @since 4.0.0 */ @@ -826,7 +855,7 @@ export const mapTryCatch: { * **Example** (Requiring repeated values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const sourceFlag = Flag.atLeast(Flag.File("source"), 2) * // Requires at least 2 source files @@ -839,6 +868,7 @@ export const mapTryCatch: { * const kinds = [sourceFlag.kind, tagFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category repetition * @since 4.0.0 */ @@ -853,7 +883,7 @@ export const atLeast: { * **Example** (Limiting repeated values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const warningFlag = Flag.atMost(Flag.String("warning"), 3) * // Allows up to 3 warning flags @@ -866,6 +896,7 @@ export const atLeast: { * const kinds = [warningFlag.kind, debugFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category repetition * @since 4.0.0 */ @@ -880,7 +911,7 @@ export const atMost: { * **Example** (Bounding repeated values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const hostFlag = Flag.between(Flag.String("host"), 1, 3) * // Requires 1-3 host flags @@ -893,6 +924,7 @@ export const atMost: { * const kinds = [hostFlag.kind, excludeFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category repetition * @since 4.0.0 */ @@ -908,7 +940,7 @@ export const between: { * * ```ts import.meta.vitest * import { Option } from "effect" - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Parse positive integers only * const positiveInt = Flag.Int("count").pipe( @@ -928,6 +960,7 @@ export const between: { * const kinds = [positiveInt.kind, emailFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category filtering * @since 4.0.0 */ @@ -946,7 +979,7 @@ export const filterMap: { * **Example** (Filtering parsed values) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Ensure port is in valid range * const portFlag = Flag.Int("port").pipe( @@ -966,6 +999,7 @@ export const filterMap: { * const kinds = [portFlag.kind, nameFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category filtering * @since 4.0.0 */ @@ -984,7 +1018,7 @@ export const filter: { * **Example** (Falling back to another flag) * * ```ts import.meta.vitest - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * // Try parsing as integer, fallback to string * const valueFlag = Flag.orElse( @@ -1000,6 +1034,7 @@ export const filter: { * const kinds = [valueFlag.kind, configFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category alternatives * @since 4.0.0 */ @@ -1015,8 +1050,8 @@ export const orElse: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Result, Stdio, Terminal } from "effect" - * import { Flag } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Flag } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1051,6 +1086,7 @@ export const orElse: { * await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer))) // => Result.fail("https://example.com") * ``` * + * @stability unstable * @category alternatives * @since 4.0.0 */ @@ -1066,7 +1102,7 @@ export const orElseResult: { * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Flag } from "effect/unstable/cli" + * import { Flag } from "effect/cli" * * const isEmail = Schema.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, { * message: "Must be a valid email address" @@ -1094,6 +1130,7 @@ export const orElseResult: { * const kinds = [emailFlag.kind, configFlag.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category schemas * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/GlobalFlag.ts b/repos/effect/packages/effect/src/cli/GlobalFlag.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cli/GlobalFlag.ts rename to repos/effect/packages/effect/src/cli/GlobalFlag.ts index cee0615b32..348e208159 100644 --- a/repos/effect/packages/effect/src/unstable/cli/GlobalFlag.ts +++ b/repos/effect/packages/effect/src/cli/GlobalFlag.ts @@ -8,14 +8,15 @@ * defines the built-in help, version, wizard, shell-completion, and log-level flags * used by `Command.run` and `Command.runWith`. * + * @stability unstable * @since 4.0.0 */ -import * as Console from "../../Console.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import type { LogLevel as LogLevelType } from "../../LogLevel.ts" -import * as Option from "../../Option.ts" +import * as Console from "../Console.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import type { LogLevel as LogLevelType } from "../LogLevel.ts" +import * as Option from "../Option.ts" import * as CliOutput from "./CliOutput.ts" import type * as Command from "./Command.ts" import * as Completions_ from "./Completions.ts" @@ -30,6 +31,7 @@ import * as HelpInternal from "./internal/help.ts" /** * Context passed to action handlers. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -43,6 +45,7 @@ export interface HandlerContext { /** * Action flag: side effect + exit (--help, --version, --completions). * + * @stability unstable * @category models * @since 4.0.0 */ @@ -58,6 +61,7 @@ export interface Action { /** * Setting flag: configure command handler's environment (--log-level, --config). * + * @stability unstable * @category services * @since 4.0.0 */ @@ -70,6 +74,7 @@ export interface Setting extends Context.Service = `effect/unstable/cli/GlobalFlag/${Id}` + export type Identifier = `effect/cli/GlobalFlag/${Id}` } /** * Global flag discriminated union. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -98,6 +105,7 @@ export type GlobalFlag = Action | Setting /** * Creates an Action flag that performs a side effect and exits. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -116,6 +124,7 @@ export const Action = (options: { /** * Creates a Setting flag that configures the command handler's environment. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -127,7 +136,7 @@ export const Setting = ( }): Setting => { settingIdCounter += 1 const ref = Context.Service, A>( - `effect/unstable/cli/GlobalFlag/${id}/${settingIdCounter}` + `effect/cli/GlobalFlag/${id}/${settingIdCounter}` ) return Object.assign(ref, { _tag: "Setting" as const, @@ -149,6 +158,7 @@ let settingIdCounter = 0 * @see {@link BuiltIns} for the default list containing this flag * @see {@link Action} for defining custom action global flags * + * @stability unstable * @category references * @since 4.0.0 */ @@ -172,6 +182,7 @@ export const Help: Action = Action({ * * Use to add a built-in `--version / -v` flag to a command runner. * + * @stability unstable * @category references * @since 4.0.0 */ @@ -195,6 +206,7 @@ export const Version: Action = Action({ * `Command.run` and `Command.runWith` handle this action specially so the * generated arguments can be passed back through the command parser. * + * @stability unstable * @category references * @since 4.0.0 */ @@ -215,6 +227,7 @@ export const Wizard: Action = Action({ * Accepted values are `bash`, `zsh`, `fish`, and `sh`; `sh` is normalized to * `bash`. * + * @stability unstable * @category references * @since 4.0.0 */ @@ -243,6 +256,7 @@ export const Completions: Action> = Actio * Use to add a built-in `--log-level` option that configures the minimum log * level for the command. * + * @stability unstable * @category references * @since 4.0.0 */ @@ -295,6 +309,7 @@ export const LogLevel: Setting<"log-level", Option.Option> = Setti * @see {@link Completions} for the shell-completions action flag * @see {@link LogLevel} for the built-in log-level setting flag * + * @stability unstable * @category references * @since 4.0.0 */ @@ -309,6 +324,7 @@ export const BuiltIns: readonly [ /** * Global flag included in the default command-runner configuration. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/HelpDoc.ts b/repos/effect/packages/effect/src/cli/HelpDoc.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/cli/HelpDoc.ts rename to repos/effect/packages/effect/src/cli/HelpDoc.ts index 6618c91a79..aee2d99705 100644 --- a/repos/effect/packages/effect/src/unstable/cli/HelpDoc.ts +++ b/repos/effect/packages/effect/src/cli/HelpDoc.ts @@ -7,12 +7,13 @@ * This module only defines the data shapes used to describe help. Rendering * that data as terminal text is handled by `CliOutput`. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import type * as Context from "../../Context.ts" -import type * as Option from "../../Option.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import type * as Context from "../Context.ts" +import type * as Option from "../Option.ts" /** * Structured representation of help documentation for a command. @@ -23,7 +24,7 @@ import type * as Option from "../../Option.ts" * * ```ts import.meta.vitest * import { Context, Option as O } from "effect" - * import type { HelpDoc } from "effect/unstable/cli" + * import type { HelpDoc } from "effect/cli" * * const deployCommandHelp: HelpDoc.HelpDoc = { * description: "Deploy your application to the cloud", @@ -59,6 +60,7 @@ import type * as Option from "../../Option.ts" * deployCommandHelp.usage // => "myapp deploy [options] " * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -108,6 +110,7 @@ export interface HelpDoc { /** * Documentation for a command usage example * + * @stability unstable * @category models * @since 4.0.0 */ @@ -130,7 +133,7 @@ export interface ExampleDoc { * * ```ts import.meta.vitest * import { Option as O } from "effect" - * import type { HelpDoc } from "effect/unstable/cli" + * import type { HelpDoc } from "effect/cli" * * const verboseFlag: HelpDoc.FlagDoc = { * name: "verbose", @@ -151,6 +154,7 @@ export interface ExampleDoc { * const names = [verboseFlag.name, portFlag.name] // => ["verbose", "port"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -188,7 +192,7 @@ export interface FlagDoc { * * ```ts import.meta.vitest * import { Context, Option as O } from "effect" - * import type { HelpDoc } from "effect/unstable/cli" + * import type { HelpDoc } from "effect/cli" * * const deploySubcommand: HelpDoc.SubcommandDoc = { * name: "deploy", @@ -219,6 +223,7 @@ export interface FlagDoc { * mainCommandHelp.subcommands?.[0].commands.map((command) => command.name) // => ["deploy", "build"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -247,6 +252,7 @@ export interface SubcommandDoc { /** * Documentation for a grouped subcommand listing * + * @stability unstable * @category models * @since 4.0.0 */ @@ -270,7 +276,7 @@ export interface SubcommandGroupDoc { * * ```ts import.meta.vitest * import { Context, Option as O } from "effect" - * import type { HelpDoc } from "effect/unstable/cli" + * import type { HelpDoc } from "effect/cli" * * const sourceArg: HelpDoc.ArgDoc = { * name: "source", @@ -300,6 +306,7 @@ export interface SubcommandGroupDoc { * copyCommandHelp.args?.map((arg) => arg.name) // => ["source", "files"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/Param.ts b/repos/effect/packages/effect/src/cli/Param.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cli/Param.ts rename to repos/effect/packages/effect/src/cli/Param.ts index 59d7d62dfb..96bf81bba4 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Param.ts +++ b/repos/effect/packages/effect/src/cli/Param.ts @@ -8,18 +8,19 @@ * aliases, defaults, prompts, configuration fallbacks, validation, schema * decoding, fallback parameters, and traversal helpers. * + * @stability unstable * @since 4.0.0 */ -import * as Config from "../../Config.ts" -import * as Effect from "../../Effect.ts" -import { dual, identity, type LazyArg } from "../../Function.ts" -import * as Option from "../../Option.ts" -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import type * as Redacted_ from "../../Redacted.ts" -import * as Result from "../../Result.ts" -import * as Schema from "../../Schema.ts" -import type { Covariant } from "../../Types.ts" +import * as Config from "../Config.ts" +import * as Effect from "../Effect.ts" +import { dual, identity, type LazyArg } from "../Function.ts" +import * as Option from "../Option.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import type * as Redacted_ from "../Redacted.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import type { Covariant } from "../Types.ts" import * as CliError from "./CliError.ts" import type { Environment } from "./Command.ts" import * as Primitive from "./Primitive.ts" @@ -35,6 +36,7 @@ const TypeId = "~effect/cli/Param" * A parameter knows whether it consumes positional arguments or flags and * parses a `ParsedArgs` value into its typed result. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -48,6 +50,7 @@ export interface Param extends Param.Variance * Discriminator for whether a `Param` parses positional arguments or * command-line flags. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -64,6 +67,7 @@ export type ParamKind = "argument" | "flag" * @see {@link flagKind} for the named flag parameter discriminator * @see {@link ParamKind} for the full parameter kind union * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -79,6 +83,7 @@ export const argumentKind: "argument" = "argument" as const * * @see {@link argumentKind} for the positional argument parameter discriminator * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -87,6 +92,7 @@ export const flagKind: "flag" = "flag" as const /** * Represents any parameter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -95,6 +101,7 @@ export type Any = Param /** * Represents any positional argument parameter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -103,6 +110,7 @@ export type AnyArgument = Param /** * Represents any flag parameter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -117,6 +125,7 @@ export type AnyFlag = Param * It returns the remaining positional arguments together with the parsed value, * or fails with a `CliError` while requiring the CLI parsing environment. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -129,12 +138,14 @@ export type Parse = (args: ParsedArgs) => Effect.Effect< /** * Namespace containing type-level utilities attached to the `Param` interface. * + * @stability unstable * @since 4.0.0 */ export declare namespace Param { /** * Variance and pipeability marker carried by every `Param` value. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -149,6 +160,7 @@ export declare namespace Param { * Map of flag names to their provided string values. * Multiple occurrences of a flag produce multiple values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -159,6 +171,7 @@ export type Flags = Record> * - `flags`: already-collected flag values by canonical flag name * - `arguments`: remaining positional arguments to be consumed * + * @stability unstable * @category models * @since 4.0.0 */ @@ -171,6 +184,7 @@ export interface ParsedArgs { * Represents a fallback prompt that can either be provided directly or * computed effectfully when the parameter is missing. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -186,6 +200,7 @@ export type FallbackPrompt = * Single parameters carry the user-facing name, aliases, description, primitive * type, and optional metavar/type name used in help output. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -204,6 +219,7 @@ export interface Single extends Param { * Parameter node that maps the successfully parsed value of another parameter * with a pure function. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -219,6 +235,7 @@ export interface Map extends Param extends Para * Parameter node that turns a missing argument or flag into `Option.none()` and * a present parsed value into `Option.some(value)`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -251,6 +269,7 @@ export interface Optional extends Param true * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -295,7 +315,7 @@ export const isParam = (u: unknown): u is Param => Predicate.has * **Example** (Checking for single params) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const nameParam = Param.String(Param.flagKind, "name") * const optionalParam = Param.optional(nameParam) @@ -304,6 +324,7 @@ export const isParam = (u: unknown): u is Param => Predicate.has * Param.isSingle(optionalParam) // => false * ``` * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -329,6 +350,7 @@ export const isFlagParam = ( * The returned parser reads either one positional argument or the named flag, * depending on `kind`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -361,7 +383,7 @@ export const makeSingle = (params: { * **Example** (Creating string parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create a string flag * const nameFlag = Param.String(Param.flagKind, "name") @@ -373,6 +395,7 @@ export const makeSingle = (params: { * const kinds = [nameFlag.kind, fileArg.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -392,7 +415,7 @@ export const String = ( * **Example** (Creating boolean parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create a boolean flag * const verboseFlag = Param.Boolean(Param.flagKind, "verbose") @@ -406,6 +429,7 @@ export const String = ( * const kinds = [verboseFlag.kind, enableArg.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -425,7 +449,7 @@ export const Boolean = ( * **Example** (Creating integer parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create an integer flag * const portFlag = Param.Int(Param.flagKind, "port") @@ -437,6 +461,7 @@ export const Boolean = ( * const kinds = [portFlag.kind, countArg.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -456,7 +481,7 @@ export const Int = ( * **Example** (Creating finite number parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const rateFlag = Param.Finite(Param.flagKind, "rate") * @@ -465,6 +490,7 @@ export const Int = ( * const kinds = [rateFlag.kind, thresholdArg.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -484,7 +510,7 @@ export const Finite = ( * **Example** (Creating date parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create a date flag * const startFlag = Param.Date(Param.flagKind, "start-date") @@ -497,6 +523,7 @@ export const Finite = ( * const kinds = [startFlag.kind, dueDateArg.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -517,7 +544,7 @@ export const Date = ( * **Example** (Creating valued choices) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * type Animal = Dog | Cat * @@ -536,6 +563,7 @@ export const Date = ( * animal.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -556,7 +584,7 @@ export const ChoiceWithValue = < * **Example** (Creating string choices) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const logLevel = Param.Literals(Param.flagKind, "log-level", [ * "debug", @@ -567,6 +595,7 @@ export const ChoiceWithValue = < * logLevel.kind // => "flag" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -584,7 +613,7 @@ export const Literals = < * **Example** (Creating path parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Basic path parameter * const outputPath = Param.Path(Param.flagKind, "output") @@ -601,6 +630,7 @@ export const Literals = < * const kinds = [outputPath.kind, inputPath.kind, configFile.kind] // => ["flag", "flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -631,7 +661,7 @@ export const Path = ( * **Example** (Creating directory parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Basic directory parameter * const outputDir = Param.Directory(Param.flagKind, "output-dir") @@ -643,6 +673,7 @@ export const Path = ( * const kinds = [outputDir.kind, sourceDir.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -670,7 +701,7 @@ export const Directory = ( * **Example** (Creating file parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Basic file parameter * const outputFile = Param.File(Param.flagKind, "output") @@ -682,6 +713,7 @@ export const Directory = ( * const kinds = [outputFile.kind, inputFile.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -705,7 +737,7 @@ export const File = ( * **Example** (Creating redacted parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create a password parameter * const password = Param.Redacted(Param.flagKind, "password") @@ -717,6 +749,7 @@ export const File = ( * const kinds = [password.kind, apiKey.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -736,7 +769,7 @@ export const Redacted = ( * **Example** (Reading file text) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Read a config file as string * const configContent = Param.FileText(Param.flagKind, "config") @@ -748,6 +781,7 @@ export const Redacted = ( * const kinds = [configContent.kind, templateContent.kind] // => ["flag", "argument"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -769,7 +803,7 @@ export const FileText = (kind: Kind, name: string): Para * **Example** (Parsing file contents) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Will use the extension of the file passed on the command line to determine * // the parser to use @@ -782,6 +816,7 @@ export const FileText = (kind: Kind, name: string): Para * const kinds = [config.kind, jsonConfig.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -803,7 +838,7 @@ export const FileParse = ( * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * // Parse JSON config file * const configSchema = Schema.Struct({ * port: Schema.Number, @@ -823,6 +858,7 @@ export const FileParse = ( * const kinds = [config.kind, yamlConfig.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -854,7 +890,7 @@ export const FileSchema = ( * **Example** (Parsing key-value pairs) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const env = Param.KeyValuePair(Param.flagKind, "env") * // --env FOO=bar --env BAZ=qux will parse to { FOO: "bar", BAZ: "qux" } @@ -864,6 +900,7 @@ export const FileSchema = ( * const kinds = [env.kind, props.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -889,7 +926,7 @@ export const KeyValuePair = ( * **Example** (Creating sentinel parameters) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const disabledDebugParam = Param.Never(Param.flagKind) * @@ -900,6 +937,7 @@ export const KeyValuePair = ( * makeDebugParam(false) === disabledDebugParam // => true * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -928,7 +966,7 @@ const FLAG_DASH_REGEXP = /^-+/ * **Example** (Adding parameter aliases) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const force = Param.Boolean(Param.flagKind, "force").pipe( * Param.withAlias("-f"), @@ -943,6 +981,7 @@ const FLAG_DASH_REGEXP = /^-+/ * const kinds = [force.kind, count.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -968,7 +1007,7 @@ export const withAlias: { * **Example** (Adding help descriptions) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const verbose = Param.Boolean(Param.flagKind, "verbose").pipe( * Param.withAlias("-v"), @@ -977,6 +1016,7 @@ export const withAlias: { * verbose.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1003,7 +1043,7 @@ export const withDescription: { * **Example** (Hiding a flag from help) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const experimental = Param.Boolean(Param.flagKind, "experimental-foo").pipe( * Param.withHidden @@ -1011,6 +1051,7 @@ export const withDescription: { * experimental.kind // => "flag" * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -1027,7 +1068,7 @@ export const withHidden = (self: Param): Par * **Example** (Mapping parsed values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const port = Param.Int(Param.flagKind, "port").pipe( * Param.map((n) => ({ port: n, url: `http://localhost:${n}` })) @@ -1035,6 +1076,7 @@ export const withHidden = (self: Param): Par * port.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1082,8 +1124,8 @@ const transform = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { CliError, Param } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { CliError, Param } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1126,6 +1168,7 @@ const transform = ( * value // => "alice@example.com" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1158,8 +1201,8 @@ export const mapEffect: { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Param } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Param } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -1195,6 +1238,7 @@ export const mapEffect: { * value // => { enabled: true } * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1249,7 +1293,7 @@ export const mapTryCatch: { * **Example** (Making parameters optional) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Create an optional port option * // - When not provided: returns Option.none() @@ -1258,6 +1302,7 @@ export const mapTryCatch: { * port.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1292,7 +1337,7 @@ export const optional = ( * **Example** (Providing default values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Using the pipe operator to make an option optional * const port = Param.Int(Param.flagKind, "port").pipe( @@ -1308,6 +1353,7 @@ export const optional = ( * const kinds = [port.kind, verbose.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1356,6 +1402,7 @@ export const withDefault: { * @see {@link withDefault} for a pure default value * @see {@link withFallbackPrompt} for prompting interactively when input is missing * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1415,6 +1462,7 @@ export const withFallbackConfig: { * @see {@link withFallbackConfig} for loading a fallback from config * @see {@link withDefault} for a pure default value * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1442,6 +1490,7 @@ export const withFallbackPrompt: { /** * Represent options which can be used to configure variadic parameters. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -1468,7 +1517,7 @@ export type VariadicParamOptions = { * **Example** (Accepting multiple values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Basic variadic parameter (0 to infinity) * const tags = Param.variadic(Param.String(Param.flagKind, "tag")) @@ -1487,6 +1536,7 @@ export type VariadicParamOptions = { * const kinds = [tags.kind, inputs.kind, limited.kind] // => ["flag", "flag", "flag"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1523,7 +1573,7 @@ export const variadic = ( * **Example** (Bounding repeated values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Allow 1-3 file inputs * const files = Param.String(Param.flagKind, "file").pipe( @@ -1544,6 +1594,7 @@ export const variadic = ( * const kinds = [files.kind, tags.kind] // => ["flag", "flag"] * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1572,7 +1623,7 @@ export const between: { * **Example** (Limiting repeated values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Allow at most 3 warning suppressions * const suppressions = Param.String(Param.flagKind, "suppress").pipe( @@ -1584,6 +1635,7 @@ export const between: { * suppressions.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1608,7 +1660,7 @@ export const atMost: { * **Example** (Requiring repeated values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * // Require at least 2 input files * const inputs = Param.String(Param.flagKind, "input").pipe( @@ -1621,6 +1673,7 @@ export const atMost: { * inputs.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1647,7 +1700,7 @@ export const atLeast: { * * ```ts import.meta.vitest * import { Option } from "effect" - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * const positiveInt = Param.Int(Param.flagKind, "count").pipe( * Param.filterMap( * (n) => n > 0 ? Option.some(n) : Option.none(), @@ -1657,6 +1710,7 @@ export const atLeast: { * positiveInt.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1698,7 +1752,7 @@ export const filterMap: { * **Example** (Filtering parsed values) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const evenNumber = Param.Int(Param.flagKind, "num").pipe( * Param.filter( @@ -1709,6 +1763,7 @@ export const filterMap: { * evenNumber.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1739,7 +1794,7 @@ export const filter: { * **Example** (Setting metavars) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const port = Param.Int(Param.flagKind, "port").pipe( * Param.withMetavar("PORT"), @@ -1751,6 +1806,7 @@ export const filter: { * port.kind // => "flag" * ``` * + * @stability unstable * @category metadata * @since 4.0.0 */ @@ -1774,7 +1830,7 @@ export const withMetavar: { * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * const isEmail = Schema.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/) * * const Email = Schema.String.pipe( @@ -1787,6 +1843,7 @@ export const withMetavar: { * email.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1823,7 +1880,7 @@ export const withSchema: { * **Example** (Falling back to another parameter) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const config = Param.File(Param.flagKind, "config").pipe( * Param.orElse(() => Param.String(Param.flagKind, "config-url")) @@ -1831,6 +1888,7 @@ export const withSchema: { * config.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -1863,7 +1921,7 @@ export const orElse: { * **Example** (Returning fallback results) * * ```ts import.meta.vitest - * import { Param } from "effect/unstable/cli" + * import { Param } from "effect/cli" * * const configSource = Param.File(Param.flagKind, "config").pipe( * Param.orElseResult(() => Param.String(Param.flagKind, "config-url")) @@ -1872,6 +1930,7 @@ export const orElse: { * configSource.kind // => "flag" * ``` * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cli/Primitive.ts b/repos/effect/packages/effect/src/cli/Primitive.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cli/Primitive.ts rename to repos/effect/packages/effect/src/cli/Primitive.ts index ff1cfb504f..5f8a890336 100644 --- a/repos/effect/packages/effect/src/unstable/cli/Primitive.ts +++ b/repos/effect/packages/effect/src/cli/Primitive.ts @@ -8,21 +8,22 @@ * scalar values, paths, files, structured config files, schema-decoded input, * redacted values, and key-value pairs. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as FileSystem from "../../FileSystem.ts" -import { format } from "../../Formatter.ts" -import { identity } from "../../Function.ts" -import * as Path_ from "../../Path.ts" -import * as Redacted_ from "../../Redacted.ts" -import * as Schema from "../../Schema.ts" -import type { Formatter } from "../../SchemaIssue.ts" -import type * as Struct from "../../Struct.ts" -import type { Covariant } from "../../Types.ts" +import * as Effect from "../Effect.ts" import * as Ini from "../encoding/Ini.ts" import * as Toml from "../encoding/Toml.ts" import * as Yaml from "../encoding/Yaml.ts" +import * as FileSystem from "../FileSystem.ts" +import { format } from "../Formatter.ts" +import { identity } from "../Function.ts" +import * as Path_ from "../Path.ts" +import * as Redacted_ from "../Redacted.ts" +import * as Schema from "../Schema.ts" +import type { Formatter } from "../SchemaIssue.ts" +import type * as Struct from "../Struct.ts" +import type { Covariant } from "../Types.ts" import type { Environment } from "./Command.ts" const TypeId = "~effect/cli/Primitive" @@ -34,8 +35,8 @@ const TypeId = "~effect/cli/Primitive" * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -64,6 +65,7 @@ const TypeId = "~effect/cli/Primitive" * await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer))) // => ["hello", 42, true] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -75,12 +77,14 @@ export interface Primitive extends Primitive.Variance { /** * Namespace containing type-level helpers for `Primitive`. * + * @stability unstable * @since 4.0.0 */ export declare namespace Primitive { /** * Type-level variance marker for the value parsed by a `Primitive`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -97,13 +101,19 @@ const Proto = { } } -/** @internal */ +/** + * @internal + */ export const isTrueLiteral = Schema.is(Schema.TrueLiterals) -/** @internal */ +/** + * @internal + */ export const isFalseLiteral = Schema.is(Schema.FalseLiterals) -/** @internal */ +/** + * @internal + */ export const isBoolean = (p: Primitive): p is Primitive => p._tag === "Boolean" const makePrimitive = ( @@ -137,8 +147,8 @@ const makeSchemaPrimitive = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -167,6 +177,7 @@ const makeSchemaPrimitive = ( * await Effect.runPromise(parseBoolean.pipe(Effect.provide(CliTestLayer))) // => [true, true, false, false] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -182,8 +193,8 @@ export const Boolean: Primitive = makeSchemaPrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -211,6 +222,7 @@ export const Boolean: Primitive = makeSchemaPrimitive( * await Effect.runPromise(parseFloat.pipe(Effect.provide(CliTestLayer))) // => [3.14, -42.5, 0] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -226,8 +238,8 @@ export const Finite: Primitive = makeSchemaPrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -255,6 +267,7 @@ export const Finite: Primitive = makeSchemaPrimitive( * await Effect.runPromise(parseInteger.pipe(Effect.provide(CliTestLayer))) // => [42, -123, 0] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -270,8 +283,8 @@ export const Int: Primitive = makeSchemaPrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -298,6 +311,7 @@ export const Int: Primitive = makeSchemaPrimitive( * await Effect.runPromise(parseDate.pipe(Effect.provide(CliTestLayer))) // => "2023-12-25T00:00:00.000Z" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -313,8 +327,8 @@ export const Date: Primitive = makeSchemaPrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -342,6 +356,7 @@ export const Date: Primitive = makeSchemaPrimitive( * await Effect.runPromise(parseString.pipe(Effect.provide(CliTestLayer))) // => ["hello world", "", "123"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -354,8 +369,8 @@ export const String: Primitive = makePrimitive("String", (value) => Effe * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -391,6 +406,7 @@ export const String: Primitive = makePrimitive("String", (value) => Effe * await Effect.runPromise(parseLogLevel.pipe(Effect.provide(CliTestLayer))) // => ["info", "debug"] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -414,7 +430,7 @@ export const Choice = ( * **Example** (Choosing path validation) * * ```ts import.meta.vitest - * import { Primitive } from "effect/unstable/cli" + * import { Primitive } from "effect/cli" * * // Only accept files * const filePath = Primitive.Path("file", true) @@ -428,6 +444,7 @@ export const Choice = ( * const tags = [filePath._tag, dirPath._tag, anyPath._tag] // => ["Path", "Path", "Path"] * ``` * + * @stability unstable * @category models * @since 4.0.0 */ @@ -440,8 +457,8 @@ export type PathType = "file" | "directory" | "either" * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const services = Layer.mergeAll( * Path.layer, @@ -472,6 +489,7 @@ export type PathType = "file" | "directory" | "either" * await Effect.runPromise(program) // => true * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -532,8 +550,8 @@ export const Path = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Redacted, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -560,6 +578,7 @@ export const Path = ( * await Effect.runPromise(parseRedacted.pipe(Effect.provide(CliTestLayer))) // => ["secret-password", ""] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -575,8 +594,8 @@ export const Redacted: Primitive> = makePrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const services = Layer.mergeAll( * Path.layer, @@ -607,6 +626,7 @@ export const Redacted: Primitive> = makePrimitive( * await Effect.runPromise(readConfigFile) // => { private: true } * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -655,6 +675,7 @@ export const FileText: Primitive = makePrimitive( * Represents options which can be provided to methods that deal with parsing * file content. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -683,8 +704,8 @@ const fileParsers: Record unknown> = { * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const services = Layer.mergeAll( * Path.layer, @@ -717,6 +738,7 @@ const fileParsers: Record unknown> = { * await Effect.runPromise(loadConfig) // => { private: true } * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -742,6 +764,7 @@ export const FileParse = (options?: FileParseOptions): Primitive => { * Represents options which can be provided to methods that deal with parsing * file content and decoding the file content with a `Schema`. * + * @stability unstable * @category options * @since 4.0.0 */ @@ -758,8 +781,8 @@ export type FileSchemaOptions = Struct.Simplify< * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Schema, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const services = Layer.mergeAll( * Path.layer, @@ -797,6 +820,7 @@ export type FileSchemaOptions = Struct.Simplify< * await Effect.runPromise(loadConfig) // => { private: true } * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -828,8 +852,8 @@ export const FileSchema = ( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -858,6 +882,7 @@ export const FileSchema = ( * result // => [{ name: "john" }, { port: "3000" }, { debug: "true" }] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -888,8 +913,8 @@ export const KeyValuePair: Primitive> = makePrimitive( * * ```ts import.meta.vitest * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect" - * import { Primitive } from "effect/unstable/cli" - * import { ChildProcessSpawner } from "effect/unstable/process" + * import { Primitive } from "effect/cli" + * import { ChildProcessSpawner } from "effect/process" * * const CliTestLayer = Layer.mergeAll( * FileSystem.layerNoop({}), @@ -915,6 +940,7 @@ export const KeyValuePair: Primitive> = makePrimitive( * await Effect.runPromise(Effect.flip(program).pipe(Effect.provide(CliTestLayer))) // => "This option does not accept values" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -931,7 +957,7 @@ export const Never: Primitive = makePrimitive("Never", () => Effect.fail( * **Example** (Getting primitive type names) * * ```ts import.meta.vitest - * import { Primitive } from "effect/unstable/cli" + * import { Primitive } from "effect/cli" * * Primitive.getTypeName(Primitive.String) // => "string" * Primitive.getTypeName(Primitive.Int) // => "integer" @@ -946,6 +972,7 @@ export const Never: Primitive = makePrimitive("Never", () => Effect.fail( * Primitive.getTypeName(logLevelChoice) // => "choice" * ``` * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -982,10 +1009,14 @@ export const getTypeName = (primitive: Primitive): string => { } } -/** @internal */ +/** + * @internal + */ export const getChoiceKeys = (primitive: Primitive): ReadonlyArray | undefined => primitive._tag === "Choice" ? (primitive as any).choiceKeys : undefined -/** @internal */ +/** + * @internal + */ export const getPathType = (primitive: Primitive): PathType | undefined => primitive._tag === "Path" ? (primitive as any).pathType : undefined diff --git a/repos/effect/packages/effect/src/cli/Prompt.ts b/repos/effect/packages/effect/src/cli/Prompt.ts new file mode 100644 index 0000000000..c6fba52d4b --- /dev/null +++ b/repos/effect/packages/effect/src/cli/Prompt.ts @@ -0,0 +1,4120 @@ +/** + * Builds interactive terminal prompts for CLI applications. + * + * A `Prompt` describes a small terminal UI that renders frames, reads + * keyboard input, validates responses, and eventually produces an `A`. Prompts + * can ask for simple values, selections, lists, files, hidden text, or custom + * interactions. This module includes prompt constructors, tools for combining + * and transforming prompt output, and support for running prompts through the + * `Terminal` service. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Arr from "../Array.ts" +import type { NoSuchElementError } from "../Cause.ts" +import type * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Effectable from "../Effectable.ts" +import * as FileSystem from "../FileSystem.ts" +import { dual, pipe } from "../Function.ts" +import * as EffectNumber from "../Number.ts" +import * as Option from "../Option.ts" +import * as Path from "../Path.ts" +import * as Predicate from "../Predicate.ts" +import * as Queue from "../Queue.ts" +import * as Redacted from "../Redacted.ts" +import * as Terminal from "../Terminal.ts" +import type { Covariant } from "../Types.ts" +import * as Ansi from "./internal/ansi.ts" +import type * as Primitive from "./Primitive.ts" + +declare const process: { + readonly platform: string + readonly cwd: () => string +} + +const TypeId = "~effect/cli/Prompt" + +/** + * Represents an interactive terminal prompt that produces an `Output` value. + * + * **Details** + * + * A `Prompt` is an `Effect` that may fail with `Terminal.QuitError` and + * requires the prompt environment needed to render frames, read input, and + * access files or paths when a prompt uses them. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Prompt extends Effect.Effect { + readonly [TypeId]: { + readonly _Output: Covariant + } +} + +/** + * Returns `true` if the provided value is a `Prompt`. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isPrompt = (u: unknown): u is Prompt => Predicate.hasProperty(u, TypeId) + +/** + * Represents the services available to a custom `Prompt`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Environment = FileSystem.FileSystem | Path.Path | Terminal.Terminal + +/** + * Represents the action that should be taken by a `Prompt` based upon user + * input or an external event received during the current frame. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Action = Data.TaggedEnum<{ + readonly Beep: {} + readonly NextFrame: { readonly state: State } + readonly Submit: { readonly value: Output } +}> + +/** + * Type-level definition for the tagged `Prompt.Action` variants. + * + * **Details** + * + * It connects the action state and output type parameters to the `Beep`, + * `NextFrame`, and `Submit` action cases. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface ActionDefinition extends Data.TaggedEnum.WithGenerics<2> { + readonly taggedEnum: Action +} + +/** + * Represents the input that should be processed by a `Prompt` based upon user + * input or an external event received during the current frame. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type ProcessInput = Data.TaggedEnum<{ + readonly Input: { readonly input: Terminal.UserInput } + readonly Event: { readonly value: A } +}> + +/** + * Represents the set of handlers used by a `Prompt`. + * + * **Details** + * + * The handlers render the current frame, process user input into the next + * `Prompt.Action`, and clear the terminal screen before the next frame. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Handlers { + /** + * A function that is called to render the current frame of the `Prompt`. + */ + readonly render: ( + state: State, + action: Action + ) => Effect.Effect + /** + * A function that is called to process user input and determine the next + * `Prompt.Action` that should be taken. + */ + readonly process: ( + input: Input, + state: State + ) => Effect.Effect, never, Environment> + /** + * A function that is called to clear the terminal screen before rendering + * the next frame of the `Prompt`. + */ + readonly clear: ( + state: State, + action: Action + ) => Effect.Effect +} + +/** + * Defines the symbols used to render built-in prompts. + * + * **Details** + * + * Set a symbol to an empty string to omit both the symbol and its adjacent + * spacing. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Theme { + /** The leading symbol on an active prompt line. */ + readonly prefix: string + /** The marker for the highlighted choice. */ + readonly pointer: string + /** The trailing prompt and validation marker. */ + readonly pointerSmall: string + /** The marker shown when a prompt has been submitted. */ + readonly ellipsis: string + /** The leading symbol on a completed prompt line. */ + readonly tick: string + /** The marker indicating choices above the current page. */ + readonly arrowUp: string + /** The marker indicating choices below the current page. */ + readonly arrowDown: string + /** The marker for a selected multi-select choice. */ + readonly checkboxOn: string + /** The marker for an unselected multi-select choice. */ + readonly checkboxOff: string + /** The separator between a choice and its description. */ + readonly descriptionSeparator: string + /** The character used to mask password input. */ + readonly passwordMask: string + /** The separator between toggle labels. */ + readonly toggleSeparator: string + /** The color used for active prompt elements. */ + readonly primaryColor: string + /** The color used for secondary prompt elements. */ + readonly mutedColor: string + /** The color used for completed prompt markers. */ + readonly successColor: string + /** The color used for validation errors. */ + readonly errorColor: string + /** The color used for submitted values. */ + readonly submittedColor: string +} + +/** + * Options shared by built-in prompts that support theme overrides. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ThemeOptions { + /** Overrides the context theme for this prompt. */ + readonly theme?: Partial +} + +type OptionsReq = Required> & ThemeOptions + +/** + * Options for a confirmation prompt that asks the user to choose a boolean + * yes/no value. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ConfirmOptions extends ThemeOptions { + /** + * The message to display in the prompt. + */ + readonly message: string + /** + * The initial value of the confirm prompt (defaults to `false`). + */ + readonly initial?: boolean + /** + * The label to display after a user has responded to the prompt. + */ + readonly label?: { + /** + * The label used if the prompt is confirmed (defaults to `"yes"`). + */ + readonly confirm: string + /** + * The label used if the prompt is not confirmed (defaults to `"no"`). + */ + readonly deny: string + } + /** + * The placeholder to display when a user is responding to the prompt. + */ + readonly placeholder?: { + /** + * The placeholder to use if the `initial` value of the prompt is `true` + * (defaults to `"(Y/n)"`). + */ + readonly defaultConfirm?: string + /** + * The placeholder to use if the `initial` value of the prompt is `false` + * (defaults to `"(y/N)"`). + */ + readonly defaultDeny?: string + } +} + +/** + * Options for a date prompt, including the displayed message, initial value, + * format mask, validation, and locale labels. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface DateOptions extends ThemeOptions { + /** + * The message to display in the prompt. + */ + readonly message: string + /** + * The initial date value to display in the prompt (defaults to the current + * date). + */ + readonly initial?: globalThis.Date + /** + * The format mask of the date (defaults to `YYYY-MM-DD HH:mm:ss`). + */ + readonly dateMask?: string + /** + * An effectful function that can be used to validate the value entered into + * the prompt before final submission. + */ + readonly validate?: (value: globalThis.Date) => Effect.Effect + /** + * Custom locales that can be used in place of the defaults. + */ + readonly locales?: { + /** + * The full names of each month of the year. + */ + readonly months: [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + /** + * The short names of each month of the year. + */ + readonly monthsShort: [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + /** + * The full names of each day of the week. + */ + readonly weekdays: [string, string, string, string, string, string, string] + /** + * The short names of each day of the week. + */ + readonly weekdaysShort: [string, string, string, string, string, string, string] + } +} + +/** + * Options for an integer prompt, including bounds, keyboard step sizes, and + * additional validation. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface IntOptions extends ThemeOptions { + /** + * The message to display in the prompt. + */ + readonly message: string + /** + * The default value of the integer prompt. + */ + readonly default?: number + /** + * The minimum value that can be entered by the user (defaults to `-Infinity`). + */ + readonly min?: number + /** + * The maximum value that can be entered by the user (defaults to `Infinity`). + */ + readonly max?: number + /** + * The value that will be used to increment the prompt value when using the + * up arrow key (defaults to `1`). + */ + readonly incrementBy?: number + /** + * The value that will be used to decrement the prompt value when using the + * down arrow key (defaults to `1`). + */ + readonly decrementBy?: number + /** + * An effectful function that can be used to validate the value entered into + * the prompt before final submission. + */ + readonly validate?: (value: number) => Effect.Effect +} + +/** + * Options for `Number`, extending `IntOptions` with display precision. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface NumberOptions extends IntOptions { + /** + * The precision to use for the floating point value (defaults to `2`). + */ + readonly precision?: number +} + +/** + * Options for a text prompt that returns a list of strings by splitting the + * input on a delimiter. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ListOptions extends TextOptions { + /** + * The delimiter that separates list entries. + */ + readonly delimiter?: string +} + +/** + * Options for a file-system selection prompt. + * + * **Details** + * + * They control which path type can be selected, the starting directory, paging, + * and filtering of displayed entries. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface FileOptions extends ThemeOptions { + /** + * The path type that will be selected, defaulting to `"file"`. + */ + readonly type?: Primitive.PathType + /** + * The message to display in the prompt, defaulting to `"Choose a file"`. + */ + readonly message?: string + /** + * Where the user will initially be prompted to select files from, defaulting + * to the current working directory. + */ + readonly startingPath?: string + /** + * The default path to select when the prompt is first displayed. + */ + readonly default?: string + /** + * The number of choices to display at one time, defaulting to `10`. + */ + readonly maxPerPage?: number + /** + * A predicate or effect that keeps a file in the prompt when it returns + * `true`, defaulting to returning all files. + */ + readonly filter?: (file: string) => boolean | Effect.Effect +} + +/** + * Options for a prompt that asks the user to select one value from a list of + * choices. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface SelectOptions extends ThemeOptions { + /** + * The prompt message. Omit to display only the choices. + */ + readonly message?: string | undefined + /** + * The choices to display to the user. + */ + readonly choices: ReadonlyArray> + /** + * The number of choices to display at one time (defaults to `10`). + */ + readonly maxPerPage?: number +} + +/** + * Options for an autocomplete prompt that lets the user filter selectable + * choices by typing. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface AutoCompleteOptions extends SelectOptions { + /** + * The required prompt message. + */ + readonly message: string + /** + * The label used for the filter display (defaults to "filter"). + */ + readonly filterLabel?: string + /** + * The placeholder shown when the filter is empty (defaults to "type to filter"). + */ + readonly filterPlaceholder?: string + /** + * The message displayed when no choices match (defaults to "No matches"). + */ + readonly emptyMessage?: string +} + +/** + * Options for a multi-select prompt, including bulk-selection labels and + * minimum or maximum selection counts. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface MultiSelectOptions { + /** + * Text for the "Select All" option (defaults to "Select All"). + */ + readonly selectAll?: string + /** + * Text for the "Select None" option (defaults to "Select None"). + */ + readonly selectNone?: string + /** + * Text for the "Inverse Selection" option (defaults to "Inverse Selection"). + */ + readonly inverseSelection?: string + /** + * The minimum number of choices that must be selected. + */ + readonly min?: number + /** + * The maximum number of choices that can be selected. + */ + readonly max?: number +} + +/** + * Represents one choice displayed by select, autocomplete, and multi-select + * prompts. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface SelectChoice { + /** + * The name of the select option that is displayed to the user. + */ + readonly title: string + /** + * The underlying value of the select option. + */ + readonly value: A + /** + * An optional description for the select option which will be displayed + * to the user. + */ + readonly description?: string + /** + * Whether or not this select option is disabled. + */ + readonly disabled?: boolean + /** + * Whether this option should be selected by default (only used by MultiSelect). + */ + readonly selected?: boolean +} + +/** + * Options for text-entry prompts, including the displayed message, default + * text, and effectful validation before submission. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface TextOptions extends ThemeOptions { + /** + * The message to display in the prompt. + */ + readonly message: string + /** + * The default value of the text option. + */ + readonly default?: string + /** + * An effectful function that can be used to validate the value entered into + * the prompt before final submission. + */ + readonly validate?: (value: string) => Effect.Effect +} + +/** + * Options for a toggle prompt that lets the user switch between active and + * inactive boolean states. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface ToggleOptions extends ThemeOptions { + /** + * The message to display in the prompt. + */ + readonly message: string + /** + * The intitial value of the toggle prompt (defaults to `false`). + */ + readonly initial?: boolean + /** + * The text to display when the toggle is in the active state (defaults to + * `on`). + */ + readonly active?: string + /** + * The text to display when the toggle is in the inactive state (defaults to + * `off`). + */ + readonly inactive?: string +} + +const defaultTheme: Theme = { + prefix: "?", + arrowUp: "↑", + arrowDown: "↓", + checkboxOn: "☒", + checkboxOff: "☐", + tick: "✔", + ellipsis: "…", + pointerSmall: "›", + pointer: "❯", + descriptionSeparator: "- ", + passwordMask: "*", + toggleSeparator: "/", + primaryColor: Ansi.cyanBright, + mutedColor: Ansi.blackBright, + successColor: Ansi.green, + errorColor: Ansi.red, + submittedColor: Ansi.white +} + +const windowsTheme: Theme = { + ...defaultTheme, + checkboxOn: "[*]", + checkboxOff: "[ ]", + tick: "√", + ellipsis: "...", + pointerSmall: "»", + pointer: ">" +} + +/** + * Creates a prompt theme using the current platform defaults. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const makeTheme = (options?: Partial): Theme => ({ + ...(process.platform === "win32" ? windowsTheme : defaultTheme), + ...options +}) + +/** + * Context reference for the theme used by built-in prompts. + * + * **Details** + * + * Provide this reference once to theme every prompt in an application. A + * prompt's `theme` option takes precedence over the context value. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const Theme: Context.Reference = Context.Reference("effect/cli/Prompt/Theme", { + defaultValue: makeTheme +}) + +const getTheme = (options: ThemeOptions): Effect.Effect => + Effect.map(Theme, (theme) => ({ ...theme, ...options.theme })) + +/** + * Type alias for any `Prompt`, regardless of its output type. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ +export type Any = Prompt + +/** + * Namespace containing return-type helpers for `Prompt.all`. + * + * @stability unstable + * @since 4.0.0 + */ +export declare namespace All { + /** + * Computes the prompt returned by `Prompt.all` for an iterable of prompts. + * + * **Details** + * + * The resulting prompt produces an array of each prompt's output value. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ + export type ReturnIterable> = [T] extends [Iterable>] ? Prompt> + : never + + /** + * Computes the prompt returned by `Prompt.all` for a readonly tuple or array + * of prompts, preserving tuple positions in the output type. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ + export type ReturnTuple> = Prompt< + T[number] extends never ? [] + : { -readonly [K in keyof T]: [T[K]] extends [Prompt] ? _A : never } + > extends infer X ? X : never + + /** + * Computes the prompt returned by `Prompt.all` for a record of prompts, + * preserving the record keys and replacing each prompt with its output type. + * + * @stability unstable + * @category utility types + * @since 4.0.0 + */ + export type ReturnObject = [T] extends [{ [K: string]: Any }] ? Prompt< + { + -readonly [K in keyof T]: [T[K]] extends [Prompt] ? _A : never + } + > + : never + + /** + * Computes the return prompt type for `Prompt.all` based on the input + * structure. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ + export type Return< + Arg extends Iterable | Record + > = [Arg] extends [ReadonlyArray] ? ReturnTuple + : [Arg] extends [Iterable] ? ReturnIterable + : [Arg] extends [Record] ? ReturnObject + : never +} + +/** + * Runs all the provided prompts in sequence respecting the structure provided + * in input. + * + * **Details** + * + * Supports either a tuple / iterable of prompts or a record / struct of prompts + * as an argument. + * + * **Example** (Collecting prompt results) + * + * ```ts import.meta.vitest + * import { Effect, FileSystem, Layer, Path, Terminal } from "effect" + * import { Prompt } from "effect/cli" + * + * const terminal = Terminal.make({ + * columns: Effect.succeed(80), + * rows: Effect.succeed(24), + * readInput: Effect.succeed({} as never), + * readLine: Effect.die("unused"), + * display: () => Effect.void + * }) + * const services = Layer.mergeAll( + * FileSystem.layerNoop({}), + * Path.layer, + * Layer.succeed(Terminal.Terminal, terminal) + * ) + * + * const username = Prompt.succeed("alice") + * const password = Prompt.succeed("secret") + * + * const allWithTuple = Prompt.all([username, password]) + * + * const allWithRecord = Prompt.all({ username, password }) + * + * await Effect.runPromise(Effect.provide(allWithTuple, services)) // => ["alice", "secret"] + * await Effect.runPromise(Effect.provide(allWithRecord, services)) // => { username: "alice", password: "secret" } + * ``` + * + * @stability unstable + * @category combining + * @since 4.0.0 + */ +export const all: < + const Arg extends Iterable> | Record> +>(arg: Arg) => All.Return = function() { + if (arguments.length === 1) { + if (isPrompt(arguments[0])) { + return map(arguments[0], (x) => [x]) as any + } else if (Predicate.isIterable(arguments[0])) { + return allTupled(Arr.fromIterable(arguments[0] as Iterable>)) as any + } else { + const entries = Object.entries(arguments[0] as Readonly<{ [K: string]: Prompt }>) + if (entries.length === 0) { + return succeed({}) as any + } + let result = map(entries[0][1], (value) => ({ [entries[0][0]]: value })) + if (entries.length === 1) { + return result as any + } + const rest = entries.slice(1) + for (const [key, prompt] of rest) { + result = result.pipe( + flatMap((record) => + prompt.pipe(map((value) => ({ + ...record, + [key]: value + }))) + ) + ) + } + return result as any + } + } + return allTupled(arguments[0]) as any +} + +const annotateLine = (line: string): string => Ansi.annotate(line, Ansi.bold) +const annotateErrorLine = (line: string, color: string): string => + Ansi.annotate(line, Ansi.combine(Ansi.italicized, color)) +const annotateSymbol = (symbol: string, ...styles: Array>): string => + symbol.length === 0 ? "" : Ansi.annotate(symbol, ...styles) +const separateSymbol = (symbol: string, text: string): string => symbol.length === 0 ? text : symbol + " " + text +const renderPagingPrefix = (theme: Theme, showArrowUp: boolean, showArrowDown: boolean): string => { + const width = Math.max(theme.arrowUp.length, theme.arrowDown.length) + if (showArrowUp) { + return theme.arrowUp.padEnd(width) + } + if (showArrowDown) { + return theme.arrowDown.padEnd(width) + } + return " ".repeat(width) +} + +/** + * Creates a confirmation prompt that asks the user to choose a boolean yes/no + * value. + * + * **When to use** + * + * Use to ask for a yes/no answer that can be submitted directly. + * + * **Details** + * + * `initial` defaults to `false`. Enter submits the current default, yes-style + * input submits `true`, no-style input submits `false`, and other input beeps. + * + * @see {@link Toggle} for an interactive switch-before-submit boolean prompt + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Confirm = (options: ConfirmOptions): Prompt => { + const opts: ConfirmOptionsReq = { + initial: false, + ...options, + label: { + confirm: "yes", + deny: "no", + ...options.label + }, + placeholder: { + defaultConfirm: "(Y/n)", + defaultDeny: "(y/N)", + ...options.placeholder + } + } + const initialState: ConfirmState = { value: opts.initial } + return Custom(initialState, { + render: handleConfirmRender(opts), + process: (input) => handleConfirmProcess(input, opts.initial), + clear: handleConfirmClear(opts) + }) +} + +/** + * Creates a custom `Prompt` from the specified initial state and handlers. + * + * **Details** + * + * The initial state can either be a pure value or an `Effect`. This is + * particularly useful when the initial state of the `Prompt` must be computed + * by performing an effectful computation, such as reading data from the file + * system. A `Prompt` runs as a render loop: `render` returns ANSI output for + * the current frame, the `Terminal` obtains user input, `process` returns the + * next prompt action, and `clear` returns ANSI output used to clear the previous + * frame. + * + * Optionally, an external `events` dequeue can be provided as the third + * argument. When present, the render loop will race user input against events + * from the dequeue, allowing background events to trigger re-renders without + * waiting for a keypress. When an event is received from the dequeue, the + * `receive` handler is called instead of `process`. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Custom: { + ( + initialState: State | Effect.Effect, + handlers: Handlers + ): Prompt + ( + initialState: State | Effect.Effect, + events: Queue.Dequeue, + handlers: Handlers> + ): Prompt +} = ( + initialState: State | Effect.Effect, + ...args: + | [handlers: Handlers] + | [events: Queue.Dequeue, handlers: Handlers>] +): Prompt => { + const [events, handlers] = args.length === 1 + ? [undefined, args[0]] as const + : [args[0], args[1]] as const + const op = Object.create(proto) + op._tag = "Loop" + op.initialState = initialState + op.render = handlers.render + op.process = handlers.process + op.clear = handlers.clear + op.events = events + return op +} + +/** + * Creates a date prompt that lets the user edit a formatted date value and + * validates the final `Date` before submission. + * + * **Details** + * + * `initial` defaults to the current `Date`, `dateMask` defaults to + * `YYYY-MM-DD HH:mm:ss`, mask parsing creates editable date parts plus literal + * tokens, `locales` customizes month and weekday labels, and `validate` runs on + * submission. + * + * **Gotchas** + * + * A supplied `initial` `Date` is edited in place during prompt interaction. + * Date edits use JavaScript `Date` setters, so out-of-range typed values can + * normalize before validation. If the prompt is meant to be editable, + * `dateMask` should contain at least one editable date token. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Date = (options: DateOptions): Prompt => { + const opts: DateOptionsReq = { + initial: new globalThis.Date(), + dateMask: "YYYY-MM-DD HH:mm:ss", + validate: Effect.succeed, + ...options, + locales: { + ...defaultLocales, + ...options.locales + } + } + const dateParts = makeDateParts(opts.dateMask, opts.initial, opts.locales) + const initialCursorPosition = dateParts.findIndex((part) => !part.isToken()) + const initialState: DateState = { + dateParts, + typed: "", + cursor: initialCursorPosition, + value: opts.initial, + error: Option.none() + } + return Custom(initialState, { + render: handleDateRender(opts), + process: handleDateProcess(opts), + clear: handleDateClear(opts) + }) +} + +/** + * Creates a file-system selection prompt and returns the selected path. + * + * **Details** + * + * The prompt can be configured to select files, directories, or either path + * type. + * + * You can also type to filter the listed entries. Every printable character is + * appended to the filter query, so navigation is bound to the arrow keys, + * `tab`, and the `Ctrl+P` / `Ctrl+N` chords used by `readline` and `fzf` + * (`Ctrl+K` also moves up). `Ctrl+U` clears the query. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const File = (options: FileOptions = {}): Prompt => { + const opts: FileOptionsReq = { + ...options, + type: options.type ?? "file", + message: options.message ?? `Choose a file`, + startingPath: Option.fromUndefinedOr(options.startingPath), + default: Option.fromUndefinedOr(options.default), + maxPerPage: options.maxPerPage ?? 10, + filter: options.filter ?? (() => Effect.succeed(true)) + } + const initialState: Effect.Effect< + FileState, + never, + Environment + > = Effect.gen(function*() { + const currentPath = yield* resolveCurrentPath(Option.none(), opts) + const path = yield* Path.Path + const defaultPath = Option.map(opts.default, (defaultValue) => path.resolve(currentPath, defaultValue)) + const initialPath = Option.match(defaultPath, { + onNone: () => currentPath, + onSome: (defaultPath) => path.dirname(defaultPath) + }) + const files = yield* getFileList(initialPath, opts) + const cursor = Option.match(defaultPath, { + onNone: () => 0, + onSome: (defaultPath) => { + const index = files.indexOf(path.basename(defaultPath)) + return index === -1 ? 0 : index + } + }) + const confirm = FileConfirm.Hide() + return { cursor, files, allFiles: files, query: "", path: Option.map(defaultPath, path.dirname), confirm } + }) + return Custom(initialState, { + render: handleFileRender(opts), + process: handleFileProcess(opts), + clear: handleFileClear(opts) + }) +} + +/** + * Composes prompts by using the output of this prompt to create the next prompt. + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const flatMap: { + ( + f: (output: Output) => Prompt + ): (self: Prompt) => Prompt + ( + self: Prompt, + f: (output: Output) => Prompt + ): Prompt +} = dual(2, ( + self: Prompt, + f: (output: Output) => Prompt +) => { + const op = Object.create(proto) + op._tag = "OnSuccess" + op.prompt = self + op.onSuccess = f + return op +}) + +/** + * Creates a floating-point number prompt. + * + * **Details** + * + * The prompt supports minimum and maximum bounds, keyboard step sizes, display + * precision, and additional validation before submission. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Number = (options: NumberOptions): Prompt => { + const opts: NumberOptionsReq = { + default: 0, + min: globalThis.Number.NEGATIVE_INFINITY, + max: globalThis.Number.POSITIVE_INFINITY, + incrementBy: 1, + decrementBy: 1, + precision: 2, + validate: (n) => { + if (n < opts.min) { + return Effect.fail(`${n} must be greater than or equal to ${opts.min}`) + } + if (n > opts.max) { + return Effect.fail(`${n} must be less than or equal to ${opts.max}`) + } + return Effect.succeed(n) + }, + ...options + } + const initialValue = options.default === undefined ? "" : `${opts.default}` + const initialState: NumberState = { + cursor: initialValue.length, + value: initialValue, + error: Option.none() + } + return Custom(initialState, { + render: handleRenderFloat(opts), + process: handleProcessFloat(opts), + clear: handleNumberClear(opts) + }) +} +/** + * Creates a text prompt that does not echo typed input and returns the + * submitted value wrapped in `Redacted`. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Hidden = ( + options: TextOptions +): Prompt => basePrompt(options, "hidden").pipe(map(Redacted.make)) + +/** + * Creates an integer prompt. + * + * **Details** + * + * The prompt supports minimum and maximum bounds, keyboard step sizes, and + * additional validation before submission. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Int = (options: IntOptions): Prompt => { + const opts: IntOptionsReq = { + default: 0, + min: globalThis.Number.NEGATIVE_INFINITY, + max: globalThis.Number.POSITIVE_INFINITY, + incrementBy: 1, + decrementBy: 1, + validate: (n) => { + if (n < opts.min) { + return Effect.fail(`${n} must be greater than or equal to ${opts.min}`) + } + if (n > opts.max) { + return Effect.fail(`${n} must be less than or equal to ${opts.max}`) + } + return Effect.succeed(n) + }, + ...options + } + const initialValue = options.default === undefined ? "" : `${opts.default}` + const initialState: NumberState = { + cursor: initialValue.length, + value: initialValue, + error: Option.none() + } + return Custom(initialState, { + render: handleRenderInteger(opts), + process: handleProcessInteger(opts), + clear: handleNumberClear(opts) + }) +} + +/** + * Creates a text prompt that returns an array of strings by splitting the + * submitted input on the configured delimiter. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const List = (options: ListOptions): Prompt> => + String(options).pipe( + map((output) => output.split(options.delimiter || ",")) + ) + +/** + * Transforms the output value produced by a prompt. + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const map: { + ( + f: (output: Output) => Output2 + ): (self: Prompt) => Prompt + ( + self: Prompt, + f: (output: Output) => Output2 + ): Prompt +} = dual(2, ( + self: Prompt, + f: (output: Output) => Output2 +) => flatMap(self, (a) => succeed(f(a)))) + +/** + * Creates a password prompt that masks typed input and returns the submitted + * value wrapped in `Redacted`. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Password = ( + options: TextOptions +): Prompt => basePrompt(options, "password").pipe(map(Redacted.make)) + +/** + * Runs a prompt by reading terminal input and rendering prompt frames until the + * prompt submits a value. + * + * **Gotchas** + * + * The returned effect may fail with `Terminal.QuitError` if terminal input ends + * or the prompt is quit. + * + * @stability unstable + * @category running + * @since 4.0.0 + */ +export const run: ( + self: Prompt +) => Effect.Effect< + Output, + Terminal.QuitError, + Environment +> = Effect.fnUntraced( + function*(self: Prompt) { + const terminal = yield* Terminal.Terminal + const input = yield* terminal.readInput + return yield* runWithInput(self, terminal, input) + }, + Effect.mapError(() => new Terminal.QuitError({})), + Effect.scoped +) + +const getSelectInitialIndex = (choices: ReadonlyArray>): number => { + let initialIndex = 0 + let seenSelected = -1 + for (let i = 0; i < choices.length; i++) { + const choice = choices[i] as SelectChoice + if (choice.selected === true) { + if (seenSelected !== -1) { + throw new Error("InvalidArgumentException: only a single choice can be selected by default for Prompt.Select") + } + seenSelected = i + } + } + if (seenSelected !== -1) { + initialIndex = seenSelected + } + return initialIndex +} + +/** + * Creates a prompt that lets the user select a single value from a list of + * choices. + * + * **Gotchas** + * + * At most one choice may be marked as selected by default. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Select = (options: SelectOptions): Prompt => { + const opts: SelectOptionsReq = { + maxPerPage: 10, + ...options + } + const initialIndex = getSelectInitialIndex(opts.choices) + return Custom(initialIndex, { + render: handleSelectRender(opts), + process: handleSelectProcess(opts), + clear: handleSelectClear(opts) + }) +} + +/** + * Creates a prompt that lets users filter select choices by typing. + * + * **Details** + * + * Every printable character is appended to the filter query, so navigation is + * bound to the arrow keys, `tab`, and the `Ctrl+P` / `Ctrl+N` chords used by + * `readline` and `fzf` (`Ctrl+K` also moves up). `Ctrl+U` clears the query. + * + * **Example** (Filtering choices with autocomplete) + * + * ```ts import.meta.vitest + * import { Prompt } from "effect/cli" + * + * const language = Prompt.AutoComplete({ + * message: "Choose a language", + * choices: [ + * { title: "TypeScript", value: "ts" }, + * { title: "Rust", value: "rs" }, + * { title: "Kotlin", value: "kt" } + * ] + * }) + * + * Prompt.isPrompt(language) // => true + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const AutoComplete = (options: AutoCompleteOptions): Prompt => { + const opts: AutoCompleteOptionsReq = { + maxPerPage: 10, + filterLabel: "filter", + filterPlaceholder: "type to filter", + emptyMessage: "No matches", + ...options + } + const initialIndex = getSelectInitialIndex(opts.choices) + const filtered = filterAutoCompleteChoices(opts.choices, "") + const index = filtered.length === 0 + ? 0 + : filtered.includes(initialIndex) + ? initialIndex + : filtered[0] + const initialState: AutoCompleteState = { + query: "", + index, + filtered + } + return Custom(initialState, { + render: handleAutoCompleteRender(opts), + process: handleAutoCompleteProcess(opts), + clear: handleAutoCompleteClear(opts) + }) +} + +/** + * Creates a prompt that lets the user select multiple choices and returns their + * values as an array. + * + * **Details** + * + * The prompt supports default selected choices, bulk-selection commands, and + * minimum or maximum selection counts. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const MultiSelect = ( + options: SelectOptions & MultiSelectOptions +): Prompt> => { + const opts: SelectOptionsReq & MultiSelectOptionsReq = { + maxPerPage: 10, + ...options + } + // Seed initial selection from choices marked as selected: true + const initialSelected = new Set() + for (let i = 0; i < opts.choices.length; i++) { + const choice = opts.choices[i] as SelectChoice + if (choice.selected === true && !choice.disabled) { + initialSelected.add(i) + } + } + const initialState: MultiSelectState = { index: 0, selectedIndices: initialSelected, error: Option.none() } + return Custom(initialState, { + render: handleMultiSelectRender(opts), + process: handleMultiSelectProcess(opts), + clear: handleMultiSelectClear(opts) + }) +} + +/** + * Creates a `Prompt` which immediately succeeds with the specified value. + * + * **Details** + * + * This prompt does not attempt to obtain user input or render anything to the + * screen. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const succeed = (value: A): Prompt => { + const op = Object.create(proto) + op._tag = "Succeed" + op.value = value + return op +} + +/** + * Creates a text-entry prompt that echoes input and returns the submitted + * string after validation. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const String = ( + options: TextOptions +): Prompt => basePrompt(options, "text") + +/** + * Creates a toggle prompt that lets the user switch between active and inactive + * states and returns the selected boolean value. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const Toggle = (options: ToggleOptions): Prompt => { + const opts: ToggleOptionsReq = { + initial: false, + active: "on", + inactive: "off", + ...options + } + return Custom(opts.initial, { + render: handleToggleRender(opts), + process: handleToggleProcess, + clear: () => handleToggleClear(opts) + }) +} + +const proto = { + ...Effectable.Prototype>({ + label: "Prompt", + evaluate() { + return run(this) + } + }), + [TypeId]: { + _Output: (_: never) => _ + } +} + +type Op = Prompt & Body & { + readonly _tag: Tag +} + +type PromptPrimitive = Loop | OnSuccess | Succeed + +interface Loop extends + Op<"Loop", { + readonly initialState: unknown | Effect.Effect + readonly render: Handlers["render"] + readonly process: ( + input: unknown, + state: unknown + ) => Effect.Effect, never, Environment> + readonly clear: Handlers["clear"] + readonly events: Queue.Dequeue | undefined + }> +{} + +/** + * @internal + */ +export interface OnSuccess extends + Op<"OnSuccess", { + readonly prompt: PromptPrimitive + readonly onSuccess: (value: unknown) => Prompt + }> +{} + +interface Succeed extends + Op<"Succeed", { + readonly value: unknown + }> +{} + +const allTupled = >>(arg: T): Prompt< + { + [K in keyof T]: [T[K]] extends [Prompt] ? A : never + } +> => { + if (arg.length === 0) { + return succeed([]) as any + } + if (arg.length === 1) { + return map(arg[0], (x) => [x]) as any + } + let result = map(arg[0], (x) => [x]) + for (let i = 1; i < arg.length; i++) { + const curr = arg[i] + result = flatMap(result, (tuple) => map(curr, (a) => [...tuple, a])) + } + return result as any +} + +const runWithInput = ( + prompt: Prompt, + terminal: Terminal.Terminal, + input: Queue.Dequeue +): Effect.Effect => + Effect.suspend(() => { + const op = prompt as PromptPrimitive + switch (op._tag) { + case "Loop": { + return runLoop(op, terminal, input) + } + case "OnSuccess": { + return Effect.flatMap( + runWithInput(op.prompt, terminal, input), + (a) => runWithInput(op.onSuccess(a), terminal, input) + ) as any + } + case "Succeed": { + return Effect.succeed(op.value) + } + } + }) + +const runLoop = Effect.fnUntraced( + function*( + loop: Loop, + terminal: Terminal.Terminal, + input: Queue.Dequeue + ) { + let state = Effect.isEffect(loop.initialState) ? yield* loop.initialState : loop.initialState + let action: Action = Action.NextFrame({ state }) + let clear = "" + while (true) { + const msg = yield* loop.render(state, action) + yield* Effect.orDie(terminal.display(clear + msg)) + clear = "" + if (loop.events) { + const takeInput = Queue.take(input).pipe( + Effect.map((input) => ({ _tag: "Input" as const, input })) + ) + const result = yield* Effect.raceFirst( + takeInput, + Queue.take(loop.events).pipe(Effect.map((value) => ({ _tag: "Event" as const, value }))) + ) + action = yield* loop.process(result, state) + } else { + const result = yield* Queue.take(input) + action = yield* loop.process(result, state) + } + switch (action._tag) { + case "Beep": + continue + case "NextFrame": { + clear = yield* loop.clear(state, action) + state = action.state + continue + } + case "Submit": { + clear = yield* loop.clear(state, action) + const msg = yield* loop.render(state, action) + yield* Effect.orDie(terminal.display(clear + msg)) + return action.value + } + } + } + }, + (effect, _, terminal) => Effect.ensuring(effect, Effect.orDie(terminal.display(Ansi.cursorShow))) +) + +const Action = Data.taggedEnum() + +/** + * Clears all lines taken up by the specified `text`. + */ +const eraseText = (text: string, columns: number): string => { + if (columns === 0) { + return Ansi.eraseLine + Ansi.cursorTo(0) + } + let rows = 0 + const lines = text.split(NEWLINE_REGEXP) + for (const line of lines) { + rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns) + } + return Ansi.eraseLines(rows) +} + +const lines = (prompt: string, columns: number): number => { + const lines = prompt.split(NEWLINE_REGEXP) + return columns === 0 + ? lines.length + : pipe( + Arr.map(lines, (line) => Math.ceil(line.length / columns)), + Arr.reduce(0, (left, right) => left + right) + ) +} + +const clearOutputWithError = (outputText: string, columns: number, errorText?: string): string => { + if (errorText !== undefined && errorText.length > 0) { + return Ansi.cursorDown(lines(errorText, columns)) + + eraseText(`\n${errorText}`, columns) + + eraseText(outputText, columns) + } + return eraseText(outputText, columns) +} + +interface ConfirmOptionsReq extends OptionsReq {} + +interface ConfirmState { + readonly value: boolean +} + +const renderBeep = Ansi.beep + +const NEWLINE_REGEXP = /\r?\n/ + +const handleConfirmClear = (options: ConfirmOptionsReq) => { + return Effect.fnUntraced(function*(state: ConfirmState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const confirmMessage = state.value + ? options.placeholder.defaultConfirm! + : options.placeholder.defaultDeny! + const promptText = renderConfirmOutput( + confirmMessage, + figures.prefix, + figures.pointerSmall, + options, + { plain: true } + ) + const clearOutput = eraseText(promptText, columns) + const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft + return clearOutput + resetCurrentLine + }) +} + +const renderConfirmOutput = ( + confirm: string, + leadingSymbol: string, + trailingSymbol: string, + options: ConfirmOptionsReq, + renderOptions?: RenderOptions | undefined +) => renderPrompt(confirm, options.message, leadingSymbol, trailingSymbol, renderOptions) + +const renderConfirmNextFrame = Effect.fnUntraced(function*(state: ConfirmState, options: ConfirmOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + // Marking these explicitly as present with `!` because they always will be + // and there is really no value in adding a `DeepRequired` type helper just + // for these internal cases + const confirmMessage = state.value + ? options.placeholder.defaultConfirm! + : options.placeholder.defaultDeny! + const confirm = Ansi.annotate(confirmMessage, figures.mutedColor) + const promptMsg = renderConfirmOutput(confirm, leadingSymbol, trailingSymbol, options) + return Ansi.cursorHide + promptMsg +}) + +const renderConfirmSubmission = Effect.fnUntraced(function*(value: boolean, options: ConfirmOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const confirmMessage = value ? options.label.confirm : options.label.deny + const promptMsg = renderConfirmOutput(confirmMessage, leadingSymbol, trailingSymbol, options) + return promptMsg + "\n" +}) + +const handleConfirmRender = (options: ConfirmOptionsReq) => { + return (_: ConfirmState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderConfirmNextFrame(state, options), + Submit: ({ value }) => renderConfirmSubmission(value, options) + }) + } +} + +const TRUE_VALUE_REGEXP = /^y|t$/ +const FALSE_VALUE_REGEXP = /^n|f$/ + +const handleConfirmProcess = (input: Terminal.UserInput, defaultValue: boolean) => { + const value = Option.getOrElse(input.input, () => "") + if (input.key.name === "enter" || input.key.name === "return") { + return Effect.succeed(Action.Submit({ value: defaultValue })) + } + if (TRUE_VALUE_REGEXP.test(value.toLowerCase())) { + return Effect.succeed(Action.Submit({ value: true })) + } + if (FALSE_VALUE_REGEXP.test(value.toLowerCase())) { + return Effect.succeed(Action.Submit({ value: false })) + } + return Effect.succeed(Action.Beep()) +} + +interface DateOptionsReq extends OptionsReq {} + +interface DateState { + readonly typed: string + readonly cursor: number + readonly value: globalThis.Date + readonly dateParts: ReadonlyArray + readonly error: Option.Option +} + +const handleDateClear = (options: DateOptionsReq) => { + return Effect.fnUntraced(function*(state: DateState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft + const parts = Arr.reduce(state.dateParts, "", (doc, part) => doc + part.toString()) + const promptText = renderDateOutput(figures.prefix, figures.pointerSmall, parts, options, { plain: true }) + const errorText = Option.isSome(state.error) + ? Arr.match(state.error.value.split(NEWLINE_REGEXP), { + onEmpty: () => "", + onNonEmpty: (errorLines) => separateSymbol(figures.pointerSmall, errorLines.join("\n")) + }) + : "" + const clearOutput = clearOutputWithError(promptText, columns, errorText) + return clearOutput + resetCurrentLine + }) +} + +const renderDateError = (state: DateState, pointer: string, theme: Theme): string => { + if (Option.isSome(state.error)) { + const errorLines = state.error.value.split(NEWLINE_REGEXP) + if (Arr.isReadonlyArrayNonEmpty(errorLines)) { + const prefix = annotateSymbol(pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition + } + } + return "" +} + +const renderParts = (state: DateState, theme: Theme, submitted: boolean = false) => { + return Arr.reduce( + state.dateParts, + "", + (doc, part, currentIndex) => { + const partDoc = part.toString() + if (currentIndex === state.cursor && !submitted) { + const annotation = Ansi.combine(Ansi.underlined, theme.primaryColor) + return doc + Ansi.annotate(partDoc, annotation) + } + return doc + partDoc + } + ) +} + +const renderDateOutput = ( + leadingSymbol: string, + trailingSymbol: string, + parts: string, + options: DateOptionsReq, + renderOptions?: RenderOptions | undefined +) => renderPrompt(parts, options.message, leadingSymbol, trailingSymbol, renderOptions) + +const renderDateNextFrame = Effect.fnUntraced(function*(state: DateState, options: DateOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const parts = renderParts(state, figures) + const promptMsg = renderDateOutput(leadingSymbol, trailingSymbol, parts, options) + const errorMsg = renderDateError(state, figures.pointerSmall, figures) + return Ansi.cursorHide + promptMsg + errorMsg +}) + +const renderDateSubmission = Effect.fnUntraced(function*(state: DateState, options: DateOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const parts = renderParts(state, figures, true) + const promptMsg = renderDateOutput(leadingSymbol, trailingSymbol, parts, options) + return promptMsg + "\n" +}) + +const processUp = (state: DateState) => { + state.dateParts[state.cursor].increment() + return Action.NextFrame({ + state: { ...state, typed: "" } + }) +} + +const processDown = (state: DateState) => { + state.dateParts[state.cursor].decrement() + return Action.NextFrame({ + state: { ...state, typed: "" } + }) +} + +const processDateCursorLeft = (state: DateState) => { + const previous = state.dateParts[state.cursor].previousPart() + if (Option.isSome(previous)) { + return Action.NextFrame({ + state: { + ...state, + typed: "", + cursor: state.dateParts.indexOf(previous.value) + } + }) + } + return Action.Beep() +} + +const processDateCursorRight = (state: DateState) => { + const next = state.dateParts[state.cursor].nextPart() + if (Option.isSome(next)) { + return Action.NextFrame({ + state: { + ...state, + typed: "", + cursor: state.dateParts.indexOf(next.value) + } + }) + } + return Action.Beep() +} + +const processDateNext = (state: DateState) => { + const next = state.dateParts[state.cursor].nextPart() + const cursor = Option.match(next, { + onNone: () => state.dateParts.findIndex((part) => !part.isToken()), + onSome: (next) => state.dateParts.indexOf(next) + }) + return Action.NextFrame({ + state: { ...state, typed: "", cursor } + }) +} + +const defaultDateProcessor = (value: string, state: DateState) => { + if (/\d/.test(value)) { + const typed = state.typed + value + state.dateParts[state.cursor].setValue(typed) + return Action.NextFrame({ + state: { ...state, typed } + }) + } + return Action.Beep() +} + +const defaultLocales: DateOptionsReq["locales"] = { + months: [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], + monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + weekdaysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] +} + +const handleDateRender = (options: DateOptionsReq) => { + return (state: DateState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderDateNextFrame(state, options), + Submit: () => renderDateSubmission(state, options) + }) + } +} + +const handleDateProcess = (options: DateOptionsReq) => { + return (input: Terminal.UserInput, state: DateState) => { + switch (input.key.name) { + case "left": { + return Effect.succeed(processDateCursorLeft(state)) + } + case "right": { + return Effect.succeed(processDateCursorRight(state)) + } + case "k": + case "up": { + return Effect.succeed(processUp(state)) + } + case "j": + case "down": { + return Effect.succeed(processDown(state)) + } + case "tab": { + return Effect.succeed(processDateNext(state)) + } + case "enter": + case "return": { + return Effect.match(options.validate(state.value), { + onFailure: (error) => + Action.NextFrame({ + state: { + ...state, + error: Option.some(error) + } + }), + onSuccess: (value) => Action.Submit({ value }) + }) + } + default: { + return Effect.succeed(defaultDateProcessor(Option.getOrElse(input.input, () => ""), state)) + } + } + } +} + +const DATE_PART_REGEXP = + /\\(.)|"((?:\\["\\]|[^"])+)"|(D[Do]?|d{3,4}|d)|(M{1,4})|(YY(?:YY)?)|([aA])|([Hh]{1,2})|(m{1,2})|(s{1,2})|(S{1,4})|./g + +const regExpGroups: Record DatePart> = { + 1: ({ token, ...opts }) => new Token({ token: token.replace(/\\(.)/g, "$1"), ...opts }), + 2: (opts) => new Day(opts), + 3: (opts) => new Month(opts), + 4: (opts) => new Year(opts), + 5: (opts) => new Meridiem(opts), + 6: (opts) => new Hours(opts), + 7: (opts) => new Minutes(opts), + 8: (opts) => new Seconds(opts), + 9: (opts) => new Milliseconds(opts) +} + +const makeDateParts = ( + dateMask: string, + date: globalThis.Date, + locales: DateOptions["locales"] +) => { + const parts: Array = [] + let result: RegExpExecArray | null = null + // oxlint-disable-next-line no-cond-assign + while (result = DATE_PART_REGEXP.exec(dateMask)) { + const match = result.shift() + const index = result.findIndex((group) => group !== undefined) + if (index in regExpGroups) { + const token = (result[index] || match)! + parts.push(regExpGroups[index]({ token, date, parts, locales })) + } else { + parts.push(new Token({ token: (result[index] || match)!, date, parts, locales })) + } + } + const orderedParts = parts.reduce((array, element) => { + const lastElement = array[array.length - 1] + if (element.isToken() && lastElement !== undefined && lastElement.isToken()) { + lastElement.setValue(element.token) + } else { + array.push(element) + } + return array + }, Arr.empty()) + parts.splice(0, parts.length, ...orderedParts) + return parts +} + +interface DatePartParams { + readonly token: string + readonly locales: DateOptions["locales"] + readonly date?: globalThis.Date + readonly parts?: ReadonlyArray +} + +abstract class DatePart { + token: string + readonly date: globalThis.Date + readonly parts: ReadonlyArray + readonly locales: DateOptions["locales"] + + constructor(params: DatePartParams) { + this.token = params.token + this.locales = params.locales + this.date = params.date || new globalThis.Date() + this.parts = params.parts || [this] + } + + /** + * Increments this date part. + */ + abstract increment(): void + + /** + * Decrements this date part. + */ + abstract decrement(): void + + /** + * Sets the current value of this date part to the provided value. + */ + abstract setValue(value: string): void + + /** + * Returns `true` if this `DatePart` is a `Token`, `false` otherwise. + */ + isToken(): this is Token { + return false + } + + /** + * Retrieves the next date part in the list of parts. + */ + nextPart(): Option.Option { + const currentPartIndex = Option.getOrElse(Arr.findFirstIndex(this.parts, (part) => part === this), () => 0) + return Arr.findFirst(this.parts.slice(currentPartIndex + 1), (part) => !part.isToken()) + } + + /** + * Retrieves the previous date part in the list of parts. + */ + previousPart(): Option.Option { + const currentPartIndex = Arr.findFirstIndex(this.parts, (part) => part === this) + if (Option.isSome(currentPartIndex)) { + return Arr.findLast(this.parts.slice(0, currentPartIndex.value), (part) => !part.isToken()) + } + return Option.none() + } + + toString() { + return globalThis.String(this.date) + } +} + +class Token extends DatePart { + increment(): void {} + + decrement(): void {} + + setValue(value: string): void { + this.token = this.token + value + } + + override isToken(): this is Token { + return true + } + + override toString() { + return this.token + } +} + +class Milliseconds extends DatePart { + increment(): void { + this.date.setMilliseconds(this.date.getMilliseconds() + 1) + } + + decrement(): void { + this.date.setMilliseconds(this.date.getMilliseconds() - 1) + } + + setValue(value: string): void { + this.date.setMilliseconds(globalThis.Number.parseInt(value.slice(-this.token.length))) + } + + override toString() { + const millis = `${this.date.getMilliseconds()}` + return millis.padStart(4, "0").substring(0, this.token.length) + } +} + +class Seconds extends DatePart { + increment(): void { + this.date.setSeconds(this.date.getSeconds() + 1) + } + + decrement(): void { + this.date.setSeconds(this.date.getSeconds() - 1) + } + + setValue(value: string): void { + this.date.setSeconds(globalThis.Number.parseInt(value.slice(-2))) + } + + override toString() { + const seconds = `${this.date.getSeconds()}` + return this.token.length > 1 + ? seconds.padStart(2, "0") + : seconds + } +} + +class Minutes extends DatePart { + increment(): void { + this.date.setMinutes(this.date.getMinutes() + 1) + } + + decrement(): void { + this.date.setMinutes(this.date.getMinutes() - 1) + } + + setValue(value: string): void { + this.date.setMinutes(globalThis.Number.parseInt(value.slice(-2))) + } + + override toString() { + const minutes = `${this.date.getMinutes()}` + return this.token.length > 1 + ? minutes.padStart(2, "0") : + minutes + } +} + +class Hours extends DatePart { + increment(): void { + this.date.setHours(this.date.getHours() + 1) + } + + decrement(): void { + this.date.setHours(this.date.getHours() - 1) + } + + setValue(value: string): void { + this.date.setHours(globalThis.Number.parseInt(value.slice(-2))) + } + + override toString() { + const hours = /h/.test(this.token) + ? this.date.getHours() % 12 || 12 + : this.date.getHours() + return this.token.length > 1 + ? `${hours}`.padStart(2, "0") + : `${hours}` + } +} + +class Day extends DatePart { + increment(): void { + this.date.setDate(this.date.getDate() + 1) + } + + decrement(): void { + this.date.setDate(this.date.getDate() - 1) + } + + setValue(value: string): void { + this.date.setDate(globalThis.Number.parseInt(value.slice(-2))) + } + + override toString() { + const date = this.date.getDate() + const day = this.date.getDay() + switch (this.token) { + case "DD": + return `${date}`.padStart(2, "0") + case "Do": + return `${date}${this.ordinalIndicator(date)}` + case "d": + return `${day + 1}` + case "ddd": + return this.locales!.weekdaysShort[day]! + case "dddd": + return this.locales!.weekdays[day]! + default: + return `${date}` + } + } + + private ordinalIndicator(day: number): string { + if (day >= 11 && day <= 13) { + return "th" + } + switch (day % 10) { + case 1: + return "st" + case 2: + return "nd" + case 3: + return "rd" + default: + return "th" + } + } +} + +class Month extends DatePart { + increment(): void { + this.date.setMonth(this.date.getMonth() + 1) + } + + decrement(): void { + this.date.setMonth(this.date.getMonth() - 1) + } + + setValue(value: string): void { + const month = globalThis.Number.parseInt(value.slice(-2)) - 1 + this.date.setMonth(month < 0 ? 0 : month) + } + + override toString() { + const month = this.date.getMonth() + switch (this.token.length) { + case 2: + return `${month + 1}`.padStart(2, "0") + case 3: + return this.locales!.monthsShort[month]! + case 4: + return this.locales!.months[month]! + default: + return `${month + 1}` + } + } +} + +class Year extends DatePart { + increment(): void { + this.date.setFullYear(this.date.getFullYear() + 1) + } + + decrement(): void { + this.date.setFullYear(this.date.getFullYear() - 1) + } + + setValue(value: string): void { + this.date.setFullYear(globalThis.Number.parseInt(value.slice(-4))) + } + + override toString() { + const year = `${this.date.getFullYear()}`.padStart(4, "0") + return this.token.length === 2 + ? year.slice(-2) + : year + } +} + +class Meridiem extends DatePart { + increment(): void { + this.date.setHours((this.date.getHours() + 12) % 24) + } + + decrement(): void { + this.increment() + } + + setValue(_value: string): void {} + + override toString() { + const meridiem = this.date.getHours() >= 12 ? "pm" : "am" + return /A/.test(this.token) + ? meridiem.toUpperCase() + : meridiem + } +} + +interface FileOptionsReq extends OptionsReq> { + readonly startingPath: Option.Option + readonly default: Option.Option +} + +interface FileState { + readonly cursor: number + readonly files: ReadonlyArray + readonly allFiles: ReadonlyArray + readonly query: string + readonly path: Option.Option + readonly confirm: FileConfirm +} + +const CONFIRM_MESSAGE = "The selected directory contains files. Would you like to traverse the selected directory?" +const FILE_FILTER_LABEL = "filter" +const FILE_FILTER_PLACEHOLDER = "type to filter" +const FILE_EMPTY_MESSAGE = "No matches" +type FileConfirm = Data.TaggedEnum<{ + readonly Show: {} + readonly Hide: {} +}> +const FileConfirm = Data.taggedEnum() + +const showConfirmation = FileConfirm.$is("Show") + +const resolveCurrentPath = ( + path: Option.Option, + options: FileOptionsReq +): Effect.Effect => { + if (Option.isSome(path)) { + return Effect.succeed(path.value) + } + if (Option.isSome(options.startingPath)) { + const startingPath = options.startingPath.value + return Effect.flatMap(FileSystem.FileSystem, (fs) => + // Ensure the user provided starting path exists + Effect.orDie(fs.exists(startingPath)).pipe( + Effect.flatMap((exists) => + exists ? Effect.void : Effect.die( + `The provided starting path '${startingPath}' does not exist` + ) + ), + Effect.as(startingPath) + )) + } + return Effect.sync(() => process.cwd()) +} + +const getFileList = Effect.fnUntraced(function*(directory: string, options: FileOptionsReq) { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const files = yield* Effect.orDie(fs.readDirectory(directory)).pipe( + // Always prepend the `".."` option to the file list but allow it + // to be filtered out if the user so desires + Effect.map((files) => ["..", ...files]) + ) + return yield* Effect.filter(files, (file) => { + const result = options.filter(file) + const userDefinedFilter = Effect.isEffect(result) + ? result + : Effect.succeed(result) + const directoryFilter = options.type === "directory" + ? Effect.map( + Effect.orDie(fs.stat(path.join(directory, file))), + (info) => info.type === "Directory" + ) + : Effect.succeed(true) + return Effect.zipWith(userDefinedFilter, directoryFilter, (a, b) => a && b) + }, { concurrency: files.length }) +}) + +const filterFiles = (files: ReadonlyArray, query: string) => { + if (query.length === 0) { + return files + } + const normalizedQuery = query.toLowerCase() + const filtered: Array = [] + for (let index = 0; index < files.length; index++) { + if (files[index].toLowerCase().includes(normalizedQuery)) { + filtered.push(files[index]) + } + } + return filtered +} + +const updateFileState = ( + state: FileState, + query: string, + allFiles: ReadonlyArray = state.allFiles +): FileState => { + const files = filterFiles(allFiles, query) + if (files.length === 0) { + return { ...state, query, allFiles, files, cursor: 0 } + } + const selected = state.files[state.cursor] + const cursor = selected === undefined ? 0 : files.indexOf(selected) + return { + ...state, + query, + allFiles, + files, + cursor: cursor === -1 ? 0 : cursor + } +} + +const handleFileClear = (options: FileOptionsReq) => { + return Effect.fnUntraced(function*(state: FileState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const path = yield* Path.Path + const figures = yield* getTheme(options) + const currentPath = yield* resolveCurrentPath(state.path, options) + const selectedPath = state.files[state.cursor] + const resolvedPath = selectedPath === undefined ? currentPath : path.resolve(currentPath, selectedPath) + const resolvedPathText = separateSymbol(figures.pointerSmall, resolvedPath) + const isConfirming = showConfirmation(state.confirm) + const promptText = isConfirming + ? renderPrompt("(Y/n)", CONFIRM_MESSAGE, figures.prefix, figures.pointerSmall, { plain: true }) + : renderPrompt( + renderFileFilter(state, figures, { plain: true }), + options.message, + figures.tick, + figures.ellipsis, + { + plain: true + } + ) + const filesText = isConfirming + ? "" + : renderFiles(state, state.files, figures, options, { plain: true }) + const outputText = isConfirming + ? `${promptText}\n${resolvedPathText}` + : `${promptText}\n${resolvedPathText}\n${filesText}` + const clearOutput = eraseText(outputText, columns) + const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft + return clearOutput + resetCurrentLine + }) +} + +type RenderOptions = { + readonly plain?: boolean +} + +const renderPrompt = ( + confirm: string, + message: string, + leadingSymbol: string, + trailingSymbol: string, + options?: RenderOptions | undefined +) => { + const prefix = leadingSymbol.length === 0 ? "" : leadingSymbol + " " + const renderLine = (line: string) => { + let output = prefix + line + if (trailingSymbol.length > 0) { + output += " " + trailingSymbol + } + if (confirm.length > 0) { + output += " " + confirm + } else if (trailingSymbol.length > 0) { + output += " " + } + return output + } + const annotate = options?.plain === true + ? (line: string) => line + : annotateLine + return Arr.match(message.split(NEWLINE_REGEXP), { + onEmpty: () => renderLine(""), + onNonEmpty: (promptLines) => { + const lines = Arr.map(promptLines, (line) => annotate(line)) + return renderLine(lines.join("\n")) + } + }) +} + +const renderPrefix = ( + state: FileState, + toDisplay: { readonly startIndex: number; readonly endIndex: number }, + currentIndex: number, + length: number, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + const prefix = renderPagingPrefix( + figures, + currentIndex === toDisplay.startIndex && toDisplay.startIndex > 0, + currentIndex === toDisplay.endIndex - 1 && toDisplay.endIndex < length + ) + if (state.cursor === currentIndex) { + return renderOptions?.plain === true + ? figures.pointer + prefix + : annotateSymbol(figures.pointer, figures.primaryColor) + prefix + } + return prefix + " ".repeat(figures.pointer.length) +} + +const renderFileName = ( + file: string, + isSelected: boolean, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (renderOptions?.plain === true) { + return file + } + return isSelected + ? Ansi.annotate(file, Ansi.combine(Ansi.underlined, theme.primaryColor)) + : file +} + +const renderFileFilter = (state: FileState, theme: Theme, renderOptions?: RenderOptions | undefined) => { + const filterValue = state.query.length === 0 + ? renderOptions?.plain === true + ? FILE_FILTER_PLACEHOLDER + : Ansi.annotate(FILE_FILTER_PLACEHOLDER, theme.mutedColor) + : renderOptions?.plain === true + ? state.query + : Ansi.annotate(state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) + return `[${FILE_FILTER_LABEL}: ${filterValue}]` +} + +const renderFiles = ( + state: FileState, + files: ReadonlyArray, + figures: Theme, + options: FileOptionsReq, + renderOptions?: RenderOptions | undefined +) => { + const length = files.length + if (length === 0) { + return renderOptions?.plain === true + ? FILE_EMPTY_MESSAGE + : Ansi.annotate(FILE_EMPTY_MESSAGE, figures.mutedColor) + } + const toDisplay = entriesToDisplay(state.cursor, length, options.maxPerPage) + const documents: Array = [] + for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) { + const isSelected = state.cursor === index + const prefix = renderPrefix(state, toDisplay, index, length, figures, renderOptions) + const fileName = renderFileName(files[index], isSelected, figures, renderOptions) + documents.push(prefix + fileName) + } + return documents.join("\n") +} + +const renderFileNextFrame = Effect.fnUntraced(function*(state: FileState, options: FileOptionsReq) { + const path = yield* Path.Path + const figures = yield* getTheme(options) + const currentPath = yield* resolveCurrentPath(state.path, options) + const selectedPath = state.files[state.cursor] + const resolvedPath = selectedPath === undefined ? currentPath : path.resolve(currentPath, selectedPath) + const resolvedPathMsg = Ansi.annotate(separateSymbol(figures.pointerSmall, resolvedPath), figures.mutedColor) + + if (showConfirmation(state.confirm)) { + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const confirm = Ansi.annotate("(Y/n)", figures.mutedColor) + const promptMsg = renderPrompt(confirm, CONFIRM_MESSAGE, leadingSymbol, trailingSymbol) + return Ansi.cursorHide + promptMsg + "\n" + resolvedPathMsg + } + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderPrompt(renderFileFilter(state, figures), options.message, leadingSymbol, trailingSymbol) + const files = renderFiles(state, state.files, figures, options) + return Ansi.cursorHide + promptMsg + "\n" + resolvedPathMsg + "\n" + files +}) + +const renderFileSubmission = Effect.fnUntraced(function*(state: FileState, value: string, options: FileOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderPrompt(renderFileFilter(state, figures), options.message, leadingSymbol, trailingSymbol) + return promptMsg + " " + Ansi.annotate(value, figures.submittedColor) + "\n" +}) + +const handleFileRender = (options: FileOptionsReq) => { + return ( + state: FileState, + action: Action + ): Effect.Effect => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderFileNextFrame(state, options), + Submit: ({ value }) => renderFileSubmission(state, value, options) + }) + } +} + +const processFileCursorUp = (state: FileState) => { + if (state.files.length === 0) { + return Effect.succeed(Action.Beep()) + } + const cursor = state.cursor - 1 + return Effect.succeed(Action.NextFrame({ + state: { ...state, cursor: cursor < 0 ? state.files.length - 1 : cursor } + })) +} + +const processFileCursorDown = (state: FileState) => { + if (state.files.length === 0) { + return Effect.succeed(Action.Beep()) + } + return Effect.succeed(Action.NextFrame({ + state: { ...state, cursor: (state.cursor + 1) % state.files.length } + })) +} + +const processFileBackspace = (state: FileState) => { + if (state.query.length === 0) { + return Effect.succeed(Action.Beep()) + } + const query = state.query.slice(0, state.query.length - 1) + return Effect.succeed(Action.NextFrame({ state: updateFileState(state, query) })) +} + +const processFileClear = (state: FileState) => Effect.succeed(Action.NextFrame({ state: updateFileState(state, "") })) + +const processFileInput = (input: string, state: FileState) => { + if (input.length === 0) { + return Effect.succeed(Action.Beep()) + } + const query = state.query + input + return Effect.succeed(Action.NextFrame({ state: updateFileState(state, query) })) +} + +const processSelection = Effect.fnUntraced(function*(state: FileState, options: FileOptionsReq) { + if (state.files.length === 0) { + return Action.Beep() + } + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const currentPath = yield* resolveCurrentPath(state.path, options) + const selectedPath = state.files[state.cursor] + const resolvedPath = path.resolve(currentPath, selectedPath) + const info = yield* Effect.orDie(fs.stat(resolvedPath)) + if (info.type === "Directory") { + const files = yield* getFileList(resolvedPath, options) + const filesWithoutParent = files.filter((file) => file !== "..") + // If the user selected a directory AND the prompt type can result with + // a directory, we must confirm: + // - If the selected directory has any files + // - Confirm whether or not the user wants to traverse those files + if (options.type === "directory" || options.type === "either") { + return filesWithoutParent.length === 0 + // Directory is empty so it's safe to select it + ? Action.Submit({ value: resolvedPath }) + // Directory has contents - show confirmation to user + : Action.NextFrame({ + state: { ...state, confirm: FileConfirm.Show() } + }) + } + return Action.NextFrame({ + state: { + cursor: 0, + files, + allFiles: files, + query: "", + path: Option.some(resolvedPath), + confirm: FileConfirm.Hide() + } + }) + } + return Action.Submit({ value: resolvedPath }) +}) + +const handleFileProcess = (options: FileOptionsReq) => { + return Effect.fnUntraced(function*(input: Terminal.UserInput, state: FileState) { + // Navigation is bound to ctrl chords rather than bare `j`/`k`, so that every + // printable character reaches the filter input. + if (input.key.ctrl) { + switch (input.key.name) { + case "u": { + if (showConfirmation(state.confirm)) { + return Action.Beep() + } + return yield* processFileClear(state) + } + case "p": + case "k": { + return yield* processFileCursorUp(state) + } + case "n": { + return yield* processFileCursorDown(state) + } + default: { + return Action.Beep() + } + } + } + switch (input.key.name) { + case "up": { + return yield* processFileCursorUp(state) + } + case "down": + case "tab": { + return yield* processFileCursorDown(state) + } + case "backspace": { + if (showConfirmation(state.confirm)) { + return Action.Beep() + } + return yield* processFileBackspace(state) + } + case "enter": + case "return": { + return yield* processSelection(state, options) + } + case "y": + case "t": { + if (showConfirmation(state.confirm)) { + const path = yield* Path.Path + const currentPath = yield* resolveCurrentPath(state.path, options) + const selectedPath = state.files[state.cursor] + const resolvedPath = path.resolve(currentPath, selectedPath) + const files = yield* getFileList(resolvedPath, options) + return Action.NextFrame({ + state: { + cursor: 0, + files, + allFiles: files, + query: "", + path: Option.some(resolvedPath), + confirm: FileConfirm.Hide() + } + }) + } + return yield* processFileInput(Option.getOrElse(input.input, () => ""), state) + } + case "n": + case "f": { + if (showConfirmation(state.confirm)) { + const path = yield* Path.Path + const currentPath = yield* resolveCurrentPath(state.path, options) + const selectedPath = state.files[state.cursor] + const resolvedPath = path.resolve(currentPath, selectedPath) + return Action.Submit({ value: resolvedPath }) + } + return yield* processFileInput(Option.getOrElse(input.input, () => ""), state) + } + default: { + if (showConfirmation(state.confirm)) { + return Action.Beep() + } + return yield* processFileInput(Option.getOrElse(input.input, () => ""), state) + } + } + }) +} + +interface SelectOptionsReq extends SelectOptions { + readonly maxPerPage: number +} +interface MultiSelectOptionsReq extends MultiSelectOptions {} + +type MultiSelectState = { + index: number + selectedIndices: Set + error: Option.Option +} + +const renderMultiSelectError = ( + state: MultiSelectState, + pointer: string, + theme: Theme, + renderOptions?: RenderOptions | undefined +): string => { + if (Option.isSome(state.error)) { + return Arr.match(state.error.value.split(NEWLINE_REGEXP), { + onEmpty: () => "", + onNonEmpty: (errorLines) => { + if (renderOptions?.plain === true) { + return separateSymbol(pointer, errorLines.join("\n")) + } + const prefix = annotateSymbol(pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition + } + }) + } + return "" +} + +const renderChoiceDescription = ( + choice: SelectChoice, + isActive: boolean, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (!choice.disabled && choice.description && isActive) { + const text = theme.descriptionSeparator + choice.description + return renderOptions?.plain === true + ? text + : Ansi.annotate(text, theme.mutedColor) + } + return "" +} + +const metaOptionsCount = 2 + +const renderMultiSelectTitle = ( + title: string, + isHighlighted: boolean, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (renderOptions?.plain === true || !isHighlighted) { + return title + } + return Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.primaryColor)) +} + +const renderMultiSelectChoices = ( + state: MultiSelectState, + options: SelectOptionsReq & MultiSelectOptionsReq, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + const choices = options.choices + const checkboxWidth = Math.max(figures.checkboxOn.length, figures.checkboxOff.length) + const selectableCount = choices.filter((choice) => !choice.disabled).length + const selectedCount = Array.from(state.selectedIndices).filter((index) => !choices[index].disabled).length + const allSelected = selectedCount === selectableCount + + const selectAllText = allSelected + ? options?.selectNone ?? "Select None" + : options?.selectAll ?? "Select All" + + const inverseSelectionText = options?.inverseSelection ?? "Inverse Selection" + + const metaOptions = [ + { title: selectAllText }, + { title: inverseSelectionText } + ] + const allChoices = [...metaOptions, ...choices] + const toDisplay = entriesToDisplay(state.index, allChoices.length, options.maxPerPage) + const documents: Array = [] + for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) { + const choice = allChoices[index] + const isHighlighted = state.index === index + const prefix = renderPagingPrefix( + figures, + index === toDisplay.startIndex && toDisplay.startIndex > 0, + index === toDisplay.endIndex - 1 && toDisplay.endIndex < allChoices.length + ) + if (index < metaOptions.length) { + // Meta options + const title = renderMultiSelectTitle(choice.title, isHighlighted, figures, renderOptions) + documents.push(prefix + (prefix.length === 0 ? "" : " ") + title) + } else { + // Regular choices + const choiceIndex = index - metaOptions.length + const isSelected = state.selectedIndices.has(choiceIndex) + const checkbox = isSelected ? figures.checkboxOn : figures.checkboxOff + const annotatedCheckbox = isHighlighted && renderOptions?.plain !== true + ? Ansi.annotate(checkbox, figures.primaryColor) + : checkbox + const selectChoice = choice as SelectChoice + const title = renderChoiceTitle(selectChoice, isHighlighted, figures, renderOptions) + const description = renderChoiceDescription(selectChoice, isHighlighted, figures, renderOptions) + const checkboxPrefix = checkboxWidth === 0 + ? "" + : annotatedCheckbox + " ".repeat(checkboxWidth - checkbox.length + 1) + documents.push( + prefix + (prefix.length === 0 ? "" : " ") + checkboxPrefix + title + " " + description + ) + } + } + return documents.join("\n") +} + +const renderMultiSelectNextFrame = Effect.fnUntraced( + function*(state: MultiSelectState, options: SelectOptionsReq) { + const figures = yield* getTheme(options) + const choices = renderMultiSelectChoices(state, options, figures) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) + const error = renderMultiSelectError(state, figures.pointer, figures) + return Ansi.cursorHide + withSelectHeader(promptMsg, choices) + error + } +) + +const renderMultiSelectSubmission = Effect.fnUntraced( + function*(state: MultiSelectState, options: SelectOptionsReq) { + const figures = yield* getTheme(options) + const selectedChoices = Array.from(state.selectedIndices).sort(EffectNumber.Order).map((index) => + options.choices[index].title + ) + const selectedText = selectedChoices.join(", ") + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) ?? leadingSymbol + return promptMsg + " " + Ansi.annotate(selectedText, figures.submittedColor) + "\n" + } +) + +const processMultiSelectCursorUp = (state: MultiSelectState, totalChoices: number) => { + const newIndex = state.index === 0 ? totalChoices - 1 : state.index - 1 + return Effect.succeed(Action.NextFrame({ state: { ...state, index: newIndex } })) +} + +const processMultiSelectCursorDown = (state: MultiSelectState, totalChoices: number) => { + const newIndex = (state.index + 1) % totalChoices + return Effect.succeed(Action.NextFrame({ state: { ...state, index: newIndex } })) +} + +const processSpace = ( + state: MultiSelectState, + options: SelectOptionsReq +) => { + const selectedIndices = new Set(state.selectedIndices) + if (state.index === 0) { + const selectableCount = options.choices.filter((choice) => !choice.disabled).length + const selectedCount = Array.from(state.selectedIndices).filter((index) => !options.choices[index].disabled).length + if (selectedCount === selectableCount) { + selectedIndices.clear() + } else { + for (let i = 0; i < options.choices.length; i++) { + if (!options.choices[i].disabled) { + selectedIndices.add(i) + } + } + } + } else if (state.index === 1) { + for (let i = 0; i < options.choices.length; i++) { + if (options.choices[i].disabled || state.selectedIndices.has(i)) { + selectedIndices.delete(i) + } else { + selectedIndices.add(i) + } + } + } else { + const choiceIndex = state.index - metaOptionsCount + if (options.choices[choiceIndex].disabled) { + return Effect.succeed(Action.Beep()) + } else if (selectedIndices.has(choiceIndex)) { + selectedIndices.delete(choiceIndex) + } else { + selectedIndices.add(choiceIndex) + } + } + return Effect.succeed(Action.NextFrame({ state: { ...state, selectedIndices } })) +} + +const handleMultiSelectClear = (options: SelectOptionsReq) => + Effect.fnUntraced(function*(state: MultiSelectState, _: Action>) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft + const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, { plain: true }) + const choicesText = renderMultiSelectChoices(state, options, figures, { plain: true }) + const errorText = renderMultiSelectError(state, figures.pointer, figures, { plain: true }) + const clearOutput = clearOutputWithError(withSelectHeader(promptText, choicesText), columns, errorText) + return clearOutput + clearPrompt + }) + +const handleMultiSelectProcess = (options: SelectOptionsReq & MultiSelectOptionsReq) => { + return (input: Terminal.UserInput, state: MultiSelectState) => { + const totalChoices = options.choices.length + metaOptionsCount + switch (input.key.name) { + case "k": + case "up": { + return processMultiSelectCursorUp({ ...state, error: Option.none() }, totalChoices) + } + case "j": + case "down": + case "tab": { + return processMultiSelectCursorDown({ ...state, error: Option.none() }, totalChoices) + } + case "space": { + return processSpace(state, options) + } + case "enter": + case "return": { + const selectedIndices = Array.from(state.selectedIndices).filter((index) => !options.choices[index].disabled) + const selectedCount = selectedIndices.length + if (options.min !== undefined && selectedCount < options.min) { + return Effect.succeed( + Action.NextFrame({ state: { ...state, error: Option.some(`At least ${options.min} are required`) } }) + ) + } + if (options.max !== undefined && selectedCount > options.max) { + return Effect.succeed( + Action.NextFrame({ state: { ...state, error: Option.some(`At most ${options.max} choices are allowed`) } }) + ) + } + const selectedValues = selectedIndices.sort(EffectNumber.Order).map((index) => options.choices[index].value) + return Effect.succeed(Action.Submit({ value: selectedValues })) + } + default: { + return Effect.succeed(Action.Beep()) + } + } + } +} + +const handleMultiSelectRender = (options: SelectOptionsReq) => { + return (state: MultiSelectState, action: Action>) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderMultiSelectNextFrame(state, options), + Submit: () => renderMultiSelectSubmission(state, options) + }) + } +} + +interface IntOptionsReq extends OptionsReq {} +interface NumberOptionsReq extends OptionsReq {} + +interface NumberState { + readonly cursor: number + readonly value: string + readonly error: Option.Option +} + +const handleNumberClear = (options: IntOptionsReq) => { + return Effect.fnUntraced(function*(state: NumberState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft + const errorText = renderNumberError(state, figures.pointerSmall, figures, { plain: true }) + const promptText = renderNumberOutput(state, figures.prefix, figures.pointerSmall, options, figures, { + plain: true + }) + const clearOutput = clearOutputWithError(promptText, columns, errorText) + return clearOutput + resetCurrentLine + }) +} + +const renderNumberInput = ( + state: NumberState, + submitted: boolean, + theme: Theme, + renderOptions?: RenderOptions | undefined +): string => { + const value = state.value === "" ? "" : `${state.value}` + if (submitted || renderOptions?.plain === true) { + return value + } + const annotation = Option.isSome(state.error) ? + theme.errorColor : + Ansi.combine(Ansi.underlined, theme.primaryColor) + return Ansi.annotate(value, annotation) +} + +const renderNumberError = ( + state: NumberState, + pointer: string, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (Option.isSome(state.error)) { + return Arr.match(state.error.value.split(NEWLINE_REGEXP), { + onEmpty: () => "", + onNonEmpty: (errorLines) => { + if (renderOptions?.plain === true) { + return separateSymbol(pointer, errorLines.join("\n")) + } + const prefix = annotateSymbol(pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition + } + }) + } + return "" +} + +const renderNumberOutput = ( + state: NumberState, + leadingSymbol: string, + trailingSymbol: string, + options: IntOptionsReq, + theme: Theme, + renderOptions?: RenderOptions | undefined, + submitted: boolean = false +) => { + const value = renderNumberInput(state, submitted, theme, renderOptions) + return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, renderOptions) +} + +const renderNumberNextFrame = Effect.fnUntraced(function*(state: NumberState, options: IntOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const errorMsg = renderNumberError(state, figures.pointerSmall, figures) + const promptMsg = renderNumberOutput(state, leadingSymbol, trailingSymbol, options, figures) + return promptMsg + errorMsg +}) + +const renderNumberSubmission = Effect.fnUntraced(function*(nextState: NumberState, options: IntOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderNumberOutput(nextState, leadingSymbol, trailingSymbol, options, figures, undefined, true) + return promptMsg + "\n" +}) + +const processNumberBackspace = (state: NumberState) => { + if (state.value.length <= 0) { + return Effect.succeed(Action.Beep()) + } + const value = state.value.slice(0, state.value.length - 1) + return Effect.succeed(Action.NextFrame({ + state: { ...state, value, error: Option.none() } + })) +} + +const processNumberClear = (state: NumberState) => + Effect.succeed(Action.NextFrame({ + state: { ...state, cursor: 0, value: "", error: Option.none() } + })) + +const defaultIntProcessor = (input: string, state: NumberState) => { + if (state.value.length === 0 && input === "-") { + return Effect.succeed(Action.NextFrame({ + state: { ...state, value: "-", error: Option.none() } + })) + } + + const parsed = globalThis.Number.parseInt(state.value + input) + if (globalThis.Number.isNaN(parsed)) { + return Effect.succeed(Action.Beep()) + } else { + return Effect.succeed(Action.NextFrame({ + state: { ...state, value: `${parsed}`, error: Option.none() } + })) + } +} + +const defaultFloatProcessor = (input: string, state: NumberState) => { + if (input === "." && state.value.includes(".")) { + return Effect.succeed(Action.Beep()) + } + if (state.value.length === 0 && input === "-") { + return Effect.succeed(Action.NextFrame({ + state: { ...state, value: "-", error: Option.none() } + })) + } + + const parsed = globalThis.Number.parseFloat(state.value + input) + if (globalThis.Number.isNaN(parsed)) { + return Effect.succeed(Action.Beep()) + } else { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + value: input === "." + ? `${parsed}.` + : state.value.includes(".") && /^\d$/.test(input) + ? state.value + input + : `${parsed}`, + error: Option.none() + } + })) + } +} + +const handleRenderInteger = (options: IntOptionsReq) => { + return (state: NumberState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderNumberNextFrame(state, options), + Submit: () => renderNumberSubmission(state, options) + }) + } +} + +const handleProcessInteger = (options: IntOptionsReq) => { + return (input: Terminal.UserInput, state: NumberState) => { + if (input.key.ctrl && input.key.name === "u") { + return processNumberClear(state) + } + switch (input.key.name) { + case "backspace": { + return processNumberBackspace(state) + } + case "k": + case "up": { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + value: state.value === "" || state.value === "-" + ? `${options.incrementBy}` + : `${globalThis.Number.parseInt(state.value) + options.incrementBy}`, + error: Option.none() + } + })) + } + case "j": + case "down": { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + value: state.value === "" || state.value === "-" + ? `-${options.decrementBy}` + : `${globalThis.Number.parseInt(state.value) - options.decrementBy}`, + error: Option.none() + } + })) + } + case "enter": + case "return": { + const parsed = globalThis.Number.parseInt(state.value) + if (globalThis.Number.isNaN(parsed)) { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + error: Option.some("Must provide an integer value") + } + })) + } else { + return Effect.match(options.validate(parsed), { + onFailure: (error) => + Action.NextFrame({ + state: { + ...state, + error: Option.some(error) + } + }), + onSuccess: (value) => Action.Submit({ value }) + }) + } + } + default: { + return defaultIntProcessor(Option.getOrElse(input.input, () => ""), state) + } + } + } +} + +const handleRenderFloat = (options: NumberOptionsReq) => { + return (state: NumberState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderNumberNextFrame(state, options), + Submit: () => renderNumberSubmission(state, options) + }) + } +} + +const handleProcessFloat = (options: NumberOptionsReq) => { + return (input: Terminal.UserInput, state: NumberState) => { + if (input.key.ctrl && input.key.name === "u") { + return processNumberClear(state) + } + switch (input.key.name) { + case "backspace": { + return processNumberBackspace(state) + } + case "k": + case "up": { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + value: state.value === "" || state.value === "-" + ? `${options.incrementBy}` + : `${globalThis.Number.parseFloat(state.value) + options.incrementBy}`, + error: Option.none() + } + })) + } + case "j": + case "down": { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + value: state.value === "" || state.value === "-" + ? `-${options.decrementBy}` + : `${globalThis.Number.parseFloat(state.value) - options.decrementBy}`, + error: Option.none() + } + })) + } + case "enter": + case "return": { + const parsed = globalThis.Number.parseFloat(state.value) + if (globalThis.Number.isNaN(parsed)) { + return Effect.succeed(Action.NextFrame({ + state: { + ...state, + error: Option.some("Must provide a floating point value") + } + })) + } else { + return Effect.flatMap( + Effect.sync(() => EffectNumber.round(parsed, options.precision)), + (rounded) => + Effect.match(options.validate(rounded), { + onFailure: (error) => + Action.NextFrame({ + state: { + ...state, + error: Option.some(error) + } + }), + onSuccess: (value) => Action.Submit({ value }) + }) + ) + } + } + default: { + return defaultFloatProcessor(Option.getOrElse(input.input, () => ""), state) + } + } + } +} + +type SelectState = number + +type AutoCompleteState = { + readonly query: string + readonly index: number + readonly filtered: ReadonlyArray +} + +interface AutoCompleteOptionsReq extends OptionsReq> {} + +const filterAutoCompleteChoices = (choices: ReadonlyArray>, query: string) => { + const normalizedQuery = query.toLowerCase() + const indices: Array = [] + for (let i = 0; i < choices.length; i++) { + if (choices[i].title.toLowerCase().includes(normalizedQuery)) { + indices.push(i) + } + } + return indices +} + +const updateAutoCompleteState = ( + state: AutoCompleteState, + options: AutoCompleteOptionsReq, + query: string +): AutoCompleteState => { + const filtered = filterAutoCompleteChoices(options.choices, query) + if (filtered.length === 0) { + return { ...state, query, filtered, index: 0 } + } + if (filtered.includes(state.index)) { + return { ...state, query, filtered } + } + return { ...state, query, filtered, index: filtered[0] } +} + +const autoCompleteCursor = (state: AutoCompleteState) => + Option.getOrElse(Arr.findFirstIndex(state.filtered, (index) => index === state.index), () => 0) + +const renderSelectOutput = ( + leadingSymbol: string, + trailingSymbol: string, + options: SelectOptionsReq, + renderOptions?: RenderOptions | undefined +): string | undefined => + options.message === undefined + ? undefined + : renderPrompt("", options.message, leadingSymbol, trailingSymbol, renderOptions) + +const withSelectHeader = (header: string | undefined, body: string): string => + header === undefined ? body : header + "\n" + body + +const renderAutoCompleteFilter = ( + state: AutoCompleteState, + options: AutoCompleteOptionsReq, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + const filterValue = state.query.length === 0 + ? renderOptions?.plain === true + ? options.filterPlaceholder + : Ansi.annotate(options.filterPlaceholder, theme.mutedColor) + : renderOptions?.plain === true + ? state.query + : Ansi.annotate(state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) + return `[${options.filterLabel}: ${filterValue}]` +} + +const renderAutoCompleteOutput = ( + state: AutoCompleteState, + leadingSymbol: string, + trailingSymbol: string, + options: AutoCompleteOptionsReq, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + const filter = renderAutoCompleteFilter(state, options, theme, renderOptions) + return renderPrompt(filter, options.message, leadingSymbol, trailingSymbol, renderOptions) +} + +const renderChoicePrefix = ( + state: SelectState, + choices: SelectOptionsReq["choices"], + toDisplay: { readonly startIndex: number; readonly endIndex: number }, + currentIndex: number, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + const prefix = renderPagingPrefix( + figures, + currentIndex === toDisplay.startIndex && toDisplay.startIndex > 0, + currentIndex === toDisplay.endIndex - 1 && toDisplay.endIndex < choices.length + ) + if (renderOptions?.plain === true) { + return state === currentIndex + ? figures.pointer + prefix + : prefix + " ".repeat(figures.pointer.length) + } + if (choices[currentIndex].disabled) { + const annotation = Ansi.combine(Ansi.bold, figures.mutedColor) + return state === currentIndex + ? annotateSymbol(figures.pointer, annotation) + prefix + : prefix + " ".repeat(figures.pointer.length) + } + return state === currentIndex + ? annotateSymbol(figures.pointer, figures.primaryColor) + prefix + : prefix + " ".repeat(figures.pointer.length) +} + +const renderAutoCompleteChoicePrefix = ( + state: AutoCompleteState, + options: AutoCompleteOptionsReq, + toDisplay: { readonly startIndex: number; readonly endIndex: number }, + currentIndex: number, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + const prefix = renderPagingPrefix( + figures, + currentIndex === toDisplay.startIndex && toDisplay.startIndex > 0, + currentIndex === toDisplay.endIndex - 1 && toDisplay.endIndex < state.filtered.length + ) + const choiceIndex = state.filtered[currentIndex] + if (renderOptions?.plain === true) { + return state.index === choiceIndex + ? figures.pointer + prefix + : prefix + " ".repeat(figures.pointer.length) + } + const choice = options.choices[choiceIndex] + if (choice.disabled) { + const annotation = Ansi.combine(Ansi.bold, figures.mutedColor) + return state.index === choiceIndex + ? annotateSymbol(figures.pointer, annotation) + prefix + : prefix + " ".repeat(figures.pointer.length) + } + return state.index === choiceIndex + ? annotateSymbol(figures.pointer, figures.primaryColor) + prefix + : prefix + " ".repeat(figures.pointer.length) +} + +const renderChoiceTitle = ( + choice: SelectChoice, + isSelected: boolean, + theme: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (renderOptions?.plain === true) { + return choice.title + } + const title = choice.title + if (isSelected) { + return choice.disabled + ? Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.mutedColor)) + : Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.primaryColor)) + } + return choice.disabled + ? Ansi.annotate(title, Ansi.combine(Ansi.strikethrough, theme.mutedColor)) + : title +} + +const renderSelectChoices = ( + state: SelectState, + options: SelectOptionsReq, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + const choices = options.choices + const toDisplay = entriesToDisplay(state, choices.length, options.maxPerPage) + const documents: Array = [] + for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) { + const choice = choices[index] + const isSelected = state === index + const prefix = renderChoicePrefix(state, choices, toDisplay, index, figures, renderOptions) + const title = renderChoiceTitle(choice, isSelected, figures, renderOptions) + const description = renderChoiceDescription(choice, isSelected, figures, renderOptions) + documents.push(prefix + title + " " + description) + } + return documents.join("\n") +} + +const renderAutoCompleteChoices = ( + state: AutoCompleteState, + options: AutoCompleteOptionsReq, + figures: Theme, + renderOptions?: RenderOptions | undefined +) => { + if (state.filtered.length === 0) { + return renderOptions?.plain === true + ? options.emptyMessage + : Ansi.annotate(options.emptyMessage, figures.mutedColor) + } + const cursor = autoCompleteCursor(state) + const toDisplay = entriesToDisplay(cursor, state.filtered.length, options.maxPerPage) + const documents: Array = [] + for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) { + const choiceIndex = state.filtered[index] + const choice = options.choices[choiceIndex] + const isSelected = state.index === choiceIndex + const prefix = renderAutoCompleteChoicePrefix(state, options, toDisplay, index, figures, renderOptions) + const title = renderChoiceTitle(choice, isSelected, figures, renderOptions) + const description = renderChoiceDescription(choice, isSelected, figures, renderOptions) + documents.push(prefix + title + " " + description) + } + return documents.join("\n") +} + +const renderSelectNextFrame = Effect.fnUntraced(function*(state: SelectState, options: SelectOptionsReq) { + const figures = yield* getTheme(options) + const choices = renderSelectChoices(state, options, figures) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) + return Ansi.cursorHide + withSelectHeader(promptMsg, choices) +}) + +const renderAutoCompleteNextFrame = Effect.fnUntraced(function*( + state: AutoCompleteState, + options: AutoCompleteOptionsReq +) { + const figures = yield* getTheme(options) + const choices = renderAutoCompleteChoices(state, options, figures) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const promptMsg = renderAutoCompleteOutput(state, leadingSymbol, trailingSymbol, options, figures) + return Ansi.cursorHide + promptMsg + "\n" + choices +}) + +const renderSelectSubmission = Effect.fnUntraced(function*(state: SelectState, options: SelectOptionsReq) { + const figures = yield* getTheme(options) + const selected = options.choices[state].title + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) ?? leadingSymbol + return promptMsg + " " + Ansi.annotate(selected, figures.submittedColor) + "\n" +}) + +const renderAutoCompleteSubmission = Effect.fnUntraced(function*( + state: AutoCompleteState, + options: AutoCompleteOptionsReq +) { + const figures = yield* getTheme(options) + const selected = options.choices[state.index].title + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderAutoCompleteOutput(state, leadingSymbol, trailingSymbol, options, figures) + return promptMsg + " " + Ansi.annotate(selected, figures.submittedColor) + "\n" +}) + +const processSelectCursorUp = (state: SelectState, choices: SelectOptionsReq["choices"]) => { + if (state === 0) { + return Effect.succeed(Action.NextFrame({ state: choices.length - 1 })) + } + return Effect.succeed(Action.NextFrame({ state: state - 1 })) +} + +const processSelectCursorDown = (state: SelectState, choices: SelectOptionsReq["choices"]) => { + if (state === choices.length - 1) { + return Effect.succeed(Action.NextFrame({ state: 0 })) + } + return Effect.succeed(Action.NextFrame({ state: state + 1 })) +} + +const processSelectNext = (state: SelectState, choices: SelectOptionsReq["choices"]) => { + return Effect.succeed(Action.NextFrame({ state: (state + 1) % choices.length })) +} + +const processAutoCompleteCursorUp = (state: AutoCompleteState) => { + if (state.filtered.length === 0) { + return Effect.succeed(Action.Beep()) + } + const cursor = autoCompleteCursor(state) + const nextCursor = cursor === 0 ? state.filtered.length - 1 : cursor - 1 + return Effect.succeed(Action.NextFrame({ state: { ...state, index: state.filtered[nextCursor] } })) +} + +const processAutoCompleteCursorDown = (state: AutoCompleteState) => { + if (state.filtered.length === 0) { + return Effect.succeed(Action.Beep()) + } + const cursor = autoCompleteCursor(state) + const nextCursor = (cursor + 1) % state.filtered.length + return Effect.succeed(Action.NextFrame({ state: { ...state, index: state.filtered[nextCursor] } })) +} + +const processAutoCompleteNext = (state: AutoCompleteState) => processAutoCompleteCursorDown(state) + +const processAutoCompleteBackspace = (state: AutoCompleteState, options: AutoCompleteOptionsReq) => { + if (state.query.length === 0) { + return Effect.succeed(Action.Beep()) + } + const query = state.query.slice(0, state.query.length - 1) + return Effect.succeed(Action.NextFrame({ state: updateAutoCompleteState(state, options, query) })) +} + +const processAutoCompleteClear = (state: AutoCompleteState, options: AutoCompleteOptionsReq) => + Effect.succeed(Action.NextFrame({ state: updateAutoCompleteState(state, options, "") })) + +const processAutoCompleteInput = (input: string, state: AutoCompleteState, options: AutoCompleteOptionsReq) => { + if (input.length === 0) { + return Effect.succeed(Action.Beep()) + } + const query = state.query + input + return Effect.succeed(Action.NextFrame({ state: updateAutoCompleteState(state, options, query) })) +} + +const handleSelectRender = (options: SelectOptionsReq) => { + return (state: SelectState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderSelectNextFrame(state, options), + Submit: () => renderSelectSubmission(state, options) + }) + } +} + +const handleAutoCompleteRender = (options: AutoCompleteOptionsReq) => { + return (state: AutoCompleteState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderAutoCompleteNextFrame(state, options), + Submit: () => renderAutoCompleteSubmission(state, options) + }) + } +} + +const handleSelectClear = (options: SelectOptionsReq) => + Effect.fnUntraced(function*(state: SelectState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft + const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, { plain: true }) + const choicesText = renderSelectChoices(state, options, figures, { plain: true }) + const clearOutput = eraseText(withSelectHeader(promptText, choicesText), columns) + return clearOutput + clearPrompt + }) + +const handleAutoCompleteClear = (options: AutoCompleteOptionsReq) => + Effect.fnUntraced(function*(state: AutoCompleteState, _: Action) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft + const promptText = renderAutoCompleteOutput( + state, + figures.prefix, + figures.pointerSmall, + options, + figures, + { plain: true } + ) + const choicesText = renderAutoCompleteChoices(state, options, figures, { plain: true }) + const clearOutput = eraseText(`${promptText}\n${choicesText}`, columns) + return clearOutput + clearPrompt + }) + +const handleSelectProcess = (options: SelectOptionsReq) => { + return (input: Terminal.UserInput, state: SelectState) => { + switch (input.key.name) { + case "k": + case "up": { + return processSelectCursorUp(state, options.choices) + } + case "j": + case "down": { + return processSelectCursorDown(state, options.choices) + } + case "tab": { + return processSelectNext(state, options.choices) + } + case "enter": + case "return": { + const selected = options.choices[state] + if (selected.disabled) { + return Effect.succeed(Action.Beep()) + } + return Effect.succeed(Action.Submit({ value: selected.value })) + } + default: { + return Effect.succeed(Action.Beep()) + } + } + } +} + +const handleAutoCompleteProcess = (options: AutoCompleteOptionsReq) => { + return (input: Terminal.UserInput, state: AutoCompleteState) => { + // Navigation is bound to ctrl chords rather than bare `j`/`k`, so that every + // printable character reaches the filter input. + if (input.key.ctrl) { + switch (input.key.name) { + case "u": { + return processAutoCompleteClear(state, options) + } + case "p": + case "k": { + return processAutoCompleteCursorUp(state) + } + case "n": { + return processAutoCompleteCursorDown(state) + } + default: { + return Effect.succeed(Action.Beep()) + } + } + } + switch (input.key.name) { + case "up": { + return processAutoCompleteCursorUp(state) + } + case "down": { + return processAutoCompleteCursorDown(state) + } + case "tab": { + return processAutoCompleteNext(state) + } + case "backspace": { + return processAutoCompleteBackspace(state, options) + } + case "enter": + case "return": { + if (state.filtered.length === 0) { + return Effect.succeed(Action.Beep()) + } + const selected = options.choices[state.index] + if (selected.disabled) { + return Effect.succeed(Action.Beep()) + } + return Effect.succeed(Action.Submit({ value: selected.value })) + } + default: { + return processAutoCompleteInput(Option.getOrElse(input.input, () => ""), state, options) + } + } + } +} + +interface TextOptionsReq extends OptionsReq { + /** + * The type of the text option. + */ + readonly type: "hidden" | "password" | "text" +} + +interface TextState { + readonly cursor: number + readonly value: string + readonly error: Option.Option +} + +const renderClearScreen = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft + const errorText = renderTextError(state, figures.pointerSmall, figures, { plain: true }) + const clearOutput = clearOutputWithError( + renderTextOutput(state, figures.prefix, figures.pointerSmall, options, figures, { plain: true }), + columns, + errorText + ) + return clearOutput + resetCurrentLine +}) + +const renderTextInput = ( + nextState: TextState, + options: TextOptionsReq, + theme: Theme, + submitted: boolean, + renderOptions?: RenderOptions | undefined +) => { + const text = nextState.value + if (renderOptions?.plain === true) { + switch (options.type) { + case "hidden": { + return "" + } + case "password": { + return theme.passwordMask.repeat(text.length) + } + case "text": { + return text + } + } + } + + if (text.length === 0) { + // Avoid wrapping an empty value in ANSI codes, which would make renderPrompt + // add spacing for content that occupies no terminal columns. + return "" + } + + const annotation = Option.isSome(nextState.error) ? + theme.errorColor + : submitted ? + theme.submittedColor + : Ansi.combine(Ansi.underlined, theme.primaryColor) + + switch (options.type) { + case "hidden": { + return "" + } + case "password": { + return annotateSymbol(theme.passwordMask.repeat(text.length), annotation) + } + case "text": { + return Ansi.annotate(text, annotation) + } + } +} + +const renderTextError = ( + nextState: TextState, + pointer: string, + theme: Theme, + renderOptions?: RenderOptions | undefined +): string => { + if (Option.isSome(nextState.error)) { + return Arr.match(nextState.error.value.split(NEWLINE_REGEXP), { + onEmpty: () => "", + onNonEmpty: (errorLines) => { + if (renderOptions?.plain === true) { + return separateSymbol(pointer, errorLines.join("\n")) + } + const prefix = annotateSymbol(pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition + } + }) + } + return "" +} + +const renderTextOutput = ( + nextState: TextState, + leadingSymbol: string, + trailingSymbol: string, + options: TextOptionsReq, + theme: Theme, + renderOptions?: RenderOptions | undefined, + submitted: boolean = false +) => { + const value = renderTextInput(nextState, options, theme, submitted, renderOptions) + return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, renderOptions) +} + +const renderTextNextFrame = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const promptMsg = renderTextOutput(state, leadingSymbol, trailingSymbol, options, figures) + const errorMsg = renderTextError(state, figures.pointerSmall, figures) + const cursorWidth = options.type === "password" ? figures.passwordMask.length : 1 + const offset = (state.cursor - state.value.length) * cursorWidth + return promptMsg + errorMsg + Ansi.cursorMove(offset) +}) + +const renderTextSubmission = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const promptMsg = renderTextOutput(state, leadingSymbol, trailingSymbol, options, figures, undefined, true) + return promptMsg + "\n" +}) + +const processTextBackspace = (state: TextState) => { + if (state.cursor <= 0) { + return Effect.succeed(Action.Beep()) + } + const beforeCursor = state.value.slice(0, state.cursor - 1) + const afterCursor = state.value.slice(state.cursor) + const cursor = state.cursor - 1 + const value = `${beforeCursor}${afterCursor}` + return Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor, value, error: Option.none() } + }) + ) +} + +const processTextClear = (state: TextState) => + Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor: 0, value: "", error: Option.none() } + }) + ) + +const processTextCursorLeft = (state: TextState) => { + if (state.cursor <= 0) { + return Effect.succeed(Action.Beep()) + } + const cursor = state.cursor - 1 + return Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor, error: Option.none() } + }) + ) +} + +const processTextCursorRight = (state: TextState) => { + if (state.cursor >= state.value.length) { + return Effect.succeed(Action.Beep()) + } + const cursor = Math.min(state.cursor + 1, state.value.length) + return Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor, error: Option.none() } + }) + ) +} + +const processTextCursorStart = (state: TextState) => + Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor: 0, error: Option.none() } + }) + ) + +const processTextCursorEnd = (state: TextState) => + Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor: state.value.length, error: Option.none() } + }) + ) + +const processTab = (state: TextState, options: TextOptionsReq) => { + if (state.value === options.default) { + return Effect.succeed(Action.Beep()) + } + const value = state.value.length === 0 ? options.default : state.value + return Effect.succeed( + Action.NextFrame({ + state: { ...state, value, cursor: value.length, error: Option.none() } + }) + ) +} + +const defaultTextProcessor = (input: string, state: TextState) => { + const beforeCursor = state.value.slice(0, state.cursor) + const afterCursor = state.value.slice(state.cursor) + const value = `${beforeCursor}${input}${afterCursor}` + const cursor = state.cursor + input.length + return Effect.succeed( + Action.NextFrame({ + state: { ...state, cursor, value, error: Option.none() } + }) + ) +} + +const handleTextRender = (options: TextOptionsReq) => { + return (state: TextState, action: Action) => { + return Action.$match(action, { + Beep: () => Effect.succeed(renderBeep), + NextFrame: ({ state }) => renderTextNextFrame(state, options), + Submit: () => renderTextSubmission(state, options) + }) + } +} + +const handleTextProcess = (options: TextOptionsReq) => { + return (input: Terminal.UserInput, state: TextState) => { + if (input.key.ctrl) { + switch (input.key.name) { + case "u": { + return processTextClear(state) + } + case "a": { + return processTextCursorStart(state) + } + case "e": { + return processTextCursorEnd(state) + } + default: { + return Effect.succeed(Action.Beep()) + } + } + } + switch (input.key.name) { + case "backspace": { + return processTextBackspace(state) + } + case "left": { + return processTextCursorLeft(state) + } + case "right": { + return processTextCursorRight(state) + } + case "home": { + return processTextCursorStart(state) + } + case "end": { + return processTextCursorEnd(state) + } + case "enter": + case "return": { + const value = state.value + return Effect.match(options.validate(value), { + onFailure: (error) => + Action.NextFrame({ + state: { ...state, value, error: Option.some(error) } + }), + onSuccess: (value) => Action.Submit({ value }) + }) + } + case "tab": { + return processTab(state, options) + } + default: { + return defaultTextProcessor(Option.getOrElse(input.input, () => ""), state) + } + } + } +} + +const handleTextClear = (options: TextOptionsReq) => { + return (state: TextState, _: Action) => { + return renderClearScreen(state, options) + } +} + +const basePrompt = ( + options: TextOptions, + type: TextOptionsReq["type"] +): Prompt => { + const opts: TextOptionsReq = { + default: "", + type, + validate: Effect.succeed, + ...options + } + + const initialState: TextState = { + cursor: opts.default.length, + value: opts.default, + error: Option.none() + } + return Custom(initialState, { + render: handleTextRender(opts), + process: handleTextProcess(opts), + clear: handleTextClear(opts) + }) +} + +interface ToggleOptionsReq extends OptionsReq {} + +type ToggleState = boolean + +const handleToggleClear = Effect.fnUntraced(function*(options: ToggleOptionsReq) { + const terminal = yield* Terminal.Terminal + const columns = yield* terminal.columns + const figures = yield* getTheme(options) + const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft + const toggleText = options.active + " " + separateSymbol(figures.toggleSeparator, options.inactive) + const promptText = renderPrompt(toggleText, options.message, figures.prefix, figures.pointerSmall, { plain: true }) + const clearOutput = eraseText(promptText, columns) + return clearOutput + clearPrompt +}) + +const renderToggle = ( + value: boolean, + options: ToggleOptionsReq, + theme: Theme, + submitted: boolean = false +) => { + const separator = annotateSymbol(theme.toggleSeparator, theme.mutedColor) + const selectedAnnotation = Ansi.combine( + Ansi.underlined, + submitted ? theme.submittedColor : theme.primaryColor + ) + const inactive = value + ? options.inactive + : Ansi.annotate(options.inactive, selectedAnnotation) + const active = value + ? Ansi.annotate(options.active, selectedAnnotation) + : options.active + return active + " " + separateSymbol(separator, inactive) +} + +const renderToggleOutput = ( + toggle: string, + leadingSymbol: string, + trailingSymbol: string, + options: ToggleOptionsReq +) => { + return renderPrompt(toggle, options.message, leadingSymbol, trailingSymbol) +} + +const renderToggleNextFrame = Effect.fnUntraced(function*(state: ToggleState, options: ToggleOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const toggle = renderToggle(state, options, figures) + const promptMsg = renderToggleOutput(toggle, leadingSymbol, trailingSymbol, options) + return Ansi.cursorHide + promptMsg +}) + +const renderToggleSubmission = Effect.fnUntraced(function*(value: boolean, options: ToggleOptionsReq) { + const figures = yield* getTheme(options) + const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const toggle = renderToggle(value, options, figures, true) + const promptMsg = renderToggleOutput(toggle, leadingSymbol, trailingSymbol, options) + return promptMsg + "\n" +}) + +const activate = Effect.succeed(Action.NextFrame({ state: true })) +const deactivate = Effect.succeed(Action.NextFrame({ state: false })) + +const handleToggleRender = (options: ToggleOptionsReq) => { + return (state: ToggleState, action: Action) => { + switch (action._tag) { + case "Beep": { + return Effect.succeed(renderBeep) + } + case "NextFrame": { + return renderToggleNextFrame(state, options) + } + case "Submit": { + return renderToggleSubmission(state, options) + } + } + } +} + +const handleToggleProcess = (input: Terminal.UserInput, state: ToggleState) => { + switch (input.key.name) { + case "0": + case "j": + case "delete": + case "right": + case "down": { + return deactivate + } + case "1": + case "k": + case "left": + case "up": { + return activate + } + case " ": + case "tab": { + return state ? deactivate : activate + } + case "enter": + case "return": { + return Effect.succeed(Action.Submit({ value: state })) + } + default: { + return Effect.succeed(Action.Beep()) + } + } +} + +const entriesToDisplay = (cursor: number, total: number, maxVisible?: number) => { + const max = maxVisible === undefined ? total : maxVisible + let startIndex = Math.min(total - max, cursor - Math.floor(max / 2)) + if (startIndex < 0) { + startIndex = 0 + } + const endIndex = Math.min(startIndex + max, total) + return { startIndex, endIndex } +} diff --git a/repos/effect/packages/effect/src/cli/index.ts b/repos/effect/packages/effect/src/cli/index.ts new file mode 100644 index 0000000000..4ed3433cab --- /dev/null +++ b/repos/effect/packages/effect/src/cli/index.ts @@ -0,0 +1,78 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Argument from "./Argument.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as CliConfig from "./CliConfig.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as CliError from "./CliError.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as CliOutput from "./CliOutput.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Command from "./Command.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Completions from "./Completions.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Flag from "./Flag.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as GlobalFlag from "./GlobalFlag.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HelpDoc from "./HelpDoc.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Param from "./Param.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Primitive from "./Primitive.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Prompt from "./Prompt.ts" diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/ansi.ts b/repos/effect/packages/effect/src/cli/internal/ansi.ts similarity index 84% rename from repos/effect/packages/effect/src/unstable/cli/internal/ansi.ts rename to repos/effect/packages/effect/src/cli/internal/ansi.ts index bfc4392ab8..207d5825b5 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/ansi.ts +++ b/repos/effect/packages/effect/src/cli/internal/ansi.ts @@ -15,58 +15,94 @@ const ESC = "\x1B[" const BEL = "\x07" const SEP = ";" -/** @internal */ +/** + * @internal + */ export const reset = `${ESC}0m` -/** @internal */ +/** + * @internal + */ export const bold = `${ESC}1m` -/** @internal */ +/** + * @internal + */ export const italicized = `${ESC}3m` -/** @internal */ +/** + * @internal + */ export const underlined = `${ESC}4m` -/** @internal */ +/** + * @internal + */ export const strikethrough = `${ESC}9m` -/** @internal */ +/** + * @internal + */ export const cursorShow = `${ESC}?25h` -/** @internal */ +/** + * @internal + */ export const cursorHide = `${ESC}?25l` -/** @internal */ +/** + * @internal + */ export const cursorLeft = `${ESC}G` -/** @internal */ +/** + * @internal + */ export const cursorSavePosition = `${ESC}s` -/** @internal */ +/** + * @internal + */ export const cursorRestorePosition = `${ESC}u` -/** @internal */ +/** + * @internal + */ export const eraseLine = `${ESC}2K` -/** @internal */ +/** + * @internal + */ export const beep = BEL -/** @internal */ +/** + * @internal + */ export const red = `${ESC}31m` -/** @internal */ +/** + * @internal + */ export const green = `${ESC}32m` -/** @internal */ +/** + * @internal + */ export const magenta = `${ESC}35m` -/** @internal */ +/** + * @internal + */ export const white = `${ESC}37m` -/** @internal */ +/** + * @internal + */ export const blackBright = `${ESC}90m` -/** @internal */ +/** + * @internal + */ export const cyanBright = `${ESC}96m` export const annotate = (text: string, ...styles: Array>) => { @@ -76,7 +112,9 @@ export const annotate = (text: string, ...styles: Array>) export const combine = (...styles: Array): Array => styles -/** @internal */ +/** + * @internal + */ export const cursorTo = (column: number, row?: number): string => { if (row === undefined) { return `${ESC}${Math.max(column + 1, 0)}G` @@ -84,12 +122,16 @@ export const cursorTo = (column: number, row?: number): string => { return `${ESC}${row + 1}${SEP}${Math.max(column + 1, 0)}H` } -/** @internal */ +/** + * @internal + */ export const cursorDown = (lines: number = 1): string => { return `${ESC}${lines}B` } -/** @internal */ +/** + * @internal + */ export const cursorMove = (column: number, row: number = 0): string => { let command = "" if (row < 0) { @@ -108,7 +150,9 @@ export const cursorMove = (column: number, row: number = 0): string => { return command } -/** @internal */ +/** + * @internal + */ export const eraseLines = (rows: number): string => { let command = "" for (let i = 0; i < rows; i++) { diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/auto-suggest.ts b/repos/effect/packages/effect/src/cli/internal/auto-suggest.ts similarity index 100% rename from repos/effect/packages/effect/src/unstable/cli/internal/auto-suggest.ts rename to repos/effect/packages/effect/src/cli/internal/auto-suggest.ts diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/command.ts b/repos/effect/packages/effect/src/cli/internal/command.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/cli/internal/command.ts rename to repos/effect/packages/effect/src/cli/internal/command.ts index a71fc7a89f..af161fc6db 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/command.ts +++ b/repos/effect/packages/effect/src/cli/internal/command.ts @@ -5,12 +5,12 @@ * Internal implementation details for CLI commands. * Public API is in ../Command.ts */ -import * as Arr from "../../../Array.ts" -import * as Context from "../../../Context.ts" -import * as Effect from "../../../Effect.ts" -import * as Effectable from "../../../Effectable.ts" -import * as Option from "../../../Option.ts" -import * as Predicate from "../../../Predicate.ts" +import * as Arr from "../../Array.ts" +import * as Context from "../../Context.ts" +import * as Effect from "../../Effect.ts" +import * as Effectable from "../../Effectable.ts" +import * as Option from "../../Option.ts" +import * as Predicate from "../../Predicate.ts" import * as CliError from "../CliError.ts" import type * as GlobalFlag from "../GlobalFlag.ts" import type { ArgDoc, ExampleDoc, FlagDoc, HelpDoc, SubcommandGroupDoc } from "../HelpDoc.ts" diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/completions/bash.ts b/repos/effect/packages/effect/src/cli/internal/completions/bash.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/cli/internal/completions/bash.ts rename to repos/effect/packages/effect/src/cli/internal/completions/bash.ts index e0c882f8d2..d71d0dfd12 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/completions/bash.ts +++ b/repos/effect/packages/effect/src/cli/internal/completions/bash.ts @@ -289,7 +289,9 @@ const generateFunction = ( } } -/** @internal */ +/** + * @internal + */ export const generate = ( executableName: string, descriptor: Completions.CommandDescriptor diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/completions/descriptor.ts b/repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts similarity index 98% rename from repos/effect/packages/effect/src/unstable/cli/internal/completions/descriptor.ts rename to repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts index f591ca215d..c84db2bc33 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/completions/descriptor.ts +++ b/repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts @@ -4,7 +4,7 @@ * * @internal */ -import * as Option from "../../../../Option.ts" +import * as Option from "../../../Option.ts" import type { Command } from "../../Command.ts" import type * as Completions from "../../Completions.ts" import * as Param from "../../Param.ts" @@ -69,7 +69,9 @@ const toArgumentType = (single: Param.Single<"argument", unknown>): Completions. // Extraction // --------------------------------------------------------------------------- -/** @internal */ +/** + * @internal + */ export const fromCommand = ( cmd: Command.Any, inheritedFlags: ReadonlyArray = [] diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/completions/fish.ts b/repos/effect/packages/effect/src/cli/internal/completions/fish.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/cli/internal/completions/fish.ts rename to repos/effect/packages/effect/src/cli/internal/completions/fish.ts index 02a8292cc1..aaca1cfca2 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/completions/fish.ts +++ b/repos/effect/packages/effect/src/cli/internal/completions/fish.ts @@ -234,7 +234,9 @@ const generateCompletions = ( } } -/** @internal */ +/** + * @internal + */ export const generate = ( executableName: string, descriptor: Completions.CommandDescriptor diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/completions/zsh.ts b/repos/effect/packages/effect/src/cli/internal/completions/zsh.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/cli/internal/completions/zsh.ts rename to repos/effect/packages/effect/src/cli/internal/completions/zsh.ts index ab26d64beb..e7a58ce8e0 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/completions/zsh.ts +++ b/repos/effect/packages/effect/src/cli/internal/completions/zsh.ts @@ -196,7 +196,9 @@ const generateFunction = ( } } -/** @internal */ +/** + * @internal + */ export const generate = ( executableName: string, descriptor: Completions.CommandDescriptor diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/config.ts b/repos/effect/packages/effect/src/cli/internal/config.ts similarity index 98% rename from repos/effect/packages/effect/src/unstable/cli/internal/config.ts rename to repos/effect/packages/effect/src/cli/internal/config.ts index 8556a9fe2e..2d8653b69b 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/config.ts +++ b/repos/effect/packages/effect/src/cli/internal/config.ts @@ -46,7 +46,7 @@ * 1. Flat iteration over all params for parsing/validation * 2. Reconstruction of original nested shape for handler input */ -import * as InternalRecord from "../../../internal/record.ts" +import * as InternalRecord from "../../internal/record.ts" import * as Param from "../Param.ts" /* ========================================================================== */ @@ -179,7 +179,9 @@ export const parseConfig = (config: Config): ConfigInternal => { } } -/** @internal */ +/** + * @internal + */ export const emptyConfig: ConfigInternal = parseConfig({}) const shiftNodeIndexes = (node: ConfigInternal.Node, offset: number): ConfigInternal.Node => { @@ -210,7 +212,9 @@ const shiftTreeIndexes = (tree: ConfigInternal.Tree, offset: number): ConfigInte return output } -/** @internal */ +/** + * @internal + */ export const mergeConfig = ( left: ConfigInternal, right: ConfigInternal diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/help.ts b/repos/effect/packages/effect/src/cli/internal/help.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/cli/internal/help.ts rename to repos/effect/packages/effect/src/cli/internal/help.ts index 0de041ccc6..804c175592 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/help.ts +++ b/repos/effect/packages/effect/src/cli/internal/help.ts @@ -5,7 +5,7 @@ * Internal helpers for generating help documentation. * Extracted from command.ts to avoid circular dependencies. */ -import * as Effect from "../../../Effect.ts" +import * as Effect from "../../Effect.ts" import type { Command } from "../Command.ts" import type * as GlobalFlag from "../GlobalFlag.ts" import type { FlagDoc, HelpDoc } from "../HelpDoc.ts" diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/lexer.ts b/repos/effect/packages/effect/src/cli/internal/lexer.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/cli/internal/lexer.ts rename to repos/effect/packages/effect/src/cli/internal/lexer.ts index 40e47641bb..71fc7d343b 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/lexer.ts +++ b/repos/effect/packages/effect/src/cli/internal/lexer.ts @@ -7,21 +7,27 @@ * The `--` delimiter is handled before tokenization: everything after it is * returned as trailing operands so values that look like options are preserved * exactly. A lone `-` is also preserved as a value instead of being interpreted - * as an option. + * as an option, as are negative numbers such as `-3` or `-3.70`. + */ +/** + * @internal */ -/** @internal */ export type Token = | { _tag: "LongOption"; name: string; raw: string; value?: string } | { _tag: "ShortOption"; flag: string; raw: string; value?: string } | { _tag: "Value"; value: string } -/** @internal */ +/** + * @internal + */ export interface LexResult { readonly tokens: ReadonlyArray readonly trailingOperands: ReadonlyArray } -/** @internal */ +/** + * @internal + */ export function lex(argv: ReadonlyArray): LexResult { const endIndex = argv.indexOf("--") @@ -40,11 +46,13 @@ export function lex(argv: ReadonlyArray): LexResult { } } +const negativeNumberRegex = /^-(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/ + const lexTokens = (args: ReadonlyArray): ReadonlyArray => { const tokens: Array = [] for (const arg of args) { - if (!arg.startsWith("-")) { + if (!arg.startsWith("-") || negativeNumberRegex.test(arg)) { tokens.push({ _tag: "Value", value: arg }) } else if (arg.startsWith("--")) { const equalIndex = arg.indexOf("=") diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/parser.ts b/repos/effect/packages/effect/src/cli/internal/parser.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/cli/internal/parser.ts rename to repos/effect/packages/effect/src/cli/internal/parser.ts index cfec25bfe6..627a214103 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/parser.ts +++ b/repos/effect/packages/effect/src/cli/internal/parser.ts @@ -24,8 +24,8 @@ * - Only the first Value token can open a subcommand * - Errors accumulate rather than throwing exceptions */ -import * as Effect from "../../../Effect.ts" -import * as Option from "../../../Option.ts" +import * as Effect from "../../Effect.ts" +import * as Option from "../../Option.ts" import * as CliError from "../CliError.ts" import type { Command, Environment, ParsedTokens } from "../Command.ts" import * as Param from "../Param.ts" @@ -38,14 +38,18 @@ import type { LexResult, Token } from "./lexer.ts" /* Public API */ /* ========================================================================== */ -/** @internal */ +/** + * @internal + */ export const getCommandPath = (parsedInput: ParsedTokens): ReadonlyArray => Option.match(parsedInput.subcommand, { onNone: () => [], onSome: (subcommand) => [subcommand.name, ...getCommandPath(subcommand.parsedInput)] }) -/** @internal */ +/** + * @internal + */ export const parseArgs = ( lexResult: LexResult, command: Command.Any, diff --git a/repos/effect/packages/effect/src/unstable/cli/internal/wizard.ts b/repos/effect/packages/effect/src/cli/internal/wizard.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/cli/internal/wizard.ts rename to repos/effect/packages/effect/src/cli/internal/wizard.ts index d34123e89c..f4ddbebe8f 100644 --- a/repos/effect/packages/effect/src/unstable/cli/internal/wizard.ts +++ b/repos/effect/packages/effect/src/cli/internal/wizard.ts @@ -1,8 +1,8 @@ -import * as Console from "../../../Console.ts" -import * as Effect from "../../../Effect.ts" -import * as Option from "../../../Option.ts" -import * as Redacted from "../../../Redacted.ts" -import type * as Terminal from "../../../Terminal.ts" +import * as Console from "../../Console.ts" +import * as Effect from "../../Effect.ts" +import * as Option from "../../Option.ts" +import * as Redacted from "../../Redacted.ts" +import type * as Terminal from "../../Terminal.ts" import type * as CliError from "../CliError.ts" import type * as Command from "../Command.ts" import * as Param from "../Param.ts" diff --git a/repos/effect/packages/effect/src/unstable/cluster/ClusterCron.ts b/repos/effect/packages/effect/src/cluster/ClusterCron.ts similarity index 90% rename from repos/effect/packages/effect/src/unstable/cluster/ClusterCron.ts rename to repos/effect/packages/effect/src/cluster/ClusterCron.ts index e9db9cc762..a71539e554 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ClusterCron.ts +++ b/repos/effect/packages/effect/src/cluster/ClusterCron.ts @@ -7,20 +7,21 @@ * for distributed maintenance work where the job should be owned by the cluster * rather than by every runner independently. * + * @stability unstable * @since 4.0.0 */ -import * as Cron from "../../Cron.ts" -import * as DateTime from "../../DateTime.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as PrimaryKey from "../../PrimaryKey.ts" -import * as Schedule from "../../Schedule.ts" -import * as Schema from "../../Schema.ts" -import type { Scope } from "../../Scope.ts" +import * as Cron from "../Cron.ts" +import * as DateTime from "../DateTime.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as PrimaryKey from "../PrimaryKey.ts" import * as Rpc from "../rpc/Rpc.ts" +import * as Schedule from "../Schedule.ts" +import * as Schema from "../Schema.ts" +import type { Scope } from "../Scope.ts" import * as ClusterSchema from "./ClusterSchema.ts" import { Persisted, Uninterruptible } from "./ClusterSchema.ts" import * as DeliverAt from "./DeliverAt.ts" @@ -37,6 +38,7 @@ import * as Singleton from "./Singleton.ts" * scheduling step and optional controls for shard group, next-run calculation, * and skipping stale scheduled runs. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/ClusterError.ts b/repos/effect/packages/effect/src/cluster/ClusterError.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/cluster/ClusterError.ts rename to repos/effect/packages/effect/src/cluster/ClusterError.ts index 6e74365ee7..d6cde8c79d 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ClusterError.ts +++ b/repos/effect/packages/effect/src/cluster/ClusterError.ts @@ -6,12 +6,13 @@ * envelope boundaries. Cluster clients, runners, and storage adapters use these * shared error values to report failures through typed Effect errors. * + * @stability unstable * @since 4.0.0 */ -import * as Cause from "../../Cause.ts" -import * as Effect from "../../Effect.ts" -import { hasProperty, isTagged } from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" +import * as Cause from "../Cause.ts" +import * as Effect from "../Effect.ts" +import { hasProperty, isTagged } from "../Predicate.ts" +import * as Schema from "../Schema.ts" import { EntityAddress } from "./EntityAddress.ts" import { RunnerAddress } from "./RunnerAddress.ts" import { SnowflakeFromString } from "./Snowflake.ts" @@ -22,6 +23,7 @@ const TypeId = "~effect/cluster/ClusterError" * Represents an error that occurs when a Runner receives a message for an entity * that is not assigned to the receiving runner. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -34,6 +36,7 @@ export class EntityNotAssignedToRunner /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -41,6 +44,7 @@ export class EntityNotAssignedToRunner /** * Returns `true` when the value is an `EntityNotAssignedToRunner` error. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is EntityNotAssignedToRunner { @@ -57,6 +61,7 @@ export class EntityNotAssignedToRunner * `cause` carries the underlying failure. `refail` maps encode and decode * failures into `MalformedMessage` values. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -67,6 +72,7 @@ export class MalformedMessage extends Schema.Error(`${TypeId}/ /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -74,6 +80,7 @@ export class MalformedMessage extends Schema.Error(`${TypeId}/ /** * Returns `true` when the value is a `MalformedMessage` error. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is MalformedMessage { @@ -83,6 +90,7 @@ export class MalformedMessage extends Schema.Error(`${TypeId}/ /** * Maps failures from the supplied effect into `MalformedMessage` errors. * + * @stability unstable * @since 4.0.0 */ static refail: (effect: Effect.Effect) => Effect.Effect< @@ -96,6 +104,7 @@ export class MalformedMessage extends Schema.Error(`${TypeId}/ * Represents an error that occurs when a message fails to be persisted into * cluster's mailbox storage. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -106,6 +115,7 @@ export class PersistenceError extends Schema.Error(`${TypeId}/ /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -113,6 +123,7 @@ export class PersistenceError extends Schema.Error(`${TypeId}/ /** * Maps failures from the supplied effect into `PersistenceError` values. * + * @stability unstable * @since 4.0.0 */ static refail(effect: Effect.Effect): Effect.Effect { @@ -124,6 +135,7 @@ export class PersistenceError extends Schema.Error(`${TypeId}/ * Represents an error that occurs when a Runner is not registered with the shard * manager. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -134,6 +146,7 @@ export class RunnerNotRegistered extends Schema.Error(`${Ty /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -142,6 +155,7 @@ export class RunnerNotRegistered extends Schema.Error(`${Ty /** * Represents an error that occurs when a Runner is unresponsive. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -152,6 +166,7 @@ export class RunnerUnavailable extends Schema.Error(`${TypeId /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -159,6 +174,7 @@ export class RunnerUnavailable extends Schema.Error(`${TypeId /** * Returns `true` when the value is a `RunnerUnavailable` error. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is RunnerUnavailable { @@ -180,6 +196,7 @@ export class RunnerUnavailable extends Schema.Error(`${TypeId * Volatile requests fail immediately. Persisted or durable messages are retried * or resumed from storage when the mailbox is full. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -190,6 +207,7 @@ export class MailboxFull extends Schema.Error(`${TypeId}/MailboxFul /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -197,6 +215,7 @@ export class MailboxFull extends Schema.Error(`${TypeId}/MailboxFul /** * Returns `true` when the value is a `MailboxFull` error. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is MailboxFull { @@ -212,6 +231,7 @@ export class MailboxFull extends Schema.Error(`${TypeId}/MailboxFul * * Carries the `address` and `envelopeId` for the affected request envelope. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -225,6 +245,7 @@ export class AlreadyProcessingMessage /** * Marks this value as a cluster error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -232,6 +253,7 @@ export class AlreadyProcessingMessage /** * Returns `true` when the value is an `AlreadyProcessingMessage` error. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is AlreadyProcessingMessage { diff --git a/repos/effect/packages/effect/src/unstable/cluster/ClusterMetrics.ts b/repos/effect/packages/effect/src/cluster/ClusterMetrics.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cluster/ClusterMetrics.ts rename to repos/effect/packages/effect/src/cluster/ClusterMetrics.ts index 6b091a7999..f9feb76950 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ClusterMetrics.ts +++ b/repos/effect/packages/effect/src/cluster/ClusterMetrics.ts @@ -7,9 +7,10 @@ * manage entities, runners, singletons, and sharding update these gauges while * the cluster is running. * + * @stability unstable * @since 4.0.0 */ -import * as Metric from "../../Metric.ts" +import * as Metric from "../Metric.ts" /** * Creates a gauge tracking the number of active entity instances for each entity type on @@ -33,6 +34,7 @@ import * as Metric from "../../Metric.ts" * @see {@link singletons} for singleton process counts on the current runner * @see {@link shards} for shard ownership on the current runner * + * @stability unstable * @category metrics * @since 4.0.0 */ @@ -42,6 +44,7 @@ export const entities = Metric.gauge("effect_cluster_entities", { bigint: true } * Creates a gauge tracking the number of singleton processes currently running on the * current runner. * + * @stability unstable * @category metrics * @since 4.0.0 */ @@ -60,6 +63,7 @@ export const singletons = Metric.gauge("effect_cluster_singletons", { bigint: tr * * @see {@link runnersHealthy} for the healthy-runner subset * + * @stability unstable * @category metrics * @since 4.0.0 */ @@ -83,6 +87,7 @@ export const runners = Metric.gauge("effect_cluster_runners", { bigint: true }) * * @see {@link runners} for the total registered-runner gauge * + * @stability unstable * @category metrics * @since 4.0.0 */ @@ -106,6 +111,7 @@ export const runnersHealthy = Metric.gauge("effect_cluster_runners_healthy", { b * This is runner-local, not a cluster-wide shard total. Aggregate per-runner * values carefully. * + * @stability unstable * @category metrics * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/ClusterSchema.ts b/repos/effect/packages/effect/src/cluster/ClusterSchema.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cluster/ClusterSchema.ts rename to repos/effect/packages/effect/src/cluster/ClusterSchema.ts index fe35f0d0ae..c6744cd311 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ClusterSchema.ts +++ b/repos/effect/packages/effect/src/cluster/ClusterSchema.ts @@ -4,11 +4,12 @@ * requests are persisted, handled in transactions, interrupted, traced, and * routed to shard groups without changing the request or response schema. * + * @stability unstable * @since 4.0.0 */ -import * as Cause from "../../Cause.ts" -import * as Context from "../../Context.ts" -import { constFalse, constTrue, constUndefined, identity } from "../../Function.ts" +import * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import { constFalse, constTrue, constUndefined, identity } from "../Function.ts" import type * as Rpc from "../rpc/Rpc.ts" import type { EntityId } from "./EntityId.ts" import type { Request } from "./Envelope.ts" @@ -17,6 +18,7 @@ import type { Request } from "./Envelope.ts" * Annotation carried by an interruption when a persisted cluster request is * abandoned by its current runner and must continue under another owner. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -38,6 +40,7 @@ export class Abandon extends Context.Service()("effect/cluster/Cl * * The default value is `false`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -64,6 +67,7 @@ export const Persisted = Context.Reference("effect/cluster/ClusterSchem * This annotation has transactional behavior only when the configured * `MessageStorage` implements it. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -82,6 +86,7 @@ export const WithTransaction = Context.Reference( * handling only, `"server"` for server-side handling only, or `false` to allow * interruption. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -101,6 +106,7 @@ export const Uninterruptible = Context.Reference( * @see {@link Uninterruptible} for the annotation values interpreted by this helper * @see {@link isUninterruptibleForClient} for the client-side counterpart * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -126,6 +132,7 @@ export const isUninterruptibleForServer = (context: Context.Context): boo * @see {@link Uninterruptible} for the annotation values interpreted by this helper * @see {@link isUninterruptibleForServer} for the server-side counterpart * + * @stability unstable * @category predicates * @since 4.0.0 */ @@ -141,6 +148,7 @@ export const isUninterruptibleForClient = (context: Context.Context): boo * * By default, every entity id is assigned to the `"default"` shard group. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -157,6 +165,7 @@ export const ShardGroup = Context.Reference<(entityId: EntityId) => string>( * * The default value is `true`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -177,6 +186,7 @@ export const ClientTracingEnabled = Context.Reference("effect/cluster/C * This only applies to requests handled by the entity, not to the generated * client. * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/ClusterWorkflowEngine.ts b/repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts similarity index 81% rename from repos/effect/packages/effect/src/unstable/cluster/ClusterWorkflowEngine.ts rename to repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts index adfda6621f..75e856e337 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ClusterWorkflowEngine.ts +++ b/repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts @@ -4,27 +4,29 @@ * executions, activities, deferred completions, resumes, interrupts, and durable * clock wakeups are represented as persisted cluster entity messages. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as DateTime from "../../DateTime.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import * as Fiber from "../../Fiber.ts" -import * as Latch from "../../Latch.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as PrimaryKey from "../../PrimaryKey.ts" -import * as RcMap from "../../RcMap.ts" -import type * as Record from "../../Record.ts" -import * as Schedule from "../../Schedule.ts" -import * as Schema from "../../Schema.ts" -import type * as Scope from "../../Scope.ts" -import * as Semaphore from "../../Semaphore.ts" +import * as Context from "../Context.ts" +import * as DateTime from "../DateTime.ts" +import * as Deferred from "../Deferred.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import * as Fiber from "../Fiber.ts" import * as Headers from "../http/Headers.ts" +import * as Latch from "../Latch.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as PrimaryKey from "../PrimaryKey.ts" +import * as RcMap from "../RcMap.ts" +import type * as Record from "../Record.ts" import * as Rpc from "../rpc/Rpc.ts" import { ClientAbort } from "../rpc/RpcSchema.ts" +import * as Schedule from "../Schedule.ts" +import * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" +import * as Semaphore from "../Semaphore.ts" import * as Activity from "../workflow/Activity.ts" import * as DurableClock from "../workflow/DurableClock.ts" import * as DurableDeferred from "../workflow/DurableDeferred.ts" @@ -38,6 +40,7 @@ import * as EntityId from "./EntityId.ts" import * as EntityType from "./EntityType.ts" import * as Envelope from "./Envelope.ts" import * as ClusterAbandon from "./internal/clusterAbandon.ts" +import { CurrentActivationScope } from "./internal/entityActivation.ts" import * as Message from "./Message.ts" import { MessageStorage } from "./MessageStorage.ts" import type { WithExitEncoded } from "./Reply.ts" @@ -54,6 +57,7 @@ import * as Snowflake from "./Snowflake.ts" * Workflow executions, activities, deferred completions, resumes, interrupts, * and durable clock wakeups are coordinated through persisted cluster entities. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -130,6 +134,14 @@ export const make = Effect.gen(function*() { const interruptedActivities = new Set() const activityLatches = new Map() const deferredState = WorkflowEngine.makeDeferredState() + // Rebuilds share an activation; overlapping activations have separate results. + // Weak keys do not retain scopes, but contexts captured by clients, activities, + // or finalizers can keep a closed scope (and its results) reachable until later. + const pendingByActivation = new WeakMap> + }>() const clients = yield* RcMap.make({ lookup: Effect.fnUntraced(function*(workflowName: string) { const entity = entities.get(workflowName) @@ -287,7 +299,14 @@ export const make = Effect.gen(function*() { ) if (Option.isNone(maybeSuspended)) return - yield* sharding.reset(Snowflake.Snowflake(maybeSuspended.value.requestId)) + if ( + !(yield* sharding.reset( + Snowflake.Snowflake(maybeSuspended.value.requestId), + { expectedReplyId: Snowflake.Snowflake(maybeSuspended.value.id) } + )) + ) { + return yield* Effect.fail(new Error("Failed to reset workflow run")) + } yield* sharding.pollStorage }) @@ -311,9 +330,9 @@ export const make = Effect.gen(function*() { yield* sharding.reset(requestId.value) }, Effect.scoped) - // A child may complete while its parent run is still unwinding. Subscribe to - // the run's reply before reading storage so the wake cannot fall between the - // parent's last read and its suspended reply. + // A child or deferred may complete while a run is still unwinding. Subscribe + // to the run's reply before reading storage so the wake cannot fall between + // the run's last read and its suspended reply. const waitForRunReply = Effect.fnUntraced(function*(workflow: Workflow.Any, request: Entity.Request) { const waiter = yield* storage.registerReplyHandler( new Message.OutgoingRequest({ @@ -375,20 +394,41 @@ export const make = Effect.gen(function*() { const engine = WorkflowEngine.makeUnsafe({ register: (workflow, execute) => - Effect.suspend(() => - sharding.registerEntity( + Effect.suspend(() => { + const existing = workflows.get(workflow._tag) + const registration = sharding.registerEntity( ensureEntity(workflow), Effect.gen(function*() { const address = yield* Entity.CurrentAddress const executionId = address.entityId - // Latest run request for this entity; replays reuse its request id. - let currentRun: Entity.Request | undefined + const activation = yield* CurrentActivationScope + let pending = pendingByActivation.get(activation) + if (!pending) { + pending = { workflowName: workflow._tag, executionId, results: new Map() } + pendingByActivation.set(activation, pending) + } + // Retain awaited names through reply persistence, including suspension. + // Replays reuse the request id but track a fresh set of awaits. + let currentRun: { + readonly request: Entity.Request + readonly awaitedDeferreds: ReadonlySet + } | undefined // Concurrent wakes share one wait for the current run to publish its reply. const resumeGate = Semaphore.makeUnsafe(1) + const resumeCurrentRun = Effect.suspend(() => + resumeGate.withPermitsIfAvailable(1)( + currentRun ? waitForRunReply(workflow, currentRun.request) : Effect.void + ) + ).pipe( + // Release the gate before reset can start another run, so later + // completions can wake that replay. + Effect.flatMap((waited) => Option.isSome(waited) ? resume(workflow, executionId) : Effect.void), + ensureSuccess + ) return { run: (request: Entity.Request) => { - currentRun = request const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId) + currentRun = { request, awaitedDeferreds: instance.awaitedDeferreds } const parent = (request.payload as any)[payloadParentKey] as | { workflowName: string; executionId: string } | undefined @@ -425,7 +465,9 @@ export const make = Effect.gen(function*() { ) }), Workflow.intoResult, - (effect) => deferredState.trackRun(instance, effect) + (effect) => deferredState.trackRun(instance, effect), + // Request middleware and local callers may carry another entity's context. + Effect.provideService(CurrentActivationScope, activation) ) as any }, @@ -444,6 +486,7 @@ export const make = Effect.gen(function*() { } const context = entry.context.pipe( Context.add(WorkflowEngine.WorkflowInstance, instance), + Context.add(CurrentActivationScope, activation), Context.add(Activity.CurrentAttempt, payload.attempt) ) return yield* entry.activity.executeEncoded.pipe( @@ -472,29 +515,47 @@ export const make = Effect.gen(function*() { ) }, - deferred: Effect.fnUntraced(function*(request: Entity.Request) { + deferred: (request: Entity.Request) => { const payload = request.payload as any - yield* deferredState.deferredDone(executionId, payload.name, payload.exit) - yield* ensureSuccess(resume(workflow, executionId)) - return payload.exit - }), - - resume: () => - resumeGate.withPermitsIfAvailable(1)( - currentRun ? waitForRunReply(workflow, currentRun) : Effect.void - ).pipe( - // Release the gate before reset can start another parent run, so - // later child completions can wake that replay. - Effect.flatMap((waited) => Option.isSome(waited) ? resume(workflow, executionId) : Effect.void), - ensureSuccess, - Rpc.wrap({ fork: true, uninterruptible: true }) + pending.results.set(payload.name, payload.exit) + // Await registers before reading the cache. If this run has not + // registered yet, a later await will see the result just stored. + // Check when the wake runs and skip preemption too: interrupting + // a later await would require waiting for its reply before reset. + const wake = Effect.suspend(() => + currentRun && !currentRun.awaitedDeferreds.has(payload.name) + ? ensureSuccess(resume(workflow, executionId)) + : deferredState.deferredDone(executionId, payload.name).pipe(Effect.andThen(resumeCurrentRun)) + ) + // An asynchronous reply releases the RPC concurrency permit while + // the wake waits; the pending request still occupies mailbox capacity. + // The activation owns the wake so it survives handler rebuilds, + // but activation shutdown still interrupts it for durable redelivery. + const reply = Deferred.makeUnsafe>() + return wake.pipe( + Effect.as(payload.exit), + Deferred.into(reply), + Effect.forkIn(activation, { startImmediately: true }), + Effect.as(reply) ) + }, + + resume: () => resumeCurrentRun.pipe(Rpc.wrap({ fork: true, uninterruptible: true })) } }), - // Reserve a slot for deferred completions to wake the active run. + // Alongside the run, let deferred handlers briefly acquire a permit to + // fork their wake and return an asynchronous reply. { concurrency: 2, maxIdleTime: entityMaxIdleTime } ) as Effect.Effect - ), + if (existing === undefined || existing === workflow) return registration + return Effect.logWarning( + `Workflow "${workflow._tag}" is already registered with payload shape ${ + payloadShape( + existing + ) + }; ignoring duplicate definition with payload shape ${payloadShape(workflow)}` + ).pipe(Effect.andThen(registration)) + }), execute: (workflow, { discard, executionId, parent, payload }) => { ensureEntity(workflow) @@ -601,12 +662,18 @@ export const make = Effect.gen(function*() { deferredResult: (deferred) => WorkflowEngine.WorkflowInstance.pipe( - Effect.flatMap((instance) => { - const exit = deferredState.pendingResult(instance.executionId, deferred.name) + Effect.flatMap(Effect.fnUntraced(function*(instance) { + const activation = yield* Effect.serviceOption(CurrentActivationScope) + const pending = Option.isSome(activation) ? pendingByActivation.get(activation.value) : undefined + // Captured contexts can cross workflow boundaries. Never use another + // execution's completion, even if its deferred has the same name. + const exit = pending?.workflowName === instance.workflow._tag && pending.executionId === instance.executionId + ? pending.results.get(deferred.name) + : undefined if (exit) { - return Effect.succeedSome(exit) + return Option.some(exit) } - return requestReply({ + return yield* requestReply({ workflow: instance.workflow, entityType: `Workflow/${instance.workflow._tag}`, executionId: instance.executionId, @@ -625,7 +692,7 @@ export const make = Effect.gen(function*() { ) }) ) - }), + })), Effect.retry({ while: (e) => e._tag === "PersistenceError", times: 3, @@ -844,6 +911,7 @@ const InterruptSignal = DurableDeferred.make("Workflow/InterruptSignal") * It requires cluster sharding and message storage, and also registers the * durable clock entity used for workflow wakeups. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -854,3 +922,12 @@ export const layer: Layer.Layer< > = ClockEntityLayer.pipe( Layer.provideMerge(Layer.effect(WorkflowEngine.WorkflowEngine)(make)) ) + +const payloadShape = (workflow: Workflow.Any): string => { + try { + const { definitions, schema } = Schema.toJsonSchemaDocument(workflow.payloadSchema) + return JSON.stringify(Object.keys(definitions).length === 0 ? schema : { ...schema, $defs: definitions }) + } catch { + return "" + } +} diff --git a/repos/effect/packages/effect/src/unstable/cluster/DeliverAt.ts b/repos/effect/packages/effect/src/cluster/DeliverAt.ts similarity index 87% rename from repos/effect/packages/effect/src/unstable/cluster/DeliverAt.ts rename to repos/effect/packages/effect/src/cluster/DeliverAt.ts index 1da653e0ec..bdd03bba82 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/DeliverAt.ts +++ b/repos/effect/packages/effect/src/cluster/DeliverAt.ts @@ -4,10 +4,11 @@ * by exposing a method at the `DeliverAt` symbol that returns the target * `DateTime`. * + * @stability unstable * @since 4.0.0 */ -import type { DateTime } from "../../DateTime.ts" -import { hasProperty } from "../../Predicate.ts" +import type { DateTime } from "../DateTime.ts" +import { hasProperty } from "../Predicate.ts" /** * Defines the property key used by values that provide a scheduled delivery time. @@ -17,6 +18,7 @@ import { hasProperty } from "../../Predicate.ts" * Use to implement the scheduled-delivery protocol on cluster message payloads * by defining a method at this property key. * + * @stability unstable * @category symbols * @since 4.0.0 */ @@ -27,6 +29,7 @@ export const symbol = "~effect/cluster/DeliverAt" * by returning the target delivery `DateTime` through the `DeliverAt` symbol * method. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -38,6 +41,7 @@ export interface DeliverAt { * Returns `true` if the value implements the `DeliverAt` scheduled-delivery * protocol. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -47,6 +51,7 @@ export const isDeliverAt = (self: unknown): self is DeliverAt => hasProperty(sel * Returns the scheduled delivery time in epoch milliseconds when the value * implements `DeliverAt`, or `null` otherwise. * + * @stability unstable * @category accessors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Entity.ts b/repos/effect/packages/effect/src/cluster/Entity.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cluster/Entity.ts rename to repos/effect/packages/effect/src/cluster/Entity.ts index 5915062974..b9da75bee7 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Entity.ts +++ b/repos/effect/packages/effect/src/cluster/Entity.ts @@ -8,32 +8,33 @@ * clients, layer builders for registering handlers, and services that expose * the current entity address while a request is being handled. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import type * as Cause from "../../Cause.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import type * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Equal from "../../Equal.ts" -import * as Exit from "../../Exit.ts" -import { identity } from "../../Function.ts" -import * as Hash from "../../Hash.ts" -import type * as Latch from "../../Latch.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Predicate from "../../Predicate.ts" -import * as Queue from "../../Queue.ts" -import type * as Schedule from "../../Schedule.ts" -import { Scope } from "../../Scope.ts" -import * as Stream from "../../Stream.ts" +import * as Arr from "../Array.ts" +import type * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import type * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Equal from "../Equal.ts" +import * as Exit from "../Exit.ts" +import { identity } from "../Function.ts" +import * as Hash from "../Hash.ts" import * as Headers from "../http/Headers.ts" +import type * as Latch from "../Latch.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Predicate from "../Predicate.ts" +import * as Queue from "../Queue.ts" import * as Rpc from "../rpc/Rpc.ts" import * as RpcClient from "../rpc/RpcClient.ts" import * as RpcGroup from "../rpc/RpcGroup.ts" import * as RpcSchema from "../rpc/RpcSchema.ts" import * as RpcServer from "../rpc/RpcServer.ts" +import type * as Schedule from "../Schedule.ts" +import { Scope } from "../Scope.ts" +import * as Stream from "../Stream.ts" import type { AlreadyProcessingMessage, EntityNotAssignedToRunner, @@ -45,6 +46,7 @@ import { EntityAddress } from "./EntityAddress.ts" import type { EntityId } from "./EntityId.ts" import { EntityType } from "./EntityType.ts" import * as Envelope from "./Envelope.ts" +import { CurrentActivationScope } from "./internal/entityActivation.ts" import { hashString } from "./internal/hash.ts" import { ResourceMap } from "./internal/resourceMap.ts" import * as Message from "./Message.ts" @@ -65,6 +67,7 @@ const TypeId = "~effect/cluster/Entity" * An entity defines how ids map to shard groups, exposes a sharded client, and * can be registered as a layer using RPC handlers or a mailbox queue. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -207,6 +210,7 @@ export interface Entity< * Type alias for any cluster `Entity`, regardless of entity type or RPC * protocol. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -221,6 +225,7 @@ export type Any = Entity * Each handler receives the entity request envelope for that RPC and returns the * RPC result or a supported RPC wrapper. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -237,6 +242,7 @@ export type HandlersFrom = { * * The check is based on the internal entity type identifier. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -404,6 +410,7 @@ const Proto = { * Creates a new `Entity` of the specified `type` which will accept messages * that adhere to the provided `RpcGroup`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -446,6 +453,7 @@ export const fromRpcGroup = ( * * @see {@link fromRpcGroup} for creating an entity from an existing `RpcGroup` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -469,6 +477,7 @@ export const make = { /** * Helper types used by the `Replier` API. * + * @stability unstable * @since 4.0.0 */ export declare namespace Replier { @@ -540,6 +552,7 @@ export declare namespace Replier { * For streaming RPCs this may be either a stream of success chunks or a dequeue * of success chunks. For non-streaming RPCs it is the RPC success value. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -556,6 +569,7 @@ export declare namespace Replier { * It includes the underlying request envelope plus the last stream reply chunk * that was sent, allowing handlers to resume chunk sequencing after a restart. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -567,6 +581,7 @@ export class Request extends Data.Class< /** * Most recent success chunk value sent by the entity, when one exists. * + * @stability unstable * @since 4.0.0 */ get lastSentChunkValue(): Option.Option> { @@ -576,6 +591,7 @@ export class Request extends Data.Class< /** * Sequence number to use for the entity's next outgoing success chunk. * + * @stability unstable * @since 4.0.0 */ get nextSequence(): number { @@ -596,6 +612,7 @@ const shardingTag = Context.Service("effect/clust * The returned function creates a no-serialization RPC client for each entity ID, * using a test sharding service instead of the cluster transport. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -655,6 +672,7 @@ export const makeTestClient: (TypeId)({ /** * Marks this value as a cluster entity address for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -36,6 +39,7 @@ export class EntityAddress extends Schema.Class(TypeId)({ /** * Formats the entity type, entity id, and shard id as a readable address. * + * @stability unstable * @since 4.0.0 */ override toString() { @@ -45,6 +49,7 @@ export class EntityAddress extends Schema.Class(TypeId)({ /** * Compares entity addresses by entity type, entity id, and shard id. * + * @stability unstable * @since 4.0.0 */ [Equal.symbol](that: EntityAddress): boolean { @@ -55,6 +60,7 @@ export class EntityAddress extends Schema.Class(TypeId)({ /** * Computes a structural hash from the entity type, entity id, and shard id. * + * @stability unstable * @since 4.0.0 */ [Hash.symbol]() { @@ -83,6 +89,7 @@ export class EntityAddress extends Schema.Class(TypeId)({ * @see {@link EntityAddress} for the equality, hashing, and string formatting behavior of constructed addresses * @see {@link ShardId} for the shard identifier included in the address * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/EntityId.ts b/repos/effect/packages/effect/src/cluster/EntityId.ts similarity index 90% rename from repos/effect/packages/effect/src/unstable/cluster/EntityId.ts rename to repos/effect/packages/effect/src/cluster/EntityId.ts index 28be3bc0f4..f7a10adfdf 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/EntityId.ts +++ b/repos/effect/packages/effect/src/cluster/EntityId.ts @@ -4,13 +4,15 @@ * hashes, stores in entity addresses, and uses when sending messages to a * running entity. * + * @stability unstable * @since 4.0.0 */ -import * as Schema from "../../Schema.ts" +import * as Schema from "../Schema.ts" /** * Schema for branded string entity identifiers used inside the cluster. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -19,6 +21,7 @@ export const EntityId = Schema.String.pipe(Schema.brand("~effect/cluster/EntityI /** * Branded string type representing the ID of an entity instance. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -44,6 +47,7 @@ export type EntityId = typeof EntityId.Type * * @see {@link EntityId} for the schema that validates and encodes branded entity identifiers * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/EntityProxy.ts b/repos/effect/packages/effect/src/cluster/EntityProxy.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/EntityProxy.ts rename to repos/effect/packages/effect/src/cluster/EntityProxy.ts index 8dbd70d2e9..8082f01359 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/EntityProxy.ts +++ b/repos/effect/packages/effect/src/cluster/EntityProxy.ts @@ -7,13 +7,14 @@ * original payload and success schema, adds cluster client errors, and creates * a discard variant for fire-and-forget delivery. * + * @stability unstable * @since 4.0.0 */ -import * as Schema from "../../Schema.ts" -import * as HttpApiEndpoint from "../httpapi/HttpApiEndpoint.ts" -import * as HttpApiGroup from "../httpapi/HttpApiGroup.ts" +import * as HttpApiEndpoint from "../http-api/HttpApiEndpoint.ts" +import * as HttpApiGroup from "../http-api/HttpApiGroup.ts" import * as Rpc from "../rpc/Rpc.ts" import * as RpcGroup from "../rpc/RpcGroup.ts" +import * as Schema from "../Schema.ts" import { AlreadyProcessingMessage, EntityNotAssignedToRunner, MailboxFull, PersistenceError } from "./ClusterError.ts" import type * as Entity from "./Entity.ts" import type { EntityId } from "./EntityId.ts" @@ -36,8 +37,8 @@ const requestErrors = [ * * ```ts import.meta.vitest * import { Layer, Schema } from "effect" - * import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/unstable/cluster" - * import { Rpc, RpcServer } from "effect/unstable/rpc" + * import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/cluster" + * import { Rpc, RpcServer } from "effect/rpc" * * export const Counter = Entity.make("Counter", [ * Rpc.make("Increment", { @@ -58,6 +59,7 @@ const requestErrors = [ * const result = [MyRpcs.requests.size, Layer.isLayer(RpcServerLayer)] // => [2, true] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -71,13 +73,6 @@ export const toRpcGroup = ( entityId: Schema.String, payload: parentRpc.payloadSchema }) - const oldMake = payloadSchema.make - payloadSchema.make = (input: any, options?: Schema.MakeOptions) => { - return oldMake({ - entityId: input.entityId, - payload: parentRpc.payloadSchema.make(input.payload, options) - }, options) - } const rpc = Rpc.make(`${entity.type}.${parentRpc._tag}`, { payload: payloadSchema, error: Schema.Union([parentRpc.errorSchema, ...requestErrors]), @@ -101,6 +96,7 @@ export const toRpcGroup = ( * whose payload includes `entityId`, and whose errors include cluster client * errors. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -156,9 +152,9 @@ const entityIdPath = { * * ```ts import.meta.vitest * import { Layer, Schema } from "effect" - * import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/unstable/cluster" - * import { HttpApi, HttpApiBuilder } from "effect/unstable/httpapi" - * import { Rpc } from "effect/unstable/rpc" + * import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/cluster" + * import { HttpApi, HttpApiBuilder } from "effect/http-api" + * import { Rpc } from "effect/rpc" * * export const Counter = Entity.make("Counter", [ * Rpc.make("Increment", { @@ -185,6 +181,7 @@ const entityIdPath = { * const result = [Object.keys(MyApi.groups.counter.endpoints).length, Layer.isLayer(ApiLayer)] // => [2, true] * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -232,6 +229,7 @@ const tagToPath = (tag: string): string => * discard endpoint at `//:entityId/discard`, including cluster client * errors. * + * @stability unstable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/EntityProxyServer.ts b/repos/effect/packages/effect/src/cluster/EntityProxyServer.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/EntityProxyServer.ts rename to repos/effect/packages/effect/src/cluster/EntityProxyServer.ts index ac37069731..a13d929c90 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/EntityProxyServer.ts +++ b/repos/effect/packages/effect/src/cluster/EntityProxyServer.ts @@ -8,14 +8,15 @@ * normal requests and discard requests are forwarded to the underlying entity * client. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import type * as HttpApi from "../httpapi/HttpApi.ts" -import * as HttpApiBuilder from "../httpapi/HttpApiBuilder.ts" -import type * as HttpApiGroup from "../httpapi/HttpApiGroup.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import type * as HttpApi from "../http-api/HttpApi.ts" +import * as HttpApiBuilder from "../http-api/HttpApiBuilder.ts" +import type * as HttpApiGroup from "../http-api/HttpApiGroup.ts" +import * as Layer from "../Layer.ts" import type * as Rpc from "../rpc/Rpc.ts" import type * as Entity from "./Entity.ts" import type { Sharding } from "./Sharding.ts" @@ -29,6 +30,7 @@ import type { Sharding } from "./Sharding.ts" * request payload to the corresponding entity client method, including discard * endpoints. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -98,6 +100,7 @@ export const layerHttpApi = < * The handlers forward each prefixed proxy RPC to the target entity client using * the `entityId` embedded in the proxy payload. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -137,6 +140,7 @@ export const layerRpcHandlers = < * * Includes both the normal prefixed RPC handler and its discard variant. * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/EntityResource.ts b/repos/effect/packages/effect/src/cluster/EntityResource.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/EntityResource.ts rename to repos/effect/packages/effect/src/cluster/EntityResource.ts index 6753346205..f7501d6417 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/EntityResource.ts +++ b/repos/effect/packages/effect/src/cluster/EntityResource.ts @@ -8,14 +8,15 @@ * restarts, a generic resource constructor, and a Kubernetes Pod resource * helper built on `K8sHttpClient`. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import { identity } from "../../Function.ts" -import * as RcRef from "../../RcRef.ts" -import * as Scope from "../../Scope.ts" +import * as Context from "../Context.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import { identity } from "../Function.ts" +import * as RcRef from "../RcRef.ts" +import * as Scope from "../Scope.ts" import * as Entity from "./Entity.ts" import * as K8sHttpClient from "./K8sHttpClient.ts" import type { Pod as K8sPod } from "./K8sTypes.ts" @@ -24,6 +25,7 @@ import type { Sharding } from "./Sharding.ts" /** * Type identifier used to brand `EntityResource` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -32,6 +34,7 @@ export const TypeId: TypeId = "~effect/cluster/EntityResource" /** * Literal type of the `EntityResource` type identifier. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -45,6 +48,7 @@ export type TypeId = "~effect/cluster/EntityResource" * `get` acquires or reuses the resource in the caller's scope, while `close` * invalidates it so its close scope can be released. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -66,6 +70,7 @@ export interface EntityResource { * * It is not closed during restarts, due to shard movement or node shutdowns. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -92,6 +97,7 @@ export class CloseScope extends Context.Service< * By default, the `idleTimeToLive` is infinite, meaning the resource will only * be released when `close` is called. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -156,6 +162,7 @@ export const make: (options: { * The pod is created and waited on through `K8sHttpClient`, and is kept alive * until the resource is closed or its idle time to live expires. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/EntityType.ts b/repos/effect/packages/effect/src/cluster/EntityType.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/EntityType.ts rename to repos/effect/packages/effect/src/cluster/EntityType.ts index 6a3401199a..cca86f0c6b 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/EntityType.ts +++ b/repos/effect/packages/effect/src/cluster/EntityType.ts @@ -4,13 +4,15 @@ * identity: they distinguish one family of entities from another before an * individual entity id is considered. * + * @stability unstable * @since 4.0.0 */ -import * as Schema from "../../Schema.ts" +import * as Schema from "../Schema.ts" /** * Schema for branded string names that identify entity types in the cluster. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -19,6 +21,7 @@ export const EntityType = Schema.String.pipe(Schema.brand("~effect/cluster/Entit /** * Branded string type representing an entity type name. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -46,6 +49,7 @@ export type EntityType = typeof EntityType.Type * * @see {@link EntityType} for schema-based decoding, validation, and encoding of entity type names * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Envelope.ts b/repos/effect/packages/effect/src/cluster/Envelope.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/Envelope.ts rename to repos/effect/packages/effect/src/cluster/Envelope.ts index 2f60b5c955..8ed6179ffe 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Envelope.ts +++ b/repos/effect/packages/effect/src/cluster/Envelope.ts @@ -7,21 +7,23 @@ * envelopes for in-flight requests, JSON codecs for partially decoded * envelopes, guards, request constructors, and storage primary-key helpers. * + * @stability unstable * @since 4.0.0 */ -import * as Predicate from "../../Predicate.ts" -import * as PrimaryKey from "../../PrimaryKey.ts" -import type { ReadonlyRecord } from "../../Record.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" import type * as Headers from "../http/Headers.ts" +import * as Predicate from "../Predicate.ts" +import * as PrimaryKey from "../PrimaryKey.ts" +import type { ReadonlyRecord } from "../Record.ts" import type * as Rpc from "../rpc/Rpc.ts" +import * as Schema from "../Schema.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" import { EntityAddress } from "./EntityAddress.ts" import { type Snowflake, SnowflakeFromBigInt } from "./Snowflake.ts" /** * Type identifier used to mark runtime cluster envelope values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -40,6 +42,7 @@ export const TypeId = "~effect/cluster/Envelope" * text transports stay wire-compatible. A binary codec compiles it as a * bytes leaf. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -64,6 +67,7 @@ export const OpaqueHole: Schema.declare = Schema.declare( * An envelope is either a request, an acknowledgement for a streamed reply chunk, * or an interrupt signal. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -72,6 +76,7 @@ export type Envelope = Request | AckChunk | Interrupt /** * JSON-serializable form of a cluster envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -80,12 +85,14 @@ export type Encoded = PartialRequestEncoded | AckChunkEncoded | InterruptEncoded /** * Helper types associated with cluster envelopes. * + * @stability unstable * @since 4.0.0 */ export declare namespace Envelope { /** * Envelope type for any RPC protocol. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -100,6 +107,7 @@ export declare namespace Envelope { * It carries the request ID, entity address, RPC tag, decoded payload, request * headers, and optional tracing context. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -124,6 +132,7 @@ export interface Request { * The envelope metadata is decoded, while the payload remains `unknown` until it * is decoded with the target RPC payload schema. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -147,6 +156,7 @@ export class PartialRequest extends Schema.Opaque()(Schema.Struc * Identifiers are encoded as strings and the RPC payload remains unknown until * decoded with the RPC schema. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -177,6 +187,7 @@ export interface PartialRequestEncoded { * * The `replyId` identifies the chunk reply that has been received. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -190,6 +201,7 @@ export class AckChunk extends Schema.Class("effect/cluster/Envelope/Ac /** * Marks this value as a cluster envelope for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -197,6 +209,7 @@ export class AckChunk extends Schema.Class("effect/cluster/Envelope/Ac /** * Returns a copy of this acknowledgement associated with the supplied request id. * + * @stability unstable * @since 4.0.0 */ withRequestId(requestId: Snowflake): AckChunk { @@ -210,6 +223,7 @@ export class AckChunk extends Schema.Class("effect/cluster/Envelope/Ac /** * Serialized JSON shape of an `AckChunk` envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -231,6 +245,7 @@ export interface AckChunkEncoded { /** * Represents an envelope used to interrupt an in-flight entity request. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -243,6 +258,7 @@ export class Interrupt extends Schema.Class("effect/cluster/Envelope/ /** * Marks this value as a cluster envelope for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -250,6 +266,7 @@ export class Interrupt extends Schema.Class("effect/cluster/Envelope/ /** * Returns a copy of this interrupt associated with the supplied request id. * + * @stability unstable * @since 4.0.0 */ withRequestId(requestId: Snowflake): Interrupt { @@ -263,6 +280,7 @@ export class Interrupt extends Schema.Class("effect/cluster/Envelope/ /** * Serialized JSON shape of an `Interrupt` envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -287,6 +305,7 @@ export interface InterruptEncoded { * * It accepts `PartialRequest`, `AckChunk`, and `Interrupt` envelope values. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -301,6 +320,7 @@ export const Partial: Schema.Union< /** * Decoded value type produced by the `Partial` envelope schema. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -309,6 +329,7 @@ export type Partial = typeof Partial.Type /** * JSON codec for partial cluster envelopes. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -320,6 +341,7 @@ export const PartialJson: Schema.Codec< /** * Schema for mutable arrays of JSON-encoded partial cluster envelopes. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -330,12 +352,14 @@ export const PartialArray: Schema.mutable< /** * Helper types associated with request envelopes. * + * @stability unstable * @since 4.0.0 */ export declare namespace Request { /** * Request envelope type for any RPC protocol. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -349,6 +373,7 @@ export declare namespace Request { * * The check is based on the envelope type identifier. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -361,6 +386,7 @@ export const isEnvelope = (u: unknown): u is Envelope => Predicate.hasPrope * * Tracing fields are included only when a `traceId` is provided. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -395,6 +421,7 @@ export const makeRequest = ( /** * Schema for runtime cluster envelopes recognized by their type identifier. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -405,6 +432,7 @@ export const Envelope = Schema.declare(isEnvelope, { /** * Schema for runtime request envelopes. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -417,6 +445,7 @@ export const Request = Schema.declare( * Transforms plain request data with `makeRequest` and encodes * request envelopes back to their raw representation. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -432,6 +461,7 @@ export const RequestTransform: SchemaTransformation.Transformation< * Returns the storage primary key for a request envelope whose payload has a * primary key, or `null` when the envelope is not a keyed request. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -450,6 +480,7 @@ export const primaryKey = (envelope: Envelope): string | n * Builds a storage primary-key string from an entity address, RPC tag, and * payload primary-key ID. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/HttpRunner.ts b/repos/effect/packages/effect/src/cluster/HttpRunner.ts similarity index 96% rename from repos/effect/packages/effect/src/unstable/cluster/HttpRunner.ts rename to repos/effect/packages/effect/src/cluster/HttpRunner.ts index 570d463de3..e3c5289c5f 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/HttpRunner.ts +++ b/repos/effect/packages/effect/src/cluster/HttpRunner.ts @@ -7,21 +7,22 @@ * installing runner endpoints into an `HttpRouter`, and ready-made layers for * HTTP or WebSocket runner communication. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import type { Scope } from "../../Scope.ts" +import * as Effect from "../Effect.ts" import * as HttpClient from "../http/HttpClient.ts" import * as HttpClientRequest from "../http/HttpClientRequest.ts" import * as HttpRouter from "../http/HttpRouter.ts" import type * as HttpServer from "../http/HttpServer.ts" import type { HttpServerRequest } from "../http/HttpServerRequest.ts" import type { HttpServerResponse } from "../http/HttpServerResponse.ts" +import * as Layer from "../Layer.ts" import * as NetAddress from "../net/NetAddress.ts" import * as RpcClient from "../rpc/RpcClient.ts" import * as RpcSerialization from "../rpc/RpcSerialization.ts" import * as RpcServer from "../rpc/RpcServer.ts" +import type { Scope } from "../Scope.ts" import * as Socket from "../socket/Socket.ts" import type { MessageStorage } from "./MessageStorage.ts" import type { RunnerHealth } from "./RunnerHealth.ts" @@ -41,6 +42,7 @@ import type * as ShardingConfig from "./ShardingConfig.ts" * The configured path is appended to each runner address, and `https` switches * the generated URL from `http` to `https`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -78,6 +80,7 @@ export const layerClientProtocolHttp = (options: { /** * Default HTTP runner client protocol layer using path `/`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -96,6 +99,7 @@ export const layerClientProtocolHttpDefault: Layer.Layer< * The configured path is appended to each runner address, and `https` switches * the generated URL from `ws` to `wss`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -133,6 +137,7 @@ export const layerClientProtocolWebsocket = (options: { /** * Default WebSocket runner client protocol layer using path `/`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -150,6 +155,7 @@ export const layerClientProtocolWebsocketDefault: Layer.Layer< * The returned effect is produced from `RunnerServer.layerHandlers` and the * cluster runner RPC group. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -182,6 +188,7 @@ export const toHttpEffect: Effect.Effect< * The returned effect is produced from `RunnerServer.layerHandlers` and the * cluster runner RPC group. * + * @stability unstable * @category running * @since 4.0.0 */ @@ -210,6 +217,7 @@ export const toHttpEffectWebsocket: Effect.Effect< * Layer that provides `Sharding` and `Runners` using the configured runner RPC * client protocol and storage services. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -224,6 +232,7 @@ export const layerClient: Layer.Layer< /** * Layer that adds HTTP runner routes to the provided `HttpRouter`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -247,6 +256,7 @@ export const layerHttpOptions = (options: { /** * Layer that adds WebSocket runner routes to the provided `HttpRouter`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -275,6 +285,7 @@ export const layerWebsocketOptions = (options: { * It serves runner routes at `/` and configures runner clients to communicate * over HTTP. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -305,6 +316,7 @@ export const layerHttp: Layer.Layer< * It configures runner clients to communicate over HTTP without serving runner * HTTP routes. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -328,6 +340,7 @@ export const layerHttpClientOnly: Layer.Layer< * It serves runner routes at `/` and configures runner clients to communicate * over WebSocket. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -358,6 +371,7 @@ export const layerWebsocket: Layer.Layer< * It configures runner clients to communicate over WebSocket without serving * runner WebSocket routes. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/K8sHttpClient.ts b/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/cluster/K8sHttpClient.ts rename to repos/effect/packages/effect/src/cluster/K8sHttpClient.ts index dc1e98c89f..6e0a441592 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/K8sHttpClient.ts +++ b/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts @@ -4,26 +4,28 @@ * shared HTTP client, points requests at the in-cluster API endpoint, and uses * the mounted service-account token when one is available. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as FileSystem from "../../FileSystem.ts" -import { identity } from "../../Function.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Result from "../../Result.ts" -import * as Schedule from "../../Schedule.ts" -import * as Schema from "../../Schema.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as FileSystem from "../FileSystem.ts" +import { identity } from "../Function.ts" import * as HttpClient from "../http/HttpClient.ts" import * as HttpClientError from "../http/HttpClientError.ts" import * as HttpClientRequest from "../http/HttpClientRequest.ts" import * as HttpClientResponse from "../http/HttpClientResponse.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Result from "../Result.ts" +import * as Schedule from "../Schedule.ts" +import * as Schema from "../Schema.ts" import type { Pod as K8sPod } from "./K8sTypes.ts" /** * Service tag for the HTTP client used to call the Kubernetes API. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -41,6 +43,7 @@ export class K8sHttpClient extends Context.Service< * bearer token when available, requires successful HTTP statuses, and retries * transient failures. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -74,6 +77,7 @@ export const layer: Layer.Layer< * The request can be limited by namespace and label selector, and the result is a * map keyed by pod IP address. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -123,6 +127,7 @@ export const makeGetPods: ( * The pod defaults to the `default` namespace and is deleted when the surrounding * scope closes. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -227,6 +232,7 @@ export const makeCreatePod = Effect.gen(function*() { /** * Schema for the subset of Kubernetes Pod status used by cluster helpers. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -248,6 +254,7 @@ export class PodStatus extends Schema.Class("@effect/cluster/K8sHttpC * * The model exposes readiness helpers derived from the pod status conditions. * + * @stability unstable * @category schemas * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/K8sTypes.ts b/repos/effect/packages/effect/src/cluster/K8sTypes.ts similarity index 98% rename from repos/effect/packages/effect/src/unstable/cluster/K8sTypes.ts rename to repos/effect/packages/effect/src/cluster/K8sTypes.ts index 3593667b6f..c06d23009d 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/K8sTypes.ts +++ b/repos/effect/packages/effect/src/cluster/K8sTypes.ts @@ -1,6 +1,7 @@ /** * Kubernetes resource types used by the cluster helpers. * + * @stability unstable * @since 4.0.0 */ /* @@ -33,6 +34,7 @@ * * An AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -50,6 +52,7 @@ export interface AWSElasticBlockStoreVolumeSource { /** * Affinity is a group of affinity scheduling rules. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -65,6 +68,7 @@ export interface Affinity { /** * AppArmorProfile defines a pod or container's AppArmor settings. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -83,6 +87,7 @@ export interface AppArmorProfile { /** * AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -104,6 +109,7 @@ export interface AzureDiskVolumeSource { /** * AzureFile represents an Azure File Service mount on the host and bind mount to the pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -119,6 +125,7 @@ export interface AzureFileVolumeSource { /** * Represents a source location of a volume to mount, managed by an external CSI driver * + * @stability unstable * @category models * @since 4.0.0 */ @@ -140,6 +147,7 @@ export interface CSIVolumeSource { /** * Adds and removes POSIX capabilities from running containers. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -153,6 +161,7 @@ export interface Capabilities { /** * Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -174,6 +183,7 @@ export interface CephFSVolumeSource { /** * Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -195,6 +205,7 @@ export interface CinderVolumeSource { * * Exactly one of these fields should be set. Consumers of this type must treat an empty object as if it has an unknown value. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -216,6 +227,7 @@ export interface ClaimSource { /** * ClusterTrustBundleProjection describes how to select a set of ClusterTrustBundle objects and project their contents into the pod filesystem. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -239,6 +251,7 @@ export interface ClusterTrustBundleProjection { * * The contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -252,6 +265,7 @@ export interface ConfigMapEnvSource { /** * Selects a key from a ConfigMap. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -271,6 +285,7 @@ export interface ConfigMapKeySelector { * * The contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -290,6 +305,7 @@ export interface ConfigMapProjection { * * The contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -307,6 +323,7 @@ export interface ConfigMapVolumeSource { /** * A single application container that you want to run within a pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -364,6 +381,7 @@ export interface Container { /** * ContainerPort represents a network port in a single container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -383,6 +401,7 @@ export interface ContainerPort { /** * ContainerResizePolicy represents resource resize policy for the container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -396,6 +415,7 @@ export interface ContainerResizePolicy { /** * ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -411,6 +431,7 @@ export interface ContainerState { /** * ContainerStateRunning is a running state of a container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -422,6 +443,7 @@ export interface ContainerStateRunning { /** * ContainerStateTerminated is a terminated state of a container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -445,6 +467,7 @@ export interface ContainerStateTerminated { /** * ContainerStateWaiting is a waiting state of a container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -458,6 +481,7 @@ export interface ContainerStateWaiting { /** * ContainerStatus contains details for the current status of this container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -499,6 +523,7 @@ export interface ContainerStatus { /** * Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -510,6 +535,7 @@ export interface DownwardAPIProjection { /** * DownwardAPIVolumeFile represents information to create the file containing the pod field * + * @stability unstable * @category models * @since 4.0.0 */ @@ -527,6 +553,7 @@ export interface DownwardAPIVolumeFile { /** * DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -540,6 +567,7 @@ export interface DownwardAPIVolumeSource { /** * Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -553,6 +581,7 @@ export interface EmptyDirVolumeSource { /** * EnvFromSource represents the source of a set of ConfigMaps * + * @stability unstable * @category models * @since 4.0.0 */ @@ -568,6 +597,7 @@ export interface EnvFromSource { /** * EnvVar represents an environment variable present in a Container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -583,6 +613,7 @@ export interface EnvVar { /** * EnvVarSource represents a source for the value of an EnvVar. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -604,6 +635,7 @@ export interface EnvVarSource { * * To add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -669,6 +701,7 @@ export interface EphemeralContainer { /** * Represents an ephemeral volume that is handled by a normal storage driver. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -690,6 +723,7 @@ export interface EphemeralVolumeSource { /** * ExecAction describes a "run in container" action. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -701,6 +735,7 @@ export interface ExecAction { /** * Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -720,6 +755,7 @@ export interface FCVolumeSource { /** * FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -741,6 +777,7 @@ export interface FlexVolumeSource { /** * Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -758,6 +795,7 @@ export interface FlockerVolumeSource { * * A GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -775,6 +813,7 @@ export interface GCEPersistentDiskVolumeSource { /** * GRPCAction specifies an action involving a gRPC service. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -798,6 +837,7 @@ export interface GRPCAction { * * DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -813,6 +853,7 @@ export interface GitRepoVolumeSource { /** * Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -828,6 +869,7 @@ export interface GlusterfsVolumeSource { /** * HTTPGetAction describes an action based on HTTP Get requests. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -847,6 +889,7 @@ export interface HTTPGetAction { /** * HTTPHeader describes a custom header to be used in HTTP probes * + * @stability unstable * @category models * @since 4.0.0 */ @@ -860,6 +903,7 @@ export interface HTTPHeader { /** * HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -873,6 +917,7 @@ export interface HostAlias { /** * HostIP represents a single IP address allocated to the host. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -884,6 +929,7 @@ export interface HostIP { /** * Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -897,6 +943,7 @@ export interface HostPathVolumeSource { /** * Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -928,6 +975,7 @@ export interface ISCSIVolumeSource { /** * Maps a string key to a path within a volume. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -943,6 +991,7 @@ export interface KeyToPath { /** * Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -956,6 +1005,7 @@ export interface Lifecycle { /** * LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -973,6 +1023,7 @@ export interface LifecycleHandler { /** * LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -984,6 +1035,7 @@ export interface LocalObjectReference { /** * Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -999,6 +1051,7 @@ export interface NFSVolumeSource { /** * Node affinity is a group of node affinity scheduling rules. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1012,6 +1065,7 @@ export interface NodeAffinity { /** * A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1023,6 +1077,7 @@ export interface NodeSelector { /** * A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1038,6 +1093,7 @@ export interface NodeSelectorRequirement { /** * A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1051,6 +1107,7 @@ export interface NodeSelectorTerm { /** * ObjectFieldSelector selects an APIVersioned field of an object. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1064,6 +1121,7 @@ export interface ObjectFieldSelector { /** * PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1100,6 +1158,7 @@ export interface PersistentVolumeClaimSpec { /** * PersistentVolumeClaimTemplate is used to produce PersistentVolumeClaim objects as part of an EphemeralVolumeSource. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1113,6 +1172,7 @@ export interface PersistentVolumeClaimTemplate { /** * PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system). * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1126,6 +1186,7 @@ export interface PersistentVolumeClaimVolumeSource { /** * Represents a Photon Controller persistent disk resource. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1139,6 +1200,7 @@ export interface PhotonPersistentDiskVolumeSource { /** * Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1158,6 +1220,7 @@ export interface Pod { /** * Pod affinity is a group of inter pod affinity scheduling rules. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1171,6 +1234,7 @@ export interface PodAffinity { /** * Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1192,6 +1256,7 @@ export interface PodAffinityTerm { /** * Pod anti affinity is a group of inter pod anti affinity scheduling rules. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1205,6 +1270,7 @@ export interface PodAntiAffinity { /** * PodCondition contains details for the current condition of this pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1226,6 +1292,7 @@ export interface PodCondition { /** * PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1241,6 +1308,7 @@ export interface PodDNSConfig { /** * PodDNSConfigOption defines DNS resolver options of a pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1253,6 +1321,7 @@ export interface PodDNSConfigOption { /** * PodIP represents a single IP address allocated to the pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1264,6 +1333,7 @@ export interface PodIP { /** * PodOS defines the OS parameters of a pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1275,6 +1345,7 @@ export interface PodOS { /** * PodReadinessGate contains the reference to a pod condition * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1286,6 +1357,7 @@ export interface PodReadinessGate { /** * PodResourceClaim references exactly one ResourceClaim through a ClaimSource. It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. Containers that need access to the ResourceClaim reference it with this name. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1299,6 +1371,7 @@ export interface PodResourceClaim { /** * PodResourceClaimStatus is stored in the PodStatus for each PodResourceClaim which references a ResourceClaimTemplate. It stores the generated name for the corresponding ResourceClaim. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1312,6 +1385,7 @@ export interface PodResourceClaimStatus { /** * PodSchedulingGate is associated to a Pod to guard its scheduling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1323,6 +1397,7 @@ export interface PodSchedulingGate { /** * PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1362,6 +1437,7 @@ export interface PodSecurityContext { /** * PodSpec is a description of a pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1475,6 +1551,7 @@ export interface PodSpec { /** * PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1524,6 +1601,7 @@ export interface PodStatus { /** * PortworxVolumeSource represents a Portworx volume resource. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1539,6 +1617,7 @@ export interface PortworxVolumeSource { /** * An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1552,6 +1631,7 @@ export interface PreferredSchedulingTerm { /** * Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1581,6 +1661,7 @@ export interface Probe { /** * Represents a projected volume source * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1594,6 +1675,7 @@ export interface ProjectedVolumeSource { /** * Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1615,6 +1697,7 @@ export interface QuobyteVolumeSource { /** * Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1640,6 +1723,7 @@ export interface RBDVolumeSource { /** * ResourceClaim references one entry in PodSpec.ResourceClaims. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1651,6 +1735,7 @@ export interface ResourceClaim { /** * ResourceFieldSelector represents container resources (cpu, memory) and their output format * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1666,6 +1751,7 @@ export interface ResourceFieldSelector { /** * ResourceRequirements describes the compute resource requirements. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1693,6 +1779,7 @@ export interface ResourceRequirements { /** * SELinuxOptions are the labels to be applied to the container * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1710,6 +1797,7 @@ export interface SELinuxOptions { /** * ScaleIOVolumeSource represents a persistent ScaleIO volume * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1739,6 +1827,7 @@ export interface ScaleIOVolumeSource { /** * SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1762,6 +1851,7 @@ export interface SeccompProfile { * * The contents of the target Secret's Data field will represent the key-value pairs as environment variables. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1775,6 +1865,7 @@ export interface SecretEnvSource { /** * SecretKeySelector selects a key of a Secret. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1794,6 +1885,7 @@ export interface SecretKeySelector { * * The contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1813,6 +1905,7 @@ export interface SecretProjection { * * The contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1830,6 +1923,7 @@ export interface SecretVolumeSource { /** * SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1863,6 +1957,7 @@ export interface SecurityContext { /** * ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise). * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1878,6 +1973,7 @@ export interface ServiceAccountTokenProjection { /** * SleepAction describes a "sleep" action. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1889,6 +1985,7 @@ export interface SleepAction { /** * Represents a StorageOS persistent volume resource. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1908,6 +2005,7 @@ export interface StorageOSVolumeSource { /** * Sysctl defines a kernel parameter to be set * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1921,6 +2019,7 @@ export interface Sysctl { /** * TCPSocketAction describes an action based on opening a socket * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1934,6 +2033,7 @@ export interface TCPSocketAction { /** * The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . * + * @stability unstable * @category models * @since 4.0.0 */ @@ -1953,6 +2053,7 @@ export interface Toleration { /** * TopologySpreadConstraint specifies how to spread matching pods among the given topology. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2007,6 +2108,7 @@ export interface TopologySpreadConstraint { /** * TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2022,6 +2124,7 @@ export interface TypedLocalObjectReference { /** * TypedObjectReference contains enough information to locate a typed referenced object. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2039,6 +2142,7 @@ export interface TypedObjectReference { /** * Volume represents a named volume in a pod that may be accessed by any container in the pod. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2125,6 +2229,7 @@ export interface Volume { /** * volumeDevice describes a mapping of a raw block device within a container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2138,6 +2243,7 @@ export interface VolumeDevice { /** * VolumeMount describes a mounting of a Volume within a container. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2173,6 +2279,7 @@ export interface VolumeMount { /** * VolumeMountStatus shows status of volume mounts. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2190,6 +2297,7 @@ export interface VolumeMountStatus { /** * Projection that may be projected along with other supported volume types * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2219,6 +2327,7 @@ export interface VolumeProjection { /** * VolumeResourceRequirements describes the storage resource requirements for a volume. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2236,6 +2345,7 @@ export interface VolumeResourceRequirements { /** * Represents a vSphere volume resource. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2253,6 +2363,7 @@ export interface VsphereVirtualDiskVolumeSource { /** * The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2266,6 +2377,7 @@ export interface WeightedPodAffinityTerm { /** * WindowsSecurityContextOptions contain Windows-specific options and credentials. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2289,6 +2401,7 @@ export interface WindowsSecurityContextOptions { * * The exact format is defined in sigs.k8s.io/structured-merge-diff * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2298,6 +2411,7 @@ export interface FieldsV1 { /** * A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2313,6 +2427,7 @@ export interface LabelSelector { /** * A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2328,6 +2443,7 @@ export interface LabelSelectorRequirement { /** * ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2351,6 +2467,7 @@ export interface ManagedFieldsEntry { /** * ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2432,6 +2549,7 @@ export interface ObjectMeta { /** * OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2453,6 +2571,7 @@ export interface OwnerReference { /** * Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -2461,6 +2580,7 @@ export type Time = string /** * Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. * + * @stability unstable * @category models * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/MachineId.ts b/repos/effect/packages/effect/src/cluster/MachineId.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/MachineId.ts rename to repos/effect/packages/effect/src/cluster/MachineId.ts index a89f26803a..56a9452377 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/MachineId.ts +++ b/repos/effect/packages/effect/src/cluster/MachineId.ts @@ -4,13 +4,15 @@ * generation, while keeping the value distinct from an ordinary `number` in * TypeScript APIs. * + * @stability unstable * @since 4.0.0 */ -import * as Schema from "../../Schema.ts" +import * as Schema from "../Schema.ts" /** * Schema for branded integer machine identifiers used by the cluster. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -24,6 +26,7 @@ export const MachineId = Schema.Int.pipe( /** * Branded integer type representing a cluster machine ID. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -48,6 +51,7 @@ export type MachineId = typeof MachineId.Type * * @see {@link MachineId} for the schema that validates branded integer machine identifiers * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Message.ts b/repos/effect/packages/effect/src/cluster/Message.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/Message.ts rename to repos/effect/packages/effect/src/cluster/Message.ts index 05c98186ff..5277dc075c 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Message.ts +++ b/repos/effect/packages/effect/src/cluster/Message.ts @@ -7,15 +7,17 @@ * acknowledgements, and interrupts. It also provides helpers for local delivery * and for encoding or decoding request payloads with matching RPC schemas. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as Option from "../../Option.ts" -import * as Schema from "../../Schema.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Option from "../Option.ts" import * as Rpc from "../rpc/Rpc.ts" import type * as RpcSerialization from "../rpc/RpcSerialization.ts" +import * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" import type { PersistenceError } from "./ClusterError.ts" import { MalformedMessage } from "./ClusterError.ts" import * as ClusterSchema from "./ClusterSchema.ts" @@ -32,6 +34,7 @@ import type { Snowflake } from "./Snowflake.ts" * An incoming message is either a persisted request with an encoded payload or an * incoming control envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -44,6 +47,7 @@ export type Incoming = IncomingRequest | IncomingEnvelope * * It is either a request with a decoded payload or an incoming control envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -57,6 +61,7 @@ export type IncomingLocal = IncomingRequestLocal | Incomin * Request messages keep their decoded payload and response callback, while * control envelopes are wrapped as incoming envelopes. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -84,6 +89,7 @@ export const incomingLocalFromOutgoing = (self: Outgoing): * It carries the last reply that was sent and a callback for persisting encoded * replies. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -98,6 +104,13 @@ export class IncomingRequest extends Data.TaggedClass("Incomi * server sets it to the codec of its transport. */ readonly codecFor: RpcSerialization.CodecFor + /** + * Scope that cancels interruptible, non-persisted requests when closed. + * + * @stability unstable + * @since 4.0.0 + */ + readonly callerScope?: Scope.Scope | undefined }> {} /** @@ -108,6 +121,7 @@ export class IncomingRequest extends Data.TaggedClass("Incomi * It includes dynamic annotations, the last sent reply, and a callback for * replying with decoded replies. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -116,11 +130,19 @@ export class IncomingRequestLocal extends Data.TaggedClass("I readonly lastSentReply: Option.Option> readonly respond: (reply: Reply.Reply) => Effect.Effect readonly annotations: Context.Context + /** + * Scope that cancels interruptible, non-persisted requests when closed. + * + * @stability unstable + * @since 4.0.0 + */ + readonly callerScope?: Scope.Scope | undefined }> {} /** * Represents an incoming control envelope carrying an `AckChunk` or `Interrupt`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -136,6 +158,7 @@ export class IncomingEnvelope extends Data.TaggedClass("IncomingEnvelope")<{ * * An outgoing message is either an entity request or a control envelope. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -149,6 +172,7 @@ export type Outgoing = OutgoingRequest | OutgoingEnvelope * It carries the service context used for serialization, the last received reply, * the reply callback, dynamic annotations, and an optional encoded request cache. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -164,6 +188,7 @@ export class OutgoingRequest extends Data.TaggedClass("Outgoi * Cached encoded envelope payload and the codec that produced it. The cache * is reused only when the requested codec is the same function. * + * @stability unstable * @since 4.0.0 */ public encodedCache?: { @@ -180,6 +205,7 @@ export class OutgoingRequest extends Data.TaggedClass("Outgoi * Use to construct an interrupt envelope for an * in-flight request. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -190,6 +216,7 @@ export class OutgoingEnvelope extends Data.TaggedClass("OutgoingEnvelope")<{ /** * Creates an outgoing interrupt envelope for the supplied request. * + * @stability unstable * @since 4.0.0 */ static interrupt(options: { @@ -220,6 +247,7 @@ const neverRpc = Rpc.make("Never", { * Control envelopes pass through unchanged. Requests are encoded with their RPC * payload schema, reusing the cached encoded request when available. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -249,6 +277,7 @@ export const serialize = ( * * Schema encoding failures are converted to `MalformedMessage`. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -268,6 +297,7 @@ export const serializeEnvelope = ( * * The result is a `PartialRequest` suitable for storage or transport. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -295,6 +325,7 @@ export const serializeRequest = ( * `OutgoingRequest` so the payload can be decoded with the correct RPC schema and * context. * + * @stability unstable * @category serialization * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/MessageStorage.ts b/repos/effect/packages/effect/src/cluster/MessageStorage.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/MessageStorage.ts rename to repos/effect/packages/effect/src/cluster/MessageStorage.ts index 3a4575c3f1..3822e36126 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/MessageStorage.ts +++ b/repos/effect/packages/effect/src/cluster/MessageStorage.ts @@ -8,22 +8,23 @@ * This module also includes the encoded storage-driver contract and no-op or * in-memory implementations for local use and tests. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import { Clock } from "../../Clock.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import { constFalse, identity } from "../../Function.ts" -import * as Latch from "../../Latch.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import type { Predicate } from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" +import * as Arr from "../Array.ts" +import { Clock } from "../Clock.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import { constFalse, identity } from "../Function.ts" +import * as Latch from "../Latch.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import type { Predicate } from "../Predicate.ts" import type * as Rpc from "../rpc/Rpc.ts" import type * as RpcSerialization from "../rpc/RpcSerialization.ts" +import * as Schema from "../Schema.ts" import { EntityNotAssignedToRunner, MalformedMessage, type PersistenceError } from "./ClusterError.ts" import * as DeliverAt from "./DeliverAt.ts" import type { EntityAddress } from "./EntityAddress.ts" @@ -46,6 +47,7 @@ const codecForJson = Schema.toCodecJson as RpcSerialization.CodecFor * messages; manages reply handlers; and provides transaction wrapping for storage * operations. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -72,9 +74,16 @@ export class MessageStorage extends Context.Service Effect.Effect /** - * Clear the `Reply`s for the given request id. + * Clear replies for the given request id. When options.expectedReplyId is + * provided, clear only if it is still the latest reply at the storage boundary. + * Without it, clear unconditionally. + * Custom storage implementations must honor this condition to protect + * workflows from stale concurrent resumes. */ - readonly clearReplies: (requestId: Snowflake.Snowflake) => Effect.Effect + readonly clearReplies: ( + requestId: Snowflake.Snowflake, + options?: { readonly expectedReplyId?: Snowflake.Snowflake | undefined } + ) => Effect.Effect /** * Retrieves the replies for the specified requests. @@ -178,6 +187,11 @@ export class MessageStorage extends Context.Service Effect.Effect + /** Release request claims without changing replies or processed state. */ + readonly resetRequests: ( + requestIds: ReadonlyArray + ) => Effect.Effect + /** * Reset the mailbox state for the provided addresses. */ @@ -208,6 +222,7 @@ export class MessageStorage extends Context.Service = SaveResult.Success | SaveResult.Dupl /** * Constructors and matchers for decoded save results. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -225,6 +241,7 @@ export const SaveResult = Data.taggedEnum() * Constructors and matchers for encoded save results returned by storage * drivers. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -233,6 +250,7 @@ export const SaveResultEncoded = Data.taggedEnum() /** * Variants and helper types for `SaveResult`. * + * @stability unstable * @since 4.0.0 */ export declare namespace SaveResult { @@ -244,6 +262,7 @@ export declare namespace SaveResult { * Duplicate results contain an encoded last received reply instead of a decoded * reply. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -252,6 +271,7 @@ export declare namespace SaveResult { /** * Variant indicating that the message was saved as a new storage entry. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -267,6 +287,7 @@ export declare namespace SaveResult { * It carries the original request ID and the latest decoded reply, when one is * available. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -284,6 +305,7 @@ export declare namespace SaveResult { * It carries the original request ID and the latest encoded reply, when one is * available. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -296,6 +318,7 @@ export declare namespace SaveResult { /** * Generic tagged enum constructor type for `SaveResult`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -312,6 +335,7 @@ export declare namespace SaveResult { * Implementations persist encoded messages, track primary keys and delayed * delivery, read unprocessed messages, and provide transaction wrapping. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -333,9 +357,16 @@ export type Encoded = { readonly saveReply: (reply: Reply.Encoded) => Effect.Effect /** - * Remove the replies for the specified request. + * Remove the replies for the specified request. If options.expectedReplyId + * is provided, compare it with the latest reply atomically before clearing. + * Without it, clear unconditionally. + * Custom storage implementations must honor this condition to protect + * workflows from stale concurrent resumes. */ - readonly clearReplies: (requestId: Snowflake.Snowflake) => Effect.Effect + readonly clearReplies: ( + requestId: Snowflake.Snowflake, + options?: { readonly expectedReplyId?: Snowflake.Snowflake | undefined } + ) => Effect.Effect /** * Retrieves the request id for the specified primary key. @@ -412,6 +443,11 @@ export type Encoded = { PersistenceError > + /** Release request claims without changing replies or processed state. */ + readonly resetRequests: ( + requestIds: ReadonlyArray + ) => Effect.Effect + /** * Reset the mailbox state for the provided addresses. */ @@ -449,6 +485,7 @@ export type Encoded = { * The fields distinguish existing shards from newly assigned shards and carry the * driver-specific pagination cursor. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -466,6 +503,7 @@ export type EncodedUnprocessedOptions = { * The fields distinguish existing requests from new requests and carry the * driver-specific pagination cursor. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -483,6 +521,7 @@ export type EncodedRepliesOptions = { * The returned service can register waiting reply handlers, notify them when * replies are saved, and fail them when a request or shard is unregistered. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -608,6 +647,7 @@ export const make = ( * generation, delayed delivery checks, duplicate decoding, and malformed-message * defect replies. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -716,6 +756,7 @@ export const makeEncoded: (encoded: Encoded) => Effect.Effect< ) }, resetAddress: (address) => encoded.resetAddresses([address]), + resetRequests: (requestIds) => requestIds.length === 0 ? Effect.void : encoded.resetRequests(requestIds), resetAddresses: (addresses) => addresses.length === 0 ? Effect.void : encoded.resetAddresses(addresses), clearAddress: encoded.clearAddress, resetShards: (shardIds) => { @@ -832,6 +873,7 @@ export const makeEncoded: (encoded: Encoded) => Effect.Effect< /** * No-op `MessageStorage` service that does not persist messages or replies. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -846,6 +888,7 @@ export const noop: MessageStorage["Service"] = Effect.runSync(make({ unprocessedMessages: () => Effect.succeed([]), unprocessedMessagesById: () => Effect.succeed([]), resetAddress: () => Effect.void, + resetRequests: () => Effect.void, resetAddresses: () => Effect.void, clearAddress: () => Effect.void, resetShards: () => Effect.void, @@ -860,6 +903,7 @@ export const noop: MessageStorage["Service"] = Effect.runSync(make({ * It stores the encoded envelope, last acknowledged chunk, accumulated replies, * and optional delivery time. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -873,6 +917,7 @@ export type MemoryEntry = { /** * Provides a context reference used in tests to simulate a transaction. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -892,6 +937,7 @@ const claimExpirationMillis = 10 * 60 * 1000 * maps used to track requests, primary keys, unprocessed envelopes, reply IDs, * and the journal. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -1022,10 +1068,13 @@ export class MemoryDriver extends Context.Service()("effect/cluste replyIds.add(reply.id) replyLatch.openUnsafe() }), - clearReplies: (id) => + clearReplies: (id, options) => Effect.sync(() => { const entry = requests.get(String(id)) if (!entry) return + if ( + options?.expectedReplyId !== undefined && entry.replies.at(-1)?.id !== String(options.expectedReplyId) + ) return entry.replies = [] entry.lastReceivedChunk = undefined unprocessed.add(entry.envelope) @@ -1067,6 +1116,7 @@ export class MemoryDriver extends Context.Service()("effect/cluste if (claimedAt !== undefined && claimedAt > now - claimExpirationMillis) { continue } + // Memory storage models claim expiry, but not SQL reply filtering or transactions. messages.push({ envelope, lastSentReply: Option.fromNullishOr(entry.replies[entry.replies.length - 1]) @@ -1091,6 +1141,13 @@ export class MemoryDriver extends Context.Service()("effect/cluste return unprocessedWith((envelope) => envelopeIds.has(envelope.requestId)) }), resetAddresses, + resetRequests: (requestIds) => + Effect.sync(() => { + for (const id of requestIds) { + const entry = requests.get(String(id)) + if (entry) lastRead.delete(entry.envelope) + } + }), clearAddress: (address) => Effect.sync(() => { for (const [primaryKey, entry] of requestsByPrimaryKey) { @@ -1146,6 +1203,7 @@ export class MemoryDriver extends Context.Service()("effect/cluste /** * Layer that provides the in-memory message storage driver. * + * @stability unstable * @since 4.0.0 */ static readonly layer: Layer.Layer = Layer.effect(this)(this.make).pipe( @@ -1156,6 +1214,7 @@ export class MemoryDriver extends Context.Service()("effect/cluste /** * Layer that provides the no-op `MessageStorage` service. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -1164,6 +1223,7 @@ export const layerNoop: Layer.Layer = Layer.succeed(MessageStora /** * Layer that provides in-memory message storage and its backing `MemoryDriver`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Reply.ts b/repos/effect/packages/effect/src/cluster/Reply.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/Reply.ts rename to repos/effect/packages/effect/src/cluster/Reply.ts index d8ace50c62..6be195b4c9 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Reply.ts +++ b/repos/effect/packages/effect/src/cluster/Reply.ts @@ -8,23 +8,24 @@ * carrying encoding services, and serialization helpers for storage or * transport. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import * as Option from "../../Option.ts" -import { hasProperty } from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaIssue from "../../SchemaIssue.ts" -import * as SchemaParser from "../../SchemaParser.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import * as Option from "../Option.ts" +import { hasProperty } from "../Predicate.ts" import * as Rpc from "../rpc/Rpc.ts" import type * as RpcMessage from "../rpc/RpcMessage.ts" import type * as RpcSchema from "../rpc/RpcSchema.ts" import type * as RpcSerialization from "../rpc/RpcSerialization.ts" +import * as Schema from "../Schema.ts" +import * as SchemaIssue from "../SchemaIssue.ts" +import * as SchemaParser from "../SchemaParser.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" import { MalformedMessage } from "./ClusterError.ts" import * as Envelope from "./Envelope.ts" import type { OutgoingRequest } from "./Message.ts" @@ -36,6 +37,7 @@ const TypeId = "~effect/cluster/Reply" * Returns `true` when the supplied value is a runtime cluster reply, based on the * reply type identifier. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -45,6 +47,7 @@ export const isReply = (u: unknown): u is Reply => hasProperty(u, TypeI * Runtime reply sent for an RPC request, either as a final exit or a chunk of a * streaming success value. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -53,6 +56,7 @@ export type Reply = WithExit | Chunk /** * JSON-serializable form of a cluster reply. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -65,6 +69,7 @@ export type Encoded = WithExitEncoded | ChunkEncoded * * Per-RPC payload validation is performed by `Reply(rpc)`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -79,6 +84,7 @@ export const Encoded: Schema.Codec = Envelope.OpaqueHole as any * Use to carry a runtime reply together with the RPC schema and services needed * to encode it for storage or transport. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -90,6 +96,7 @@ export class ReplyWithContext extends Data.TaggedClass("Reply /** * Creates a terminal reply context that dies with the supplied defect. * + * @stability unstable * @since 4.0.0 */ static fromDefect(options: { @@ -110,6 +117,7 @@ export class ReplyWithContext extends Data.TaggedClass("Reply /** * Creates a terminal reply context that interrupts the supplied request. * + * @stability unstable * @since 4.0.0 */ static interrupt(options: { @@ -138,6 +146,7 @@ const neverRpc = Rpc.make("Never", { * Wire-format representation of a terminal reply containing the request id, reply * id, and encoded RPC exit value. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -152,6 +161,7 @@ export interface WithExitEncoded { * Wire-format representation of a streaming reply chunk, including the request id, * reply id, sequence number, and non-empty encoded values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -171,6 +181,7 @@ const schemaCaches = new WeakMap extends Data.TaggedClass("Chunk")<{ /** * Marks this value as a runtime cluster reply. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -190,6 +202,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Creates an empty chunk reply for the supplied request id. * + * @stability unstable * @since 4.0.0 */ static emptyFrom(requestId: Snowflake) { @@ -204,6 +217,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Schema that accepts any runtime chunk reply without validating payload values. * + * @stability unstable * @since 4.0.0 */ static readonly Any = Schema.declare((u): u is Chunk => isReply(u) && u._tag === "Chunk") @@ -211,6 +225,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Transformation between encoded chunk records and `Chunk` instances. * + * @stability unstable * @since 4.0.0 */ static readonly transform: SchemaTransformation.Transformation = SchemaTransformation.transform({ @@ -221,6 +236,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Builds a chunk schema from the streaming success schema of an RPC. * + * @stability unstable * @since 4.0.0 */ static schema( @@ -236,6 +252,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Builds a chunk schema that validates each success value with the supplied schema. * + * @stability unstable * @since 4.0.0 */ static schemaFrom( @@ -276,6 +293,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ /** * Returns a copy of this chunk associated with the supplied request id. * + * @stability unstable * @since 4.0.0 */ withRequestId(requestId: Snowflake): Chunk { @@ -295,6 +313,7 @@ export class Chunk extends Data.TaggedClass("Chunk")<{ * Use to represent the final success, typed failure, defect, or interruption * for a clustered RPC request. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -306,6 +325,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ /** * Marks this value as a runtime cluster reply. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -313,6 +333,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ /** * Returns `true` when the value is a terminal `WithExit` reply. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is WithExit { @@ -322,6 +343,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ /** * Builds a terminal reply schema from the exit schema of an RPC. * + * @stability unstable * @since 4.0.0 */ static schema( @@ -337,6 +359,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ /** * Builds a terminal reply schema that validates the encoded exit value. * + * @stability unstable * @since 4.0.0 */ static schemaFrom< @@ -384,6 +407,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ /** * Returns a copy of this terminal reply associated with the supplied request id. * + * @stability unstable * @since 4.0.0 */ withRequestId(requestId: Snowflake): WithExit { @@ -398,6 +422,7 @@ export class WithExit extends Data.TaggedClass("WithExit")<{ * Builds the transport codec for replies to the specified RPC, covering terminal * `WithExit` replies and streaming `Chunk` replies. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -429,6 +454,7 @@ export const Reply = ( * reply's RPC schema and context and refailing encoding errors as * `MalformedMessage`. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -449,6 +475,7 @@ export const serialize = ( * Serializes a `ReplyWithContext`, falling back to a serializable defect reply * when the original reply cannot be encoded. * + * @stability unstable * @category serialization * @since 4.0.0 */ @@ -475,6 +502,7 @@ export const serializeOrDefect = ( * `None` when no reply has been received and refailing encoding errors as * `MalformedMessage`. * + * @stability unstable * @category serialization * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Runner.ts b/repos/effect/packages/effect/src/cluster/Runner.ts similarity index 88% rename from repos/effect/packages/effect/src/unstable/cluster/Runner.ts rename to repos/effect/packages/effect/src/cluster/Runner.ts index 1cbe895aa1..7c077c1137 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Runner.ts +++ b/repos/effect/packages/effect/src/cluster/Runner.ts @@ -5,12 +5,13 @@ * shard groups that process participates in, and the relative weight used when * the sharding service distributes shards across healthy runners. * + * @stability unstable * @since 4.0.0 */ -import * as Equal from "../../Equal.ts" -import * as Hash from "../../Hash.ts" -import { NodeInspectSymbol } from "../../Inspectable.ts" -import * as Schema from "../../Schema.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import { NodeInspectSymbol } from "../Inspectable.ts" +import * as Schema from "../Schema.ts" import { RunnerAddress } from "./RunnerAddress.ts" const TypeId = "~effect/cluster/Runner" @@ -23,6 +24,7 @@ const TypeId = "~effect/cluster/Runner" * Each runner has a unique network `address`, the shard `groups` it participates * in, and a relative `weight` used when assigning shards across runners. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -34,6 +36,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Formatter for rendering runner values consistently. * + * @stability unstable * @since 4.0.0 */ static format = Schema.toFormatter(this) @@ -41,6 +44,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Marks this value as a cluster runner for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -48,6 +52,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Decodes a runner from its JSON string representation. * + * @stability unstable * @since 4.0.0 */ static readonly decodeSync = Schema.decodeSync(Schema.fromJsonString(Runner)) @@ -55,6 +60,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Encodes a runner to its JSON string representation. * + * @stability unstable * @since 4.0.0 */ static readonly encodeSync = Schema.encodeSync(Schema.fromJsonString(Runner)) @@ -62,6 +68,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Formats this runner as a string. * + * @stability unstable * @since 4.0.0 */ override toString(): string { @@ -71,6 +78,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Formats this runner for Node.js inspection. * + * @stability unstable * @since 4.0.0 */ [NodeInspectSymbol](): string { @@ -80,6 +88,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Compares runners by address and shard-assignment weight. * + * @stability unstable * @since 4.0.0 */ [Equal.symbol](that: Runner): boolean { @@ -89,6 +98,7 @@ export class Runner extends Schema.Class(TypeId)({ /** * Computes a structural hash from the runner address and shard-assignment weight. * + * @stability unstable * @since 4.0.0 */ [Hash.symbol](): number { @@ -119,6 +129,7 @@ export class Runner extends Schema.Class(TypeId)({ * @see {@link Runner} for the value created by this helper * @see {@link RunnerAddress} for the network address accepted in `props.address` * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/RunnerAddress.ts b/repos/effect/packages/effect/src/cluster/RunnerAddress.ts similarity index 86% rename from repos/effect/packages/effect/src/unstable/cluster/RunnerAddress.ts rename to repos/effect/packages/effect/src/cluster/RunnerAddress.ts index 308bb6d7f0..a2afe74bec 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/RunnerAddress.ts +++ b/repos/effect/packages/effect/src/cluster/RunnerAddress.ts @@ -4,13 +4,14 @@ * hashing, Node.js inspection, and a stable primary key formatted from the host * and port. * + * @stability unstable * @since 4.0.0 */ -import * as Equal from "../../Equal.ts" -import * as Hash from "../../Hash.ts" -import { NodeInspectSymbol } from "../../Inspectable.ts" -import * as PrimaryKey from "../../PrimaryKey.ts" -import * as Schema from "../../Schema.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import { NodeInspectSymbol } from "../Inspectable.ts" +import * as PrimaryKey from "../PrimaryKey.ts" +import * as Schema from "../Schema.ts" const TypeId = "~effect/cluster/RunnerAddress" @@ -23,6 +24,7 @@ const TypeId = "~effect/cluster/RunnerAddress" * Use to represent the host and port that identify a runner in cluster routing, * registration, and health checks. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -33,6 +35,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Marks this value as a cluster runner address for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId; @@ -40,6 +43,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Compares runner addresses by host and port. * + * @stability unstable * @since 4.0.0 */ [Equal.symbol](that: RunnerAddress): boolean { @@ -49,6 +53,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Computes a structural hash from the host and port. * + * @stability unstable * @since 4.0.0 */ [Hash.symbol]() { @@ -58,6 +63,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Stable primary key used to identify the runner address. * + * @stability unstable * @since 4.0.0 */ [PrimaryKey.symbol](): string { @@ -67,6 +73,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Formats the runner address with its host and port. * + * @stability unstable * @since 4.0.0 */ override toString(): string { @@ -76,6 +83,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ /** * Formats the runner address for Node.js inspection. * + * @stability unstable * @since 4.0.0 */ [NodeInspectSymbol](): string { @@ -105,6 +113,7 @@ export class RunnerAddress extends Schema.Class(TypeId)({ * * @see {@link RunnerAddress} for the constructed address type and its equality, hashing, primary-key, and formatting behavior * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/RunnerHealth.ts b/repos/effect/packages/effect/src/cluster/RunnerHealth.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/RunnerHealth.ts rename to repos/effect/packages/effect/src/cluster/RunnerHealth.ts index 966d0d8cda..350641a7a4 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/RunnerHealth.ts +++ b/repos/effect/packages/effect/src/cluster/RunnerHealth.ts @@ -7,13 +7,14 @@ * ping-based checker, and a Kubernetes-based checker that looks at pod readiness * for the runner host. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as Schedule from "../../Schedule.ts" -import type * as Scope from "../../Scope.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Schedule from "../Schedule.ts" +import type * as Scope from "../Scope.ts" import * as K8s from "./K8sHttpClient.ts" import type { RunnerAddress } from "./RunnerAddress.ts" import * as Runners from "./Runners.ts" @@ -27,6 +28,7 @@ import * as Runners from "./Runners.ts" * still be processing messages. If a Runner is not responsive, then its * associated shards can and will be re-assigned to a different Runner. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -45,6 +47,7 @@ export class RunnerHealth extends Context.Service< * Use when you need a runner-health layer for tests or local development where * active health checks are unnecessary. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -57,6 +60,7 @@ export const layerNoop = Layer.succeed(RunnerHealth, { * failed pings on a short schedule and treating a successful ping within the * timeout as healthy. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -82,6 +86,7 @@ export const makePing: Effect.Effect< /** * Layer that pings runners directly to check whether they are healthy. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -99,6 +104,7 @@ export const layerPing: Layer.Layer< * * If the Kubernetes API check fails, the runner is treated as healthy. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -130,6 +136,7 @@ export const makeK8s = Effect.fnUntraced(function*(options?: { * * If the Kubernetes API check fails, the runner is treated as healthy. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/cluster/RunnerServer.ts b/repos/effect/packages/effect/src/cluster/RunnerServer.ts new file mode 100644 index 0000000000..bcb9978ac3 --- /dev/null +++ b/repos/effect/packages/effect/src/cluster/RunnerServer.ts @@ -0,0 +1,260 @@ +/** + * Provides server-side layers for the cluster runner protocol. + * + * Runner protocol handlers receive ping, notification, request, stream, and + * envelope messages from other runners. They forward those messages into + * `Sharding` and coordinate persisted replies through `MessageStorage`. This + * module includes the handler layer, a transport-independent RPC server layer, a + * full server layer that also provides runner clients, and a client-only layer + * for applications that do not serve runner RPCs. + * + * @stability unstable + * @since 4.0.0 + */ +import type * as Cause from "../Cause.ts" +import * as Effect from "../Effect.ts" +import type * as Exit from "../Exit.ts" +import * as Fiber from "../Fiber.ts" +import { constant } from "../Function.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Queue from "../Queue.ts" +import type * as Rpc from "../rpc/Rpc.ts" +import * as RpcServer from "../rpc/RpcServer.ts" +import type * as ClusterError from "./ClusterError.ts" +import * as Message from "./Message.ts" +import * as MessageStorage from "./MessageStorage.ts" +import * as Reply from "./Reply.ts" +import * as RunnerHealth from "./RunnerHealth.ts" +import * as Runners from "./Runners.ts" +import type * as RunnerStorage from "./RunnerStorage.ts" +import * as Sharding from "./Sharding.ts" +import { ShardingConfig } from "./ShardingConfig.ts" + +const constVoid = constant(Effect.void) + +const serializeDefectReply = ( + reply: Reply.ReplyWithContext, + defect: unknown, + codecFor: RpcServer.Protocol["Service"]["codecFor"] +): Effect.Effect => + Effect.orDie(Reply.serialize( + Reply.ReplyWithContext.fromDefect({ + id: reply.reply.id, + requestId: reply.reply.requestId, + defect + }), + codecFor + )) + +/** + * Layer that handles runner protocol RPCs by forwarding requests to `Sharding` + * and `MessageStorage`. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layerHandlers = Runners.Rpcs.toLayer(Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const storage = yield* MessageStorage.MessageStorage + const { codecFor } = yield* RpcServer.Protocol + + return { + Ping: () => Effect.void, + Notify: ({ envelope, persisted }) => { + const message = envelope._tag === "Request" + ? new Message.IncomingRequest({ + envelope, + respond: constVoid, + lastSentReply: Option.none(), + codecFor + }) + : new Message.IncomingEnvelope({ envelope }) + return persisted ? sharding.notify(message) : sharding.send(message) + }, + Effect: Effect.fnUntraced(function*({ persisted, request }) { + const callerScope = yield* Effect.scope + let replyEncoded: Option.Option> = Option + .none() + let resume = (reply: Effect.Effect) => { + replyEncoded = Option.some(reply) + } + const message = new Message.IncomingRequest({ + envelope: request, + lastSentReply: Option.none(), + codecFor, + callerScope, + respond(reply) { + resume(Reply.serializeOrDefect(reply, codecFor)) + return Effect.void + } + }) + if (persisted) { + return yield* Effect.callback< + Reply.Encoded, + ClusterError.EntityNotAssignedToRunner + >((resume_) => { + resume = resume_ + const parent = Fiber.getCurrent()! + const onExit = ( + exit: Exit.Exit< + any, + ClusterError.EntityNotAssignedToRunner + > + ) => { + if (exit._tag === "Failure") { + resume(exit as any) + } + } + const runFork = Effect.runForkWith(parent.context) + const fiber = runFork(storage.registerReplyHandler(message)) + fiber.addObserver(onExit) + runFork(Effect.catchTag( + sharding.notify(message, constWaitUntilRead), + "AlreadyProcessingMessage", + () => Effect.void + )).addObserver(onExit) + return Fiber.interrupt(fiber) + }) + } + yield* sharding.send(message) + return yield* Effect.callback((resume_) => { + if (Option.isSome(replyEncoded)) { + resume_(replyEncoded.value) + } else { + resume = resume_ + } + }) + }), + Stream: Effect.fnUntraced(function*({ persisted, request }) { + const callerScope = yield* Effect.scope + const queue = yield* Queue.make() + const message = new Message.IncomingRequest({ + envelope: request, + lastSentReply: Option.none(), + codecFor, + callerScope, + respond(reply) { + return Reply.serialize(reply, codecFor).pipe( + Effect.flatMap((reply) => { + Queue.offerUnsafe(queue, reply) + if (reply._tag === "WithExit") { + Queue.endUnsafe(queue) + } + return Effect.void + }), + Effect.catchTag( + "MalformedMessage", + (error) => + Effect.flatMap(serializeDefectReply(reply, error, codecFor), (reply) => { + // the fallback defect reply is terminal, so end the stream + Queue.offerUnsafe(queue, reply) + Queue.endUnsafe(queue) + return Effect.void + }) + ) + ) + } + }) + if (persisted) { + yield* storage.registerReplyHandler(message).pipe( + Effect.onError((cause) => Queue.failCause(queue, cause)), + Effect.forkScoped + ) + yield* sharding.notify(message, constWaitUntilRead) + } else { + yield* sharding.send(message) + } + return queue + }), + Envelope: ({ envelope }) => sharding.send(new Message.IncomingEnvelope({ envelope })) + } +})) + +const constWaitUntilRead = { waitUntilRead: true } as const + +/** + * Creates the runner RPC server layer, which receives messages from other + * runners, forwards them to the `Sharding` layer, and responds to `Ping` + * requests. + * + * **When to use** + * + * Use when a runner process should accept runner-to-runner protocol messages + * over a provided server `RpcServer.Protocol`. + * + * **Gotchas** + * + * This layer does not choose or provide the wire transport; provide a + * transport-specific `RpcServer.Protocol` separately. + * + * @see {@link layerHandlers} for the lower-level handler layer used when the RPC server is supplied elsewhere + * @see {@link layerWithClients} for a runner server layer that also provides the `Sharding` and `Runners` clients + * @see {@link layerClientOnly} for embedding a cluster client without serving runner RPCs + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer: Layer.Layer< + never, + never, + | RpcServer.Protocol + | Sharding.Sharding + | MessageStorage.MessageStorage +> = RpcServer.layer(Runners.Rpcs, { + spanPrefix: "RunnerServer", + disableTracing: true, + disableFatalDefects: true +}).pipe(Layer.provide(layerHandlers)) + +/** + * Layer that provides `RunnerServer` together with `Runners` and `Sharding` + * clients. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layerWithClients: Layer.Layer< + Sharding.Sharding | Runners.Runners, + never, + | RpcServer.Protocol + | ShardingConfig + | Runners.RpcClientProtocol + | MessageStorage.MessageStorage + | RunnerStorage.RunnerStorage + | RunnerHealth.RunnerHealth +> = layer.pipe( + Layer.provideMerge(Sharding.layer), + Layer.provideMerge(Runners.layerRpc) +) + +/** + * Creates a client-only `Runners` layer. + * + * **When to use** + * + * Use to embed a cluster client inside another Effect application without registering with + * the ShardManager or receiving shard assignments. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layerClientOnly: Layer.Layer< + Sharding.Sharding | Runners.Runners, + never, + | ShardingConfig + | Runners.RpcClientProtocol + | MessageStorage.MessageStorage + | RunnerStorage.RunnerStorage +> = Sharding.layer.pipe( + Layer.provideMerge(Runners.layerRpc), + Layer.provide(RunnerHealth.layerNoop), + Layer.updateService(ShardingConfig, (config) => ({ + ...config, + runnerAddress: Option.none() + })) +) diff --git a/repos/effect/packages/effect/src/unstable/cluster/RunnerStorage.ts b/repos/effect/packages/effect/src/cluster/RunnerStorage.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/cluster/RunnerStorage.ts rename to repos/effect/packages/effect/src/cluster/RunnerStorage.ts index e06689bfc7..37e3437ce4 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/RunnerStorage.ts +++ b/repos/effect/packages/effect/src/cluster/RunnerStorage.ts @@ -7,13 +7,14 @@ * string-encoded backend interface, an adapter from encoded storage to the typed * service, and an in-memory implementation for tests and local use. * + * @stability unstable * @since 4.0.0 */ -import { isArrayNonEmpty, type NonEmptyArray } from "../../Array.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as MutableHashMap from "../../MutableHashMap.ts" +import { isArrayNonEmpty, type NonEmptyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as MutableHashMap from "../MutableHashMap.ts" import type { PersistenceError } from "./ClusterError.ts" import * as MachineId from "./MachineId.ts" import { Runner } from "./Runner.ts" @@ -24,6 +25,7 @@ import * as ShardId from "./ShardId.ts" * Represents a generic interface to the persistent storage required by the * cluster. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -84,6 +86,7 @@ export class RunnerStorage extends Context.Service * * Runner health and shard acquisition are kept only in process memory. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -233,6 +238,7 @@ export const makeMemory = Effect.gen(function*() { /** * Layer that provides the in-memory `RunnerStorage` implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Runners.ts b/repos/effect/packages/effect/src/cluster/Runners.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/cluster/Runners.ts rename to repos/effect/packages/effect/src/cluster/Runners.ts index 227bcbdb71..f0c29d54ce 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Runners.ts +++ b/repos/effect/packages/effect/src/cluster/Runners.ts @@ -8,24 +8,25 @@ * RPC-backed implementations, local persistence support, reply recovery, and * the protocol service used by transport-specific runner layers. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import * as Latch from "../../Latch.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Queue from "../../Queue.ts" -import * as RcMap from "../../RcMap.ts" -import * as Schema from "../../Schema.ts" -import type { Scope } from "../../Scope.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import * as Latch from "../Latch.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Queue from "../Queue.ts" +import * as RcMap from "../RcMap.ts" import * as Rpc from "../rpc/Rpc.ts" import * as RpcClient_ from "../rpc/RpcClient.ts" import type { RpcClientError } from "../rpc/RpcClientError.ts" import * as RpcGroup from "../rpc/RpcGroup.ts" import * as RpcSchema from "../rpc/RpcSchema.ts" import type * as RpcSerialization from "../rpc/RpcSerialization.ts" +import * as Schema from "../Schema.ts" +import type { Scope } from "../Scope.ts" import type { MalformedMessage, PersistenceError } from "./ClusterError.ts" import { AlreadyProcessingMessage, EntityNotAssignedToRunner, MailboxFull, RunnerUnavailable } from "./ClusterError.ts" import { Persisted } from "./ClusterSchema.ts" @@ -42,6 +43,7 @@ import * as Snowflake from "./Snowflake.ts" * sending and notifying messages, coordinating persisted replies, and marking * runners unavailable. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -156,6 +158,7 @@ export class Runners extends Context.Service hasProperty(u, TypeId) * Schema for shard identifiers encoded as `{ group, id }` objects and decoded * via `make`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -84,6 +88,7 @@ export const ShardId = S.declare(isShardId, { * @see {@link toString} for formatting an existing shard id as `group:id` * @see {@link fromString} for constructing a cached shard id from the `group:id` string form * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -125,6 +130,7 @@ const ShardIdProto = { /** * Formats a shard identifier as `group:id`. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -142,6 +148,7 @@ export const toString = (shardId: { * Throws an `Error` when the string has no colon separator or the id segment is * not numeric. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -169,6 +176,7 @@ export function fromStringEncoded(s: string): { * Throws an `Error` when the string has no colon separator or the id segment is * not numeric. * + * @stability unstable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Sharding.ts b/repos/effect/packages/effect/src/cluster/Sharding.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/cluster/Sharding.ts rename to repos/effect/packages/effect/src/cluster/Sharding.ts index 65f787c400..740f7fbddd 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Sharding.ts +++ b/repos/effect/packages/effect/src/cluster/Sharding.ts @@ -8,38 +8,39 @@ * layer connects these responsibilities to runner communication, storage, * health checks, configuration, and local resources. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Cause from "../../Cause.ts" -import { Clock } from "../../Clock.ts" -import * as Context from "../../Context.ts" -import type { Input } from "../../Duration.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Equal from "../../Equal.ts" -import type * as Exit from "../../Exit.ts" -import * as Fiber from "../../Fiber.ts" -import * as FiberMap from "../../FiberMap.ts" -import { constant, flow } from "../../Function.ts" -import * as HashRing from "../../HashRing.ts" -import * as InternalRecord from "../../internal/record.ts" -import * as Latch from "../../Latch.ts" -import * as Layer from "../../Layer.ts" -import * as MutableHashMap from "../../MutableHashMap.ts" -import * as MutableHashSet from "../../MutableHashSet.ts" -import * as MutableRef from "../../MutableRef.ts" -import * as Option from "../../Option.ts" -import * as PubSub from "../../PubSub.ts" -import { CurrentLogAnnotations } from "../../References.ts" -import * as Result from "../../Result.ts" -import * as Schedule from "../../Schedule.ts" -import * as Scope from "../../Scope.ts" -import * as Semaphore from "../../Semaphore.ts" -import * as Stream from "../../Stream.ts" +import * as Arr from "../Array.ts" +import * as Cause from "../Cause.ts" +import { Clock } from "../Clock.ts" +import * as Context from "../Context.ts" +import type { Input } from "../Duration.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Equal from "../Equal.ts" +import type * as Exit from "../Exit.ts" +import * as Fiber from "../Fiber.ts" +import * as FiberMap from "../FiberMap.ts" +import { constant, flow } from "../Function.ts" +import * as HashRing from "../HashRing.ts" +import * as InternalRecord from "../internal/record.ts" +import * as Latch from "../Latch.ts" +import * as Layer from "../Layer.ts" +import * as MutableHashMap from "../MutableHashMap.ts" +import * as MutableHashSet from "../MutableHashSet.ts" +import * as MutableRef from "../MutableRef.ts" +import * as Option from "../Option.ts" +import * as PubSub from "../PubSub.ts" +import { CurrentLogAnnotations } from "../References.ts" +import * as Result from "../Result.ts" import type * as Rpc from "../rpc/Rpc.ts" import * as RpcClient from "../rpc/RpcClient.ts" import type { FromServer } from "../rpc/RpcMessage.ts" +import * as Schedule from "../Schedule.ts" +import * as Scope from "../Scope.ts" +import * as Semaphore from "../Semaphore.ts" +import * as Stream from "../Stream.ts" import type { MailboxFull, PersistenceError } from "./ClusterError.ts" import { AlreadyProcessingMessage, EntityNotAssignedToRunner } from "./ClusterError.ts" import * as ClusterMetrics from "./ClusterMetrics.ts" @@ -83,6 +84,7 @@ import * as Snowflake from "./Snowflake.ts" * Use to access or provide cluster routing, shard ownership, entity * registration, singleton registration, and persisted-work polling. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -193,9 +195,13 @@ export class Sharding extends Context.Service /** - * Reset the state of a message + * Reset the state of a message. When options.expectedReplyId is provided, + * reset only if it is still the latest reply. Otherwise reset unconditionally. */ - readonly reset: (requestId: Snowflake.Snowflake) => Effect.Effect + readonly reset: ( + requestId: Snowflake.Snowflake, + options?: { readonly expectedReplyId?: Snowflake.Snowflake | undefined } + ) => Effect.Effect /** * Trigger a storage read, which will read all unprocessed messages. @@ -243,6 +249,25 @@ const make = Effect.gen(function*() { const entityManagers = new Map() let entityRegistrationStartMillis: number | undefined let entityRegistrationFallbackStartMillis: number | undefined + const entityRegistrationTimeoutMillis = Duration.toMillis( + Duration.fromInputUnsafe(config.entityRegistrationTimeout) + ) + const entityRegistrationTimeRemaining = () => { + const now = clock.currentTimeMillisUnsafe() + const registrationStarted = entityRegistrationStartMillis !== undefined + // The first missing entity observed by either the storage read loop or a + // local send starts the shared fallback registration window. + const timeoutStartMillis = entityRegistrationStartMillis ?? + (entityRegistrationFallbackStartMillis ??= now) + // If registration never starts, allow two intervals from the first missing + // entity before failing. + const timeoutMillis = registrationStarted + ? entityRegistrationTimeoutMillis + : entityRegistrationTimeoutMillis * 2 + return Math.max(0, timeoutMillis - (now - timeoutStartMillis)) + } + const entityNotRegistered = (entityType: string) => + Effect.die(new Error(`Entity type '${entityType}' not registered`)) const shardAssignments = MutableHashMap.empty() const selfShards = MutableHashSet.empty() @@ -624,9 +649,6 @@ const make = Effect.gen(function*() { if (storageEnabled && initialRunnerAddress) { const selfAddress = initialRunnerAddress - const entityRegistrationTimeoutMillis = Duration.toMillis( - Duration.fromInputUnsafe(config.entityRegistrationTimeout) - ) yield* Effect.gen(function*() { yield* Effect.logDebug("Starting") @@ -637,12 +659,22 @@ const make = Effect.gen(function*() { let deliveredThisRead = false const removableNotifications = new Set() const resetAddresses = MutableHashSet.empty() + const resetRequestIds: Array = [] const cappedAddresses = MutableHashSet.empty() const markDelivered = Effect.sync(() => { deliveredThisRead = true }) + // A completed request may remain deduplicated after storage claims its reset. + // Release the claim so the reset can be redelivered immediately. + const releaseCompletedClaim = (message: Message.Incoming) => { + const state = entityManagers.get(message.envelope.address.entityType) + if (!state?.manager.isProcessingFor(message, { excludeCompleted: true })) { + resetRequestIds.push(message.envelope.requestId) + } + } + const processMessages = Effect.whileLoop({ while: () => index < messages.length, step: () => index++, @@ -668,21 +700,13 @@ const make = Effect.gen(function*() { } const state = entityManagers.get(address.entityType) if (!state) { - const now = clock.currentTimeMillisUnsafe() - const registrationStarted = entityRegistrationStartMillis !== undefined - const timeoutStartMillis = entityRegistrationStartMillis ?? - (entityRegistrationFallbackStartMillis ??= now) - // If registration never starts, allow two intervals from the first missing read before failing. - const timeoutMillis = registrationStarted - ? entityRegistrationTimeoutMillis - : entityRegistrationTimeoutMillis * 2 - if (now - timeoutStartMillis < timeoutMillis) { + if (entityRegistrationTimeRemaining() > 0) { // reset address in the case that the entity is slow to register MutableHashSet.add(resetAddresses, address) return Effect.void } // if the entity did not register in time, we save a defect reply - return Effect.die(new Error(`Entity type '${address.entityType}' not registered`)) + return entityNotRegistered(address.entityType) } else if (state.status === "closed") { return Effect.void } @@ -696,6 +720,7 @@ const make = Effect.gen(function*() { } else if (isProcessing || state.status === "closing") { // If the request is already processing, we skip it. // Or if the entity is closing, we skip all incoming messages. + if (isProcessing) releaseCompletedClaim(message) return Effect.void } else if (message._tag === "IncomingRequest" && pendingNotifications.has(message.envelope.requestId)) { const entry = pendingNotifications.get(message.envelope.requestId)! @@ -738,6 +763,11 @@ const make = Effect.gen(function*() { defect: Cause.squash(cause) })) } + if (error.success._tag === "AlreadyProcessingMessage") { + // Completion raced with decoding. + releaseCompletedClaim(message) + return Effect.void + } if (error.success._tag === "MailboxFull") { const address = message.envelope.address const state = entityManagers.get(address.entityType) @@ -850,6 +880,11 @@ const make = Effect.gen(function*() { // let the resuming entities check if they are done yield* storageReadLock.release(1) + if (resetRequestIds.length > 0) { + yield* Effect.ignore(storage.resetRequests(resetRequestIds)) + resetRequestIds.length = 0 + } + if (cappedAddressesToReset !== undefined) { yield* Effect.ignore(storage.resetAddresses(cappedAddressesToReset)) } @@ -1177,8 +1212,8 @@ const make = Effect.gen(function*() { ) } - const reset: Sharding["Service"]["reset"] = (requestId) => - Effect.matchCause(storage.clearReplies(requestId), { + const reset: Sharding["Service"]["reset"] = (requestId, options) => + Effect.matchCause(storage.clearReplies(requestId, options), { onSuccess: () => true, onFailure: () => false }) @@ -1668,13 +1703,23 @@ const make = Effect.gen(function*() { } ) + // Sleeps until the registration deadline shared with the storage read loop, + // re-checking once in case registration started and moved the deadline. + const awaitRegistrationDeadline = (entityType: string): Effect.Effect => + Effect.suspend(() => { + const remaining = entityRegistrationTimeRemaining() + return remaining > 0 + ? Effect.flatMap(Effect.sleep(remaining), () => awaitRegistrationDeadline(entityType)) + : entityNotRegistered(entityType) + }) + const waitForEntityManager = (entityType: string) => { let latch = entityManagerLatches.get(entityType) if (!latch) { latch = Latch.makeUnsafe() entityManagerLatches.set(entityType, latch) } - return latch.await + return Effect.raceFirst(latch.await, awaitRegistrationDeadline(entityType)) } // --- Runner health checks --- @@ -1795,6 +1840,7 @@ const make = Effect.gen(function*() { * * @see {@link Sharding} for the service provided by this layer * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/ShardingConfig.ts b/repos/effect/packages/effect/src/cluster/ShardingConfig.ts similarity index 96% rename from repos/effect/packages/effect/src/unstable/cluster/ShardingConfig.ts rename to repos/effect/packages/effect/src/cluster/ShardingConfig.ts index 458c932974..4b8b8474c9 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ShardingConfig.ts +++ b/repos/effect/packages/effect/src/cluster/ShardingConfig.ts @@ -8,21 +8,23 @@ * layers, a `Config` description for loading values, and helpers for normalizing * assigned shard groups. * + * @stability unstable * @since 4.0.0 */ -import * as Config from "../../Config.ts" -import * as ConfigProvider from "../../ConfigProvider.ts" -import * as Context from "../../Context.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Schema from "../../Schema.ts" +import * as Config from "../Config.ts" +import * as ConfigProvider from "../ConfigProvider.ts" +import * as Context from "../Context.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Schema from "../Schema.ts" import { RunnerAddress } from "./RunnerAddress.ts" /** * Represents the configuration for the `Sharding` service on a given runner. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -170,6 +172,7 @@ const defaultRunnerAddress = RunnerAddress.make({ host: "localhost", port: 34431 * shard group, shard count, mailbox settings, polling intervals, and remote * serialization simulation. * + * @stability unstable * @category defaults * @since 4.0.0 */ @@ -225,6 +228,7 @@ export const defaults: ShardingConfig["Service"] = { * @see {@link layerDefaults} for a layer with no overrides * @see {@link layerFromEnv} for loading configuration from environment variables before applying explicit overrides * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -234,6 +238,7 @@ export const layer = (options?: Partial): Layer.Layer /** * Layer that provides the default `ShardingConfig` values. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -243,6 +248,7 @@ export const layerDefaults: Layer.Layer = layer() * Describes how to load `ShardingConfig` values, applying the same * defaults used by the in-memory `defaults` object. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -360,6 +366,7 @@ export const config: Config.Config = Config.all({ * Effect that loads `ShardingConfig` from environment variables using the * constant-case config provider. * + * @stability unstable * @category configuration * @since 4.0.0 */ @@ -376,6 +383,7 @@ export const configFromEnv = config.pipe( * Layer that loads `ShardingConfig` from environment variables and, when options * are provided, overlays those options on top of the loaded values. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -391,6 +399,7 @@ export const layerFromEnv = (options?: Partial | unde * Normalizes the provided `ShardingConfig` to calculate the `available` and * `assigned` shard groups. * + * @stability unstable * @category converting * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/ShardingRegistrationEvent.ts b/repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/cluster/ShardingRegistrationEvent.ts rename to repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts index 05146d8673..25fe92068c 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/ShardingRegistrationEvent.ts +++ b/repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts @@ -5,15 +5,17 @@ * inspect which capabilities a runner made available, or assert registration * behavior in tests. * + * @stability unstable * @since 4.0.0 */ -import * as Data from "../../Data.ts" +import * as Data from "../Data.ts" import type { Entity } from "./Entity.ts" import type { SingletonAddress } from "./SingletonAddress.ts" /** * Represents events that can occur when a runner registers entities or singletons. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -24,6 +26,7 @@ export type ShardingRegistrationEvent = /** * Represents an event that occurs when a new entity is registered with a runner. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -36,6 +39,7 @@ export interface EntityRegistered { * Represents an event that occurs when a new singleton is registered with a * runner. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -47,6 +51,7 @@ export interface SingletonRegistered { /** * Constructors and matchers for sharding registration events. * + * @stability unstable * @category pattern matching * @since 4.0.0 */ @@ -55,6 +60,7 @@ export const { * Pattern matches on a sharding registration event and dispatches to the * matching variant handler. * + * @stability unstable * @category pattern matching * @since 4.0.0 */ @@ -62,6 +68,7 @@ export const { /** * Creates an event for an entity registered by the local runner. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -69,6 +76,7 @@ export const { /** * Creates an event for a singleton registered by the local runner. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/SingleRunner.ts b/repos/effect/packages/effect/src/cluster/SingleRunner.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/cluster/SingleRunner.ts rename to repos/effect/packages/effect/src/cluster/SingleRunner.ts index 3f3cdb9069..87baf89d05 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/SingleRunner.ts +++ b/repos/effect/packages/effect/src/cluster/SingleRunner.ts @@ -8,11 +8,12 @@ * process handles all cluster work itself. It still requires a SQL client * because mailbox messages and replies are stored in SQL. * + * @stability unstable * @since 4.0.0 */ import * as Layer from "effect/Layer" -import type { ConfigError } from "../../Config.ts" -import type * as Crypto from "../../Crypto.ts" +import type { ConfigError } from "../Config.ts" +import type * as Crypto from "../Crypto.ts" import type * as SqlClient from "../sql/SqlClient.ts" import type * as MessageStorage from "./MessageStorage.ts" import * as RunnerHealth from "./RunnerHealth.ts" @@ -53,6 +54,7 @@ import * as SqlRunnerStorage from "./SqlRunnerStorage.ts" * @see {@link SqlRunnerStorage.layer} for the default SQL-backed runner storage selected when `runnerStorage` is omitted or `"sql"` * @see {@link RunnerStorage.layerMemory} for the in-memory runner storage selected by `runnerStorage: "memory"` * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/Singleton.ts b/repos/effect/packages/effect/src/cluster/Singleton.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/cluster/Singleton.ts rename to repos/effect/packages/effect/src/cluster/Singleton.ts index db4a129438..4e4641ab83 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Singleton.ts +++ b/repos/effect/packages/effect/src/cluster/Singleton.ts @@ -6,11 +6,12 @@ * sharding starts the effect; when ownership moves or the layer scope closes, * the running fiber is interrupted and the registration is removed. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import type { Scope } from "../../Scope.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import type { Scope } from "../Scope.ts" import { Sharding } from "./Sharding.ts" /** @@ -40,6 +41,7 @@ import { Sharding } from "./Sharding.ts" * * @see {@link Sharding} for the lower-level service that registers singletons and manages shard ownership * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/SingletonAddress.ts b/repos/effect/packages/effect/src/cluster/SingletonAddress.ts similarity index 84% rename from repos/effect/packages/effect/src/unstable/cluster/SingletonAddress.ts rename to repos/effect/packages/effect/src/cluster/SingletonAddress.ts index 2261e6e750..d52920fdb8 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/SingletonAddress.ts +++ b/repos/effect/packages/effect/src/cluster/SingletonAddress.ts @@ -5,11 +5,12 @@ * stable value for registration events, equality, hashing, and local singleton * fiber tracking. * + * @stability unstable * @since 4.0.0 */ -import * as Equal from "../../Equal.ts" -import * as Hash from "../../Hash.ts" -import * as Schema from "../../Schema.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import * as Schema from "../Schema.ts" import { ShardId } from "./ShardId.ts" const TypeId = "~effect/cluster/SingletonAddress" @@ -17,6 +18,7 @@ const TypeId = "~effect/cluster/SingletonAddress" /** * Represents the unique address of an singleton within the cluster. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -27,12 +29,14 @@ export class SingletonAddress extends Schema.Class(TypeId)({ /** * Marks this value as a cluster singleton address for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId; /** * Computes a structural hash from the singleton name and shard id. * + * @stability unstable * @since 4.0.0 */ [Hash.symbol]() { @@ -41,6 +45,7 @@ export class SingletonAddress extends Schema.Class(TypeId)({ /** * Compares singleton addresses by name and shard id. * + * @stability unstable * @since 4.0.0 */ [Equal.symbol](that: SingletonAddress): boolean { diff --git a/repos/effect/packages/effect/src/unstable/cluster/Snowflake.ts b/repos/effect/packages/effect/src/cluster/Snowflake.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/cluster/Snowflake.ts rename to repos/effect/packages/effect/src/cluster/Snowflake.ts index 8adf7576e4..398b9afeea 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/Snowflake.ts +++ b/repos/effect/packages/effect/src/cluster/Snowflake.ts @@ -8,22 +8,24 @@ * includes schemas for bigint and string encodings, helpers for creating and * reading ids, and a `Clock`-backed generator service for cluster runtime use. * + * @stability unstable * @since 4.0.0 */ -import type * as Brand from "../../Brand.ts" -import { Clock } from "../../Clock.ts" -import * as Context from "../../Context.ts" -import * as DateTime from "../../DateTime.ts" -import * as Effect from "../../Effect.ts" -import { identity } from "../../Function.ts" -import * as Layer from "../../Layer.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import type * as Brand from "../Brand.ts" +import { Clock } from "../Clock.ts" +import * as Context from "../Context.ts" +import * as DateTime from "../DateTime.ts" +import * as Effect from "../Effect.ts" +import { identity } from "../Function.ts" +import * as Layer from "../Layer.ts" +import * as Schema from "../Schema.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" import type { MachineId } from "./MachineId.ts" /** * Runtime brand identifier for cluster snowflake ids. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -32,6 +34,7 @@ export const TypeId = "~effect/cluster/Snowflake" /** * Type-level representation of the cluster snowflake brand identifier. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -41,6 +44,7 @@ export type TypeId = typeof TypeId * Branded bigint identifier composed from a timestamp, machine id, and per-machine * sequence number. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -50,6 +54,7 @@ export type Snowflake = Brand.Branded * Constructs a branded cluster snowflake id from a bigint or bigint-compatible * string. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -59,6 +64,7 @@ export const Snowflake = (input: string | number | bigint): Snowflake => /** * Namespace containing support types for snowflake parts and generators. * + * @stability unstable * @since 4.0.0 */ export declare namespace Snowflake { @@ -66,6 +72,7 @@ export declare namespace Snowflake { * Decoded components of a snowflake id: Unix timestamp milliseconds, machine id, * and sequence number. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -79,6 +86,7 @@ export declare namespace Snowflake { * Stateful generator for runner-local snowflake ids, exposing an unsafe * synchronous `nextUnsafe` operation and an effectful machine id setter. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -91,6 +99,7 @@ export declare namespace Snowflake { /** * Schema type for snowflake ids represented as branded bigints. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -99,6 +108,7 @@ export interface SnowflakeFromBigInt extends Schema.brand /** * Schema for snowflake ids represented as branded bigints. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -107,6 +117,7 @@ export const SnowflakeFromBigInt: SnowflakeFromBigInt = Schema.BigInt.pipe(Schem /** * Schema type for snowflake ids decoded from strings into branded bigints. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -116,6 +127,7 @@ export interface SnowflakeFromString extends Schema.decodeTo Number(snowflake >> c /** * Extracts the timestamp from a snowflake id as a `DateTime.Utc`. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -186,6 +202,7 @@ export const dateTime = (snowflake: Snowflake): DateTime.Utc => DateTime.makeUns /** * Extracts the machine id component from a snowflake id. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -195,6 +212,7 @@ export const machineId = (snowflake: Snowflake): MachineId => /** * Extracts the per-machine sequence component from a snowflake id. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -203,6 +221,7 @@ export const sequence = (snowflake: Snowflake): number => Number(snowflake % con /** * Decomposes a snowflake id into its timestamp, machine id, and sequence parts. * + * @stability unstable * @category converting * @since 4.0.0 */ @@ -221,6 +240,7 @@ export const toParts = (snowflake: Snowflake): Snowflake.Parts => ({ * backward, resets the sequence each millisecond, and advances the timestamp when * more than 4096 ids are requested in the same millisecond. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -265,6 +285,7 @@ export const makeGenerator: Effect.Effect = Effect.gen(func /** * Context service for a stateful snowflake id generator. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -276,6 +297,7 @@ export class Generator extends Context.Service< /** * Layer that provides the default snowflake `Generator` service. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/SocketRunner.ts b/repos/effect/packages/effect/src/cluster/SocketRunner.ts similarity index 96% rename from repos/effect/packages/effect/src/unstable/cluster/SocketRunner.ts rename to repos/effect/packages/effect/src/cluster/SocketRunner.ts index 968fd0f95d..a007a416a9 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/SocketRunner.ts +++ b/repos/effect/packages/effect/src/cluster/SocketRunner.ts @@ -7,10 +7,11 @@ * client-only socket runner layer for processes that need cluster clients * without starting a runner server or receiving shard assignments. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" import * as NetAddress from "../net/NetAddress.ts" import type * as RpcSerialization from "../rpc/RpcSerialization.ts" import * as RpcServer from "../rpc/RpcServer.ts" @@ -60,6 +61,7 @@ const withLogAddress = (layer: Layer.Layer): Layer.Layer => primaryKey.length <= 255 ? Effect.succeed(primaryKey) - : Effect.map(crypto.digest("SHA-256", encoder.encode(primaryKey)), Encoding.encodeHex) + : Effect.map(crypto.digest("SHA-256", encoder.encode(primaryKey)), Hex.encode) const messageIdEnforcesWidth = sql.onDialectOrElse({ mssql: () => true, @@ -574,14 +576,25 @@ export const makeEncoded: (options?: { ), clearReplies: Effect.fnUntraced( - function*(requestId) { - yield* sql`DELETE FROM ${repliesTableSql} WHERE request_id = ${String(requestId)} AND kind = 0` - yield* sql`DELETE FROM ${messagesTableSql} WHERE request_id = ${ - String(requestId) - } AND kind = ${messageKindInterrupt}` - yield* sql`UPDATE ${messagesTableSql} SET processed = ${sqlFalse}, last_reply_id = NULL, last_read = NULL WHERE request_id = ${ - String(requestId) - }` + function*(requestId, options) { + const expectedReplyId = options?.expectedReplyId + if (expectedReplyId === undefined) { + yield* sql`DELETE FROM ${repliesTableSql} WHERE request_id = ${String(requestId)} AND kind = 0` + yield* sql`DELETE FROM ${messagesTableSql} WHERE request_id = ${ + String(requestId) + } AND kind = ${messageKindInterrupt}` + yield* sql`UPDATE ${messagesTableSql} SET processed = ${sqlFalse}, last_reply_id = NULL, last_read = NULL WHERE request_id = ${ + String(requestId) + }` + return + } + const id = String(requestId) + const expected = String(expectedReplyId) + // Check at the storage boundary, not at the earlier workflow read. + yield* sql`DELETE FROM ${repliesTableSql} WHERE request_id = ${id} AND kind = 0 + AND EXISTS (SELECT 1 FROM ${messagesTableSql} WHERE id = ${id} AND last_reply_id = ${expected})` + yield* sql`UPDATE ${messagesTableSql} SET processed = ${sqlFalse}, last_reply_id = NULL, last_read = NULL + WHERE request_id = ${id} AND last_reply_id = ${expected}` }, sql.withTransaction, PersistenceError.refail, @@ -686,6 +699,20 @@ export const makeEncoded: (options?: { ) }, + resetRequests: (requestIds) => + requestIds.length === 0 + ? Effect.void + : sql` + UPDATE ${messagesTableSql} + SET last_read = NULL + WHERE processed = ${sqlFalse} + AND ${sql.in("id", requestIds.map(String))} + `.pipe( + Effect.asVoid, + PersistenceError.refail, + withTracerDisabled + ), + resetAddresses: (addresses) => addresses.length === 0 ? Effect.void @@ -757,6 +784,7 @@ export const makeEncoded: (options?: { * Creates a SQL-backed `MessageStorage` implementation, running its migrations * and using the optional table prefix. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -794,6 +822,7 @@ export const make: (options?: { * @see {@link layerWith} for the same SQL storage layer with a custom table prefix * @see {@link make} for the lower-level service constructor that uses an existing `Snowflake.Generator` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -808,6 +837,7 @@ export const layer: Layer.Layer< /** * Layer that provides SQL-backed `MessageStorage` using a custom table prefix. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/SqlRunnerStorage.ts b/repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/cluster/SqlRunnerStorage.ts rename to repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts index 9fdd3caa26..b82d249029 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/SqlRunnerStorage.ts +++ b/repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts @@ -7,15 +7,16 @@ * optional table prefix, uses advisory locks for PostgreSQL and MySQL when * enabled, and provides constructors and layers for the storage service. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import * as Fiber from "../../Fiber.ts" -import * as Layer from "../../Layer.ts" -import * as Scope from "../../Scope.ts" +import * as Arr from "../Array.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import * as Fiber from "../Fiber.ts" +import * as Layer from "../Layer.ts" +import * as Scope from "../Scope.ts" import * as SqlClient from "../sql/SqlClient.ts" import type { SqlError } from "../sql/SqlError.ts" import type * as Statement from "../sql/Statement.ts" @@ -64,6 +65,7 @@ const postgresLockNamespace = (prefix: string): number => { * @see {@link layer} for the default SQL-backed storage layer * @see {@link layerWith} for a SQL-backed storage layer with a custom table prefix * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -300,6 +302,10 @@ export const make = Effect.fnUntraced(function*(options: { }) const sqlNow = sql.literal(sqlNowString) + // Use the same shard ID order for all multi-row PostgreSQL lease locks to + // avoid deadlocks between acquisition, refresh and bulk release. + const pgLockOrder = sql.literal(`ORDER BY shard_id COLLATE "C"`) + const expiresSeconds = sql.literal( Math.ceil(Duration.toSeconds( Duration.fromInputUnsafe(config.shardLockExpiration) @@ -406,7 +412,9 @@ export const make = Effect.fnUntraced(function*(options: { sql`(${stringLiteral(shardId)}, ${stringLiteral(address)}, ${sqlNow})` ) return sql` - INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) VALUES ${sql.csv(values)} + INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) + SELECT * FROM (VALUES ${sql.csv(values)}) AS requested (shard_id, address, acquired_at) + ${pgLockOrder} ON CONFLICT (shard_id) DO UPDATE SET address = ${address}, acquired_at = ${sqlNow} WHERE ${locksTableSql}.address = ${address} @@ -594,9 +602,14 @@ export const make = Effect.fnUntraced(function*(options: { if (!disableAdvisoryLocks) return acquireLock return (address: string, shardIds: ReadonlyArray) => sql` + WITH locked AS ( + SELECT shard_id FROM ${locksTableSql} + WHERE address = ${address} AND shard_id IN ${stringLiteralArr(shardIds)} + ${pgLockOrder} FOR UPDATE + ) UPDATE ${locksTableSql} SET acquired_at = ${sqlNow} - WHERE address = ${address} AND shard_id IN ${stringLiteralArr(shardIds)} + WHERE address = ${address} AND shard_id IN (SELECT shard_id FROM locked) RETURNING shard_id `.pipe( execWithLockConnValues, @@ -691,7 +704,12 @@ export const make = Effect.fnUntraced(function*(options: { const releaseAllShards = sql.onDialectOrElse({ pg: () => (address: string) => disableAdvisoryLocks - ? sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(execWithLockConn) + ? sql` + WITH locked AS ( + SELECT shard_id FROM ${locksTableSql} WHERE address = ${address} ${pgLockOrder} FOR UPDATE + ) + DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id IN (SELECT shard_id FROM locked) + `.pipe(execWithLockConn) : sql`SELECT pg_advisory_unlock_all()`.pipe(execWithLockConn, Effect.asVoid), mysql: () => (address: string) => disableAdvisoryLocks @@ -777,6 +795,7 @@ export const make = Effect.fnUntraced(function*(options: { /** * Layer that provides SQL-backed `RunnerStorage` using the default table prefix. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -789,6 +808,7 @@ export const layer: Layer.Layer< /** * Layer that provides SQL-backed `RunnerStorage` using a custom table prefix. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/cluster/TestRunner.ts b/repos/effect/packages/effect/src/cluster/TestRunner.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/cluster/TestRunner.ts rename to repos/effect/packages/effect/src/cluster/TestRunner.ts index c782284867..a101e20f0f 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/TestRunner.ts +++ b/repos/effect/packages/effect/src/cluster/TestRunner.ts @@ -6,9 +6,10 @@ * coordination, and mailbox persistence without starting RPC servers or * external databases. * + * @stability unstable * @since 4.0.0 */ -import * as Layer from "../../Layer.ts" +import * as Layer from "../Layer.ts" import * as MessageStorage from "./MessageStorage.ts" import * as RunnerHealth from "./RunnerHealth.ts" import * as Runners from "./Runners.ts" @@ -23,6 +24,7 @@ import * as ShardingConfig from "./ShardingConfig.ts" * * `MessageStorage` and `RunnerStorage` are backed by in-memory drivers. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/cluster/index.ts b/repos/effect/packages/effect/src/cluster/index.ts new file mode 100644 index 0000000000..2b4b916c8d --- /dev/null +++ b/repos/effect/packages/effect/src/cluster/index.ts @@ -0,0 +1,240 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ClusterCron from "./ClusterCron.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ClusterError from "./ClusterError.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ClusterMetrics from "./ClusterMetrics.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ClusterSchema from "./ClusterSchema.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ClusterWorkflowEngine from "./ClusterWorkflowEngine.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DeliverAt from "./DeliverAt.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Entity from "./Entity.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityAddress from "./EntityAddress.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityId from "./EntityId.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityProxy from "./EntityProxy.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityProxyServer from "./EntityProxyServer.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityResource from "./EntityResource.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as EntityType from "./EntityType.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Envelope from "./Envelope.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpRunner from "./HttpRunner.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as K8sHttpClient from "./K8sHttpClient.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as K8sTypes from "./K8sTypes.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as MachineId from "./MachineId.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Message from "./Message.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as MessageStorage from "./MessageStorage.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Reply from "./Reply.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Runner from "./Runner.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as RunnerAddress from "./RunnerAddress.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as RunnerHealth from "./RunnerHealth.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Runners from "./Runners.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as RunnerServer from "./RunnerServer.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as RunnerStorage from "./RunnerStorage.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ShardId from "./ShardId.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Sharding from "./Sharding.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ShardingConfig from "./ShardingConfig.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as ShardingRegistrationEvent from "./ShardingRegistrationEvent.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SingleRunner from "./SingleRunner.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Singleton from "./Singleton.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SingletonAddress from "./SingletonAddress.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Snowflake from "./Snowflake.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SocketRunner from "./SocketRunner.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SqlMessageStorage from "./SqlMessageStorage.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SqlRunnerStorage from "./SqlRunnerStorage.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as TestRunner from "./TestRunner.ts" diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/clusterAbandon.ts b/repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts similarity index 75% rename from repos/effect/packages/effect/src/unstable/cluster/internal/clusterAbandon.ts rename to repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts index 4c13c3081a..1f8202667e 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/clusterAbandon.ts +++ b/repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts @@ -1,6 +1,6 @@ -import type * as Cause from "../../../Cause.ts" -import * as Effect from "../../../Effect.ts" -import * as Fiber from "../../../Fiber.ts" +import type * as Cause from "../../Cause.ts" +import * as Effect from "../../Effect.ts" +import * as Fiber from "../../Fiber.ts" import { Abandon } from "../ClusterSchema.ts" export const interrupt: Effect.Effect = Effect.interruptible( diff --git a/repos/effect/packages/effect/src/cluster/internal/entityActivation.ts b/repos/effect/packages/effect/src/cluster/internal/entityActivation.ts new file mode 100644 index 0000000000..9da82263fd --- /dev/null +++ b/repos/effect/packages/effect/src/cluster/internal/entityActivation.ts @@ -0,0 +1,7 @@ +import * as Context from "../../Context.ts" +import type * as Scope from "../../Scope.ts" + +/** The entity lifetime, shared by all handler rebuilds within an activation. @internal */ +export class CurrentActivationScope extends Context.Service()( + "effect/cluster/internal/CurrentActivationScope" +) {} diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/entityManager.ts b/repos/effect/packages/effect/src/cluster/internal/entityManager.ts similarity index 85% rename from repos/effect/packages/effect/src/unstable/cluster/internal/entityManager.ts rename to repos/effect/packages/effect/src/cluster/internal/entityManager.ts index 75c9b49dbf..12bc386ad7 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/entityManager.ts +++ b/repos/effect/packages/effect/src/cluster/internal/entityManager.ts @@ -1,24 +1,25 @@ -import * as Arr from "../../../Array.ts" -import * as Cause from "../../../Cause.ts" -import { Clock } from "../../../Clock.ts" -import * as Context from "../../../Context.ts" -import * as Duration from "../../../Duration.ts" -import type { Input } from "../../../Duration.ts" -import * as Effect from "../../../Effect.ts" -import * as Equal from "../../../Equal.ts" -import * as Exit from "../../../Exit.ts" -import * as Fiber from "../../../Fiber.ts" -import { identity } from "../../../Function.ts" -import * as Latch from "../../../Latch.ts" -import * as Metric from "../../../Metric.ts" -import * as Option from "../../../Option.ts" -import { CurrentLogAnnotations } from "../../../References.ts" -import * as Schedule from "../../../Schedule.ts" -import * as Schema from "../../../Schema.ts" -import * as SchemaIssue from "../../../SchemaIssue.ts" -import * as Scope from "../../../Scope.ts" +import * as Arr from "../../Array.ts" +import * as Cause from "../../Cause.ts" +import { Clock } from "../../Clock.ts" +import * as Context from "../../Context.ts" +import * as Duration from "../../Duration.ts" +import type { Input } from "../../Duration.ts" +import * as Effect from "../../Effect.ts" +import * as Equal from "../../Equal.ts" +import * as Exit from "../../Exit.ts" +import * as Fiber from "../../Fiber.ts" +import { identity } from "../../Function.ts" +import { scopeAddFinalizerUnsafe, scopeRemoveFinalizerUnsafe } from "../../internal/effect.ts" +import * as Latch from "../../Latch.ts" +import * as Metric from "../../Metric.ts" +import * as Option from "../../Option.ts" +import { CurrentLogAnnotations } from "../../References.ts" import type * as Rpc from "../../rpc/Rpc.ts" import * as RpcServer from "../../rpc/RpcServer.ts" +import * as Schedule from "../../Schedule.ts" +import * as Schema from "../../Schema.ts" +import * as SchemaIssue from "../../SchemaIssue.ts" +import * as Scope from "../../Scope.ts" import { AlreadyProcessingMessage, EntityNotAssignedToRunner, MailboxFull, MalformedMessage } from "../ClusterError.ts" import * as ClusterMetrics from "../ClusterMetrics.ts" import { isUninterruptibleForServer, Persisted, WithTransaction } from "../ClusterSchema.ts" @@ -36,12 +37,15 @@ import type { ShardId } from "../ShardId.ts" import type { Sharding } from "../Sharding.ts" import { ShardingConfig } from "../ShardingConfig.ts" import * as Snowflake from "../Snowflake.ts" +import { CurrentActivationScope } from "./entityActivation.ts" import { EntityReaper } from "./entityReaper.ts" import { acquireEntity, releaseEntity } from "./interruptors.ts" import { ResourceMap } from "./resourceMap.ts" import { ResourceRef } from "./resourceRef.ts" -/** @internal */ +/** + * @internal + */ export interface EntityManager { readonly sendLocal: ( message: Message.IncomingLocal @@ -53,6 +57,7 @@ export interface EntityManager { readonly isProcessingFor: (message: Message.Incoming, options?: { readonly excludeReplies?: boolean + readonly excludeCompleted?: boolean }) => boolean readonly clearProcessed: () => void @@ -69,7 +74,9 @@ export interface EntityManager { // Tracks how many entities are resident on the runner across all entity // managers, so the spawn of new entities can be gated by // `ShardingConfig.maxResidentEntities`. -/** @internal */ +/** + * @internal + */ export interface Residency { /** * Reserve a slot for a new entity. Returns `false` when the runner is at @@ -80,7 +87,9 @@ export interface Residency { } // Represents the entities managed by this entity manager -/** @internal */ +/** + * @internal + */ export type EntityState = { readonly address: EntityAddress readonly scope: Scope.Scope @@ -88,8 +97,11 @@ export type EntityState = { readonly rpc: Rpc.AnyWithProps readonly message: Message.IncomingRequestLocal sentReply: boolean + sentExit: boolean lastSentChunk: Option.Option> sequence: number + /** Set when the request should not outlive its caller. */ + callerScope?: Scope.Scope | undefined }> lastActiveCheck: number write: RpcServer.RpcServer["write"] @@ -97,7 +109,9 @@ export type EntityState = { keepAliveEnabled: boolean } -/** @internal */ +/** + * @internal + */ export const make = Effect.fnUntraced(function*< Type extends string, Rpcs extends Rpc.Any, @@ -193,14 +207,15 @@ export const make = Effect.fnUntraced(function*< // swap the server without losing the active requests const writeRef = yield* ResourceRef.from( scope, - Effect.fnUntraced(function*(scope) { + Effect.fnUntraced(function*(handlerScope) { let isShuttingDown = false const handlerContext = context.pipe( Context.add(CurrentAddress, address), Context.add(CurrentRunnerAddress, options.runnerAddress), Context.add(KeepAliveLatch, keepAliveLatch), - Context.add(Scope.Scope, scope), + Context.add(CurrentActivationScope, scope), + Context.add(Scope.Scope, handlerScope), Context.add(CurrentLogAnnotations, {}) ) @@ -228,6 +243,7 @@ export const make = Effect.fnUntraced(function*< if (!request) return Effect.void request.sentReply = true + request.sentExit = true if ( isShuttingDown && @@ -248,6 +264,7 @@ export const make = Effect.fnUntraced(function*< (isShuttingDown || isUninterruptibleForServer(request.message.annotations)) ) { if (!isShuttingDown) { + request.sentExit = false return server.write( 0, { @@ -259,11 +276,9 @@ export const make = Effect.fnUntraced(function*< lastSentChunk: request.lastSentChunk } as any) as any }, - Context.get(request.rpc.annotations, WithTransaction) - ? { onRequest: options.storage.withTransaction } - : undefined + requestWriteOptions(request) ).pipe( - Effect.forkIn(scope) + Effect.forkIn(handlerScope) ) } activeRequests.delete(Snowflake.Snowflake(response.requestId)) @@ -282,11 +297,12 @@ export const make = Effect.fnUntraced(function*< ) ).pipe( Effect.flatMap(() => { - processedRequestIds.add(request.message.envelope.requestId) + if (storageEnabled) { + processedRequestIds.add(request.message.envelope.requestId) + } activeRequests.delete(Snowflake.Snowflake(response.requestId)) - // ensure that the reaper does not remove the entity as we haven't - // been "idle" yet + // Start the idle timer when the last request completes. if (activeRequests.size === 0) { state.lastActiveCheck = clock.currentTimeMillisUnsafe() } @@ -327,12 +343,12 @@ export const make = Effect.fnUntraced(function*< } } }).pipe( - Scope.provide(scope), + Scope.provide(handlerScope), Effect.setContext(Context.merge(handlerContext, handlers)) ) yield* Scope.addFinalizer( - scope, + handlerScope, Effect.sync(() => { isShuttingDown = true }) @@ -342,7 +358,8 @@ export const make = Effect.fnUntraced(function*< for (const id of defectRequestIds) { const request = activeRequests.get(id) if (!request) continue - const { lastSentChunk, message, rpc } = request + const { lastSentChunk, message } = request + request.sentExit = false yield* server.write( 0, { @@ -354,9 +371,7 @@ export const make = Effect.fnUntraced(function*< lastSentChunk } as any) as any }, - Context.get(rpc.annotations, WithTransaction) - ? { onRequest: options.storage.withTransaction } - : undefined + requestWriteOptions(request) ) } defectRequestIds.clear() @@ -517,10 +532,19 @@ export const make = Effect.fnUntraced(function*< return Effect.fail(new MailboxFull({ address: message.envelope.address })) } + const callerScope = message.callerScope !== undefined && + !Context.get(message.annotations, Persisted) && + Context.get(message.annotations, ClusterSchema.Uninterruptible) === false + ? message.callerScope + : undefined + if (callerScope?.state._tag === "Closed") { + return Effect.void + } entry = { rpc, message, sentReply: false, + sentExit: false, lastSentChunk: Option.filter( message.lastSentReply, (reply): reply is Reply.Chunk => reply._tag === "Chunk" @@ -528,9 +552,19 @@ export const make = Effect.fnUntraced(function*< sequence: Option.match(message.lastSentReply, { onNone: () => 0, onSome: (reply) => reply._tag === "Chunk" ? reply.sequence + 1 : 0 - }) + }), + callerScope } server.activeRequests.set(message.envelope.requestId, entry) + if (callerScope !== undefined) { + // Register cleanup atomically with admission, including requests awaiting delivery. + scopeAddFinalizerUnsafe(callerScope, {}, () => + Effect.sync(() => { + if (server.activeRequests.delete(message.envelope.requestId) && server.activeRequests.size === 0) { + server.lastActiveCheck = clock.currentTimeMillisUnsafe() + } + })) + } return server.write( 0, { @@ -544,9 +578,7 @@ export const make = Effect.fnUntraced(function*< ) }) }, - Context.get(message.annotations, WithTransaction) - ? { onRequest: options.storage.withTransaction } - : undefined + requestWriteOptions(entry) ) } case "IncomingEnvelope": { @@ -579,6 +611,26 @@ export const make = Effect.fnUntraced(function*< ) } + // Bind each handler fiber, including replays, before its body runs. + const bindToCaller = (callerScope: Scope.Scope) => (effect: Effect.Effect) => + Effect.withFiber((fiber) => { + if (callerScope.state._tag === "Closed") return Effect.interrupt + const key = {} + scopeAddFinalizerUnsafe(callerScope, key, () => Effect.sync(() => fiber.interruptUnsafe(fiber.id))) + return Effect.ensuring(effect, Effect.sync(() => scopeRemoveFinalizerUnsafe(callerScope, key))) + }) + + const requestWriteOptions = ( + entry: { readonly message: Message.IncomingRequestLocal; readonly callerScope?: Scope.Scope | undefined } + ): Parameters[2] => { + const onTransaction = Context.get(entry.message.annotations, WithTransaction) + ? options.storage.withTransaction + : undefined + const onCaller = entry.callerScope && bindToCaller(entry.callerScope) + if (!onCaller) return onTransaction && { onRequest: onTransaction } + return { onRequest: onTransaction ? (effect) => onCaller(onTransaction(effect)) : onCaller } + } + const decodeMessage = makeMessageDecode(entityRpcs) const runFork = Effect.runForkWith(context) @@ -608,7 +660,10 @@ export const make = Effect.fnUntraced(function*< return Effect.flatMap(Fiber.joinAll(fibers), loop) }), isProcessingFor(message, options) { - if (options?.excludeReplies !== true && processedRequestIds.has(message.envelope.requestId)) { + if ( + options?.excludeReplies !== true && options?.excludeCompleted !== true && + processedRequestIds.has(message.envelope.requestId) + ) { return true } const state = activeServers.get(message.envelope.address.entityId) @@ -618,6 +673,8 @@ export const make = Effect.fnUntraced(function*< return false } else if (options?.excludeReplies && request.sentReply) { return false + } else if (options?.excludeCompleted && request.sentExit) { + return false } return true }, @@ -648,9 +705,7 @@ export const make = Effect.fnUntraced(function*< }, onSuccess: (decoded) => { if (decoded._tag === "IncomingEnvelope") { - return sendLocal( - new Message.IncomingEnvelope(decoded) - ) + return sendLocal(decoded) } const request = message as Message.IncomingRequest const rpc = entityRpcs.get(decoded.envelope.tag)! @@ -662,6 +717,7 @@ export const make = Effect.fnUntraced(function*< ), envelope: decoded.envelope, lastSentReply: decoded.lastSentReply, + callerScope: request.callerScope, respond: (reply) => request.respond( new Reply.ReplyWithContext({ @@ -710,10 +766,7 @@ const makeMessageDecode = (entityRpcs: Map) readonly _tag: "IncomingRequest" readonly envelope: Envelope.Request.Any readonly lastSentReply: Option.Option> - } | { - readonly _tag: "IncomingEnvelope" - readonly envelope: Envelope.AckChunk | Envelope.Interrupt - }, + } | Message.IncomingEnvelope, Schema.SchemaError, Rpc.ServicesServer > => { diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/entityReaper.ts b/repos/effect/packages/effect/src/cluster/internal/entityReaper.ts similarity index 88% rename from repos/effect/packages/effect/src/unstable/cluster/internal/entityReaper.ts rename to repos/effect/packages/effect/src/cluster/internal/entityReaper.ts index d50c602fc2..c0168693a6 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/entityReaper.ts +++ b/repos/effect/packages/effect/src/cluster/internal/entityReaper.ts @@ -1,14 +1,16 @@ -import { Clock } from "../../../Clock.ts" -import * as Context from "../../../Context.ts" -import * as Effect from "../../../Effect.ts" -import * as Latch from "../../../Latch.ts" -import * as Layer from "../../../Layer.ts" +import { Clock } from "../../Clock.ts" +import * as Context from "../../Context.ts" +import * as Effect from "../../Effect.ts" +import * as Latch from "../../Latch.ts" +import * as Layer from "../../Layer.ts" import type { EntityAddress } from "../EntityAddress.ts" import type { EntityId } from "../EntityId.ts" import type { EntityState } from "./entityManager.ts" import type { ResourceMap } from "./resourceMap.ts" -/** @internal */ +/** + * @internal + */ export class EntityReaper extends Context.Service()("effect/cluster/EntityReaper", { make: Effect.gen(function*() { let currentResolution = 30_000 diff --git a/repos/effect/packages/effect/src/cluster/internal/hash.ts b/repos/effect/packages/effect/src/cluster/internal/hash.ts new file mode 100644 index 0000000000..f3cf204f8a --- /dev/null +++ b/repos/effect/packages/effect/src/cluster/internal/hash.ts @@ -0,0 +1,27 @@ +/** + * Internal hashing helpers used by cluster routing to map entity identifiers to + * shard numbers. The hash is intentionally small and deterministic for + * JavaScript strings so every node that sees the same entity id and sharding + * configuration derives the same destination shard. + * + * **Gotchas** + * + * - Changing this implementation changes entity-to-shard assignment. + * - Strings are hashed as UTF-16 code units; normalize ids before routing if + * callers may produce equivalent text in different Unicode forms. + */ +/** + * @internal + */ +export const hashOptimize = (n: number): number => (n & 0xbfffffff) | ((n >>> 1) & 0x40000000) + +/** + * @internal + */ +export const hashString = (str: string) => { + let h = 5381, i = str.length + while (i) { + h = (h * 33) ^ str.charCodeAt(--i) + } + return hashOptimize(h) +} diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/interruptors.ts b/repos/effect/packages/effect/src/cluster/internal/interruptors.ts similarity index 90% rename from repos/effect/packages/effect/src/unstable/cluster/internal/interruptors.ts rename to repos/effect/packages/effect/src/cluster/internal/interruptors.ts index 596c95c5c1..8223b7a511 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/interruptors.ts +++ b/repos/effect/packages/effect/src/cluster/internal/interruptors.ts @@ -1,4 +1,4 @@ -import * as Effect from "../../../Effect.ts" +import * as Effect from "../../Effect.ts" import type { EntityAddress } from "../EntityAddress.ts" import type { ShardId } from "../ShardId.ts" @@ -28,31 +28,45 @@ const shardKey = (shardId: ShardId): string => `shard:${shardId.toString()}` const entityTypeKey = (entityType: string): string => `type:${entityType}` -/** @internal */ +/** + * @internal + */ export const acquireEntity = (address: EntityAddress): void => acquire(entityKey(address)) -/** @internal */ +/** + * @internal + */ export const releaseEntity = (address: EntityAddress): void => release(entityKey(address)) -/** @internal */ +/** + * @internal + */ export const acquireEntityType = (entityType: string): void => acquire(entityTypeKey(entityType)) -/** @internal */ +/** + * @internal + */ export const releaseEntityType = (entityType: string): void => release(entityTypeKey(entityType)) -/** @internal */ +/** + * @internal + */ export const aroundShard = ( shardId: ShardId, effect: Effect.Effect ): Effect.Effect => around(shardKey(shardId), effect) -/** @internal */ +/** + * @internal + */ export const aroundEntityType = ( entityType: string, effect: Effect.Effect ): Effect.Effect => around(entityTypeKey(entityType), effect) -/** @internal */ +/** + * @internal + */ export const isActive = (address: EntityAddress): boolean => counts.has(entityKey(address)) || counts.has(shardKey(address.shardId)) || diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/resourceMap.ts b/repos/effect/packages/effect/src/cluster/internal/resourceMap.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/internal/resourceMap.ts rename to repos/effect/packages/effect/src/cluster/internal/resourceMap.ts index e09e1f67b4..74270a885a 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/resourceMap.ts +++ b/repos/effect/packages/effect/src/cluster/internal/resourceMap.ts @@ -1,12 +1,14 @@ -import * as Context from "../../../Context.ts" -import * as Deferred from "../../../Deferred.ts" -import * as Effect from "../../../Effect.ts" -import * as Exit from "../../../Exit.ts" -import * as MutableHashMap from "../../../MutableHashMap.ts" -import * as MutableRef from "../../../MutableRef.ts" -import * as Scope from "../../../Scope.ts" +import * as Context from "../../Context.ts" +import * as Deferred from "../../Deferred.ts" +import * as Effect from "../../Effect.ts" +import * as Exit from "../../Exit.ts" +import * as MutableHashMap from "../../MutableHashMap.ts" +import * as MutableRef from "../../MutableRef.ts" +import * as Scope from "../../Scope.ts" -/** @internal */ +/** + * @internal + */ export class ResourceMap { readonly lookup: (key: K, scope: Scope.Scope) => Effect.Effect readonly entries: BackingMap diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/resourceRef.ts b/repos/effect/packages/effect/src/cluster/internal/resourceRef.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/cluster/internal/resourceRef.ts rename to repos/effect/packages/effect/src/cluster/internal/resourceRef.ts index 784263664c..3857ed16b5 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/resourceRef.ts +++ b/repos/effect/packages/effect/src/cluster/internal/resourceRef.ts @@ -1,14 +1,16 @@ -import type * as Cause from "../../../Cause.ts" -import * as Effect from "../../../Effect.ts" -import * as Exit from "../../../Exit.ts" -import * as Latch from "../../../Latch.ts" -import * as MutableRef from "../../../MutableRef.ts" -import * as Option from "../../../Option.ts" -import * as Scope from "../../../Scope.ts" +import type * as Cause from "../../Cause.ts" +import * as Effect from "../../Effect.ts" +import * as Exit from "../../Exit.ts" +import * as Latch from "../../Latch.ts" +import * as MutableRef from "../../MutableRef.ts" +import * as Option from "../../Option.ts" +import * as Scope from "../../Scope.ts" import type { EntityAddress } from "../EntityAddress.ts" import { acquireEntity, releaseEntity } from "./interruptors.ts" -/** @internal */ +/** + * @internal + */ export type State = { readonly _tag: "Closed" } | { @@ -24,7 +26,9 @@ export type State = { readonly cause: Cause.Cause } -/** @internal */ +/** + * @internal + */ export class ResourceRef { static from = Effect.fnUntraced(function*( parentScope: Scope.Scope, diff --git a/repos/effect/packages/effect/src/unstable/cluster/internal/shardLock.ts b/repos/effect/packages/effect/src/cluster/internal/shardLock.ts similarity index 82% rename from repos/effect/packages/effect/src/unstable/cluster/internal/shardLock.ts rename to repos/effect/packages/effect/src/cluster/internal/shardLock.ts index 7e37eed5e2..14cf953dbf 100644 --- a/repos/effect/packages/effect/src/unstable/cluster/internal/shardLock.ts +++ b/repos/effect/packages/effect/src/cluster/internal/shardLock.ts @@ -1,7 +1,9 @@ -import * as Duration from "../../../Duration.ts" +import * as Duration from "../../Duration.ts" import type { ShardingConfig } from "../ShardingConfig.ts" -/** @internal */ +/** + * @internal + */ export const effectiveInterval = (config: ShardingConfig["Service"]): Duration.Duration => Duration.min( Duration.fromInputUnsafe(config.shardLockRefreshInterval), diff --git a/repos/effect/packages/effect/src/unstable/devtools/DevTools.ts b/repos/effect/packages/effect/src/devtools/DevTools.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/devtools/DevTools.ts rename to repos/effect/packages/effect/src/devtools/DevTools.ts index 3074afdb21..48434199ed 100644 --- a/repos/effect/packages/effect/src/unstable/devtools/DevTools.ts +++ b/repos/effect/packages/effect/src/devtools/DevTools.ts @@ -7,15 +7,17 @@ * and metric snapshots to an external devtools process. Lower-level socket * protocol details live in `DevToolsClient`. * + * @stability unstable * @since 4.0.0 */ -import * as Layer from "../../Layer.ts" +import * as Layer from "../Layer.ts" import * as Socket from "../socket/Socket.ts" import * as DevToolsClient from "./DevToolsClient.ts" /** * Layer that installs the devtools tracer using an existing `Socket`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -25,6 +27,7 @@ export const layerSocket: Layer.Layer = DevToolsCli * Layer that installs the devtools tracer over a WebSocket connection to the * specified URL, defaulting to `ws://localhost:34437`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -59,6 +62,7 @@ export const layerWebSocket = ( * @see {@link layerWebSocket} for installing the devtools tracer with an explicit `WebSocketConstructor` requirement * @see {@link layerSocket} for installing the devtools tracer over an existing `Socket` transport * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/devtools/DevToolsClient.ts b/repos/effect/packages/effect/src/devtools/DevToolsClient.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/devtools/DevToolsClient.ts rename to repos/effect/packages/effect/src/devtools/DevToolsClient.ts index c351e8c193..4fd8879ee4 100644 --- a/repos/effect/packages/effect/src/unstable/devtools/DevToolsClient.ts +++ b/repos/effect/packages/effect/src/devtools/DevToolsClient.ts @@ -6,22 +6,23 @@ * ping messages, and metric snapshots through a socket, then exposes tracer * layers that forward telemetry while preserving the current tracer's behavior. * + * @stability unstable * @since 4.0.0 */ -import * as Cause from "../../Cause.ts" -import * as Context from "../../Context.ts" -import * as Deferred from "../../Deferred.ts" -import * as Effect from "../../Effect.ts" -import * as Fiber from "../../Fiber.ts" -import * as Layer from "../../Layer.ts" -import * as Metric from "../../Metric.ts" -import * as Queue from "../../Queue.ts" -import * as Schema from "../../Schema.ts" -import type * as Scope from "../../Scope.ts" -import * as Stream from "../../Stream.ts" -import * as Tracer from "../../Tracer.ts" +import * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Deferred from "../Deferred.ts" +import * as Effect from "../Effect.ts" import * as Ndjson from "../encoding/Ndjson.ts" +import * as Fiber from "../Fiber.ts" +import * as Layer from "../Layer.ts" +import * as Metric from "../Metric.ts" +import * as Queue from "../Queue.ts" +import * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" import * as Socket from "../socket/Socket.ts" +import * as Stream from "../Stream.ts" +import * as Tracer from "../Tracer.ts" import * as DevToolsSchema from "./DevToolsSchema.ts" const RequestSchema = Schema.toCodecJson(DevToolsSchema.Request) @@ -31,6 +32,7 @@ const ResponseSchema = Schema.toCodecJson(DevToolsSchema.Response) * Service for sending span and span-event telemetry to the Effect devtools * connection. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -135,6 +137,7 @@ const toMetricsSnapshot = ( * @see {@link makeTracer} for creating a tracer after a `DevToolsClient` is available * @see {@link layerTracer} for creating the client from the current `Socket` and installing the tracer as a layer * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -172,6 +175,7 @@ export const make: Effect.Effect< * @see {@link make} for constructing the client as a scoped effect instead of a layer * @see {@link layerTracer} for a higher-level layer that creates the client and installs the devtools tracer * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -225,6 +229,7 @@ const makeTracerEffect = Effect.gen(function*() { * Creates a tracer that delegates to the current tracer while sending span * starts, span events, and span ends to `DevToolsClient`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -239,6 +244,7 @@ export const makeTracer: Effect.Effect = m * Layer that creates a `DevToolsClient` from the current `Socket` and installs * the devtools tracer. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/devtools/DevToolsSchema.ts b/repos/effect/packages/effect/src/devtools/DevToolsSchema.ts similarity index 90% rename from repos/effect/packages/effect/src/unstable/devtools/DevToolsSchema.ts rename to repos/effect/packages/effect/src/devtools/DevToolsSchema.ts index bdfc47f2df..1fe97c273a 100644 --- a/repos/effect/packages/effect/src/unstable/devtools/DevToolsSchema.ts +++ b/repos/effect/packages/effect/src/devtools/DevToolsSchema.ts @@ -7,18 +7,20 @@ * boundaries to encode, decode, or validate custom transports that need to * interoperate with the built-in unstable devtools client and server. * + * @stability unstable * @since 4.0.0 */ -import * as Exit from "../../Exit.ts" -import { identity } from "../../Function.ts" -import type * as Option from "../../Option.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import * as Exit from "../Exit.ts" +import { identity } from "../Function.ts" +import type * as Option from "../Option.ts" +import * as Schema from "../Schema.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" /** * Schema for a span status representing a span that has started but not yet * ended. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -30,6 +32,7 @@ export const SpanStatusStarted = Schema.Struct({ /** * Type of a span status representing a span that has started but not yet ended. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -40,6 +43,7 @@ export type SpanStatusStarted = Schema.Schema.Type * end time, and encoded exit status. Encoding drops success values with * `Exit.asVoid`. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -62,6 +66,7 @@ export const SpanStatusEnded = Schema.Struct({ * Type of a span status representing an ended span with start time, end time, * and exit status. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -70,6 +75,7 @@ export type SpanStatusEnded = Schema.Schema.Type /** * Schema for devtools span status, either started or ended. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -78,6 +84,7 @@ export const SpanStatus = Schema.Union([SpanStatusStarted, SpanStatusEnded]) /** * Type of a devtools span status, either started or ended. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -87,6 +94,7 @@ export type SpanStatus = Schema.Schema.Type * Serialized parent span context for a span created outside the current * devtools span tree. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -101,6 +109,7 @@ export interface ExternalSpan { * Schema for an external parent span context containing span id, trace id, and * sampling flag. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -115,6 +124,7 @@ export const ExternalSpan: Schema.Codec = Schema.Struct({ * Telemetry payload for an Effect span sent to devtools, including identity, * attributes, status, sampling flag, and optional parent span. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -132,6 +142,7 @@ export interface Span { /** * Schema for an Effect span telemetry payload sent to devtools. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -150,6 +161,7 @@ export const Span: Schema.Codec = Schema.Struct({ * Schema for a named event emitted by a span, including trace id, span id, * start time, and optional attributes. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -165,6 +177,7 @@ export const SpanEvent = Schema.Struct({ /** * Type of a named event emitted by a span and sent to devtools. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -174,6 +187,7 @@ export type SpanEvent = Schema.Schema.Type * Type of a span parent, represented either by a devtools `Span` payload or an * `ExternalSpan` context. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -183,6 +197,7 @@ export type ParentSpan = Span | ExternalSpan * Schema for a span parent, either a full devtools `Span` payload or an * `ExternalSpan` context. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -191,6 +206,7 @@ export const ParentSpan = Schema.Union([Span, ExternalSpan]) /** * Schema for the devtools heartbeat request sent by the client. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -201,6 +217,7 @@ export const Ping = Schema.Struct({ /** * Type of the devtools heartbeat request sent by the client. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -209,6 +226,7 @@ export type Ping = Schema.Schema.Type /** * Schema for the devtools heartbeat response. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -219,6 +237,7 @@ export const Pong = Schema.Struct({ /** * Type of the devtools heartbeat response. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -227,6 +246,7 @@ export type Pong = Schema.Schema.Type /** * Schema for a devtools request asking the client to send a metrics snapshot. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -237,6 +257,7 @@ export const MetricsRequest = Schema.Struct({ /** * Type of a devtools request asking the client to send a metrics snapshot. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -245,6 +266,7 @@ export type MetricsRequest = Schema.Schema.Type /** * Schema for a metric label key/value pair in a devtools metrics snapshot. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -256,6 +278,7 @@ export const MetricLabel = Schema.Struct({ /** * Type of a metric label key/value pair in a devtools metrics snapshot. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -274,6 +297,7 @@ const metric = (type: Type * Schema for a counter metric snapshot, including the count and whether updates * are incremental. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -293,6 +317,7 @@ export const Counter = metric( * The state contains the current count and whether the counter reports * incremental updates. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -305,6 +330,7 @@ export type Counter = Schema.Schema.Type * * The metric state records occurrence counts by string key. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -322,6 +348,7 @@ export const Frequency = metric( * * The state maps observed string values to occurrence counts. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -334,6 +361,7 @@ export type Frequency = Schema.Schema.Type * * The metric state contains the current numeric or bigint value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -351,6 +379,7 @@ export const Gauge = metric( * * The state contains the current numeric or bigint value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -364,6 +393,7 @@ export type Gauge = Schema.Schema.Type * The metric state includes bucket counts plus the total count, minimum, * maximum, and sum. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -386,6 +416,7 @@ export const Histogram = metric( * The state includes bucket counts plus the total count, minimum, maximum, and * sum. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -399,6 +430,7 @@ export type Histogram = Schema.Schema.Type * The metric state contains quantile values plus the total count, minimum, * maximum, and sum. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -426,6 +458,7 @@ export const Summary = metric( * The state contains quantile values plus the total count, minimum, maximum, * and sum. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -439,6 +472,7 @@ export type Summary = Schema.Schema.Type * Accepted metric kinds are counters, frequencies, gauges, histograms, and * summaries. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -451,6 +485,7 @@ export const Metric = Schema.Union([Counter, Frequency, Gauge, Histogram, Summar * * The union covers counters, frequencies, gauges, histograms, and summaries. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -460,6 +495,7 @@ export type Metric = Schema.Schema.Type * Schema for a devtools protocol message containing the current metric * snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -471,6 +507,7 @@ export const MetricsSnapshot = Schema.Struct({ /** * Type of a devtools protocol message containing the current metric snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -483,6 +520,7 @@ export type MetricsSnapshot = Schema.Schema.Type * * Requests include heartbeat pings, spans, span events, and metric snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -495,6 +533,7 @@ export const Request = Schema.Union([Ping, Span, SpanEvent, MetricsSnapshot]) * * Requests include heartbeat pings, spans, span events, and metric snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -503,6 +542,7 @@ export type Request = Schema.Schema.Type /** * Namespace containing helper types for devtools protocol requests. * + * @stability unstable * @since 4.0.0 */ export declare namespace Request { @@ -514,6 +554,7 @@ export declare namespace Request { * `DevToolsServer` handles `Ping` internally and exposes only these requests * to client handlers. * + * @stability unstable * @since 4.0.0 */ export type WithoutPing = Exclude @@ -526,6 +567,7 @@ export declare namespace Request { * * Responses include heartbeat pongs and requests for metric snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -538,6 +580,7 @@ export const Response = Schema.Union([Pong, MetricsRequest]) * * Responses include heartbeat pongs and requests for metric snapshots. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -546,6 +589,7 @@ export type Response = Schema.Schema.Type /** * Namespace containing helper types for devtools protocol responses. * + * @stability unstable * @since 4.0.0 */ export declare namespace Response { @@ -557,6 +601,7 @@ export declare namespace Response { * `DevToolsServer` sends `Pong` internally and accepts only these responses * from client handlers. * + * @stability unstable * @since 4.0.0 */ export type WithoutPong = Exclude diff --git a/repos/effect/packages/effect/src/unstable/devtools/DevToolsServer.ts b/repos/effect/packages/effect/src/devtools/DevToolsServer.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/devtools/DevToolsServer.ts rename to repos/effect/packages/effect/src/devtools/DevToolsServer.ts index 327dda33eb..b9845be882 100644 --- a/repos/effect/packages/effect/src/unstable/devtools/DevToolsServer.ts +++ b/repos/effect/packages/effect/src/devtools/DevToolsServer.ts @@ -7,15 +7,16 @@ * metrics itself; it gives handlers a typed surface for the telemetry described * by `DevToolsSchema`. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as Queue from "../../Queue.ts" -import * as Schema from "../../Schema.ts" -import * as Stream from "../../Stream.ts" +import * as Effect from "../Effect.ts" import * as Ndjson from "../encoding/Ndjson.ts" +import * as Queue from "../Queue.ts" +import * as Schema from "../Schema.ts" import * as Socket from "../socket/Socket.ts" import * as SocketServer from "../socket/SocketServer.ts" +import * as Stream from "../Stream.ts" import * as DevToolsSchema from "./DevToolsSchema.ts" const RequestSchema = Schema.toCodecJson(DevToolsSchema.Request) @@ -29,6 +30,7 @@ const ResponseSchema = Schema.toCodecJson(DevToolsSchema.Response) * It exposes a queue of non-ping requests received from the socket and a * `send` function for non-pong responses. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -46,6 +48,7 @@ export interface Client { * requests are answered with `Pong`, and all other requests are delivered * through the `Client` passed to the handler. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/devtools/index.ts b/repos/effect/packages/effect/src/devtools/index.ts new file mode 100644 index 0000000000..1fd8507385 --- /dev/null +++ b/repos/effect/packages/effect/src/devtools/index.ts @@ -0,0 +1,30 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DevTools from "./DevTools.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DevToolsClient from "./DevToolsClient.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DevToolsSchema from "./DevToolsSchema.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as DevToolsServer from "./DevToolsServer.ts" diff --git a/repos/effect/packages/effect/src/encoding/Base64.ts b/repos/effect/packages/effect/src/encoding/Base64.ts new file mode 100644 index 0000000000..b04b5be450 --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/Base64.ts @@ -0,0 +1,183 @@ +/** + * Base64 encoding and decoding helpers. + * + * @since 4.0.0 + */ +import * as Result from "../Result.ts" +import { EncodingError } from "./EncodingError.ts" + +/** + * Encodes the given value into a Base64 (RFC 4648) string. + * + * **When to use** + * + * Use to encode text or bytes as a standard padded Base64 string for storage or + * transport. + * + * **Details** + * + * String inputs are encoded as UTF-8 bytes before Base64 encoding. + * `Uint8Array` inputs are encoded directly. The output uses the standard + * RFC 4648 alphabet with `=` padding. + * + * **Example** (Encoding Base64 strings and bytes) + * + * ```ts import.meta.vitest + * import { Base64 } from "effect/encoding" + * + * Base64.encode("hello") // => "aGVsbG8=" + * + * const bytes = new Uint8Array([72, 101, 108, 108, 111]) + * Base64.encode(bytes) // => "SGVsbG8=" + * ``` + * + * @see {@link decode} for decoding standard Base64 to bytes + * @see {@link decodeString} for decoding standard Base64 to UTF-8 text + * + * @category encoding + * @since 4.0.0 + */ +export const encode: (input: Uint8Array | string) => string = (input) => + encodeBytes(typeof input === "string" ? encoder.encode(input) : input) + +/** + * Decodes a Base64 (RFC 4648) string into bytes safely. + * + * **When to use** + * + * Use to decode a standard padded Base64 string into bytes without throwing on + * invalid input. + * + * **Details** + * + * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input is not valid Base64. + * + * **Example** (Decoding Base64 bytes) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Base64 } from "effect/encoding" + * + * Base64.decode("SGVsbG8=") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decode = (str: string): Result.Result => { + const stripped = stripCrlf(str) + const length = stripped.length + if (length % 4 !== 0) { + return Result.fail( + new EncodingError({ + kind: "Decode", + module: "Base64", + input: stripped, + message: `Length must be a multiple of 4, but is ${length}` + }) + ) + } + + const index = stripped.indexOf("=") + if (index !== -1 && ((index < length - 2) || (index === length - 2 && stripped[length - 1] !== "="))) { + return Result.fail( + new EncodingError({ + kind: "Decode", + module: "Base64", + input: stripped, + message: "Found a '=' character, but it is not at the end" + }) + ) + } + + try { + const missingOctets = stripped.endsWith("==") ? 2 : stripped.endsWith("=") ? 1 : 0 + const result = new Uint8Array(3 * (length / 4) - missingOctets) + for (let i = 0, j = 0; i < length; i += 4, j += 3) { + const buffer = getCode(stripped.charCodeAt(i)) << 18 | + getCode(stripped.charCodeAt(i + 1)) << 12 | + getCode(stripped.charCodeAt(i + 2)) << 6 | + getCode(stripped.charCodeAt(i + 3)) + result[j] = buffer >> 16 + result[j + 1] = (buffer >> 8) & 0xff + result[j + 2] = buffer & 0xff + } + return Result.succeed(result) + } catch (cause) { + return Result.fail( + new EncodingError({ + kind: "Decode", + module: "Base64", + input: stripped, + message: cause instanceof Error ? cause.message : "Invalid input" + }) + ) + } +} + +/** + * Decodes a Base64 (RFC 4648) string into UTF-8 text safely. + * + * **When to use** + * + * Use to decode a standard padded Base64 string into UTF-8 text without + * throwing on invalid input. + * + * **Details** + * + * Returns `Result.succeed` with the decoded text when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input is not valid Base64. + * + * **Example** (Decoding Base64 strings) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Base64 } from "effect/encoding" + * + * Base64.decodeString("aGVsbG8=") // => Result.succeed("hello") + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decodeString = (str: string) => Result.map(decode(str), (_) => decoder.decode(_)) + +const encodeBytes = (bytes: Uint8Array): string => { + const length = bytes.length + let result = "" + let i: number + for (i = 2; i < length; i += 3) { + result += alphabet[bytes[i - 2] >> 2] + result += alphabet[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] + result += alphabet[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)] + result += alphabet[bytes[i] & 0x3f] + } + if (i === length + 1) { + result += alphabet[bytes[i - 2] >> 2] + result += alphabet[(bytes[i - 2] & 0x03) << 4] + result += "==" + } + if (i === length) { + result += alphabet[bytes[i - 2] >> 2] + result += alphabet[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] + result += alphabet[(bytes[i - 1] & 0x0f) << 2] + result += "=" + } + return result +} + +const encoder = new TextEncoder() +const decoder = new TextDecoder() +const stripCrlf = (str: string) => str.replace(/[\n\r]/g, "") +const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +const codes = new Uint8Array(123).fill(255) +for (let i = 0; i < alphabet.length; i++) codes[alphabet.charCodeAt(i)] = i +codes["=".charCodeAt(0)] = 0 + +const getCode = (charCode: number): number => { + if (charCode >= codes.length || codes[charCode] === 255) { + throw new TypeError(`Invalid character ${String.fromCharCode(charCode)}`) + } + return codes[charCode] +} diff --git a/repos/effect/packages/effect/src/encoding/Base64Url.ts b/repos/effect/packages/effect/src/encoding/Base64Url.ts new file mode 100644 index 0000000000..01fb7d4d39 --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/Base64Url.ts @@ -0,0 +1,124 @@ +/** + * URL-safe Base64 encoding and decoding helpers. + * + * @since 4.0.0 + */ +import * as Result from "../Result.ts" +import * as Base64 from "./Base64.ts" +import { EncodingError } from "./EncodingError.ts" + +/** + * Encodes the given value into an unpadded URL-safe Base64 string. + * + * **When to use** + * + * Use to encode text or bytes in contexts that require the URL-safe alphabet. + * + * **Details** + * + * String inputs are encoded as UTF-8 bytes before Base64Url encoding. + * `Uint8Array` inputs are encoded directly. The output removes `=` padding + * and replaces `+` with `-` and `/` with `_`. + * + * **Example** (Encoding URL-safe Base64) + * + * ```ts import.meta.vitest + * import { Base64Url } from "effect/encoding" + * + * Base64Url.encode("hello?") // => "aGVsbG8_" + * + * const bytes = new Uint8Array([72, 101, 108, 108, 111, 63]) + * Base64Url.encode(bytes) // => "SGVsbG8_" + * ``` + * + * @see {@link decode} for decoding URL-safe Base64 to bytes + * @see {@link decodeString} for decoding URL-safe Base64 to UTF-8 text + * + * @category encoding + * @since 4.0.0 + */ +export const encode: (input: Uint8Array | string) => string = (input) => + Base64.encode(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") + +/** + * Decodes a padded or unpadded URL-safe Base64 string into bytes safely. + * + * **When to use** + * + * Use to decode Base64Url text into bytes without throwing on invalid input. + * + * **Details** + * + * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input is not valid + * Base64Url. Both padded and unpadded forms are accepted when otherwise valid. + * + * **Example** (Decoding URL-safe Base64 bytes) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Base64Url } from "effect/encoding" + * + * Base64Url.decode("SGVsbG8_") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111, 63])) + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decode = (str: string): Result.Result => { + const stripped = str.replace(/[\n\r]/g, "") + const length = stripped.length + if (length % 4 === 1) { + return Result.fail( + new EncodingError({ + module: "Base64Url", + kind: "Decode", + input: stripped, + message: `Length should be a multiple of 4, but is ${length}` + }) + ) + } + if (!/^[-_A-Z0-9]*?={0,2}$/i.test(stripped)) { + return Result.fail( + new EncodingError({ + module: "Base64Url", + kind: "Decode", + input: stripped, + message: "Invalid input" + }) + ) + } + let sanitized = length % 4 === 2 ? `${stripped}==` : length % 4 === 3 ? `${stripped}=` : stripped + sanitized = sanitized.replace(/-/g, "+").replace(/_/g, "/") + return Base64.decode(sanitized) +} + +/** + * Decodes a padded or unpadded URL-safe Base64 string into UTF-8 text safely. + * + * **When to use** + * + * Use to decode Base64Url text into UTF-8 text without throwing on invalid + * input. + * + * **Details** + * + * Returns `Result.succeed` with the decoded text when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input is not valid + * Base64Url. + * + * **Example** (Decoding URL-safe Base64 strings) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Base64Url } from "effect/encoding" + * + * Base64Url.decodeString("aGVsbG8_") // => Result.succeed("hello?") + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decodeString = (str: string) => Result.map(decode(str), (_) => decoder.decode(_)) + +const decoder = new TextDecoder() diff --git a/repos/effect/packages/effect/src/encoding/EncodingError.ts b/repos/effect/packages/effect/src/encoding/EncodingError.ts new file mode 100644 index 0000000000..c3ae39fff3 --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/EncodingError.ts @@ -0,0 +1,98 @@ +/** + * Shared error type for encoding and decoding operations. + * + * @since 4.0.0 + */ +import * as Data from "../Data.ts" +import { hasProperty } from "../Predicate.ts" + +/** + * Type identifier stored on `EncodingError` values and used by + * `isEncodingError`. + * + * **When to use** + * + * Use when implementing low-level `EncodingError`-compatible values that need + * to carry the runtime marker. + * + * **Details** + * + * This marker is part of the runtime representation of `EncodingError`. Prefer + * `isEncodingError` when narrowing unknown values. + * + * @see {@link isEncodingError} for the public guard that checks this marker + * + * @category type IDs + * @since 4.0.0 + */ +export const EncodingErrorTypeId = "~effect/encoding/EncodingError" as const + +/** + * Literal type of the `EncodingErrorTypeId` marker. + * + * **When to use** + * + * Use to type the marker carried by `EncodingError` values. + * + * @category type IDs + * @since 4.0.0 + */ +export type EncodingErrorTypeId = typeof EncodingErrorTypeId + +/** + * Error returned when an encoding or decoding operation cannot process its input. + * + * **When to use** + * + * Use when you need to handle or inspect failures from encoding or decoding + * operations. + * + * **Details** + * + * The error records whether the failure happened during encoding or decoding, + * which encoding module reported it, the original input, and a human-readable + * message. + * + * @see {@link isEncodingError} for checking whether a value is an EncodingError + * + * @category errors + * @since 4.0.0 + */ +export class EncodingError extends Data.TaggedError("EncodingError")<{ + kind: "Decode" | "Encode" + module: string + input: unknown + message: string +}> { + /** + * Marks this value as an encoding or decoding error for runtime guards. + * + * **When to use** + * + * Use to identify `EncodingError` instances through `isEncodingError`. + * + * @since 4.0.0 + */ + readonly [EncodingErrorTypeId]: EncodingErrorTypeId = EncodingErrorTypeId +} + +/** + * Checks whether a value is an `EncodingError`. + * + * **When to use** + * + * Use to narrow an unknown value before handling it as an `EncodingError` from + * encoding or decoding code. + * + * **Details** + * + * Returns `true` when the value carries the `EncodingErrorTypeId` marker and + * narrows the value to `EncodingError`. + * + * @see {@link EncodingError} for the structured error produced by failed + * encoding and decoding operations + * + * @category guards + * @since 4.0.0 + */ +export const isEncodingError = (u: unknown): u is EncodingError => hasProperty(u, EncodingErrorTypeId) diff --git a/repos/effect/packages/effect/src/encoding/Hex.ts b/repos/effect/packages/effect/src/encoding/Hex.ts new file mode 100644 index 0000000000..1ac78464dd --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/Hex.ts @@ -0,0 +1,268 @@ +/** + * Hexadecimal encoding, decoding, and random value helpers. + * + * @since 4.0.0 + */ +import * as Result from "../Result.ts" +import { EncodingError } from "./EncodingError.ts" + +/** + * Encodes the given value into a hex `string`. + * + * **When to use** + * + * Use to encode text or bytes as lowercase hexadecimal text. + * + * **Example** (Encoding hex strings and bytes) + * + * ```ts import.meta.vitest + * import { Hex } from "effect/encoding" + * + * // Encode a string to hex + * Hex.encode("hello") // => "68656c6c6f" + * + * // Encode binary data to hex + * const bytes = new Uint8Array([72, 101, 108, 108, 111]) + * Hex.encode(bytes) // => "48656c6c6f" + * ``` + * + * @category encoding + * @since 4.0.0 + */ +export const encode: (input: Uint8Array | string) => string = (input) => + typeof input === "string" ? hexEncodeUint8Array(encoder.encode(input)) : hexEncodeUint8Array(input) + +/** + * Generates a random lowercase hexadecimal string, optimized for lengths that + * are multiples of 8. + * + * **Details** + * + * `length` is not validated. The function generates `length >>> 3` random + * 8-character words, so non-negative lengths below `2 ** 32` are rounded down + * to a multiple of 8 and other values follow JavaScript's unsigned 32-bit + * coercion rules. + * + * This function uses `Math.random()` and is not cryptographically secure. For + * security-sensitive values, use the `Crypto.Crypto` service's `randomBytes` + * method and encode the result with {@link encode}. + * + * @category encoding + * @since 4.0.0 + */ +export const random = (length: number): string => { + switch (length) { + case 16: + return random16() + case 32: + return random32() + default: { + let result = "" + for (let i = length >>> 3; i > 0; i--) { + result += random8() + } + return result + } + } +} + +const hexCharCodes = Uint8Array.from("0123456789abcdef", (c) => c.charCodeAt(0)) + +const randomWord = (): number => (Math.random() * 0x100000000) >>> 0 + +// Trace and span identifiers are the common lengths. A single +// String.fromCharCode call produces a flat string, which avoids rope +// flattening when the identifier is later serialized. +const random8 = (): string => { + const a = randomWord() + return String.fromCharCode( + hexCharCodes[a >>> 28], + hexCharCodes[(a >>> 24) & 15], + hexCharCodes[(a >>> 20) & 15], + hexCharCodes[(a >>> 16) & 15], + hexCharCodes[(a >>> 12) & 15], + hexCharCodes[(a >>> 8) & 15], + hexCharCodes[(a >>> 4) & 15], + hexCharCodes[a & 15] + ) +} + +const random16 = (): string => { + const a = randomWord() + const b = randomWord() + return String.fromCharCode( + hexCharCodes[a >>> 28], + hexCharCodes[(a >>> 24) & 15], + hexCharCodes[(a >>> 20) & 15], + hexCharCodes[(a >>> 16) & 15], + hexCharCodes[(a >>> 12) & 15], + hexCharCodes[(a >>> 8) & 15], + hexCharCodes[(a >>> 4) & 15], + hexCharCodes[a & 15], + hexCharCodes[b >>> 28], + hexCharCodes[(b >>> 24) & 15], + hexCharCodes[(b >>> 20) & 15], + hexCharCodes[(b >>> 16) & 15], + hexCharCodes[(b >>> 12) & 15], + hexCharCodes[(b >>> 8) & 15], + hexCharCodes[(b >>> 4) & 15], + hexCharCodes[b & 15] + ) +} + +const random32 = (): string => { + const a = randomWord() + const b = randomWord() + const c = randomWord() + const d = randomWord() + return String.fromCharCode( + hexCharCodes[a >>> 28], + hexCharCodes[(a >>> 24) & 15], + hexCharCodes[(a >>> 20) & 15], + hexCharCodes[(a >>> 16) & 15], + hexCharCodes[(a >>> 12) & 15], + hexCharCodes[(a >>> 8) & 15], + hexCharCodes[(a >>> 4) & 15], + hexCharCodes[a & 15], + hexCharCodes[b >>> 28], + hexCharCodes[(b >>> 24) & 15], + hexCharCodes[(b >>> 20) & 15], + hexCharCodes[(b >>> 16) & 15], + hexCharCodes[(b >>> 12) & 15], + hexCharCodes[(b >>> 8) & 15], + hexCharCodes[(b >>> 4) & 15], + hexCharCodes[b & 15], + hexCharCodes[c >>> 28], + hexCharCodes[(c >>> 24) & 15], + hexCharCodes[(c >>> 20) & 15], + hexCharCodes[(c >>> 16) & 15], + hexCharCodes[(c >>> 12) & 15], + hexCharCodes[(c >>> 8) & 15], + hexCharCodes[(c >>> 4) & 15], + hexCharCodes[c & 15], + hexCharCodes[d >>> 28], + hexCharCodes[(d >>> 24) & 15], + hexCharCodes[(d >>> 20) & 15], + hexCharCodes[(d >>> 16) & 15], + hexCharCodes[(d >>> 12) & 15], + hexCharCodes[(d >>> 8) & 15], + hexCharCodes[(d >>> 4) & 15], + hexCharCodes[d & 15] + ) +} + +/** + * Decodes a hexadecimal string into bytes safely. + * + * **When to use** + * + * Use to decode hexadecimal text into bytes without throwing on invalid input. + * + * **Details** + * + * Returns `Result.succeed` with a `Uint8Array` when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input has an odd length or + * contains invalid hex characters. + * + * **Example** (Decoding hex bytes) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Hex } from "effect/encoding" + * + * Hex.decode("48656c6c6f") // => Result.succeed(new Uint8Array([72, 101, 108, 108, 111])) + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decode = (str: string): Result.Result => { + const bytes = new TextEncoder().encode(str) + if (bytes.length % 2 !== 0) { + return Result.fail( + new EncodingError({ + module: "Hex", + kind: "Decode", + input: str, + message: `Length must be a multiple of 2, but is ${bytes.length}` + }) + ) + } + + try { + const length = bytes.length / 2 + const result = new Uint8Array(length) + for (let i = 0; i < length; i++) { + const a = fromHexChar(bytes[i * 2]) + const b = fromHexChar(bytes[i * 2 + 1]) + result[i] = (a << 4) | b + } + + return Result.succeed(result) + } catch (e) { + return Result.fail( + new EncodingError({ + module: "Hex", + kind: "Decode", + input: str, + message: e instanceof Error ? e.message : "Invalid input" + }) + ) + } +} + +/** + * Decodes a hexadecimal string into a UTF-8 string safely. + * + * **When to use** + * + * Use to decode hexadecimal text into UTF-8 text without throwing on invalid + * input. + * + * **Details** + * + * Returns `Result.succeed` with the decoded text when decoding succeeds, or + * `Result.fail` with an `EncodingError` when the input is not valid hex. + * + * **Example** (Decoding hex strings) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Hex } from "effect/encoding" + * + * Hex.decodeString("68656c6c6f") // => Result.succeed("hello") + * ``` + * + * @category decoding + * @since 4.0.0 + */ +export const decodeString = (str: string) => Result.map(decode(str), (_) => decoder.decode(_)) + +const encoder = new TextEncoder() +const decoder = new TextDecoder() + +const byteToHex: Array = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")) + +const hexEncodeUint8Array = (bytes: Uint8Array): string => { + let result = "" + for (let i = 0; i < bytes.length; i++) { + result += byteToHex[bytes[i]] + } + return result +} + +const fromHexChar = (byte: number) => { + if (48 <= byte && byte <= 57) { + return byte - 48 + } + + if (97 <= byte && byte <= 102) { + return byte - 97 + 10 + } + + if (65 <= byte && byte <= 70) { + return byte - 65 + 10 + } + + throw new TypeError("Invalid input") +} diff --git a/repos/effect/packages/effect/src/unstable/encoding/Ini.ts b/repos/effect/packages/effect/src/encoding/Ini.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/encoding/Ini.ts rename to repos/effect/packages/effect/src/encoding/Ini.ts index 99453e9920..235d35fc16 100644 --- a/repos/effect/packages/effect/src/unstable/encoding/Ini.ts +++ b/repos/effect/packages/effect/src/encoding/Ini.ts @@ -4,6 +4,7 @@ * This module contains the decoding surface used by Effect's CLI without * pulling in the complete `ini` package. * + * @stability unstable * @since 4.0.0 */ @@ -88,6 +89,7 @@ const unquote = (input: string | undefined): string => { * in `[]` become arrays, and the scalar values `true`, `false`, and `null` are * decoded. Other scalar values remain strings. * + * @stability unstable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/encoding/Ndjson.ts b/repos/effect/packages/effect/src/encoding/Ndjson.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/encoding/Ndjson.ts rename to repos/effect/packages/effect/src/encoding/Ndjson.ts index ce9e33b424..d692a5864e 100644 --- a/repos/effect/packages/effect/src/unstable/encoding/Ndjson.ts +++ b/repos/effect/packages/effect/src/encoding/Ndjson.ts @@ -5,15 +5,17 @@ * for byte streams, string streams, and schema-checked records, so streaming * code can read or write one JSON record at a time. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Channel from "../../Channel.ts" -import * as ChannelSchema from "../../ChannelSchema.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import { dual, identity } from "../../Function.ts" -import type * as Schema from "../../Schema.ts" +import * as Arr from "../Array.ts" +import * as Channel from "../Channel.ts" +import * as ChannelSchema from "../ChannelSchema.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import { dual, identity } from "../Function.ts" +import * as Predicate from "../Predicate.ts" +import type * as Schema from "../Schema.ts" const NdjsonErrorTypeId = "~effect/encoding/Ndjson/NdjsonError" @@ -27,6 +29,7 @@ const encoder = new TextEncoder() * The `kind` field identifies whether the failure happened while packing or * unpacking, and `cause` preserves the original error. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -37,6 +40,7 @@ export class NdjsonError extends Data.TaggedError("NdjsonError")<{ /** * Marks this value as an NDJSON encoding or decoding error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [NdjsonErrorTypeId] = NdjsonErrorTypeId @@ -44,6 +48,7 @@ export class NdjsonError extends Data.TaggedError("NdjsonError")<{ /** * Uses the failed NDJSON operation as the public message. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -51,6 +56,15 @@ export class NdjsonError extends Data.TaggedError("NdjsonError")<{ } } +/** + * Checks whether a value is an `NdjsonError`. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isNdjsonError = (u: unknown): u is NdjsonError => Predicate.hasProperty(u, NdjsonErrorTypeId) + /** * Creates a channel that encodes chunks of values as NDJSON strings. * @@ -59,6 +73,7 @@ export class NdjsonError extends Data.TaggedError("NdjsonError")<{ * Each input item is `JSON.stringify`-encoded, separated by newlines, and the * output chunk ends with a trailing newline. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -91,6 +106,7 @@ export const encodeString = (): Channel.Channel< /** * Creates a channel that encodes chunks of values as UTF-8 NDJSON bytes. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -111,6 +127,7 @@ export const encode = (): Channel.Channel< * Values are first encoded with the schema and then written as UTF-8 * newline-delimited JSON. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -135,6 +152,7 @@ export const encodeSchema = ( * Values are first encoded with the schema and then written as newline-delimited * JSON strings. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -168,6 +186,7 @@ export const encodeSchemaString = ( * Set `ignoreEmptyLines` to skip blank lines before calling `JSON.parse`; * otherwise blank lines are parsed and fail as invalid JSON. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -203,6 +222,7 @@ export const decodeString = (options?: { * Lines may span input chunks, and `ignoreEmptyLines` controls whether blank * lines are skipped before JSON parsing. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -227,6 +247,7 @@ export const decode = (options?: { * The channel decodes UTF-8 bytes, parses each NDJSON line, and then decodes * each parsed value with the schema. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -253,6 +274,7 @@ export const decodeSchema = ( * The channel parses each line as JSON and then decodes each parsed value with * the schema. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -279,6 +301,7 @@ export const decodeSchemaString = ( * Outgoing values are written as UTF-8 NDJSON bytes, and incoming bytes are * parsed as NDJSON values. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -361,6 +384,7 @@ export const duplex: { * Outgoing values are written as NDJSON strings, and incoming strings are parsed * as NDJSON values. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -444,6 +468,7 @@ export const duplexString: { * Values sent to the wrapped channel are encoded with `inputSchema`; bytes * received from it are parsed as NDJSON and decoded with `outputSchema`. * + * @stability unstable * @category combinators * @since 4.0.0 */ @@ -531,6 +556,7 @@ export const duplexSchema: { * Values sent to the wrapped channel are encoded with `inputSchema`; strings * received from it are parsed as NDJSON and decoded with `outputSchema`. * + * @stability unstable * @category combinators * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/encoding/SchemaBinary.ts b/repos/effect/packages/effect/src/encoding/SchemaBinary.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/encoding/SchemaBinary.ts rename to repos/effect/packages/effect/src/encoding/SchemaBinary.ts index 48d7a75f53..a8cf71fb8e 100644 --- a/repos/effect/packages/effect/src/unstable/encoding/SchemaBinary.ts +++ b/repos/effect/packages/effect/src/encoding/SchemaBinary.ts @@ -8,32 +8,33 @@ * frames; its row shapes are presence masks instead of field id lists. Encoded * results are arena-backed views; see {@link toCodec} for ownership details. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as BigDecimal from "../../BigDecimal.ts" -import * as Cause from "../../Cause.ts" -import * as Channel from "../../Channel.ts" -import * as Chunk from "../../Chunk.ts" -import * as DateTime from "../../DateTime.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import * as Exit from "../../Exit.ts" -import { dual, memoize } from "../../Function.ts" -import * as HashMap from "../../HashMap.ts" -import * as HashSet from "../../HashSet.ts" -import { assignProperty } from "../../internal/record.ts" -import * as InternalParserProtocol from "../../internal/schema/parser.ts" -import * as Option from "../../Option.ts" -import * as Predicate from "../../Predicate.ts" -import * as Pull from "../../Pull.ts" -import * as Redacted from "../../Redacted.ts" -import * as Result from "../../Result.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import * as SchemaIssue from "../../SchemaIssue.ts" -import * as SchemaParser from "../../SchemaParser.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import * as Arr from "../Array.ts" +import * as BigDecimal from "../BigDecimal.ts" +import * as Cause from "../Cause.ts" +import * as Channel from "../Channel.ts" +import * as Chunk from "../Chunk.ts" +import * as DateTime from "../DateTime.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import { dual, memoize } from "../Function.ts" +import * as HashMap from "../HashMap.ts" +import * as HashSet from "../HashSet.ts" +import { assignProperty } from "../internal/record.ts" +import * as InternalParserProtocol from "../internal/schema/parser.ts" +import * as Option from "../Option.ts" +import * as Predicate from "../Predicate.ts" +import * as Pull from "../Pull.ts" +import * as Redacted from "../Redacted.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import * as SchemaIssue from "../SchemaIssue.ts" +import * as SchemaParser from "../SchemaParser.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" /** * Selects the wire mode. @@ -44,6 +45,7 @@ import * as SchemaTransformation from "../../SchemaTransformation.ts" * uses positional layouts and an 8-byte layout hash for smaller frames, but * requires peers to use the same schema definition. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -51,6 +53,7 @@ export interface Options { /** * Uses a compact positional layout with a schema fingerprint. * + * @stability unstable * @since 4.0.0 */ readonly fingerprint?: boolean | undefined @@ -59,6 +62,7 @@ export interface Options { /** * The codec type returned by {@link toCodec}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -92,7 +96,7 @@ const directFingerprintCodecCache = new WeakMap> * * ```ts * import { Schema } from "effect" - * import { SchemaBinary } from "effect/unstable/encoding" + * import { SchemaBinary } from "effect/encoding" * * const Person = Schema.Struct({ name: Schema.String, age: Schema.Number }) * const codec = SchemaBinary.toCodec(Person) @@ -101,6 +105,7 @@ const directFingerprintCodecCache = new WeakMap> * const person = Schema.decodeUnknownSync(codec)(bytes) * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -258,6 +263,7 @@ function concatFrames(frames: ReadonlyArray>): Uint8Arra /** * A stateful frame parser for concatenated {@link toCodec} outputs. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -265,24 +271,28 @@ export interface Parser { /** * Feeds another chunk into the parser and returns any completed values. * + * @stability unstable * @since 4.0.0 */ feed(chunk: Uint8Array): Effect.Effect, Schema.SchemaError> /** * Feeds another chunk into the parser synchronously. * + * @stability unstable * @since 4.0.0 */ feedSync(chunk: Uint8Array): ReadonlyArray /** * Finishes parsing and fails if an incomplete frame remains. * + * @stability unstable * @since 4.0.0 */ end: Effect.Effect /** * Finishes parsing synchronously and throws for an incomplete frame. * + * @stability unstable * @since 4.0.0 */ endSync(): void @@ -296,6 +306,7 @@ export interface Parser { * Values completed before a failure remain observable. After a failure, the * parser rejects further calls. Use `maxFrameSize` to limit buffered frames. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -324,6 +335,7 @@ export function parser( /** * Options for the connection-scoped pair, {@link encoder} and {@link parser}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -339,6 +351,7 @@ export interface StreamOptions { * Both ends have to set it. A schema the binary layer does not fully * validate on its own cannot use it, and asking for it throws. * + * @stability unstable * @since 4.0.0 */ readonly dictionary?: boolean | undefined @@ -357,6 +370,7 @@ function requireDictionary(exact: boolean, options: StreamOptions | undefined): * dictionary that the matching {@link parser} rebuilds as it reads them, so the * two have to be created from the same schema and the same options. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -404,6 +418,7 @@ export function encoder( /** * The writer returned by {@link encoder}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -621,6 +636,7 @@ function makeParser( * `Schema.SchemaError`, matching {@link toCodec}. `maxFrameSize` only applies * to {@link decode} and is ignored here. * + * @stability unstable * @category channels * @since 4.0.0 */ @@ -678,6 +694,7 @@ export const encode = ( * framed value, which supports async transformations and decoding services. * Failures are `Schema.SchemaError`, matching {@link toCodec}. * + * @stability unstable * @category channels * @since 4.0.0 */ @@ -786,6 +803,7 @@ export const decode = ( * Values sent to the wrapped channel are encoded with `inputSchema` as binary * frames; bytes received from it are decoded with `outputSchema`. * + * @stability unstable * @category channels * @since 4.0.0 */ @@ -883,13 +901,14 @@ export const duplex: { * * ```ts * import { Schema } from "effect" - * import { SchemaBinary } from "effect/unstable/encoding" + * import { SchemaBinary } from "effect/encoding" * * const Person = Schema.Struct({ * id: Schema.String.pipe(SchemaBinary.fieldId(1)) * }) * ``` * + * @stability unstable * @category annotations * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/encoding/Sse.ts b/repos/effect/packages/effect/src/encoding/Sse.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/encoding/Sse.ts rename to repos/effect/packages/effect/src/encoding/Sse.ts index 7363c117cf..be2cf02f45 100644 --- a/repos/effect/packages/effect/src/unstable/encoding/Sse.ts +++ b/repos/effect/packages/effect/src/encoding/Sse.ts @@ -6,21 +6,23 @@ * channel helpers, and schema-based helpers for the `id`, `event`, and `data` * fields of each event. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import * as Arr from "../../Array.ts" -import * as Cause from "../../Cause.ts" -import * as Channel from "../../Channel.ts" -import * as ChannelSchema from "../../ChannelSchema.ts" -import * as Data from "../../Data.ts" -import * as Duration from "../../Duration.ts" -import * as Effect from "../../Effect.ts" -import { hasProperty } from "../../Predicate.ts" -import * as Pull from "../../Pull.ts" -import * as Result from "../../Result.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Arr from "../Array.ts" +import * as Cause from "../Cause.ts" +import * as Channel from "../Channel.ts" +import * as ChannelSchema from "../ChannelSchema.ts" +import * as Data from "../Data.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import { hasProperty } from "../Predicate.ts" +import * as Pull from "../Pull.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import type * as SchemaAST from "../SchemaAST.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" const SseErrorTypeId = "~effect/encoding/Sse/SseError" @@ -28,6 +30,7 @@ const SseErrorTypeId = "~effect/encoding/Sse/SseError" * Error reason raised when pending Server-Sent Events state exceeds the * configured maximum size. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -42,6 +45,7 @@ export class EventTooLarge extends Data.TaggedError("EventTooLarge")<{ /** * Union of Server-Sent Events decoding error reasons. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -50,6 +54,7 @@ export type SseErrorReason = EventTooLarge /** * Error raised when decoding a Server-Sent Events stream fails. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -59,6 +64,7 @@ export class SseError extends Data.TaggedError("SseError")<{ /** * Marks this value as an SSE decoding error. * + * @stability unstable * @since 4.0.0 */ readonly [SseErrorTypeId] = SseErrorTypeId @@ -66,6 +72,7 @@ export class SseError extends Data.TaggedError("SseError")<{ /** * Delegates the public message to the underlying SSE error reason. * + * @stability unstable * @since 4.0.0 */ override get message() { @@ -76,6 +83,7 @@ export class SseError extends Data.TaggedError("SseError")<{ /** * Options for decoding Server-Sent Events streams. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -96,6 +104,7 @@ const defaultMaxEventSize = 10 * 1024 * 1024 * SSE `retry` directives are emitted as `Retry` failures so callers can * reconnect with the requested delay. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -145,6 +154,7 @@ export const decode = (options?: DecodeOptions): Channel.Channel< /** * A constraint for schemas that can decode SSE events. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -166,9 +176,14 @@ export interface EventCodec extends * * **Details** * - * The schema receives the untagged event shape containing `id`, `event`, and - * string `data`. + * The schema receives `{ event, data, id? }`, with string `data`. Absent IDs are + * omitted, even with default options: use `Schema.optional(Schema.String)`, not + * `Schema.UndefinedOr(Schema.String)`, to accept events without an ID. * + * With `onExcessProperty: "error"`, declare `event` (default: `"message"`) and + * `id` if the stream carries IDs, including inherited IDs. + * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -178,7 +193,8 @@ export const decodeSchema = < Done >( schema: S, - options?: DecodeOptions + options?: DecodeOptions, + parseOptions?: SchemaAST.ParseOptions ): Channel.Channel< NonEmptyReadonlyArray, IE | Retry | SseError | Schema.SchemaError, @@ -190,9 +206,12 @@ export const decodeSchema = < > => Channel.pipeTo( decode(options), - ChannelSchema.decode(EventEncoded.pipe( - Schema.decodeTo(schema) - ))() + ChannelSchema.decode( + Event.pipe( + Schema.decodeTo(schema, transformEvent) + ), + parseOptions + )() ) /** @@ -203,6 +222,7 @@ export const decodeSchema = < * The output preserves the SSE `event` name and optional `id` while replacing * `data` with the decoded value. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -244,6 +264,7 @@ export const decodeDataSchema = ( * callback, and call `reset` to clear any buffered event state. `feed` returns * an `SseError` if the pending event exceeds `maxEventSize`. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -414,6 +435,7 @@ const BOM = "\uFEFF" * configured pending event size is exceeded. `reset` clears buffered parser * state. * + * @stability unstable * @category decoding * @since 4.0.0 */ @@ -430,6 +452,7 @@ export interface Parser { * If the upstream channel fails with `Retry`, the retry directive is written and * the encoder completes. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -464,6 +487,7 @@ export const encode = (): Channel.Channel< * Values are schema-encoded to the untagged SSE event shape, transformed to * `Event`, and then written as Server-Sent Events text. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -490,6 +514,7 @@ export const encodeSchema = < * Encoder capable of rendering an `Event` or `Retry` value as Server-Sent * Events text. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -500,6 +525,7 @@ export interface Encoder { /** * Tagged model for a Server-Sent Events message containing the event name, optional event ID, and string data payload. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -513,6 +539,7 @@ export interface Event { /** * Schema for the untagged Server-Sent Events payload shape containing an optional `id`, `event`, and string `data` fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -529,6 +556,7 @@ export const EventEncoded: Schema.Struct<{ /** * Schema for the tagged Server-Sent Events message model that adds `_tag: "Event"` to the event name, optional event ID, and string data payload. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -548,6 +576,7 @@ export const Event: Schema.Struct<{ * Schema for transforming untagged SSE event payloads into tagged `Event` * models. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -561,7 +590,10 @@ export const transformEvent = SchemaTransformation.transform<{ readonly event: string readonly data: string }>({ - decode: (event) => event, + decode: (event) => + event.id === undefined + ? { event: event.event, data: event.data } + : { id: event.id, event: event.event, data: event.data }, encode: (event) => ({ _tag: "Event", id: event.id, @@ -573,6 +605,7 @@ export const transformEvent = SchemaTransformation.transform<{ /** * Untagged Server-Sent Events payload shape containing the event name, optional event ID, and string data payload. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -592,6 +625,7 @@ const RetryTypeId = "~effect/encoding/Sse/Retry" as const * Decoders surface this value as a failure to request reconnection after * `duration`; encoders serialize an upstream `Retry` failure as a `retry:` line. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -602,12 +636,14 @@ export class Retry extends Data.TaggedClass("Retry")<{ /** * Marks this value as an SSE retry directive for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [RetryTypeId]: typeof RetryTypeId = RetryTypeId /** * Returns `true` when the value is an SSE retry directive. * + * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is Retry { @@ -616,6 +652,7 @@ export class Retry extends Data.TaggedClass("Retry")<{ /** * Separates SSE retry directives from regular event values. * + * @stability unstable * @since 4.0.0 */ static filter(u: A): Result.Result> { @@ -627,6 +664,7 @@ export class Retry extends Data.TaggedClass("Retry")<{ * Union of SSE values that can be rendered by an `Encoder`: regular events and * retry directives. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -640,6 +678,7 @@ export type AnyEvent = Event | Retry * It renders `Event` values as `id`, `event`, and `data` lines and renders * `Retry` values as `retry:` directives. * + * @stability unstable * @category encoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/encoding/Toml.ts b/repos/effect/packages/effect/src/encoding/Toml.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/encoding/Toml.ts rename to repos/effect/packages/effect/src/encoding/Toml.ts index 04395e2e25..eae5c924c6 100644 --- a/repos/effect/packages/effect/src/unstable/encoding/Toml.ts +++ b/repos/effect/packages/effect/src/encoding/Toml.ts @@ -4,6 +4,7 @@ * The implementation covers the TOML values and table forms used by Effect's * configuration-file CLI primitive while avoiding a generated parser runtime. * + * @stability unstable * @since 4.0.0 */ @@ -500,6 +501,7 @@ class TomlParser { * Offset date-times are represented by `Date`, matching the package this * parser replaces. Local dates and times remain strings. * + * @stability unstable * @category decoding * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/encoding/Yaml.ts b/repos/effect/packages/effect/src/encoding/Yaml.ts new file mode 100644 index 0000000000..ac8c23ab93 --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/Yaml.ts @@ -0,0 +1,613 @@ +/** + * Parses YAML configuration files. + * + * Supports YAML 1.2 block and flow collections, indentless sequence values, + * multiline plain and quoted scalars, block scalars, anchors, and aliases. + * + * Unsupported: compact nested block sequences (`- - value` on one line), + * multiline flow collections, explicit complex keys, tag/directive processing, + * recursive aliases, and document streams. Mapping keys are JavaScript strings. + * + * @stability unstable + * @since 4.0.0 + */ + +/* + * The behavior is based on `yaml` 2.9.0. + * + * Copyright Eemeli Aro + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +type YamlRecord = Record + +type Line = { + readonly raw: string + readonly text: string + readonly indent: number + readonly number: number +} + +const hasOwn = Object.prototype.hasOwnProperty + +const setProperty = (record: YamlRecord, key: string, value: unknown): void => { + Object.defineProperty(record, key, { + configurable: true, + enumerable: true, + writable: true, + value + }) +} + +const closingQuote = (input: string, quote: string, start: number): number => { + for (let index = start; index < input.length; index++) { + const character = input[index] + if (quote === "\"" && character === "\\") { + index++ + } else if (character === quote) { + if (quote === "'" && input[index + 1] === quote) index++ + else return index + } + } + return -1 +} + +const findIndicator = (input: string, indicator: ":" | "#"): number => { + let depth = 0 + let tokenStart = true + for (let index = 0; index < input.length; index++) { + const character = input[index] + if (tokenStart && (character === "'" || character === "\"")) { + index = closingQuote(input, character, index + 1) + if (index === -1) return -1 + tokenStart = false + } else if (tokenStart && character === "&") { + while (index + 1 < input.length && !/[\s,[\]{}]/.test(input[index + 1])) index++ + } else if (character === "#" && (index === 0 || /\s/.test(input[index - 1]))) { + return indicator === "#" ? index : -1 + } else if (tokenStart && (character === "[" || character === "{")) { + depth++ + } else if (depth > 0 && (character === "]" || character === "}")) { + depth-- + tokenStart = false + } else if (depth > 0 && character === ",") { + tokenStart = true + } else if (character === ":" && (depth > 0 || input[index + 1] === undefined || /\s/.test(input[index + 1]))) { + if (depth === 0 && indicator === ":") return index + tokenStart = true + } else if (tokenStart && character === "-" && /\s/.test(input[index + 1] ?? "")) { + continue + } else if (!/\s/.test(character)) { + tokenStart = false + } + } + return -1 +} + +const stripComment = (input: string): string => { + const index = findIndicator(input, "#") + return index === -1 ? input : input.slice(0, index).trimEnd() +} + +const mappingSeparator = (input: string): number => findIndicator(input, ":") + +const isSequenceEntry = (text: string): boolean => text === "-" || text.startsWith("- ") + +const foldLineBreak = (blankLines: number): string => blankLines === 0 ? " " : "\n".repeat(blankLines) + +const endsWithEscape = (text: string): boolean => /\\*$/.exec(text)![0].length % 2 === 1 + +// Escaped trailing spaces are content, not folding whitespace. +const trimFoldedEnd = (line: string, quote: string): string => { + const trailing = /[ \t]+$/.exec(line) + if (trailing === null) return line + const escaped = quote === "\"" && endsWithEscape(line.slice(0, trailing.index)) + return line.slice(0, trailing.index + (escaped ? 1 : 0)) +} + +const parseDoubleQuoted = (input: string): string => { + if (!input.endsWith("\"") || input.length < 2) { + throw new SyntaxError("Unterminated double-quoted YAML string") + } + let output = "" + for (let index = 1; index < input.length - 1; index++) { + const character = input[index] + if (character !== "\\") { + output += character + continue + } + const escape = input[++index] + const escapes: Record = { + "0": "\0", + a: "\x07", + b: "\b", + t: "\t", + n: "\n", + v: "\v", + f: "\f", + r: "\r", + e: "\x1b", + " ": " ", + "\"": "\"", + "/": "/", + "\\": "\\", + N: "\u0085", + _: "\u00a0", + L: "\u2028", + P: "\u2029" + } + if (hasOwn.call(escapes, escape)) { + output += escapes[escape] + continue + } + if (escape === "x" || escape === "u" || escape === "U") { + const length = escape === "x" ? 2 : escape === "u" ? 4 : 8 + const hex = input.slice(index + 1, index + 1 + length) + if (!new RegExp(`^[0-9A-Fa-f]{${length}}$`).test(hex)) { + throw new SyntaxError("Invalid unicode escape in YAML string") + } + output += String.fromCodePoint(Number.parseInt(hex, 16)) + index += length + continue + } + throw new SyntaxError(`Invalid YAML escape '\\${escape}'`) + } + return output +} + +const parseScalar = (input: string): unknown => { + const value = input.trim() + if (value.length === 0) return null + if (value.startsWith("\"") && value.endsWith("\"")) { + return parseDoubleQuoted(value) + } + if (value.startsWith("'") && value.endsWith("'")) { + return value.slice(1, -1).replace(/''/g, "'") + } + if (/^(?:null|~)$/i.test(value)) return null + if (/^true$/i.test(value)) return true + if (/^false$/i.test(value)) return false + if (/^[+-]?\.inf$/i.test(value)) return value[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY + if (/^\.nan$/i.test(value)) return Number.NaN + + const normalized = value.replace(/_/g, "") + if (/^[+-]?0x[0-9a-f]+$/i.test(normalized)) { + const sign = normalized[0] === "-" ? -1 : 1 + return sign * Number.parseInt(normalized.replace(/^[+-]?0x/i, ""), 16) + } + if (/^[+-]?0o[0-7]+$/i.test(normalized)) { + const sign = normalized[0] === "-" ? -1 : 1 + return sign * Number.parseInt(normalized.replace(/^[+-]?0o/i, ""), 8) + } + if ( + /^[+-]?(?:0|[1-9]\d*)$/.test(normalized) || + /^[+-]?(?:(?:0|[1-9]\d*)?\.\d+|(?:0|[1-9]\d*)\.?\d*[eE][+-]?\d+)$/.test(normalized) + ) { + return Number(normalized) + } + return value +} + +const parseKey = (input: string): string => { + const value = parseScalar(input) + return value === null || value === undefined ? "" : String(value) +} + +class FlowParser { + private readonly input: string + private readonly anchors: ReadonlyMap + private index = 0 + + constructor(input: string, anchors: ReadonlyMap) { + this.input = input + this.anchors = anchors + } + + parse(): unknown { + const value = this.parseValue() + this.skipWhitespace() + if (this.index !== this.input.length) { + this.fail("Unexpected flow collection content") + } + return value + } + + private parseValue(): unknown { + this.skipWhitespace() + const character = this.peek() + if (character === "[") return this.parseSequence() + if (character === "{") return this.parseMapping() + if (character === "\"" || character === "'") return parseScalar(this.readQuoted(character)) + if (character === "*") { + this.index++ + const name = this.readUntil(/[,\]}\s]/) + if (!this.anchors.has(name)) this.fail(`Unknown alias '*${name}'`) + return this.anchors.get(name) + } + return parseScalar(this.readUntil(/[,\]}]/).trim()) + } + + private parseSequence(): Array { + this.expect("[") + const output: Array = [] + this.skipWhitespace() + if (this.peek() === "]") { + this.index++ + return output + } + while (true) { + output.push(this.parseValue()) + this.skipWhitespace() + if (this.peek() === "]") { + this.index++ + return output + } + this.expect(",") + this.skipWhitespace() + if (this.peek() === "]") { + this.index++ + return output + } + } + } + + private parseMapping(): YamlRecord { + this.expect("{") + const output: YamlRecord = {} + this.skipWhitespace() + if (this.peek() === "}") { + this.index++ + return output + } + while (true) { + const key = this.parseKey() + this.skipWhitespace() + this.expect(":") + if (hasOwn.call(output, key)) this.fail(`Duplicate key '${key}'`) + setProperty(output, key, this.parseValue()) + this.skipWhitespace() + if (this.peek() === "}") { + this.index++ + return output + } + this.expect(",") + this.skipWhitespace() + if (this.peek() === "}") { + this.index++ + return output + } + } + } + + private parseKey(): string { + this.skipWhitespace() + const character = this.peek() + if (character === "\"" || character === "'") { + return parseKey(this.readQuoted(character)) + } + return this.readUntil(/:/).trim() + } + + private readQuoted(quote: string): string { + const start = this.index + const end = closingQuote(this.input, quote, start + 1) + if (end === -1) this.fail("Unterminated quoted scalar") + this.index = end + 1 + return this.input.slice(start, this.index) + } + + private readUntil(stop: RegExp): string { + const start = this.index + while (this.index < this.input.length && !stop.test(this.peek())) this.index++ + return this.input.slice(start, this.index) + } + + private skipWhitespace(): void { + while (/\s/.test(this.peek())) this.index++ + } + + private expect(character: string): void { + if (this.peek() !== character) this.fail(`Expected '${character}'`) + this.index++ + } + + private peek(): string { + return this.input[this.index] ?? "" + } + + private fail(message: string): never { + throw new SyntaxError(`${message} at flow offset ${this.index}`) + } +} + +class YamlParser { + private readonly lines: ReadonlyArray + private index = 0 + private documentStarted = false + private readonly anchors = new Map() + + constructor(lines: ReadonlyArray) { + this.lines = lines + } + + parse(): unknown { + this.skipIgnored() + if (this.index >= this.lines.length) return null + this.documentStarted = true + const value = this.parseNode(this.lines[this.index].indent) + this.skipIgnored() + if (this.index < this.lines.length) { + this.fail(this.lines[this.index], "Unexpected content") + } + return value + } + + private parseNode(indent: number, parentIndent = indent - 1): unknown { + this.skipIgnored() + const line = this.lines[this.index] + if (line === undefined) return null + if (line.indent !== indent) this.fail(line, `Expected indentation of ${indent} spaces`) + if (isSequenceEntry(line.text)) return this.parseSequence(indent) + if (mappingSeparator(line.text) !== -1) return this.parseMapping(indent) + this.index++ + return this.parseNodeValue(line.text, parentIndent) + } + + private parseMapping(indent: number, output: YamlRecord = {}): YamlRecord { + while (true) { + this.skipIgnored() + const line = this.lines[this.index] + if (line === undefined || line.indent < indent) return output + if (line.indent > indent) this.fail(line, `Unexpected indentation of ${line.indent} spaces`) + const separator = mappingSeparator(line.text) + if (separator === -1) return output + this.index++ + this.parseMappingEntry(output, line.text, separator, indent, line) + } + } + + private parseMappingEntry( + output: YamlRecord, + text: string, + separator: number, + indent: number, + line: Line + ): void { + const key = parseKey(text.slice(0, separator).trim()) + const rawValue = text.slice(separator + 1).trimStart() + const value = this.parseNodeValue(rawValue, indent, true) + if (hasOwn.call(output, key)) this.fail(line, `Duplicate key '${key}'`) + setProperty(output, key, value) + } + + private parseSequence(indent: number): Array { + const output: Array = [] + while (true) { + this.skipIgnored() + const line = this.lines[this.index] + if (line === undefined || line.indent < indent) return output + if (line.indent > indent) this.fail(line, `Unexpected indentation of ${line.indent} spaces`) + if (!isSequenceEntry(line.text)) return output + this.index++ + const item = line.text.slice(1).trimStart() + if (isSequenceEntry(item)) this.fail(line, "Compact nested block sequences are not supported") + const separator = mappingSeparator(item) + if (separator === -1) { + output.push(this.parseNodeValue(item, indent)) + } else { + const mapping: YamlRecord = {} + this.parseMappingEntry(mapping, item, separator, indent + 2, line) + output.push(this.parseMapping(indent + 2, mapping)) + } + } + } + + private parseNodeValue(rawValue: string, parentIndent: number, allowIndentless = false): unknown { + const anchor = /^&([^\s,[\]{}]+)(?:\s+(.*))?$/.exec(rawValue) + if (anchor === null) return this.parseValue(rawValue, parentIndent, allowIndentless) + const value = this.parseValue(anchor[2] ?? "", parentIndent, allowIndentless) + this.anchors.set(anchor[1], value) + return value + } + + private parseValue(value: string, parentIndent: number, allowIndentless: boolean): unknown { + const text = value.trimEnd() + if (text.length === 0) { + this.skipIgnored() + const next = this.lines[this.index] + if (next === undefined) return null + if (next.indent > parentIndent) return this.parseNode(next.indent, parentIndent) + if (allowIndentless && next.indent === parentIndent && isSequenceEntry(next.text)) { + return this.parseSequence(parentIndent) + } + return null + } + if (/^[|>](?:[1-9]?[+-]?|[+-]?[1-9]?)$/.test(text)) return this.parseBlockScalar(text, parentIndent) + // Keep escaped trailing whitespace in quoted values. + if (value.startsWith("\"") || value.startsWith("'")) return this.parseQuotedValue(value, parentIndent) + if (text.startsWith("*")) { + const name = text.slice(1).trim() + if (!this.anchors.has(name)) throw new SyntaxError(`Unknown YAML alias '*${name}'`) + return this.anchors.get(name) + } + if (text.startsWith("[") || text.startsWith("{")) return new FlowParser(text, this.anchors).parse() + return this.parsePlainValue(text, parentIndent) + } + + private parsePlainValue(value: string, parentIndent: number): unknown { + const first = this.lines[this.index - 1] + if (/:(?:\s|$)/.test(value)) this.fail(first, "Invalid colon in plain scalar") + let output = value + let terminated = findIndicator(first.raw, "#") !== -1 + while (!terminated) { + const blankLines = this.skipBlankLines() + const next = this.lines[this.index] + if (next === undefined || next.indent <= parentIndent) break + // Quotes in plain continuations are literal. + const content = next.raw.slice(next.indent) + const comment = content.search(/(?:^|[ \t])#/) + const text = (comment === -1 ? content : content.slice(0, comment)).trimEnd() + if (text.length === 0 || (next.indent === 0 && /^(?:---|\.\.\.)(?:\s|$)/.test(text))) break + if (/:(?:\s|$)/.test(text)) this.fail(next, "Invalid colon in plain scalar") + output += foldLineBreak(blankLines) + text + this.index++ + terminated = comment !== -1 + } + return parseScalar(output) + } + + private parseQuotedValue(value: string, parentIndent: number): unknown { + const quote = value[0] + let output = quote + let line = value.slice(1) + while (true) { + const end = closingQuote(line, quote, 0) + if (end !== -1) { + const rest = line.slice(end + 1) + if (rest.trim().length > 0 && !/^[ \t]+#/.test(rest)) { + throw new SyntaxError("Unexpected content after quoted YAML scalar") + } + return parseScalar(output + line.slice(0, end + 1)) + } + if (quote === "\"" && endsWithEscape(line)) { + output += line.slice(0, -1) + } else { + output += trimFoldedEnd(line, quote) + foldLineBreak(this.skipBlankLines()) + } + const next = this.lines[this.index++] + if (next === undefined) throw new SyntaxError("Unterminated quoted YAML scalar") + if (next.raw.trim().length > 0 && next.indent <= parentIndent) { + this.fail(next, "Invalid quoted scalar indentation") + } + line = next.raw.trimStart() + } + } + + private parseBlockScalar(indicator: string, parentIndent: number): string { + const style = indicator[0] + const chomp = indicator.includes("-") ? "strip" : indicator.includes("+") ? "keep" : "clip" + const explicitIndent = Number.parseInt(indicator.replace(/[^1-9]/g, ""), 10) + const start = this.index + let end = start + let contentIndent = Number.isNaN(explicitIndent) ? Number.POSITIVE_INFINITY : parentIndent + explicitIndent + + while (end < this.lines.length) { + const line = this.lines[end] + if (line.raw.trim().length !== 0 && line.indent <= parentIndent) break + if (line.raw.trim().length !== 0) contentIndent = Math.min(contentIndent, line.indent) + end++ + } + if (contentIndent === Number.POSITIVE_INFINITY) contentIndent = parentIndent + 1 + + const content: Array = [] + for (let index = start; index < end; index++) { + const line = this.lines[index] + if (line.raw.trim().length === 0) { + content.push("") + } else if (line.indent < contentIndent) { + this.fail(line, `Expected block scalar indentation of ${contentIndent} spaces`) + } else { + content.push(line.raw.slice(contentIndent)) + } + } + this.index = end + + let output = "" + if (style === "|") { + output = content.join("\n") + } else { + let blankLines = 0 + let previousMoreIndented: boolean | undefined + for (const line of content) { + if (line.length === 0) { + blankLines++ + } else { + const moreIndented = line.startsWith(" ") + const hardBreak = moreIndented || previousMoreIndented === true + if (previousMoreIndented === undefined) output += "\n".repeat(blankLines) + else if (blankLines === 0) output += hardBreak ? "\n" : " " + else output += "\n".repeat(blankLines + (hardBreak ? 1 : 0)) + output += line + blankLines = 0 + previousMoreIndented = moreIndented + } + } + output += "\n".repeat(blankLines) + } + if (chomp === "keep") return output.endsWith("\n") ? output : `${output}\n` + output = output.replace(/\n+$/, "") + return chomp === "strip" ? output : `${output}\n` + } + + private skipBlankLines(): number { + const start = this.index + while (this.index < this.lines.length && this.lines[this.index].raw.trim().length === 0) this.index++ + return this.index - start + } + + private skipIgnored(): void { + while (this.index < this.lines.length) { + const line = this.lines[this.index] + const text = line.text.trimEnd() + if (text.length === 0 || text.startsWith("%")) { + this.index++ + } else if (line.indent === 0 && text === "---") { + if (this.documentStarted) this.fail(line, "Multiple YAML documents are not supported") + this.documentStarted = true + this.index++ + } else if (line.indent === 0 && text === "...") { + this.index++ + while (this.index < this.lines.length && this.lines[this.index].text.trimEnd().length === 0) this.index++ + if (this.index < this.lines.length) { + this.fail(this.lines[this.index], "Multiple YAML documents are not supported") + } + } else { + return + } + } + } + + private fail(line: Line, message: string): never { + throw new SyntaxError(`${message} at line ${line.number}`) + } +} + +/** + * Parses one YAML document into JavaScript values. + * + * **Details** + * + * The core YAML 1.2 scalar schema is used, so booleans, nulls, and numbers are + * decoded while date-like values remain strings. + * + * @stability unstable + * @category decoding + * @since 4.0.0 + */ +export const parse = (input: string): unknown => { + const source = input.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n") + const lines = source.split("\n").map((raw, index): Line => { + const indentation = /^( *)/.exec(raw)![1].length + if (raw.slice(0, indentation + 1).includes("\t")) { + throw new SyntaxError(`Tabs cannot be used for YAML indentation at line ${index + 1}`) + } + return { + raw, + text: stripComment(raw.slice(indentation)), + indent: indentation, + number: index + 1 + } + }) + return new YamlParser(lines).parse() +} diff --git a/repos/effect/packages/effect/src/encoding/index.ts b/repos/effect/packages/effect/src/encoding/index.ts new file mode 100644 index 0000000000..6b19d09e51 --- /dev/null +++ b/repos/effect/packages/effect/src/encoding/index.ts @@ -0,0 +1,62 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @since 4.0.0 + */ +export * as Base64 from "./Base64.ts" + +/** + * @since 4.0.0 + */ +export * as Base64Url from "./Base64Url.ts" + +/** + * @since 4.0.0 + */ +export * as EncodingError from "./EncodingError.ts" + +/** + * @since 4.0.0 + */ +export * as Hex from "./Hex.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Ini from "./Ini.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Ndjson from "./Ndjson.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as SchemaBinary from "./SchemaBinary.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Sse from "./Sse.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Toml from "./Toml.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as Yaml from "./Yaml.ts" diff --git a/repos/effect/packages/effect/src/unstable/eventlog/Event.ts b/repos/effect/packages/effect/src/eventlog/Event.ts similarity index 92% rename from repos/effect/packages/effect/src/unstable/eventlog/Event.ts rename to repos/effect/packages/effect/src/eventlog/Event.ts index 92b876fcce..301f5990d8 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/Event.ts +++ b/repos/effect/packages/effect/src/eventlog/Event.ts @@ -8,16 +8,18 @@ * to derive the SchemaBinary encoding for journal entries and remote * replication. * + * @stability unstable * @since 4.0.0 */ -import { pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" import * as SchemaBinary from "../encoding/SchemaBinary.ts" +import { pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" /** * Unique type identifier used to mark event log event definitions. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -26,6 +28,7 @@ export type TypeId = "~effect/eventlog/Event" /** * Runtime type identifier used to mark event log event definitions. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -34,6 +37,7 @@ export const TypeId: TypeId = "~effect/eventlog/Event" /** * Returns `true` when a value is an event log event definition. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -47,6 +51,7 @@ export const isEvent = (u: unknown): u is Event => Predicate * An event definition contains its tag, primary-key function, payload schema, * SchemaBinary payload codec, success schema, and error schema. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -73,6 +78,7 @@ export interface Event< * `ToService` derives this service from an `Event` so handler layers can expose * which events they implement. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -89,6 +95,7 @@ export interface EventHandler { * It preserves the runtime tag, primary-key function, payload schema, success * schema, and error schema without retaining the original type parameters. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -106,6 +113,7 @@ export interface Any { * Type-erased event definition with its runtime properties available * structurally. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -114,6 +122,7 @@ export interface AnyWithProps extends Any {} /** * Derives the handler service marker for an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -128,6 +137,7 @@ export type ToService = A extends Event< /** * Extracts the tag string from an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -142,6 +152,7 @@ export type Tag = A extends Event< /** * Extracts the error schema from an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -156,6 +167,7 @@ export type ErrorSchema = A extends Event< /** * Decoded error value type for an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -165,6 +177,7 @@ export type Error = Schema.Schema.Type> * Returns an event definition type whose error schema also includes the provided * error schema. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -179,6 +192,7 @@ export type AddError = A extends Event< /** * Extracts the payload schema from an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -193,6 +207,7 @@ export type PayloadSchema = A extends Event< /** * Extracts the payload schema for the event in a union with the specified tag. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -207,6 +222,7 @@ export type PayloadSchemaWithTag = A extends /** * Decoded payload value type for an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -220,6 +236,7 @@ export type Payload = Schema.Schema.Type> * The result contains `_tag` set to the event tag and `payload` set to the * decoded payload value. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -237,6 +254,7 @@ export type TaggedPayload = A extends Event< /** * Extracts the success schema from an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -251,6 +269,7 @@ export type SuccessSchema = A extends Event< /** * Decoded success value type for an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -264,6 +283,7 @@ export type Success = Schema.Schema.Type> * This includes payload encoding services plus success and error decoding * services. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -286,6 +306,7 @@ export type ServicesClient = A extends Event< * This includes payload decoding services plus success and error encoding * services. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -304,6 +325,7 @@ export type ServicesServer = A extends Event< * All schema services required to encode and decode the payload, success, and * error schemas for an event definition. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -324,6 +346,7 @@ export type Services = A extends Event< /** * Extracts the event definition with the specified tag from an event union. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -332,6 +355,7 @@ export type WithTag = Extract = Exclude = Payload = Success = Error +/** + * @stability unstable + */ export function make(options: { readonly tag: string readonly primaryKey: (payload: Schema.Schema.Type) => string @@ -429,6 +461,7 @@ export function make(options: { * The returned event keeps the same tag, primary key, payload, and success schema * while replacing the error schema with a union of the existing and new errors. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -436,6 +469,9 @@ export function addError( event: A, error: Error2 ): AddError +/** + * @stability unstable + */ export function addError(event: Any, error: Schema.Top): Any { return make({ tag: event.tag, diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventGroup.ts b/repos/effect/packages/effect/src/eventlog/EventGroup.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/eventlog/EventGroup.ts rename to repos/effect/packages/effect/src/eventlog/EventGroup.ts index d5c475a36c..326b1b8998 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventGroup.ts +++ b/repos/effect/packages/effect/src/eventlog/EventGroup.ts @@ -7,17 +7,19 @@ * `EventLog.group`. The group only describes events; it does not write or run * them. * + * @stability unstable * @since 4.0.0 */ -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as Record from "../../Record.ts" -import type * as Schema from "../../Schema.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Record from "../Record.ts" +import type * as Schema from "../Schema.ts" import * as Event from "./Event.ts" /** * Unique type identifier used to mark event log event groups. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -26,6 +28,7 @@ export type TypeId = "~effect/eventlog/EventGroup" /** * Runtime type identifier used to mark event log event groups. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -34,6 +37,7 @@ export const TypeId: TypeId = "~effect/eventlog/EventGroup" /** * Returns `true` when a value is an event log event group. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -48,6 +52,7 @@ export const isEventGroup = (u: unknown): u is Any => Predicate.hasProperty(u, T * Use when build groups from `empty.add(...)`, then provide implementations for the events * with `EventLog.group`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -82,6 +87,7 @@ export interface EventGroup< /** * Type-erased marker for an event log event group. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -92,6 +98,7 @@ export interface Any { /** * Type-erased event group with its events record available structurally. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -100,6 +107,7 @@ export type AnyWithProps = EventGroup /** * Derives the handler service markers required for all events in an event group. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -109,6 +117,7 @@ export type ToService = A extends EventGroup ? Event.ToService /** * Extracts the union of event definitions contained in an event group. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -118,6 +127,7 @@ export type Events = Group extends EventGroup ? _Events /** * Client-side schema services required by all events in an event group. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -126,6 +136,7 @@ export type ServicesClient = Event.ServicesClient> /** * Server-side schema services required by all events in an event group. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -187,6 +198,7 @@ const makeProto = < * Use when you need the starting `EventGroup` value before adding event * definitions with `.add(...)`. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventJournal.ts b/repos/effect/packages/effect/src/eventlog/EventJournal.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/eventlog/EventJournal.ts rename to repos/effect/packages/effect/src/eventlog/EventJournal.ts index 90e955e6d5..caaadd0db3 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventJournal.ts +++ b/repos/effect/packages/effect/src/eventlog/EventJournal.ts @@ -9,23 +9,24 @@ * and remote identifiers, schemas, and in-memory or IndexedDB-backed journal * layers. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import type { Brand } from "../../Brand.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as DateTime from "../../DateTime.ts" -import * as Effect from "../../Effect.ts" -import * as Uuid from "../../internal/uuid.ts" -import * as Layer from "../../Layer.ts" -import type { Option } from "../../Option.ts" -import * as Order from "../../Order.ts" -import * as PubSub from "../../PubSub.ts" -import * as Schema from "../../Schema.ts" -import type { Scope } from "../../Scope.ts" -import * as Semaphore from "../../Semaphore.ts" +import * as Arr from "../Array.ts" +import type { Brand } from "../Brand.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as DateTime from "../DateTime.ts" +import * as Effect from "../Effect.ts" import * as SchemaBinary from "../encoding/SchemaBinary.ts" +import * as Uuid from "../internal/uuid.ts" +import * as Layer from "../Layer.ts" +import type { Option } from "../Option.ts" +import * as Order from "../Order.ts" +import * as PubSub from "../PubSub.ts" +import * as Schema from "../Schema.ts" +import type { Scope } from "../Scope.ts" +import * as Semaphore from "../Semaphore.ts" import type { StoreId } from "./EventLogMessage.ts" /** @@ -36,6 +37,7 @@ import type { StoreId } from "./EventLogMessage.ts" * The service writes local entries, imports entries from remote journals, exposes * a stream of local changes, and provides per-store locking. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -121,6 +123,7 @@ const TypeId = "effect/eventlog/EventJournal/EventJournalError" as const * * The error records the journal method that failed and the underlying cause. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -131,6 +134,7 @@ export class EventJournalError extends Data.TaggedError("EventJournalError")<{ /** * Marks this value as an event journal error for runtime guards. * + * @stability unstable * @since 4.0.0 */ readonly [TypeId] = TypeId @@ -139,6 +143,7 @@ export class EventJournalError extends Data.TaggedError("EventJournalError")<{ /** * Brand identifier used for `RemoteId` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -147,6 +152,7 @@ export type RemoteIdTypeId = "effect/eventlog/EventJournal/RemoteId" /** * Runtime brand identifier used for `RemoteId` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -155,6 +161,7 @@ export const RemoteIdTypeId: RemoteIdTypeId = "effect/eventlog/EventJournal/Remo /** * Branded byte identifier for a remote event journal source. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -163,6 +170,7 @@ export type RemoteId = Uint8Array & Brand /** * Schema for branded remote event journal identifiers. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -181,6 +189,7 @@ export const RemoteId = Schema.Uint8Array.pipe(Schema.brand(RemoteIdTypeId)) * This is unsafe because the generated UUID bytes are cast to the brand without * schema validation. * + * @stability unstable * @category unsafe * @since 4.0.0 */ @@ -189,6 +198,7 @@ export const makeRemoteIdUnsafe = (): RemoteId => Uuid.v4Bytes() as RemoteId /** * Runtime brand identifier used for `EntryId` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -197,6 +207,7 @@ export const EntryIdTypeId: EntryIdTypeId = "effect/eventlog/EventJournal/EntryI /** * Brand identifier used for `EntryId` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -205,6 +216,7 @@ export type EntryIdTypeId = "effect/eventlog/EventJournal/EntryId" /** * Branded byte identifier for an event journal entry. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -213,6 +225,7 @@ export type EntryId = Uint8Array & Brand /** * Schema for branded event journal entry identifiers. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -223,6 +236,7 @@ export const EntryId = (Schema.Uint8Array as Schema.instanceOf((a, b) => { * This is unsafe because the generated UUID bytes are cast to the brand without * schema validation. * + * @stability unstable * @category unsafe * @since 4.0.0 */ @@ -258,6 +273,7 @@ export const makeEntryIdUnsafe = (options: { msecs?: number } = {}): EntryId => /** * Extracts the millisecond timestamp encoded in a UUID v7 `EntryId`. * + * @stability unstable * @category getters * @since 4.0.0 */ @@ -275,6 +291,7 @@ export const entryIdMillis = (entryId: EntryId): number => { * An entry records its ID, event tag, primary key, and SchemaBinary-encoded * payload, with helpers for array SchemaBinary encoding and creation timestamps. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -287,6 +304,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * SchemaBinary codec for arrays of committed event journal entries. * + * @stability unstable * @since 4.0.0 */ static arraySchemaBinary = Schema.Array(SchemaBinary.toCodec(Entry)) @@ -294,6 +312,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * Encodes arrays of committed entries with the SchemaBinary entry codec. * + * @stability unstable * @since 4.0.0 */ static encodeArray = Schema.encodeUnknownEffect(Entry.arraySchemaBinary) @@ -301,6 +320,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * Decodes arrays of committed entries with the SchemaBinary entry codec. * + * @stability unstable * @since 4.0.0 */ static decodeArray = Schema.decodeUnknownEffect(Entry.arraySchemaBinary) @@ -308,6 +328,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * Ordering for committed entries by their event journal entry id. * + * @stability unstable * @since 4.0.0 */ static Order = Order.make((a, b) => EntryIdOrder(a.id, b.id)) @@ -315,6 +336,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * String representation of the entry id. * + * @stability unstable * @since 4.0.0 */ get idString(): string { @@ -324,6 +346,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * Creation timestamp encoded in the UUID v7 entry id, in epoch milliseconds. * + * @stability unstable * @since 4.0.0 */ get createdAtMillis(): number { @@ -333,6 +356,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent /** * Creation timestamp encoded in the UUID v7 entry id, as a UTC date-time. * + * @stability unstable * @since 4.0.0 */ get createdAt(): DateTime.Utc { @@ -347,6 +371,7 @@ export class Entry extends Schema.Class("effect/eventlog/EventJournal/Ent * * It pairs the remote sequence number with the journal entry payload. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -363,6 +388,7 @@ export class RemoteEntry extends Schema.Class("effect/eventlog/Even * Entries, remote tracking state, and locks live only in the current process and * are lost when the service is discarded. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -511,6 +537,7 @@ export const makeMemory: Effect.Effect = Effect.gen(fun * All journal data is stored in process memory and is not persisted across layer * lifetimes. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -525,6 +552,7 @@ export const layerMemory: Layer.Layer = Layer.effect(EventJournal, * browser database, publishes local changes, and requires `Scope` so the database * connection can be closed when the scope ends. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -787,6 +815,7 @@ const decodeEntryIdbArray = Schema.decodeUnknownEffect(EntryIdbArray) * Provides `EventJournal` using the IndexedDB-backed implementation created by * `makeIndexedDb`. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLog.ts b/repos/effect/packages/effect/src/eventlog/EventLog.ts similarity index 95% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLog.ts rename to repos/effect/packages/effect/src/eventlog/EventLog.ts index 3b887ff928..4b94239c48 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLog.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLog.ts @@ -7,28 +7,29 @@ * committed only after the handler succeeds. This module also contains the * layers and helpers needed to assemble that runtime. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as FiberMap from "../../FiberMap.ts" -import { constant, identity } from "../../Function.ts" -import * as InternalRecord from "../../internal/record.ts" -import * as Layer from "../../Layer.ts" -import type { Pipeable } from "../../Pipeable.ts" -import { pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as PubSub from "../../PubSub.ts" -import * as Queue from "../../Queue.ts" -import type * as Record from "../../Record.ts" -import * as Redacted from "../../Redacted.ts" -import * as Schedule from "../../Schedule.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaGetter from "../../SchemaGetter.ts" -import type * as Scope from "../../Scope.ts" -import type { Covariant } from "../../Types.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as FiberMap from "../FiberMap.ts" +import { constant, identity } from "../Function.ts" +import * as InternalRecord from "../internal/record.ts" +import * as Layer from "../Layer.ts" +import type { Pipeable } from "../Pipeable.ts" +import { pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as PubSub from "../PubSub.ts" +import * as Queue from "../Queue.ts" import { Reactivity } from "../reactivity/Reactivity.ts" import * as ReactivityLayer from "../reactivity/Reactivity.ts" +import type * as Record from "../Record.ts" +import * as Redacted from "../Redacted.ts" +import * as Schedule from "../Schedule.ts" +import * as Schema from "../Schema.ts" +import * as SchemaGetter from "../SchemaGetter.ts" +import type * as Scope from "../Scope.ts" +import type { Covariant } from "../Types.ts" import type * as Event from "./Event.ts" import type * as EventGroup from "./EventGroup.ts" import { Entry, EventJournal, type EventJournalError, makeEntryIdUnsafe, type RemoteId } from "./EventJournal.ts" @@ -45,6 +46,7 @@ import type { EventLogRemote } from "./EventLogRemote.ts" * only when the handler succeeds, and exposes access to the underlying journal * entries and destroy operation. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -65,6 +67,7 @@ export class EventLog extends Context.Service>) => Effect.Effect readonly reactivityKeys: Record> -}>()("effect/unstable/eventlog/EventLog/Registry") {} +}>()("effect/eventlog/EventLog/Registry") {} /** * Provides an in-memory `Registry` for event handlers, compactors, remote * replicas, and reactivity keys. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -178,6 +182,7 @@ export const layerRegistry = Layer.effect( * The identity is used by remote replication for authentication and by the * encryption service to derive signing and encryption keys. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -189,6 +194,7 @@ export class Identity extends Context.Service { /** * Creates an `EventLogSchema` from one or more event groups. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -241,6 +251,7 @@ export const schema = >( /** * Type-level identifier used to brand `Handlers` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -249,6 +260,7 @@ export type HandlersTypeId = "~effect/eventlog/EventLog/Handlers" /** * Runtime property key used to identify `Handlers` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -263,6 +275,7 @@ export const HandlersTypeId: HandlersTypeId = "~effect/eventlog/EventLog/Handler * each call to `handle` records a handler while accumulating any required * services. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -301,6 +314,7 @@ export interface Handlers< * Namespace containing helper types for `Handlers` values and handler-producing * layers. * + * @stability unstable * @since 4.0.0 */ export declare namespace Handlers { @@ -308,6 +322,7 @@ export declare namespace Handlers { * Type that matches any `Handlers` value regardless of its services or remaining * events. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -319,6 +334,7 @@ export declare namespace Handlers { * Runtime representation of one registered event handler, including its event * metadata, captured context, and handler function. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -344,6 +360,7 @@ export declare namespace Handlers { * If any event tag remains unhandled, the type evaluates to an explanatory * compile-time error string. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -367,6 +384,7 @@ export declare namespace Handlers { /** * Extracts the error type from an effect that produces `Handlers`. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -384,6 +402,7 @@ export declare namespace Handlers { * Computes the services required by a `Handlers` value or by an effect that * produces one, including event schema services. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -410,6 +429,7 @@ export declare namespace Handlers { * * Defaults to the branded store id `"default"`. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -428,6 +448,7 @@ const RedactedUint8Array = Schema.Uint8ArrayFromBase64.pipe( * Schema for an event-log identity with a string public key and redacted * base64-encoded private key bytes. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -452,6 +473,7 @@ const IdentityStringSchema = Schema.StringFromBase64Url.pipe( * * Invalid input throws a schema decoding error. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -467,6 +489,7 @@ export const decodeIdentityString = (value: string): Identity["Service"] => { * Encodes an event-log identity as a base64url string containing the public key * and private key bytes. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -480,6 +503,7 @@ export const encodeIdentityString = (identity: Identity["Service"]): string => * Generates a new event-log identity using the configured * `EventLogEncryption` service. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -527,6 +551,7 @@ const makeHandlers = (options: { * The callback receives a `Handlers` builder; its return type is checked so every * event in the group is handled. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -564,6 +589,7 @@ export const group = ( * During remote replay, matching entries are decoded, grouped by primary key, and * passed to the compaction effect, which may write replacement entries. * + * @stability unstable * @category compaction * @since 4.0.0 */ @@ -662,6 +688,7 @@ export const groupCompaction = ( * * Pass a single key list for all events or a mapping from event tag to key list. * + * @stability unstable * @category reactivity * @since 4.0.0 */ @@ -695,6 +722,7 @@ export const groupReactivity = ( * schema, runs the matching handler with the supplied identity and store id, logs * failures, and invalidates configured reactivity keys. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -961,6 +989,7 @@ const make = Effect.gen(function*() { * Provides `EventLog` and `Registry` using the configured `EventJournal` and * `Identity`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -998,6 +1027,7 @@ export const layerEventLog: Layer.Layer( * The returned function delegates to the `EventLog` service and preserves each * event's success and error types. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLogEncryption.ts b/repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLogEncryption.ts rename to repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts index 053517f5b0..fd5b6573db 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLogEncryption.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts @@ -6,13 +6,14 @@ * hashes byte data and creates event-log identities, so remote replication can * use storage or transport that should not see plaintext event data. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as Redacted from "../../Redacted.ts" -import * as Schema from "../../Schema.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Redacted from "../Redacted.ts" +import * as Schema from "../Schema.ts" import * as Transferable from "../workers/Transferable.ts" import { Entry, EntryId, RemoteEntry } from "./EventJournal.ts" import type { Identity } from "./EventLog.ts" @@ -22,6 +23,7 @@ import { makeGetIdentityRootSecretMaterial } from "./internal/identityRootSecret * Schema for an encrypted journal entry paired with its initialization vector * and the id of the original entry. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -35,6 +37,7 @@ export const EncryptedEntry = Schema.Struct({ * Type of an encrypted remote entry, including its remote sequence number, * initialization vector, entry id, and encrypted entry bytes. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -43,6 +46,7 @@ export interface EncryptedRemoteEntry extends Schema.Schema.Type => new U * Use to provide cryptographic operations required by encrypted event-log * replication. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -96,6 +101,7 @@ export class EventLogEncryption extends Context.Service /** * Schema for branded event-log store ids. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -61,6 +66,7 @@ export const StoreId = Schema.String.pipe(Schema.brand(StoreIdTypeId)) * It records the request tag, optional identity and store information, a protocol * error code, and a human-readable message. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -76,6 +82,7 @@ export class EventLogProtocolError extends Schema.TaggedError("effect/eventlog/ /** * RPC used to start an event-log remote session and receive a `HelloResponse`. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -127,6 +137,7 @@ export class HelloRpc extends Rpc.make("EventLog.Hello", { * Ed25519 signing public key, signature over the session challenge payload, and * algorithm name. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -140,6 +151,7 @@ export class Authenticate extends Schema.Class("effect/eventlog/Ev /** * RPC used to authenticate a remote event-log session after `HelloRpc`. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -151,6 +163,7 @@ export class AuthenticateRpc extends Rpc.make("EventLog.Authenticate", { /** * Represents an entire encoded event-log payload in one transport frame. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -168,6 +181,7 @@ export class SingleMessage * Use to divide data into chunks and `join` to reassemble all chunks with * the same id once every part has arrived. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -195,6 +209,7 @@ export class ChunkedMessage /** * Splits binary event-log message data into numbered chunks. * + * @stability unstable * @since 4.0.0 */ static split(id: number, data: Uint8Array): NonEmptyReadonlyArray { @@ -215,6 +230,7 @@ export class ChunkedMessage /** * Reassembles all chunks for a message id into the original binary payload. * + * @stability unstable * @since 4.0.0 */ static join( @@ -258,6 +274,7 @@ export class ChunkedMessage /** * RPC used to send one chunk of a large encoded write payload. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -274,6 +291,7 @@ export class WriteChunkedRpc extends Rpc.make("EventLog.WriteChunked", { * It includes the client public key, target store id, and encrypted entries * with their AES-GCM initialization vectors. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -293,6 +311,7 @@ export class WriteEntries extends Schema.Class("effect/eventlog/Ev /** * Schema for plaintext event-log write payloads sent to a remote store. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -314,6 +333,7 @@ export class WriteEntriesUnencrypted /** * RPC used to send an encoded write payload that fits in one message. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -333,6 +353,7 @@ export class WriteSingleRpc extends Rpc.make("EventLog.WriteSingle", { * Responses are encoded as either `SingleMessage` values or `ChunkedMessage` * parts. * + * @stability unstable * @category protocols * @since 4.0.0 */ @@ -358,6 +379,7 @@ export class ChangesRpc extends Rpc.make("EventLog.Changes", { * RPC group containing the event-log remote handshake, authentication, write, and * changes endpoints. * + * @stability unstable * @category protocols * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLogRemote.ts b/repos/effect/packages/effect/src/eventlog/EventLogRemote.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLogRemote.ts rename to repos/effect/packages/effect/src/eventlog/EventLogRemote.ts index 3c07d731d1..9ecb392552 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLogRemote.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLogRemote.ts @@ -8,21 +8,22 @@ * untrusted network. The unencrypted constructor is intended for trusted * transports or tests. * + * @stability unstable * @since 4.0.0 */ -import * as Cache from "../../Cache.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as Layer from "../../Layer.ts" -import * as Predicate from "../../Predicate.ts" -import * as Queue from "../../Queue.ts" -import * as Redacted from "../../Redacted.ts" -import type * as Schema from "../../Schema.ts" -import type * as Scope from "../../Scope.ts" +import * as Cache from "../Cache.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Predicate from "../Predicate.ts" +import * as Queue from "../Queue.ts" +import * as Redacted from "../Redacted.ts" import * as RpcClient from "../rpc/RpcClient.ts" import type { RpcClientError } from "../rpc/RpcClientError.ts" import type * as RpcGroup from "../rpc/RpcGroup.ts" +import type * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" import type { Entry, RemoteEntry, RemoteId } from "./EventJournal.ts" import { type Identity, Registry } from "./EventLog.ts" import { EventLogEncryption, layerSubtle } from "./EventLogEncryption.ts" @@ -53,6 +54,7 @@ import { makeGetIdentityRootSecretMaterial } from "./internal/identityRootSecret * It can write local entries to the remote, stream remote changes from a sequence * number, and run effects only after the supplied identity has authenticated. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -77,6 +79,7 @@ export class EventLogRemote extends Context.Service, RpcClientError> >()( - "effect/unstable/eventlog/EventLogRemote/EventLogRemoteClient" + "effect/eventlog/EventLogRemote/EventLogRemoteClient" ) { static readonly layer = Layer.effect( EventLogRemoteClient, @@ -155,6 +160,7 @@ export class EventLogRemoteClient extends Context.Service< * responses by re-authenticating, chunks large writes, and registers itself with * the `Registry` for the current scope. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -305,6 +311,7 @@ class IdentityService extends Context.Service()( * Creates an `EventLogRemote` that encrypts outgoing entries and decrypts * incoming changes with `EventLogEncryption`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -340,6 +347,7 @@ export const makeEncrypted = Effect.gen(function*(): Effect.fn.Return< /** * Creates an `EventLogRemote` that sends and receives plaintext entry payloads. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -361,6 +369,7 @@ export const makeUnencrypted: Effect.Effect< * Provides an encrypted `EventLogRemote` using the remote RPC client and the * default Web Crypto encryption layer. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -376,6 +385,7 @@ export const layerEncrypted: Layer.Layer< /** * Provides an unencrypted `EventLogRemote` using the remote RPC client. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLogServer.ts b/repos/effect/packages/effect/src/eventlog/EventLogServer.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLogServer.ts rename to repos/effect/packages/effect/src/eventlog/EventLogServer.ts index ed39ad80da..369ed9ec08 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLogServer.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLogServer.ts @@ -6,22 +6,23 @@ * authenticated `EventLog.Identity` to later requests, accept single or chunked * writes, and stream changes back as single or chunked messages. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import * as Arr from "../../Array.ts" -import * as Cache from "../../Cache.ts" -import * as Context from "../../Context.ts" -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as Equal from "../../Equal.ts" -import * as Hash from "../../Hash.ts" -import * as Layer from "../../Layer.ts" -import * as Option from "../../Option.ts" -import * as Redacted from "../../Redacted.ts" -import * as Stream from "../../Stream.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Arr from "../Array.ts" +import * as Cache from "../Cache.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import * as Redacted from "../Redacted.ts" import type * as Rpc from "../rpc/Rpc.ts" import type * as RpcGroup from "../rpc/RpcGroup.ts" +import * as Stream from "../Stream.ts" import type { RemoteId } from "./EventJournal.ts" import * as EventLog from "./EventLog.ts" import { @@ -43,6 +44,7 @@ import * as EventLogSessionAuth from "./EventLogSessionAuth.ts" * * Requests without an identity fail with a forbidden `EventLogProtocolError`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -70,11 +72,13 @@ export const layerAuthMiddleware: Layer.Layer< * chunked writes, delegates write and change handling to the supplied callbacks, * and frames large change payloads into chunks. * + * @stability unstable * @category layers * @since 4.0.0 */ export const layerRpcHandlers = (options: { readonly remoteId: RemoteId + readonly authorizeIdentity?: (publicKey: string) => Effect.Effect readonly getOrCreateSessionAuthBinding: ( publicKey: string, signingPublicKey: Uint8Array @@ -135,6 +139,9 @@ export const layerRpcHandlers = (options: { }) } yield* Cache.invalidate(clientChallenges, client.id) + if (options.authorizeIdentity) { + yield* options.authorizeIdentity(request.publicKey) + } const signingPublicKey = yield* Cache.get( persistedSigningPublicKeys, new SessionAuthCacheKey({ @@ -238,6 +245,7 @@ export const layerRpcHandlers = (options: { * Use to keep per-client chunk assembly state while handling chunked event-log * writes. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -254,6 +262,7 @@ export class ChunkedMessageState extends Context.Reference< /** * Annotation containing the public keys authenticated on an RPC connection. * + * @stability unstable * @category services * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerEncrypted.ts b/repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLogServerEncrypted.ts rename to repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts index 5445265bfe..d79205efd1 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerEncrypted.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts @@ -8,19 +8,20 @@ * decryption. This module defines the RPC handlers, server layer, storage * contract, and in-memory storage layer for that encrypted server path. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Uuid from "../../internal/uuid.ts" -import * as Layer from "../../Layer.ts" -import * as PubSub from "../../PubSub.ts" -import * as RcMap from "../../RcMap.ts" -import * as Schema from "../../Schema.ts" -import type * as Scope from "../../Scope.ts" -import * as Stream from "../../Stream.ts" +import * as Arr from "../Array.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Uuid from "../internal/uuid.ts" +import * as Layer from "../Layer.ts" +import * as PubSub from "../PubSub.ts" +import * as RcMap from "../RcMap.ts" import * as RpcServer from "../rpc/RpcServer.ts" +import * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" +import * as Stream from "../Stream.ts" import * as Transferable from "../workers/Transferable.ts" import { EntryId, makeRemoteIdUnsafe, type RemoteId } from "./EventJournal.ts" import type { EncryptedRemoteEntry } from "./EventLogEncryption.ts" @@ -36,6 +37,7 @@ import * as EventLogServer from "./EventLogServer.ts" * change streams read encrypted entries from storage and encode them for the * remote protocol. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -114,6 +116,7 @@ export const layerRpcHandlers = Layer.unwrap(Effect.gen(function*() { * @see {@link Storage} for the storage service required by this layer * @see {@link layerStorageMemory} for the process-local in-memory storage layer * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -124,6 +127,7 @@ export const layer: Layer.Layer = Rp /** * Schema for encrypted entries persisted by the encrypted event-log server. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -137,6 +141,7 @@ export class PersistedEntry extends Schema.Class( /** * String representation of the encrypted entry id. * + * @stability unstable * @since 4.0.0 */ get entryIdString(): string { @@ -158,6 +163,7 @@ export class PersistedEntry extends Schema.Class( * persists encrypted entries, and streams encrypted changes for a public key and * store id. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -187,6 +193,7 @@ export class Storage extends Context.Service( * for conflicts, run through registered handlers, and persisted; change streams * include compacted backlog entries when compactors are registered. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -144,6 +148,17 @@ export const layerRpcHandlers: Layer.Layer< return EventLogServer.layerRpcHandlers({ remoteId, + authorizeIdentity: (publicKey) => + auth.authorizeIdentity({ publicKey }).pipe( + Effect.mapError((error) => + new EventLogProtocolError({ + requestTag: "Authenticate", + publicKey, + code: error.reason === "Forbidden" ? "Forbidden" : "Unauthorized", + message: error.message + }) + ) + ), getOrCreateSessionAuthBinding: (publicKey, signingPublicKey) => storage.getOrCreateSessionAuthBinding(publicKey, signingPublicKey), onWrite: Effect.fnUntraced(function*(data, authenticatedPublicKeys) { @@ -237,6 +252,7 @@ export const layerRpcHandlers: Layer.Layer< /** * Error raised by unencrypted server storage and store mapping operations. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -251,6 +267,7 @@ export class EventLogServerStoreError extends Data.TaggedError("EventLogServerSt * Error raised when unencrypted server authorization rejects an identity or store * operation. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -270,6 +287,7 @@ export class EventLogServerAuthError extends Data.TaggedError("EventLogServerAut * Use to provide authorization checks for plaintext event-log writes, reads, * and identity authentication. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -297,6 +315,7 @@ export class EventLogServerAuthorization extends Context.Service = Layer.effect(Storage)(ma * @see {@link makeWrite} for the accessor that retrieves the typed server-side write function from the service environment * @see {@link layerServer} for the layer form that provides this service together with an event-log `Registry` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -768,6 +793,7 @@ export const make = Effect.gen(function*() { * Use to provide the unencrypted event-log server service together with the * registry needed by event handlers. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -806,6 +832,7 @@ export const layerServer: Layer.Layer< * @see {@link layerRpcHandlers} for wiring the unencrypted RPC handlers directly * @see {@link layerServer} for constructing the server service and event-log registry without RPC handlers * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -831,6 +858,7 @@ export const layer = ( * Builds the unencrypted event-log server handlers without installing an * `RpcServer.Protocol` implementation. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/EventLogSessionAuth.ts b/repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/eventlog/EventLogSessionAuth.ts rename to repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts index 2e0182f5e2..b32c5ed0df 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/EventLogSessionAuth.ts +++ b/repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts @@ -6,10 +6,11 @@ * includes the remote id, a short-lived challenge, the event-log public key, and * the signing public key in a stable byte format. * + * @stability unstable * @since 4.0.0 */ -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" const textEncoder = new TextEncoder() const textDecoder = new TextDecoder("utf-8", { fatal: true }) @@ -25,6 +26,7 @@ const constLengthPrefixBytes = 4 * Use when you need the domain-separation string used to build canonical * event-log session authentication payloads. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -39,6 +41,7 @@ export const AuthPayloadContext = "eventlog-auth-v1" * Use when implementing session-auth serialization or validation that must * reject public keys with a non-canonical raw byte length. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -52,6 +55,7 @@ export const Ed25519PublicKeyLength = 32 * Use when implementing session-auth verification that must reject signatures * with a non-canonical byte length before cryptographic checking. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -65,6 +69,7 @@ export const Ed25519SignatureLength = 64 * * Use when you need the challenge size for event-log session authentication. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -79,6 +84,7 @@ export const SessionAuthChallengeLength = 32 * Use when you need the timeout for pending event-log session authentication * challenges. * + * @stability unstable * @category constants * @since 4.0.0 */ @@ -88,6 +94,7 @@ export const SessionAuthChallengeTimeToLiveMillis = 30_000 * Payload fields that are canonicalized and signed during session * authentication. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -102,6 +109,7 @@ export interface SessionAuthPayload { * Error raised while encoding, decoding, signing, verifying, or generating * session authentication challenges. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -269,6 +277,7 @@ const encodeRemoteIdField = (remoteId: string | Uint8Array): Uint8Array => * 4. publicKey * 5. signingPublicKey bytes * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -307,6 +316,7 @@ export const encodeSessionAuthPayload = Effect.fnUntraced(function*(payload: Ses * The decoder validates the context field, UTF-8 fields, signing public key * length, and rejects truncated or trailing bytes. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -351,6 +361,7 @@ export const decodeSessionAuthPayload = Effect.fnUntraced( * * The private key must be PKCS#8-encoded bytes importable by `SubtleCrypto`. * + * @stability unstable * @category signing * @since 4.0.0 */ @@ -399,6 +410,7 @@ export const signSessionAuthPayloadBytes = Effect.fnUntraced(function*(options: * The payload, signing public key, and signature lengths are validated before * calling `SubtleCrypto.verify`. * + * @stability unstable * @category verification * @since 4.0.0 */ @@ -437,6 +449,7 @@ export const verifySessionAuthPayloadBytes = Effect.fnUntraced(function*(options * Encodes a session authentication payload in canonical form and signs it with an * Ed25519 private key. * + * @stability unstable * @category signing * @since 4.0.0 */ @@ -458,6 +471,7 @@ export const signSessionAuthPayload = ( * Encodes a session authentication payload in canonical form and verifies its * Ed25519 signature. * + * @stability unstable * @category verification * @since 4.0.0 */ @@ -479,6 +493,7 @@ export const verifySessionAuthPayload = ( /** * Generates a random session authentication challenge using `globalThis.crypto`. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -496,6 +511,7 @@ export const makeSessionAuthChallenge: Effect.Effect< * Verifies an authentication request by requiring the `Ed25519` algorithm and * checking the signature over the canonical session authentication payload. * + * @stability unstable * @category verification * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/eventlog/SqlEventJournal.ts b/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts similarity index 97% rename from repos/effect/packages/effect/src/unstable/eventlog/SqlEventJournal.ts rename to repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts index 2daab429ee..d434f4fd5f 100644 --- a/repos/effect/packages/effect/src/unstable/eventlog/SqlEventJournal.ts +++ b/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts @@ -6,15 +6,16 @@ * tables, giving event-log programs a durable journal that can be replayed after * restart and synchronized with remote journals. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Effect from "../../Effect.ts" -import * as Uuid from "../../internal/uuid.ts" -import * as Layer from "../../Layer.ts" -import * as PubSub from "../../PubSub.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" +import * as Arr from "../Array.ts" +import * as Effect from "../Effect.ts" +import * as Uuid from "../internal/uuid.ts" +import * as Layer from "../Layer.ts" +import * as PubSub from "../PubSub.ts" +import * as Schema from "../Schema.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" import * as SqlClient from "../sql/SqlClient.ts" import * as SqlError from "../sql/SqlError.ts" import * as SqlSchema from "../sql/SqlSchema.ts" @@ -30,6 +31,7 @@ type WriteFromRemoteOptions = Parameters readonly encryptionKey: CryptoKey @@ -80,7 +86,9 @@ const deriveSecretBytes = Effect.fnUntraced(function*(options: { return new Uint8Array(digest) }) -/** @internal */ +/** + * @internal + */ export const deriveIdentityRootSecretMaterial = Effect.fnUntraced(function*(options: { readonly crypto: Crypto readonly rootSecret: Uint8Array @@ -129,7 +137,9 @@ export const deriveIdentityRootSecretMaterial = Effect.fnUntraced(function*(opti } satisfies IdentityRootSecretMaterial }) -/** @internal */ +/** + * @internal + */ export const makeGetIdentityRootSecretMaterial = (crypto: Crypto) => { const cache = new WeakMap() diff --git a/repos/effect/packages/effect/src/http-api/HttpApi.ts b/repos/effect/packages/effect/src/http-api/HttpApi.ts new file mode 100644 index 0000000000..6f9ab62b84 --- /dev/null +++ b/repos/effect/packages/effect/src/http-api/HttpApi.ts @@ -0,0 +1,515 @@ +/** + * Describes an Effect HTTP API as groups of endpoints. + * + * An `HttpApi` value is data: it has an identifier, annotations, and groups of + * endpoints that describe request inputs, responses, middleware, and route + * metadata. The same description can be used by server builders, generated + * clients, URL builders, OpenAPI generation, and reflection tools. + * + * @stability unstable + * @since 4.0.0 + */ +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import type { PathInput } from "../http/HttpRouter.ts" +import * as InternalRecord from "../internal/record.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Record from "../Record.ts" +import type * as Schema from "../Schema.ts" +import type * as SchemaAST from "../SchemaAST.ts" +import * as HttpApiEndpoint from "./HttpApiEndpoint.ts" +import type * as HttpApiGroup from "./HttpApiGroup.ts" +import type * as HttpApiMiddleware from "./HttpApiMiddleware.ts" +import * as HttpApiSchema from "./HttpApiSchema.ts" + +const TypeId = "~effect/http-api/HttpApi" + +/** + * Returns `true` when a value is an `HttpApi`. + * + * @stability unstable + * @category guards + * @since 4.0.0 + */ +export const isHttpApi = (u: unknown): u is Top => Predicate.hasProperty(u, TypeId) + +/** + * Groups indexed by their identifier. + */ +type GroupMap = { + readonly [Group in Groups as HttpApiGroup.Identifier]: Group +} + +/** + * An `HttpApi` is a collection of HTTP API groups and endpoints that represents a + * portion of your domain. + * + * **When to use** + * + * Use when endpoint implementations can be provided with `HttpApiBuilder.group`, and the + * completed API can be registered with `HttpApiBuilder.layer`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface HttpApi< + out Id extends string, + in out Groups extends HttpApiGroup.Constraint = never +> extends Pipeable { + new(_: never): {} + readonly [TypeId]: typeof TypeId + readonly identifier: Id + readonly groups: GroupMap + readonly annotations: Context.Context + + /** + * Add a `HttpApiGroup` to the `HttpApi`. + */ + add>(...groups: A): HttpApi + + /** + * Adds every group from another `HttpApi` while preserving its annotation scope. + * + * **When to use** + * + * Use when you want to compose an API from groups declared and annotated under another API. + * + * **Details** + * + * The added API is flattened into this API rather than retained as a nested value. Each added group + * is copied with the added API's annotations, leaving the added API unchanged. Annotation precedence + * from least to most specific is this API, the added API, the group, and then the endpoint. + * + * **Gotchas** + * + * Annotations from the added API do not become top-level annotations of the result and do not affect + * groups already present in this API. They remain scoped to the groups and endpoints being added. + */ + addHttpApi( + api: HttpApi + ): HttpApi + + /** + * Prefix all endpoints in the `HttpApi`. + */ + prefix(prefix: Prefix): HttpApi> + + /** + * Adds a middleware to every endpoint currently in the `HttpApi`. + * + * **Gotchas** + * + * Endpoints added after this method is called do not receive the middleware. + */ + middleware( + middleware: Context.Key + ): HttpApi> + + /** + * Annotate the `HttpApi`. + */ + annotate(tag: Context.Key, value: S): HttpApi + + /** + * Annotate the `HttpApi` with a Context. + */ + annotateMerge(context: Context.Context): HttpApi +} + +/** + * An `HttpApi` value with its identifier and group types erased. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Constraint { + readonly [TypeId]: typeof TypeId +} + +/** + * An `HttpApi` with broad identifier and group types while retaining the concrete + * runtime properties used by implementation helpers. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface Top extends HttpApi {} + +const Proto = { + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + }, + add( + this: Top, + ...toAdd: NonEmptyReadonlyArray + ) { + const groups = { ...this.groups } + for (const group of toAdd) { + InternalRecord.assignProperty(groups, group.identifier, group) + } + return makeProto({ + ...optionsFromApi(this), + groups + }) + }, + addHttpApi( + this: Top, + api: Top + ) { + const newGroups = { ...this.groups } + for (const key of Object.keys(api.groups)) { + const group = api.groups[key] + InternalRecord.assignProperty( + newGroups, + key, + group.annotateMerge(Context.merge(api.annotations, group.annotations)) + ) + } + return makeProto({ + ...optionsFromApi(this), + groups: newGroups + }) + }, + prefix(this: Top, prefix: PathInput) { + return makeProto({ + ...optionsFromApi(this), + groups: Record.map(this.groups, (group) => group.prefix(prefix)) + }) + }, + middleware(this: Top, tag: HttpApiMiddleware.AnyService) { + return makeProto({ + ...optionsFromApi(this), + groups: Record.map(this.groups, (group) => group.middleware(tag as any)) + }) + }, + annotate(this: Top, key: Context.Key, value: any) { + return makeProto({ + ...optionsFromApi(this), + annotations: Context.add(this.annotations, key, value) + }) + }, + annotateMerge(this: Top, annotations: Context.Context) { + return makeProto({ + ...optionsFromApi(this), + annotations: Context.merge(this.annotations, annotations) + }) + } +} + +const optionsFromApi = (api: Top) => ({ + identifier: api.identifier, + groups: api.groups, + annotations: api.annotations +}) + +const makeProto = ( + options: { + readonly identifier: Id + readonly groups: Record.ReadonlyRecord + readonly annotations: Context.Context + } +): HttpApi => { + function HttpApi() {} + Object.setPrototypeOf(HttpApi, Proto) + return Object.assign(HttpApi, options) as any +} + +/** + * Creates an empty `HttpApi` with the supplied identifier. + * + * **When to use** + * + * Use when you need to start defining an HTTP API, add groups with `add` or + * `addHttpApi`, provide endpoint implementations with `HttpApiBuilder.group`, + * and register the API with `HttpApiBuilder.layer`. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = (identifier: Id): HttpApi => + makeProto({ + identifier, + groups: {}, + annotations: Context.empty() + }) + +/** + * Describes the groups and endpoints in an `HttpApi`. + * + * **Details** + * + * The callbacks receive each group or endpoint with merged annotations, endpoint + * middleware, and response schemas grouped by HTTP status. + * + * @stability unstable + * @category reflection + * @since 4.0.0 + */ +export const reflect = ( + self: HttpApi, + options: { + readonly predicate?: + | Predicate.Predicate<{ + readonly endpoint: HttpApiEndpoint.Top + readonly group: HttpApiGroup.Top + }> + | undefined + readonly onGroup: (options: { + readonly group: HttpApiGroup.Top + readonly mergedAnnotations: Context.Context + }) => void + readonly onEndpoint: (options: { + readonly group: HttpApiGroup.Top + readonly endpoint: HttpApiEndpoint.Top + readonly mergedAnnotations: Context.Context + readonly middleware: ReadonlySet + readonly successes: ReadonlyMap]> + readonly errors: ReadonlyMap]> + }) => void + } +) => { + const groups = Object.values(self.groups) as any as Array + for (const group of groups) { + const groupAnnotations = Context.merge(self.annotations, group.annotations) + options.onGroup({ + group, + mergedAnnotations: groupAnnotations + }) + const endpoints = Object.values(group.endpoints) as Iterable + for (const endpoint of endpoints) { + if ( + options.predicate && !options.predicate({ + endpoint, + group + } as any) + ) continue + + options.onEndpoint({ + group, + endpoint, + middleware: endpoint.middlewares as any, + mergedAnnotations: Context.merge(groupAnnotations, endpoint.annotations), + successes: extractResponseContent( + HttpApiEndpoint.getSuccessSchemas(endpoint), + HttpApiSchema.getStatusSuccessSchema + ), + errors: extractResponseContent( + HttpApiEndpoint.getErrorSchemas(endpoint), + HttpApiSchema.getStatusErrorSchema + ) + }) + } + } +} + +// ------------------------------------------------------------------------------------- + +const extractResponseContent = ( + schemas: Array, + getStatus: (schema: Schema.Constraint) => number +): ReadonlyMap]> => { + const map = new Map]>() + + schemas.forEach(add) + + return map + + function add(schema: Schema.Top) { + const body = HttpApiSchema.isWithHeaders(schema) ? schema.schema : schema + if (HttpApiSchema.isStreamSchema(body)) return + const status = getStatus(schema) + const schemas = map.get(status) + if (schemas === undefined) { + map.set(status, [schema]) + } else { + schemas.push(schema) + } + } +} + +/** + * Adds additional schemas to components/schemas. + * The provided schemas must have a `identifier` annotation. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class AdditionalSchemas extends Context.Service< + AdditionalSchemas, + ReadonlyArray +>()("effect/http-api/HttpApi/AdditionalSchemas") {} + +/** + * Schema parse options for server and client codecs, set on an API, group, or + * endpoint. + * + * **Details** + * + * Each codec slot has its own annotation: + * + * - `ParamsParseOptions` for path params + * - `QueryParseOptions` for the query string + * - `HeadersParseOptions` for request headers and `WithHeaders` response headers + * - `PayloadParseOptions` for request bodies + * - `SuccessParseOptions` for success bodies + * - `ErrorParseOptions` for error bodies + * + * A slot annotation at any level takes precedence over `ParseOptions` at any + * level. If neither is set, Schema defaults apply. Options are replaced, not + * merged. For the same annotation, endpoint overrides group, which overrides + * API. Annotate the API before passing it to `HttpApiBuilder.group` or + * `HttpApiBuilder.endpoint`. + * + * **Gotchas** + * + * Header codecs receive all HTTP headers, including undeclared transport + * headers such as `content-type`, `content-length`, `host`, `user-agent` and + * proxy headers. Without `HeadersParseOptions`, headers use `ParseOptions`: + * + * - `onExcessProperty: "error"` rejects real requests and `WithHeaders` + * responses with transport headers. + * - `onExcessProperty: "preserve"` includes transport headers in the decoded + * value. + * + * Set `HeadersParseOptions` to `{}` at the API level to use Schema defaults for + * headers, even if an endpoint sets `ParseOptions`. + * + * **Example** (Strict bodies with default header parsing) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { HttpApi, HttpApiEndpoint, HttpApiGroup } from "effect/http-api" + * + * const api = HttpApi.make("Api") + * .add( + * HttpApiGroup.make("users").add( + * HttpApiEndpoint.post("create", "/users", { + * headers: { "x-api-key": Schema.String }, + * payload: { name: Schema.String } + * }) + * ) + * ) + * .annotate(HttpApi.ParseOptions, { onExcessProperty: "error" }) + * .annotate(HttpApi.HeadersParseOptions, {}) + * ``` + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ParseOptions extends Context.Service< + ParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/ParseOptions") {} + +/** + * Schema parse options for path params: server decoding, client encoding, and + * `HttpApiClient.urlBuilder`. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ParamsParseOptions extends Context.Service< + ParamsParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/ParamsParseOptions") {} + +/** + * Schema parse options for the query string: server decoding, client encoding, + * and `HttpApiClient.urlBuilder`. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class QueryParseOptions extends Context.Service< + QueryParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/QueryParseOptions") {} + +/** + * Schema parse options for request headers and the headers of `WithHeaders` + * responses: server decoding/encoding and client encoding/decoding, including + * buffered and streamed responses. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class HeadersParseOptions extends Context.Service< + HeadersParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/HeadersParseOptions") {} + +/** + * Schema parse options for request bodies, including multipart payloads: + * server decoding and client encoding. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class PayloadParseOptions extends Context.Service< + PayloadParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/PayloadParseOptions") {} + +/** + * Schema parse options for success bodies, including streams, SSE events, and + * the body of `WithHeaders` responses: server encoding and client decoding. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class SuccessParseOptions extends Context.Service< + SuccessParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/SuccessParseOptions") {} + +/** + * Schema parse options for error bodies: server encoding and client decoding. Falls back to `ParseOptions` when unset. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ErrorParseOptions extends Context.Service< + ErrorParseOptions, + SchemaAST.ParseOptions +>()("effect/http-api/HttpApi/ErrorParseOptions") {} + +/** + * @internal + */ +export interface SlotParseOptions { + readonly params: SchemaAST.ParseOptions | undefined + readonly query: SchemaAST.ParseOptions | undefined + readonly headers: SchemaAST.ParseOptions | undefined + readonly payload: SchemaAST.ParseOptions | undefined + readonly success: SchemaAST.ParseOptions | undefined + readonly error: SchemaAST.ParseOptions | undefined +} + +/** + * @internal + */ +export const getSlotParseOptions = (annotations: Context.Context): SlotParseOptions => { + const fallback = Context.getOrUndefined(annotations, ParseOptions) + return { + params: Context.getOrUndefined(annotations, ParamsParseOptions) ?? fallback, + query: Context.getOrUndefined(annotations, QueryParseOptions) ?? fallback, + headers: Context.getOrUndefined(annotations, HeadersParseOptions) ?? fallback, + payload: Context.getOrUndefined(annotations, PayloadParseOptions) ?? fallback, + success: Context.getOrUndefined(annotations, SuccessParseOptions) ?? fallback, + error: Context.getOrUndefined(annotations, ErrorParseOptions) ?? fallback + } +} diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts b/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts similarity index 85% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts rename to repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts index 13002a36c6..0bad0d35c9 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts @@ -6,32 +6,18 @@ * middleware and security handlers, invokes the registered endpoint handler, and * encodes successes or declared errors into `HttpServerResponse` values. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import type * as Cause from "../../Cause.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import * as Encoding from "../../Encoding.ts" -import * as Fiber from "../../Fiber.ts" -import type { FileSystem } from "../../FileSystem.ts" -import * as Function from "../../Function.ts" -import { stringOrRedacted } from "../../internal/redacted.ts" -import * as Layer from "../../Layer.ts" -import type { Path } from "../../Path.ts" -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import { hasProperty } from "../../Predicate.ts" -import * as Redacted from "../../Redacted.ts" -import * as Result from "../../Result.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import * as SchemaIssue from "../../SchemaIssue.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" -import * as Scope from "../../Scope.ts" -import * as Stream from "../../Stream.ts" -import type { NoInfer } from "../../Types.ts" -import * as UndefinedOr from "../../UndefinedOr.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import type * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Base64 from "../encoding/Base64.ts" import * as Sse from "../encoding/Sse.ts" +import * as Fiber from "../Fiber.ts" +import type { FileSystem } from "../FileSystem.ts" +import * as Function from "../Function.ts" import type { Cookie } from "../http/Cookies.ts" import type * as Etag from "../http/Etag.ts" import * as HttpEffect from "../http/HttpEffect.ts" @@ -44,7 +30,22 @@ import * as Response from "../http/HttpServerResponse.ts" import type { HttpServerResponse } from "../http/HttpServerResponse.ts" import * as Multipart from "../http/Multipart.ts" import * as UrlParams from "../http/UrlParams.ts" -import type * as HttpApi from "./HttpApi.ts" +import { stringOrRedacted } from "../internal/redacted.ts" +import * as Layer from "../Layer.ts" +import type { Path } from "../Path.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import { hasProperty } from "../Predicate.ts" +import * as Redacted from "../Redacted.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import * as SchemaIssue from "../SchemaIssue.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" +import * as Scope from "../Scope.ts" +import * as Stream from "../Stream.ts" +import type { NoInfer } from "../Types.ts" +import * as UndefinedOr from "../UndefinedOr.ts" +import * as HttpApi from "./HttpApi.ts" import * as HttpApiEndpoint from "./HttpApiEndpoint.ts" import { HttpApiSchemaError } from "./HttpApiError.ts" import type * as HttpApiGroup from "./HttpApiGroup.ts" @@ -52,11 +53,13 @@ import * as HttpApiMiddleware from "./HttpApiMiddleware.ts" import * as HttpApiSchema from "./HttpApiSchema.ts" import type * as HttpApiSecurity from "./HttpApiSecurity.ts" import * as MediaType from "./internal/mediaType.ts" +import * as HttpApiPath from "./internal/path.ts" import * as OpenApi from "./OpenApi.ts" /** * Registers an `HttpApi` with a `HttpRouter`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -85,7 +88,7 @@ export const layer = >() const routes: Array> = [] const availableGroups = Array.from(services.mapUnsafe.keys()).filter((key) => - key.startsWith("effect/httpapi/HttpApiGroup/") + key.startsWith("effect/http-api/HttpApiGroup/") ) const groups = Object.values(api.groups) as ReadonlyArray for (const group of groups) { @@ -120,6 +123,7 @@ export const layer = * be used to add handlers to the group. Implement endpoints with * `handlers.handle`. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -150,7 +154,7 @@ export const group = < : result const routes: Array> = [] for (const item of handlers.handlers.values()) { - routes.push(handlerToRoute(group as any, item, services)) + routes.push(handlerToRoute(api, group as any, item, services)) } return Context.makeUnsafe( new Map([[group.key, { @@ -169,6 +173,7 @@ export const group = < * types inferred from the endpoint, preserving the callback's service * requirements. Pass the result to `handlers.handle` when implementing the group. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -205,7 +210,7 @@ export const handler = < R > => f -const HandlersTypeId = "~effect/httpapi/HttpApiBuilder/Handlers" +const HandlersTypeId = "~effect/http-api/HttpApiBuilder/Handlers" type EndpointMap = { readonly [Endpoint in Endpoints as HttpApiEndpoint.Identifier]: Endpoint @@ -227,7 +232,9 @@ interface HandlerOptions { readonly uninterruptible?: boolean | undefined } -/** @internal */ +/** + * @internal + */ export interface HandlerRuntime { readonly endpoint: HttpApiEndpoint.Top readonly handler: HttpApiEndpoint.Handler @@ -305,6 +312,7 @@ type ValidateHandlersReturn< * adds that endpoint identifier to the type-level set of implemented endpoints. * Endpoint identifiers that were already handled are rejected at the type level. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -316,9 +324,13 @@ export interface Handlers< readonly [HandlersTypeId]: typeof HandlersTypeId readonly "~EndpointsByIdentifier": EndpointsByIdentifier readonly "~HandledIdentifiers": HandledIdentifiers - /** @internal */ + /** + * @internal + */ readonly group: HttpApiGroup.Top - /** @internal */ + /** + * @internal + */ readonly handlers: Map /** @@ -379,6 +391,7 @@ export interface Handlers< /** * Namespace containing helper types for `HttpApiBuilder` handler collections. * + * @stability unstable * @since 4.0.0 */ export declare namespace Handlers { @@ -386,6 +399,7 @@ export declare namespace Handlers { * Creates a handler collection for a group where every endpoint in the group is * still awaiting an implementation. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -399,6 +413,7 @@ export declare namespace Handlers { * handler collections and producing a descriptive type error when endpoints remain * unhandled. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -408,6 +423,7 @@ export declare namespace Handlers { * Extracts the error channel from an effect that produces a `Handlers` * collection, returning `never` for non-effectful handler collections. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -426,6 +442,7 @@ export declare namespace Handlers { * Extracts the services required by a handler collection, including both handler * requirements and the environment required to construct the handlers. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -480,6 +497,7 @@ type EndpointReturn< * Builds the server-side HTTP effect for a single endpoint in an API group using * the endpoint metadata, middleware, codecs, and supplied handler. * + * @stability unstable * @category handlers * @since 4.0.0 */ @@ -508,6 +526,7 @@ export const endpoint = < const group = api.groups[groupIdentifier] as unknown as HttpApiGroup.Top const endpoint = group.endpoints[endpointIdentifier] return Effect.succeed(handlerToHttpEffect( + api, group, endpoint, Context.omit(Scope.Scope)(context), @@ -520,6 +539,7 @@ export const endpoint = < * Decodes credentials for an HTTP API security scheme from the current request, * supporting bearer, API key, and basic authentication inputs. * + * @stability unstable * @category security * @since 4.0.0 */ @@ -567,7 +587,7 @@ export const securityDecode = return Effect.map(HttpServerRequest, (request) => { const encoded = getAuthorizationCredential(request.headers.authorization, basicScheme) if (encoded === undefined) return empty - const decoded = Result.getOrUndefined(Encoding.decodeBase64String(encoded)) + const decoded = Result.getOrUndefined(Base64.decodeString(encoded)) if (decoded === undefined) return empty // RFC 7617, Section 2: only the first colon separates the user-id from the password. // https://www.rfc-editor.org/rfc/rfc7617.html#section-2 @@ -586,6 +606,7 @@ export const securityDecode = * Registers a pre-response handler that sets an API-key cookie on the outgoing * response, defaulting the cookie to `secure` and `httpOnly` unless overridden. * + * @stability unstable * @category security * @since 4.0.0 */ @@ -720,11 +741,12 @@ type PayloadDecoder = } function buildPayloadDecoders( - payloadMap: HttpApiEndpoint.PayloadMap + payloadMap: HttpApiEndpoint.PayloadMap, + options: SchemaAST.ParseOptions | undefined ): Map { const result = new Map() payloadMap.forEach(({ encoding, schemas }, contentType) => { - const decode = Schema.decodeUnknownEffect(Schema.Union(schemas)) + const decode = Schema.decodeUnknownEffect(Schema.Union(schemas), options) if (encoding._tag === "Multipart") { result.set(contentType, { _tag: "Multipart", mode: encoding.mode, limits: encoding.limits, decode }) } else { @@ -799,25 +821,31 @@ function decodePayload( } function handlerToHttpEffect( + api: Pick, group: HttpApiGroup.Top, endpoint: HttpApiEndpoint.Top, context: Context.Context, handler: HttpApiEndpoint.Handler, isRaw: boolean ) { - const encodeSuccess = Schema.encodeUnknownEffect(makeSuccessSchema(endpoint)) - const encodeError = Schema.encodeUnknownEffect(makeErrorSchema(endpoint)) - const decodeParams = UndefinedOr.map(endpoint.params, Schema.decodeUnknownEffect) - const decodeHeaders = UndefinedOr.map(endpoint.headers, Schema.decodeUnknownEffect) + const annotations = Context.merge(Context.merge(api.annotations, group.annotations), endpoint.annotations) + const options = HttpApi.getSlotParseOptions(annotations) + const encodeSuccess = Schema.encodeUnknownEffect(makeSuccessSchema(endpoint, options.headers), options.success) + const encodeError = Schema.encodeUnknownEffect(makeErrorSchema(endpoint, options.headers), options.error) + const decodeParams = UndefinedOr.map(endpoint.params, (schema) => Schema.decodeUnknownEffect(schema, options.params)) + const decodeHeaders = UndefinedOr.map( + endpoint.headers, + (schema) => Schema.decodeUnknownEffect(schema, options.headers) + ) const decodeQuery = UndefinedOr.map( endpoint.query, - (schema) => Schema.decodeUnknownEffect(Schema.toCodecArrayFromSingle(schema)) + (schema) => Schema.decodeUnknownEffect(Schema.toCodecArrayFromSingle(schema), options.query) ) - const encodeStream = makeStreamEncoder(endpoint) - const encodeWithHeaders = makeWithHeadersEncoder(endpoint) + const encodeStream = makeStreamEncoder(endpoint, options.success) + const encodeWithHeaders = makeWithHeadersEncoder(endpoint, options.success, options.headers) const shouldParsePayload = endpoint.payload.size > 0 && !isRaw - const payloadBy = shouldParsePayload ? buildPayloadDecoders(endpoint.payload) : undefined + const payloadBy = shouldParsePayload ? buildPayloadDecoders(endpoint.payload, options.payload) : undefined return applyMiddleware( group, @@ -883,8 +911,11 @@ function handlerToHttpEffect( ) } -/** @internal */ +/** + * @internal + */ export function handlerToRoute( + api: Pick, group: HttpApiGroup.Top, handler: HandlerRuntime, context: Context.Context @@ -892,8 +923,8 @@ export function handlerToRoute( const endpoint = handler.endpoint return HttpRouter.route( endpoint.method, - endpoint.path as HttpRouter.PathInput, - handlerToHttpEffect(group, endpoint, context, handler.handler, handler.isRaw), + HttpApiPath.toRouterPath(endpoint.path, endpoint.params) as HttpRouter.PathInput, + handlerToHttpEffect(api, group, endpoint, context, handler.handler, handler.isRaw), { uninterruptible: handler.uninterruptible } ) } @@ -968,7 +999,7 @@ const makeSecurityMiddleware = ( return middleware } -const HandlerErrorTypeId = "~effect/httpapi/HttpApiBuilder/HandlerError" as const +const HandlerErrorTypeId = "~effect/http-api/HttpApiBuilder/HandlerError" as const class HandlerError { readonly [HandlerErrorTypeId] = HandlerErrorTypeId readonly error: unknown @@ -991,14 +1022,21 @@ interface WithHeadersEncoders { readonly encodeHeaders: ReadonlyMap } -function makeWithHeadersEncoder(endpoint: HttpApiEndpoint.Top): WithHeadersEncoders | undefined { +function makeWithHeadersEncoder( + endpoint: HttpApiEndpoint.Top, + options: SchemaAST.ParseOptions | undefined, + headersOptions: SchemaAST.ParseOptions | undefined +): WithHeadersEncoders | undefined { const encodeHeaders = new Map() const bodySchemas: Array> = [] for (const schema of endpoint.success) { if (!HttpApiSchema.isWithHeaders(schema)) continue - encodeHeaders.set(HttpApiSchema.getStatusSuccessSchema(schema), Schema.encodeUnknownEffect(schema.headers)) + encodeHeaders.set( + HttpApiSchema.getStatusSuccessSchema(schema), + Schema.encodeUnknownEffect(schema.headers, headersOptions) + ) if (!HttpApiSchema.isStreamSchema(schema.schema)) { - bodySchemas.push(toResponseSuccessSchema(schema)) + bodySchemas.push(toResponseSuccessSchema(schema, headersOptions)) } } if (encodeHeaders.size === 0) return undefined @@ -1010,12 +1048,15 @@ function makeWithHeadersEncoder(endpoint: HttpApiEndpoint.Top): WithHeadersEncod ? bodySchemas[0] : Schema.Union(bodySchemas) return { - encodeBody: Schema.encodeUnknownEffect(bodySchema), + encodeBody: Schema.encodeUnknownEffect(bodySchema, options), encodeHeaders } } -function makeStreamEncoder(endpoint: HttpApiEndpoint.Top): StreamEncoder | undefined { +function makeStreamEncoder( + endpoint: HttpApiEndpoint.Top, + options: SchemaAST.ParseOptions | undefined +): StreamEncoder | undefined { const successSchema = getStreamSuccessSchema(endpoint) if (successSchema === undefined) { return undefined @@ -1044,7 +1085,7 @@ function makeStreamEncoder(endpoint: HttpApiEndpoint.Top): StreamEncoder | undef } } - const sseEncoder = makeSseEncoder(streamSchema) + const sseEncoder = makeSseEncoder(streamSchema, options) return (response, context) => { if (!Stream.isStream(response)) { @@ -1089,13 +1130,14 @@ interface SseStreamEncoder { } function makeSseEncoder( - streamSchema: HttpApiSchema.StreamSse + streamSchema: HttpApiSchema.StreamSse, + options: SchemaAST.ParseOptions | undefined ): SseStreamEncoder { const CauseSchema = Schema.toCodecJson(Schema.Cause(streamSchema.error, Schema.Defect())) return { sseMode: streamSchema.sseMode, - encodeEvents: Schema.encodeUnknownEffect(Schema.Array(streamSchema.events)) as any, - encodeCause: Schema.encodeUnknownEffect(Schema.fromJsonString(CauseSchema)) + encodeEvents: Schema.encodeUnknownEffect(Schema.Array(streamSchema.events), options) as any, + encodeCause: Schema.encodeUnknownEffect(Schema.fromJsonString(CauseSchema), options) } } @@ -1129,7 +1171,7 @@ function encodeFailureEvent(cause: Cause.Cause, encoder: SseStreamEncod ) } -const reservedStreamFailureEvent = "effect/httpapi/stream/failure" +const reservedStreamFailureEvent = "effect/http-api/stream/failure" function renderSseEvent(event: Sse.EventEncoded) { return Sse.encoder.write({ @@ -1144,39 +1186,38 @@ const toResponseSuccessSchema = toResponseSchema(HttpApiSchema.getStatusSuccessS const toResponseErrorSchemaPlain = toResponseSchema(HttpApiSchema.getStatusErrorSchema) function toResponseErrorSchema( - schema: Schema.Constraint + schema: Schema.Constraint, + headersOptions: SchemaAST.ParseOptions | undefined ): Schema.ConstraintEncoder { - if (!HttpApiSchema.isWithHeaders(schema)) return toResponseErrorSchemaPlain(schema) - - const encodeBody = Schema.encodeUnknownEffect(schema.schema) - const encodeHeaders = Schema.encodeUnknownEffect(schema.headers) - const encodeResponse = getResponseEncode( - HttpApiSchema.getStatusErrorSchema(schema), - HttpApiSchema.getResponseEncodingSchema(schema), - HttpApiSchema.isNoContent(schema.schema.ast) - ) - const transformation = withHeadersTransformation>( - (body, options) => - encodeBody(body, options).pipe( - Effect.mapError((error) => error.issue), - Effect.flatMap((body) => encodeResponse(body, options)) - ), - encodeHeaders - ) - return $HttpServerResponse.pipe(Schema.decodeTo(schema, transformation)) + if (!HttpApiSchema.isWithHeaders(schema)) return toResponseErrorSchemaPlain(schema, headersOptions) + return toWithHeadersResponseSchema({ + schema, + status: HttpApiSchema.getStatusErrorSchema(schema), + encoding: HttpApiSchema.getResponseEncodingSchema(schema), + body: schema.schema, + headers: schema.headers, + headersOptions, + toPair: (value) => value as HttpApiSchema.withHeaders + }) } function makeSuccessSchema( - endpoint: HttpApiEndpoint.Top + endpoint: HttpApiEndpoint.Top, + headersOptions: SchemaAST.ParseOptions | undefined ): Schema.ConstraintEncoder { - const schemas = HttpApiEndpoint.getSuccessSchemas(endpoint).map(toResponseSuccessSchema) + const schemas = HttpApiEndpoint.getSuccessSchemas(endpoint).map((schema) => + toResponseSuccessSchema(schema, headersOptions) + ) return schemas.length === 1 ? schemas[0] : Schema.Union(schemas) } function makeErrorSchema( - endpoint: HttpApiEndpoint.Top + endpoint: HttpApiEndpoint.Top, + headersOptions: SchemaAST.ParseOptions | undefined ): Schema.ConstraintEncoder { - const schemas = HttpApiEndpoint.getErrorSchemas(endpoint).map(toResponseErrorSchema) + const schemas = HttpApiEndpoint.getErrorSchemas(endpoint).map((schema) => + toResponseErrorSchema(schema, headersOptions) + ) if (schemas.length === 0) return Schema.Never return schemas.length === 1 ? schemas[0] : Schema.Union(schemas) } @@ -1185,7 +1226,23 @@ function toResponseSchema(getStatus: (schema: Schema.Constraint) => number) { // WithHeaders wrappers share a single declaration AST, so they are cached by instance const cache = new WeakMap() - return (schema: Schema.Constraint): Schema.ConstraintEncoder => { + return ( + schema: Schema.Constraint, + headersOptions: SchemaAST.ParseOptions | undefined + ): Schema.ConstraintEncoder => { + const withHeaders = HttpApiSchema.getWithHeadersAnnotation(schema.ast) + if (withHeaders !== undefined) { + // Depends on the endpoint's header options, so it is not cached + return toWithHeadersResponseSchema({ + schema: Schema.toType(schema), + status: getStatus(withHeaders.body), + encoding: HttpApiSchema.getResponseEncoding(withHeaders.body.ast), + body: withHeaders.bodyCodec, + headers: withHeaders.headersCodec, + headersOptions, + toPair: withHeaders.transformation.encode + }) + } const key = HttpApiSchema.isWithHeaders(schema) ? schema : schema.ast const cached = cache.get(key) if (cached !== undefined) { @@ -1204,16 +1261,6 @@ function getResponseTransformation( getStatus: (schema: Schema.Constraint) => number, schema: Schema.Constraint ): SchemaTransformation.Transformation { - const withHeaders = HttpApiSchema.getWithHeadersAnnotation(schema.ast) - if (withHeaders !== undefined) { - const encodeBody = getResponseEncode( - getStatus(withHeaders.body), - HttpApiSchema.getResponseEncoding(withHeaders.body.ast), - HttpApiSchema.isNoContent(withHeaders.body.ast) - ) - return withHeadersTransformation(encodeBody, Schema.encodeUnknownEffect(withHeaders.headersCodec)) - } - const bodySchema = HttpApiSchema.isWithHeaders(schema) ? schema.schema : schema const encode = getResponseEncode( getStatus(schema), @@ -1230,30 +1277,45 @@ function getResponseTransformation( }) } -function withHeadersTransformation( - encodeBody: ( - body: unknown, - options?: SchemaAST.ParseOptions - ) => Effect.Effect, - encodeHeaders: ( - headers: unknown, - options?: SchemaAST.ParseOptions - ) => Effect.Effect -): SchemaTransformation.Transformation { - return SchemaTransformation.transformEffect({ +// Encodes the body with the options passed to the schema and the headers with +// the headers slot options. +function toWithHeadersResponseSchema(options: { + readonly schema: Schema.Top + readonly status: number + readonly encoding: HttpApiSchema.ResponseEncoding + readonly body: Schema.Top + readonly headers: Schema.Top + readonly headersOptions: SchemaAST.ParseOptions | undefined + readonly toPair: (value: unknown) => { readonly body: unknown; readonly headers: unknown } +}): Schema.ConstraintEncoder { + const encodeBody = Schema.encodeUnknownEffect(options.body) + const encodeHeaders = Schema.encodeUnknownEffect(options.headers, options.headersOptions) + const encodeResponse = getResponseEncode( + options.status, + options.encoding, + HttpApiSchema.isNoContent(options.body.ast) + ) + const toIssue = (error: Schema.SchemaError) => error.issue + const transformation = SchemaTransformation.transformEffect({ decode: (input, options) => Effect.fail( new SchemaIssue.Forbidden({ message: "Encode only schema" }, input, options) ), - encode: (value, options) => { - const pair = value as { readonly body: unknown; readonly headers: unknown } - return Effect.flatMap(encodeBody(pair.body, options), (response) => - Effect.map( - encodeHeaders(pair.headers, options).pipe(Effect.mapError((error) => error.issue)), - (headers) => Response.setHeaders(response, headers as any) - )) + encode: (value, parseOptions) => { + const pair = options.toPair(value) + return encodeBody(pair.body, parseOptions).pipe( + Effect.mapError(toIssue), + Effect.flatMap((body) => encodeResponse(body, parseOptions)), + Effect.flatMap((response) => + Effect.map( + Effect.mapError(encodeHeaders(pair.headers), toIssue), + (headers) => Response.setHeaders(response, headers as any) + ) + ) + ) } }) + return $HttpServerResponse.pipe(Schema.decodeTo(options.schema, transformation)) } function getResponseEncode( diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiClient.ts b/repos/effect/packages/effect/src/http-api/HttpApiClient.ts similarity index 86% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiClient.ts rename to repos/effect/packages/effect/src/http-api/HttpApiClient.ts index 44b04f9d3f..abfed1134c 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiClient.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiClient.ts @@ -8,23 +8,15 @@ * error responses are decoded. This module also includes helpers for building a * client for only one group, one endpoint, or only the encoded URL. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import * as Cause from "../../Cause.ts" -import type * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import { identity } from "../../Function.ts" -import * as InternalRecord from "../../internal/record.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import * as SchemaIssue from "../../SchemaIssue.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" -import * as Stream from "../../Stream.ts" -import type { Simplify } from "../../Types.ts" -import * as UndefinedOr from "../../UndefinedOr.ts" +import * as Arr from "../Array.ts" +import * as Cause from "../Cause.ts" +import type * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" import * as Sse from "../encoding/Sse.ts" +import { identity } from "../Function.ts" import * as HttpBody from "../http/HttpBody.ts" import * as HttpClient from "../http/HttpClient.ts" import * as HttpClientError from "../http/HttpClientError.ts" @@ -32,17 +24,28 @@ import * as HttpClientRequest from "../http/HttpClientRequest.ts" import type * as HttpClientResponse from "../http/HttpClientResponse.ts" import * as HttpMethod from "../http/HttpMethod.ts" import * as UrlParams from "../http/UrlParams.ts" +import * as InternalRecord from "../internal/record.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import * as SchemaIssue from "../SchemaIssue.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" +import * as Stream from "../Stream.ts" +import type { Simplify } from "../Types.ts" +import * as UndefinedOr from "../UndefinedOr.ts" import * as HttpApi from "./HttpApi.ts" import * as HttpApiEndpoint from "./HttpApiEndpoint.ts" import type * as HttpApiGroup from "./HttpApiGroup.ts" import type * as HttpApiMiddleware from "./HttpApiMiddleware.ts" import * as HttpApiSchema from "./HttpApiSchema.ts" import * as MediaType from "./internal/mediaType.ts" +import * as HttpApiPath from "./internal/path.ts" /** * The type-safe client shape generated from HTTP API groups, with non-top-level * groups exposed as nested objects and top-level endpoints exposed as methods. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -61,6 +64,7 @@ export type Client * Derives the typed client interface for an `HttpApi`, preserving any additional * client error and service requirements supplied by the caller. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -100,6 +104,7 @@ type SuccessDecodingServices = S extends HttpApiSchema.StreamSse< * Helper types used to describe generated HTTP API clients, including endpoint * methods, response modes, and grouped client shapes. * + * @stability unstable * @since 4.0.0 */ export declare namespace Client { @@ -107,6 +112,7 @@ export declare namespace Client { * The response mode accepted by generated client methods, controlling whether a * call returns the decoded success value, the raw response, or both. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -116,6 +122,7 @@ export declare namespace Client { * Computes the value returned by a client method for a success type and response * mode. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -134,6 +141,7 @@ export declare namespace Client { * The client object for one API group, mapping each endpoint identifier in that * group to its typed client method. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -168,6 +176,7 @@ export declare namespace Client { * error, and service channels reflect the endpoint schemas, middleware, and * selected response mode. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -191,6 +200,7 @@ export declare namespace Client { * Extracts client methods for endpoints in top-level groups so they can be exposed * directly on the generated client object. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -224,6 +234,7 @@ type UrlBuilderArgs = [Request] extends [void | undefined] ? [request?: * The type-safe URL builder shape for an HTTP API, mirroring the generated client * layout while returning URL strings instead of executing requests. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -255,7 +266,9 @@ type UrlBuilderTopLevelMethods = { ]: UrlBuilderMethod } -/** @internal */ +/** + * @internal + */ export const makeClient = ( api: HttpApi.HttpApi, options: { @@ -329,14 +342,20 @@ export const makeClient = = { orElse: statusOrElse } const errorAlternatives = new Map>() for (const [status, schemas] of errors.entries()) { const grouped = groupSchemasByContentType(schemas) for (const [contentType, schemas] of grouped.entries()) { - addResponseAlternative(errorAlternatives, status, contentType, schemasToResponse(schemas)) + addResponseAlternative( + errorAlternatives, + status, + contentType, + schemasToResponse(schemas, parseOptions.error, parseOptions.headers) + ) } } for (const [status, alternatives] of errorAlternatives.entries()) { @@ -363,7 +382,12 @@ export const makeClient = Schema.encodeUnknownEffect(schema, parseOptions.params) + ) const payloadSchemas = HttpApiEndpoint.getPayloadSchemas(endpoint) const encodePayload = Arr.isArrayNonEmpty(payloadSchemas) ? - HttpMethod.hasBody(endpoint.method) - ? Schema.encodeUnknownEffect(getEncodePayloadSchema(payloadSchemas, endpoint.method)) - : Schema.encodeUnknownEffect(Schema.Union(payloadSchemas)) : + Schema.encodeUnknownEffect( + HttpMethod.hasBody(endpoint.method) + ? getEncodePayloadSchema(payloadSchemas, endpoint.method) + : Schema.Union(payloadSchemas), + parseOptions.payload + ) : undefined - const encodeHeaders = UndefinedOr.map(endpoint.headers, Schema.encodeUnknownEffect) - const encodeQuery = UndefinedOr.map(endpoint.query, Schema.encodeUnknownEffect) + const encodeHeaders = UndefinedOr.map( + endpoint.headers, + (schema) => Schema.encodeUnknownEffect(schema, parseOptions.headers) + ) + const encodeQuery = UndefinedOr.map( + endpoint.query, + (schema) => Schema.encodeUnknownEffect(schema, parseOptions.query) + ) const middlewareKeys = Array.from(onEndpointOptions.middleware, (tag) => `${tag.key}/Client`) @@ -473,6 +508,7 @@ export const makeClient = "https://api.example.com/users/123" * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -666,14 +706,15 @@ export const urlBuilder = (api: Api, options?: { if (group.topLevel) return InternalRecord.assignProperty(builder, group.identifier, {}) }, - onEndpoint({ group, endpoint }) { - const makeUrl = compilePath(endpoint.path) + onEndpoint({ group, endpoint, mergedAnnotations }) { + const parseOptions = HttpApi.getSlotParseOptions(mergedAnnotations) + const makeUrl = compilePath(endpoint.path, endpoint.params) const encodeParams = endpoint.params === undefined ? undefined - : Schema.encodeSync(endpoint.params as unknown as Schema.ConstraintEncoder) + : Schema.encodeSync(endpoint.params as unknown as Schema.ConstraintEncoder, parseOptions.params) const encodeQuery = endpoint.query === undefined ? undefined - : Schema.encodeSync(endpoint.query as unknown as Schema.ConstraintEncoder) + : Schema.encodeSync(endpoint.query as unknown as Schema.ConstraintEncoder, parseOptions.query) const endpointBuilder = (request?: { readonly params?: unknown @@ -716,14 +757,16 @@ export const urlBuilder = (api: Api, options?: { const paramsRegExp = /(\/?):(\w+)(\?)?/g -const compilePath = (path: string) => { - if (!paramsRegExp.test(path)) { +const compilePath = (path: string, schema: Schema.Top | undefined) => { + if (schema === undefined || !path.includes(":")) { return (_: any) => path } - paramsRegExp.lastIndex = 0 + const paramNames = HttpApiPath.getParamNames(schema) return (params: Record) => { - paramsRegExp.lastIndex = 0 - return path.replace(paramsRegExp, (_, slash: string, key: string, optional: string | undefined) => { + return path.replace(paramsRegExp, (match, slash: string, key: string, optional: string | undefined) => { + if (paramNames !== undefined && !paramNames.has(key)) { + return match + } const value = params[key] if (value === undefined) { if (optional !== undefined) { @@ -736,15 +779,19 @@ const compilePath = (path: string) => { } } -function schemasToResponse(schemas: readonly [Schema.Constraint, ...Array]) { +function schemasToResponse( + schemas: readonly [Schema.Constraint, ...Array], + options: SchemaAST.ParseOptions | undefined, + headersOptions: SchemaAST.ParseOptions | undefined +): ResponseDecoder { const hasWithHeaders = schemas.some((schema) => HttpApiSchema.isWithHeaders(schema) || HttpApiSchema.getWithHeadersAnnotation(schema.ast) !== undefined ) const codec = hasWithHeaders - ? Schema.Union(schemas.map(toCodecArrayBufferWithHeaders)) + ? Schema.Union(schemas.map((schema) => toCodecArrayBufferWithHeaders(schema, options, headersOptions))) : toCodecArrayBuffer(schemas) - const decode = Schema.decodeEffect(codec) - return (response: HttpClientResponse.HttpClientResponse) => + const decode = Schema.decodeUnknownEffect(codec, options) + return (response) => Effect.flatMap( response.arrayBuffer, hasWithHeaders @@ -753,28 +800,39 @@ function schemasToResponse(schemas: readonly [Schema.Constraint, ...Array isWithHeaders ? HttpApiSchema.withHeaders(value) : value.body, - encode: (value: any) => isWithHeaders ? value : { body: value, headers: undefined } - }) as any - ) - ) + const parts: { + readonly body: Schema.Constraint + readonly headers: Schema.Top + readonly toValue: (pair: { readonly body: unknown; readonly headers: unknown }) => unknown + } = annotation !== undefined + ? { body: annotation.bodyCodec, headers: annotation.headersCodec, toValue: annotation.transformation.decode } + : HttpApiSchema.isWithHeaders(schema) + ? { body: schema.schema, headers: schema.headers, toValue: HttpApiSchema.withHeaders } + : { body: schema, headers: Schema.Unknown, toValue: (pair) => pair.body } + const decodeBody = Schema.decodeUnknownEffect(fromArrayBuffer(parts.body).pipe(Schema.decodeTo(parts.body)), options) + const decodeHeaders = Schema.decodeUnknownEffect(parts.headers, headersOptions) + return ResponsePair.pipe(Schema.decodeTo( + HttpApiSchema.isWithHeaders(schema) ? schema : Schema.toType(schema), + SchemaTransformation.transformEffect({ + decode: (pair) => + Effect.all({ body: decodeBody(pair.body), headers: decodeHeaders(pair.headers) }).pipe( + Effect.map(parts.toValue), + Effect.mapError((error) => error.issue) + ), + encode: (input, options) => + Effect.fail(new SchemaIssue.Forbidden({ message: "Decode only schema" }, input, options)) + }) + )) } type ResponseDecoder = ( @@ -854,7 +912,7 @@ function failUnsupportedContentType( ) } -const reservedStreamFailureEvent = "effect/httpapi/stream/failure" +const reservedStreamFailureEvent = "effect/http-api/stream/failure" type StreamSuccessSchema = | HttpApiSchema.StreamSchema @@ -875,14 +933,18 @@ function getStreamSuccessSchemas(endpoint: HttpApiEndpoint.Top): Array Effect.map(Effect.context(), (context) => @@ -894,7 +956,7 @@ function streamToResponse(successSchema: StreamSuccessSchema) { )) if (!isWithHeaders) return toStream - const decodeHeaders = Schema.decodeUnknownEffect(successSchema.headers) + const decodeHeaders = Schema.decodeUnknownEffect(successSchema.headers, headersOptions) return (response: HttpClientResponse.HttpClientResponse, sseOptions?: Sse.DecodeOptions) => Effect.flatMap( decodeHeaders(response.headers), @@ -903,17 +965,20 @@ function streamToResponse(successSchema: StreamSuccessSchema) { } function makeSseDecoder( - declaration: HttpApiSchema.StreamSse + declaration: HttpApiSchema.StreamSse, + parseOptions: SchemaAST.ParseOptions | undefined ) { const Event = Schema.Union([ Schema.Struct({ + id: Schema.optional(Schema.String), event: Schema.Literal(reservedStreamFailureEvent), data: Schema.fromJsonString(Schema.toCodecJson(Schema.Cause(declaration.error, Schema.Defect()))) }), declaration.events ]) - const defaultDecoder = Sse.decodeSchema(Event) - return (options?: Sse.DecodeOptions) => options === undefined ? defaultDecoder : Sse.decodeSchema(Event, options) + const defaultDecoder = Sse.decodeSchema(Event, undefined, parseOptions) + return (options?: Sse.DecodeOptions) => + options === undefined ? defaultDecoder : Sse.decodeSchema(Event, options, parseOptions) } function decodeSseStream( diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiEndpoint.ts b/repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts similarity index 94% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiEndpoint.ts rename to repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts index 4da6a7c64d..7149265468 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiEndpoint.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts @@ -9,37 +9,39 @@ * constructors, payload and response schema helpers, and type utilities used by * builders and generated clients. * + * @stability unstable * @since 4.0.0 */ -import * as Arr from "../../Array.ts" -import type { Brand } from "../../Brand.ts" -import * as Context from "../../Context.ts" -import type { Effect } from "../../Effect.ts" -import { identity, memoize } from "../../Function.ts" -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as AST from "../../SchemaAST.ts" -import type * as Stream from "../../Stream.ts" -import type { Simplify } from "../../Struct.ts" -import type * as Types from "../../Types.ts" +import * as Arr from "../Array.ts" +import type { Brand } from "../Brand.ts" +import * as Context from "../Context.ts" +import type { Effect } from "../Effect.ts" import type * as Sse from "../encoding/Sse.ts" +import { identity, memoize } from "../Function.ts" import type { HttpMethod } from "../http/HttpMethod.ts" import * as HttpRouter from "../http/HttpRouter.ts" import type { HttpServerRequest } from "../http/HttpServerRequest.ts" import type { HttpServerResponse } from "../http/HttpServerResponse.ts" import type * as Multipart from "../http/Multipart.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as AST from "../SchemaAST.ts" +import type * as Stream from "../Stream.ts" +import type { Simplify } from "../Struct.ts" +import type * as Types from "../Types.ts" import type * as HttpApiGroup from "./HttpApiGroup.ts" import type * as HttpApiMiddleware from "./HttpApiMiddleware.ts" import * as HttpApiSchema from "./HttpApiSchema.ts" import * as MediaType from "./internal/mediaType.ts" -const TypeId = "~effect/httpapi/HttpApiEndpoint" +const TypeId = "~effect/http-api/HttpApiEndpoint" /** * Returns `true` when a value is an `HttpApiEndpoint`, narrowing the value to the * endpoint interface. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -136,6 +138,7 @@ type RequestRawFromParts = * Maps normalized media types to a payload encoding strategy and one or more * schemas. Each schema retains its declared content type in its encoding annotation. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -148,6 +151,7 @@ export type PayloadMap = ReadonlyMap } -/** @internal */ +/** + * @internal + */ export function getPayloadSchemas(endpoint: Top): Array { const result: Array = [] for (const { schemas } of endpoint.payload.values()) { @@ -275,13 +281,17 @@ export function getPayloadSchemas(endpoint: Top): Array { return result } -/** @internal */ +/** + * @internal + */ export function getSuccessSchemas(endpoint: Top): [Schema.Top, ...Array] { const schemas = Array.from(endpoint.success) return Arr.isArrayNonEmpty(schemas) ? schemas : [HttpApiSchema.NoContent] } -/** @internal */ +/** + * @internal + */ export function getErrorSchemas(endpoint: Top): Array { const schemas = new Set(endpoint.error) const transform = endpoint.disableCodecs ? identity : transformResponseSchema @@ -298,6 +308,7 @@ export function getErrorSchemas(endpoint: Top): Array { * A widened `HttpApiEndpoint` type used when the concrete method, path, schemas, * and middleware types are not needed. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -313,6 +324,7 @@ export interface Constraint { /** * A widened endpoint type that preserves request and middleware pipeline phantom fields. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -328,6 +340,7 @@ export interface ConstraintRequest extends Constraint { * A widened endpoint type that preserves concrete runtime properties such as * method, path, schemas, annotations, and middleware sets. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -350,6 +363,7 @@ export interface Top extends /** * Extracts the endpoint identifier literal from an `HttpApiEndpoint`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -358,6 +372,7 @@ export type Identifier = Endpoint extends Constraint ? Endpoint["ident /** * Extracts the success schema associated with an endpoint. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -366,6 +381,7 @@ export type Success = Endpoint extends Constraint ? Endpoint["~Success /** * Extracts the error schema associated with an endpoint. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -374,6 +390,7 @@ export type Error = Endpoint extends Constraint ? Endpoint["~Error"] : /** * Extracts the schema used for an endpoint's path parameters. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -383,6 +400,7 @@ export type Params = Endpoint extends ConstraintRequest ? Endpoint["~P /** * Extracts the schema used for an endpoint's query parameters. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -392,6 +410,7 @@ export type Query = Endpoint extends ConstraintRequest ? Endpoint["~Qu /** * Extracts the schema used for an endpoint's request payload. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -401,6 +420,7 @@ export type Payload = Endpoint extends ConstraintRequest ? Endpoint["~ /** * Extracts the schema used for an endpoint's request headers. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -410,6 +430,7 @@ export type Headers = Endpoint extends ConstraintRequest ? Endpoint["~ /** * Extracts the middleware identifiers attached to an endpoint. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -419,6 +440,7 @@ export type Middleware = Endpoint extends { readonly "~Middleware": in /** * Computes the services provided by the middleware attached to an endpoint. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -427,6 +449,7 @@ export type MiddlewareProvides = HttpApiMiddleware.Provides = HttpApiMiddleware.MiddlewareClient = HttpApiMiddleware.Error = Endpoint extends ConstraintRequest ? * Computes the services required to encode an endpoint's error responses, * including services required by middleware error encoders. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -469,6 +495,7 @@ export type ErrorServicesEncode = Endpoint extends ConstraintRequest ? * available params, query, payload, headers, the raw request, endpoint, and group. * Multipart stream payloads are exposed as streams of parts. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -480,6 +507,7 @@ export type Request = Endpoint extends ConstraintRequest ? Endpoint["~ * params, query, and headers plus the raw request, endpoint, and group, while * leaving payload handling to the raw request. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -492,6 +520,7 @@ export type RequestRaw = Endpoint extends ConstraintRequest ? Endpoint * response mode and SSE decoding options. Multipart payloads are supplied as * `FormData`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -525,6 +554,7 @@ export type ClientRequest< * Controls what a generated client method returns: the decoded success value, * the decoded value paired with the raw response, or only the raw response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -534,6 +564,7 @@ export type ClientResponseMode = "decoded-only" | "decoded-and-response" | "resp * Computes the services required on the server to decode endpoint inputs and * encode endpoint success, error, and middleware error responses. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -551,6 +582,7 @@ export type ServerServices = Endpoint extends ConstraintRequest ? * Computes the services required on the client to encode endpoint requests and * decode endpoint success or error responses. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -566,6 +598,7 @@ export type ClientServices = Endpoint extends ConstraintRequest ? /** * Extracts the additional services required by middleware applied to an endpoint. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -576,6 +609,7 @@ export type MiddlewareServices = Endpoint extends { readonly "~Middlew * Computes the services required to decode an endpoint's error responses, * including services required by middleware error decoders. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -588,6 +622,7 @@ export type ErrorServicesDecode = Endpoint extends ConstraintRequest ? * The normal server handler for an endpoint, accepting the decoded request shape * and returning either the endpoint success value or a custom `HttpServerResponse`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -599,6 +634,7 @@ export type Handler = ( * The raw server handler for an endpoint, receiving a request shape without a * decoded payload so the handler can read the raw `HttpServerRequest` directly. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -609,6 +645,7 @@ export type HandlerRaw = ( /** * Selects the endpoint with the specified identifier from a union of endpoints. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -620,6 +657,7 @@ export type WithIdentifier = Extract< /** * Removes endpoints with the specified identifier from a union of endpoints. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -632,6 +670,7 @@ export type ExcludeIdentifier = Exclude< * Derives the normal handler type for the endpoint with the specified identifier * in an endpoint union. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -645,6 +684,7 @@ export type HandlerWithIdentifier = Exclude< * Returns an endpoint type with the supplied path prefix prepended while * preserving the endpoint's schemas, method, errors, and middleware. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -771,6 +819,7 @@ export type AddPrefix = Endpoint * Returns an endpoint type with additional middleware applied and the endpoint's * middleware service requirements updated accordingly. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -894,6 +943,7 @@ function makeProto< * Constraint for path parameter schemas: each parameter must encode to * `string | undefined`, or the schema must encode to a record of those values. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -905,6 +955,7 @@ export type ParamsConstraint = * Constraint for header schemas: each header must encode to `string | undefined`, * or the schema must encode to a record of those values. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -916,6 +967,7 @@ export type HeadersConstraint = * Constraint for query schemas: each field must encode to `string`, an array of * strings, or `undefined`, or the schema must encode to a record of those values. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -931,6 +983,7 @@ export type QueryConstraint = * - for body methods, payload may be any `Schema.Top` (or content-type keyed * schemas) and OpenAPI uses `requestBody` instead of `parameters` * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -945,6 +998,7 @@ export type PayloadConstraint = Method extends HttpMe * accept field records for query-style encoding, while body methods accept one or * more schemas. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -956,6 +1010,7 @@ export type PayloadConstraintCodecs = Method extends * Constraint for success response schemas, allowing either a single schema or a * readonly array of schemas. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -965,6 +1020,7 @@ export type SuccessConstraint = Schema.Top | ReadonlyArray * Constraint for error response schemas, allowing either a single schema or a * readonly array of schemas. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -985,6 +1041,7 @@ type ErrorNoStream = [ * and response schemas, applying automatic JSON or string-tree codecs unless * `disableCodecs` is enabled. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1157,7 +1214,7 @@ function getPayload( return result } -const reservedStreamFailureEvent = "effect/httpapi/stream/failure" +const reservedStreamFailureEvent = "effect/http-api/stream/failure" function getSuccessResponse( success: Schema.Top | ReadonlyArray | undefined, @@ -1363,14 +1420,11 @@ function transformResponse(schema: Schema.Top): Schema.Top { if (withHeaders === undefined) { return applyResponseEncoding(schema, encoding) } - const headers = Schema.toEncoded(withHeaders.headers) - return Schema.Struct({ - body: applyResponseEncoding(Schema.toEncoded(withHeaders.body), encoding), - headers - }).pipe(Schema.decodeTo(schema)).annotate({ + return schema.annotate({ "~httpApiWithHeaders": { ...withHeaders, - headersCodec: Schema.toCodecStringTree(headers) + bodyCodec: applyResponseEncoding(withHeaders.body, encoding), + headersCodec: Schema.toCodecStringTree(withHeaders.headers) } }) } @@ -1404,14 +1458,25 @@ function transformPayload(schema: Schema.Top, method: HttpMethod): Schema.Top { /** * Creates a `GET` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ export const get = make("GET") +/** + * Creates a `QUERY` endpoint declaration. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const query = make("QUERY") + /** * Creates a `POST` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1420,6 +1485,7 @@ export const post = make("POST") /** * Creates a `PUT` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1428,6 +1494,7 @@ export const put = make("PUT") /** * Creates a `PATCH` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1435,10 +1502,14 @@ export const patch = make("PATCH") const del = make("DELETE") +/** + * @stability unstable + */ export { /** * Creates a `DELETE` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1448,6 +1519,7 @@ export { /** * Creates a `HEAD` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -1456,6 +1528,7 @@ export const head = make("HEAD") /** * Creates an `OPTIONS` endpoint declaration. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiError.ts b/repos/effect/packages/effect/src/http-api/HttpApiError.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiError.ts rename to repos/effect/packages/effect/src/http-api/HttpApiError.ts index 48654e87d4..b01e145c23 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiError.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiError.ts @@ -7,15 +7,16 @@ * middleware error declarations and are understood by builders, generated * clients, reflection, and OpenAPI generation. * + * @stability unstable * @since 4.0.0 */ -import * as Data from "../../Data.ts" -import * as Effect from "../../Effect.ts" -import * as ErrorReporter from "../../ErrorReporter.ts" -import { hasProperty } from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" +import * as Data from "../Data.ts" +import * as Effect from "../Effect.ts" +import * as ErrorReporter from "../ErrorReporter.ts" import * as HttpServerRespondable from "../http/HttpServerRespondable.ts" import * as HttpServerResponse from "../http/HttpServerResponse.ts" +import { hasProperty } from "../Predicate.ts" +import * as Schema from "../Schema.ts" import * as HttpApiSchema from "./HttpApiSchema.ts" const badRequestResponse = HttpServerResponse.empty({ status: 400 }) @@ -36,6 +37,7 @@ const serviceUnavailableResponse = HttpServerResponse.empty({ status: 503 }) * Built-in HTTP API error for a `400 Bad Request` response. When used directly as * a server response, it renders as an empty response with status 400. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -56,6 +58,7 @@ export class BadRequest extends Schema.Error("effect/HttpApiError/Ba * No-content schema variant for `BadRequest`, decoding an empty 400 response into * a `BadRequest` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -67,6 +70,7 @@ export const BadRequestNoContent = BadRequest.pipe(HttpApiSchema.asNoContent({ * Built-in HTTP API error for a `401 Unauthorized` response. When used directly as * a server response, it renders as an empty response with status 401. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -86,6 +90,7 @@ export class Unauthorized extends Schema.Error("effect/HttpApiErro * No-content schema variant for `Unauthorized`, decoding an empty 401 response * into an `Unauthorized` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -97,6 +102,7 @@ export const UnauthorizedNoContent = Unauthorized.pipe(HttpApiSchema.asNoContent * Built-in HTTP API error for a `403 Forbidden` response. When used directly as a * server response, it renders as an empty response with status 403. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -116,6 +122,7 @@ export class Forbidden extends Schema.Error("effect/HttpApiError/Forb * No-content schema variant for `Forbidden`, decoding an empty 403 response into a * `Forbidden` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -127,6 +134,7 @@ export const ForbiddenNoContent = Forbidden.pipe(HttpApiSchema.asNoContent({ * Built-in HTTP API error for a `404 Not Found` response. When used directly as a * server response, it renders as an empty response with status 404. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -146,6 +154,7 @@ export class NotFound extends Schema.Error("effect/HttpApiError/NotFou * No-content schema variant for `NotFound`, decoding an empty 404 response into a * `NotFound` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -157,6 +166,7 @@ export const NotFoundNoContent = NotFound.pipe(HttpApiSchema.asNoContent({ * Built-in HTTP API error for a `405 Method Not Allowed` response. When used * directly as a server response, it renders as an empty response with status 405. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -176,6 +186,7 @@ export class MethodNotAllowed extends Schema.Error("effect/Htt * No-content schema variant for `MethodNotAllowed`, decoding an empty 405 response * into a `MethodNotAllowed` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -187,6 +198,7 @@ export const MethodNotAllowedNoContent = MethodNotAllowed.pipe(HttpApiSchema.asN * Built-in HTTP API error for a `406 Not Acceptable` response. When used directly * as a server response, it renders as an empty response with status 406. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -206,6 +218,7 @@ export class NotAcceptable extends Schema.Error("effect/HttpApiEr * No-content schema variant for `NotAcceptable`, decoding an empty 406 response * into a `NotAcceptable` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -217,6 +230,7 @@ export const NotAcceptableNoContent = NotAcceptable.pipe(HttpApiSchema.asNoConte * Built-in HTTP API error for a `408 Request Timeout` response. When used directly * as a server response, it renders as an empty response with status 408. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -236,6 +250,7 @@ export class RequestTimeout extends Schema.Error("effect/HttpApi * No-content schema variant for `RequestTimeout`, decoding an empty 408 response * into a `RequestTimeout` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -247,6 +262,7 @@ export const RequestTimeoutNoContent = RequestTimeout.pipe(HttpApiSchema.asNoCon * Built-in HTTP API error for a `409 Conflict` response. When used directly as a * server response, it renders as an empty response with status 409. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -266,6 +282,7 @@ export class Conflict extends Schema.Error("effect/HttpApiError/Confli * No-content schema variant for `Conflict`, decoding an empty 409 response into a * `Conflict` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -277,6 +294,7 @@ export const ConflictNoContent = Conflict.pipe(HttpApiSchema.asNoContent({ * Built-in HTTP API error for a `410 Gone` response. When used directly as a * server response, it renders as an empty response with status 410. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -296,6 +314,7 @@ export class Gone extends Schema.Error("effect/HttpApiError/Gone")({ * No-content schema variant for `Gone`, decoding an empty 410 response into a * `Gone` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -307,6 +326,7 @@ export const GoneNoContent = Gone.pipe(HttpApiSchema.asNoContent({ * Built-in HTTP API error for a `422 Unprocessable Entity` response. When used * directly as a server response, it renders as an empty response with status 422. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -326,6 +346,7 @@ export class UnprocessableEntity extends Schema.Error("effe * No-content schema variant for `UnprocessableEntity`, decoding an empty 422 * response into an `UnprocessableEntity` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -337,6 +358,7 @@ export const UnprocessableEntityNoContent = UnprocessableEntity.pipe(HttpApiSche * Built-in HTTP API error for a `500 Internal Server Error` response. When used * directly as a server response, it renders as an empty response with status 500. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -355,6 +377,7 @@ export class InternalServerError extends Schema.Error("effe * No-content schema variant for `InternalServerError`, decoding an empty 500 * response into an `InternalServerError` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -366,6 +389,7 @@ export const InternalServerErrorNoContent = InternalServerError.pipe(HttpApiSche * Built-in HTTP API error for a `501 Not Implemented` response. When used directly * as a server response, it renders as an empty response with status 501. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -384,6 +408,7 @@ export class NotImplemented extends Schema.Error("effect/HttpApi * No-content schema variant for `NotImplemented`, decoding an empty 501 response * into a `NotImplemented` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -395,6 +420,7 @@ export const NotImplementedNoContent = NotImplemented.pipe(HttpApiSchema.asNoCon * Built-in HTTP API error for a `503 Service Unavailable` response. When used * directly as a server response, it renders as an empty response with status 503. * + * @stability unstable * @category errors * @since 4.0.0 */ @@ -413,6 +439,7 @@ export class ServiceUnavailable extends Schema.Error("effect * No-content schema variant for `ServiceUnavailable`, decoding an empty 503 * response into a `ServiceUnavailable` error value. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -423,24 +450,27 @@ export const ServiceUnavailableNoContent = ServiceUnavailable.pipe(HttpApiSchema /** * Type-level identifier used to mark `HttpApiSchemaError` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ -export type HttpApiSchemaErrorTypeId = "~effect/httpapi/HttpApiError/HttpApiSchemaError" +export type HttpApiSchemaErrorTypeId = "~effect/http-api/HttpApiError/HttpApiSchemaError" /** * Runtime identifier used to mark and detect `HttpApiSchemaError` values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ -export const HttpApiSchemaErrorTypeId: HttpApiSchemaErrorTypeId = "~effect/httpapi/HttpApiError/HttpApiSchemaError" +export const HttpApiSchemaErrorTypeId: HttpApiSchemaErrorTypeId = "~effect/http-api/HttpApiError/HttpApiSchemaError" /** * Error raised when an HTTP API request or response component fails schema * decoding or encoding. It records which component failed and responds as an * empty `400 Bad Request` when rendered as a server response. * + * @stability unstable * @category errors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiGroup.ts b/repos/effect/packages/effect/src/http-api/HttpApiGroup.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiGroup.ts rename to repos/effect/packages/effect/src/http-api/HttpApiGroup.ts index 66feb7486a..de6bcf708c 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiGroup.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiGroup.ts @@ -8,24 +8,26 @@ * groups, adding endpoints, prefixing paths, applying middleware, annotating * groups or endpoints, and deriving builder or client types. * + * @stability unstable * @since 4.0.0 */ -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import * as Context from "../../Context.ts" -import * as InternalRecord from "../../internal/record.ts" -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import * as Predicate from "../../Predicate.ts" -import * as Record from "../../Record.ts" +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Context from "../Context.ts" import type { PathInput } from "../http/HttpRouter.ts" +import * as InternalRecord from "../internal/record.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import * as Predicate from "../Predicate.ts" +import * as Record from "../Record.ts" import type * as HttpApiEndpoint from "./HttpApiEndpoint.ts" import type * as HttpApiMiddleware from "./HttpApiMiddleware.ts" -const TypeId = "~effect/httpapi/HttpApiGroup" +const TypeId = "~effect/http-api/HttpApiGroup" /** * Returns `true` when a value is an `HttpApiGroup`, narrowing the value to the * group interface. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -46,6 +48,7 @@ type EndpointMap = { * * Endpoint implementations can be provided later with `HttpApiBuilder.group`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -136,6 +139,7 @@ export interface HttpApiGroup< * id and the group identifier so the relationship between an API and its * implemented groups is checked at compile time. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -154,6 +158,7 @@ export interface Service { * When given an API id and a group or union of groups, this type maps each group * to the `Service` identity that must be provided by `HttpApiBuilder.group`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -165,6 +170,7 @@ export type ToService = Group ex * A widened `HttpApiGroup` type used when the concrete group identifier, * endpoints, and top-level flag are not needed. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -179,6 +185,7 @@ export interface Constraint { * A widened group type that preserves concrete runtime properties such as * identifier, key, top-level status, endpoints, and annotations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -187,6 +194,7 @@ export interface Top extends HttpApiGroup /** * Selects the group with the specified identifier from a union of groups. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -195,6 +203,7 @@ export type WithIdentifier = Extract = Group extends Constraint ? Group["identifier"] : /** * Extracts the endpoint union contained in an `HttpApiGroup`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -214,6 +224,7 @@ export type Endpoints = Group extends HttpApiGroup = HttpApiEndpoint.ErrorServicesEncode = HttpApiEndpoint.ErrorServicesDecode = HttpApiEndpoint.MiddlewareError = HttpApiEndpoint.MiddlewareProvides = HttpApiEndpoint.MiddlewareClient = HttpApiEndpoint.MiddlewareServices = Group extends HttpApiGroup = Group extends /** * Returns the type of a group after applying a middleware identifier to every endpoint in the group. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -375,7 +395,7 @@ const makeProto = < }): HttpApiGroup => { function HttpApiGroup() {} Object.setPrototypeOf(HttpApiGroup, Proto) - HttpApiGroup.key = `effect/httpapi/HttpApiGroup/${options.identifier}` + HttpApiGroup.key = `effect/http-api/HttpApiGroup/${options.identifier}` return Object.assign(HttpApiGroup, options) as any } @@ -388,6 +408,7 @@ const makeProto = < * and set `topLevel` when the generated client should expose endpoint methods * directly instead of nesting them under the group identifier. * + * @stability unstable * @category constructors * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts b/repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts rename to repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts index bcdcd9f86b..3a547822c3 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts @@ -9,35 +9,37 @@ * decoration. This module defines the middleware service keys and helpers used * by `HttpApi` declarations. * + * @stability unstable * @since 4.0.0 */ /** @effect-diagnostics floatingEffect:skip-file */ /** @effect-diagnostics classSelfMismatch:off */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import { getStackTraceLimit, setStackTraceLimit } from "../../internal/stackTraceLimit.ts" -import * as Layer from "../../Layer.ts" -import { hasProperty } from "../../Predicate.ts" -import type * as Schema from "../../Schema.ts" -import { Scope } from "../../Scope.ts" -import type { unhandled } from "../../Types.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" import type * as HttpClientError from "../http/HttpClientError.ts" import type * as HttpClientRequest from "../http/HttpClientRequest.ts" import type * as HttpClientResponse from "../http/HttpClientResponse.ts" import type * as HttpRouter from "../http/HttpRouter.ts" import type { HttpServerResponse } from "../http/HttpServerResponse.ts" +import { getStackTraceLimit, setStackTraceLimit } from "../internal/stackTraceLimit.ts" +import * as Layer from "../Layer.ts" +import { hasProperty } from "../Predicate.ts" +import type * as Schema from "../Schema.ts" +import { Scope } from "../Scope.ts" +import type { unhandled } from "../Types.ts" import type * as HttpApiEndpoint from "./HttpApiEndpoint.ts" import { HttpApiSchemaError } from "./HttpApiError.ts" import type * as HttpApiGroup from "./HttpApiGroup.ts" import type * as HttpApiSecurity from "./HttpApiSecurity.ts" -const TypeId = "~effect/httpapi/HttpApiMiddleware" +const TypeId = "~effect/http-api/HttpApiMiddleware" -const SecurityTypeId = "~effect/httpapi/HttpApiMiddleware/Security" +const SecurityTypeId = "~effect/http-api/HttpApiMiddleware/Security" /** * Returns `true` when an HTTP API middleware service is security middleware. * + * @stability unstable * @category guards * @since 4.0.0 */ @@ -58,6 +60,7 @@ type ErrorSchemaFromConstraint = E extends ReadonlyArray ? * a new response effect that may require additional services and fail with the * middleware's declared error schema. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -77,6 +80,7 @@ export type HttpApiMiddleware = ( * Each property handles the credential decoded for that scheme and wraps the * endpoint response effect with the middleware's declared requirements and errors. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -108,6 +112,7 @@ export type HttpApiMiddlewareSecurity< * It receives endpoint/group metadata, the outgoing request, and a `next` function * for continuing the request pipeline. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -125,6 +130,7 @@ export interface HttpApiMiddlewareClient<_E, CE, R> { /** * Client-side service marker required when a middleware declares `requiredForClient`. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -136,6 +142,7 @@ export interface ForClient { /** * Base service key shape for HTTP API middleware services, including provided services, declared error schemas, and client requirements. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -150,6 +157,7 @@ export interface AnyService extends Context.Key { /** * Middleware service key shape for security middleware, including the security schemes handled by the service. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -161,6 +169,7 @@ export interface AnyServiceSecurity extends AnyService { /** * Type-level identifier carried by middleware services to track provided services, required services, errors, client errors, and client requirements. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -177,6 +186,7 @@ export interface AnyId { /** * Extracts the services provided by a middleware identifier. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -185,6 +195,7 @@ export type Provides = A extends { readonly [TypeId]: { readonly provides: in /** * Extracts the services required to run a middleware implementation. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -193,6 +204,7 @@ export type Requires = A extends { readonly [TypeId]: { readonly requires: in /** * Applies a middleware's service changes to an existing requirement type by removing services it provides and adding services it requires. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -201,6 +213,7 @@ export type ApplyServices = Exclude> | Requir /** * Extracts the schema or schema union used for errors declared by a middleware identifier. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -210,6 +223,7 @@ export type ErrorSchema = A extends { readonly [TypeId]: { readonly error: in /** * Extracts the decoded error type declared by a middleware identifier. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -218,6 +232,7 @@ export type Error = ErrorSchema["Type"] /** * Extracts the client-side error type for middleware that is required on generated clients. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -232,6 +247,7 @@ export type ClientError = A extends { /** * Computes the client-side service marker required for middleware that must also run in generated clients. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -245,6 +261,7 @@ export type MiddlewareClient = A extends { /** * Extracts the schema services required to encode errors declared by a middleware identifier. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -253,6 +270,7 @@ export type ErrorServicesEncode = ErrorSchema["EncodingServices"] /** * Extracts the schema services required to decode errors declared by a middleware identifier. * + * @stability unstable * @category utility types * @since 4.0.0 */ @@ -266,6 +284,7 @@ export type ErrorServicesDecode = ErrorSchema["DecodingServices"] * It combines a `Context.Service` class with the middleware metadata used by * endpoints, builders, and generated clients. * + * @stability unstable * @category services * @since 4.0.0 */ @@ -314,6 +333,7 @@ export type ServiceClass< * required services, provided services, typed error schemas, security schemes, * client errors, or a matching client middleware requirement. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -398,13 +418,13 @@ function getError(error: ErrorConstraint | undefined): ReadonlySet { * * ```ts import.meta.vitest * import { Effect, Schema, type Types } from "effect" - * import { HttpRouter, HttpServerResponse } from "effect/unstable/http" + * import { HttpRouter, HttpServerResponse } from "effect/http" * import { * HttpApiEndpoint, * HttpApiError, * HttpApiGroup, * HttpApiMiddleware - * } from "effect/unstable/httpapi" + * } from "effect/http-api" * * class CustomError extends Schema.TaggedError()("CustomError", {}) {} * @@ -462,6 +482,7 @@ function getError(error: ErrorConstraint | undefined): ReadonlySet { * const result = [messages, body] // => [["Mapping Body schema error"], "CustomError"] * ``` * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -500,6 +521,7 @@ export const layerSchemaErrorTransform = ( * The route serves the OpenAPI specification generated from the API at the * configured path, defaulting to `/docs`. * + * @stability unstable * @category layers * @since 4.0.0 */ @@ -237,6 +241,7 @@ export const layer = * configured path, defaulting to `/docs`; `version` selects the Scalar package * version loaded from the CDN. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSchema.ts b/repos/effect/packages/effect/src/http-api/HttpApiSchema.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiSchema.ts rename to repos/effect/packages/effect/src/http-api/HttpApiSchema.ts index f1f425a41f..617a816094 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSchema.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiSchema.ts @@ -7,20 +7,21 @@ * can choose response status codes, content types, body codecs, multipart * handling, and no-body response behavior. * + * @stability unstable * @since 4.0.0 */ -import { constVoid, type LazyArg } from "../../Function.ts" -import * as Predicate from "../../Predicate.ts" -import * as Schema from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import * as SchemaTransformation from "../../SchemaTransformation.ts" -import * as Stream from "../../Stream.ts" -import type * as Sse from "../encoding/Sse.ts" +import * as Sse from "../encoding/Sse.ts" +import { constVoid, type LazyArg } from "../Function.ts" import { hasBody, type HttpMethod } from "../http/HttpMethod.ts" import * as HttpStatus from "../http/HttpStatus.ts" import type * as Multipart_ from "../http/Multipart.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import * as SchemaTransformation from "../SchemaTransformation.ts" +import * as Stream from "../Stream.ts" -declare module "../../Schema.ts" { +declare module "../Schema.ts" { namespace Annotations { interface Augment { readonly httpApiStatus?: number | undefined @@ -48,12 +49,25 @@ declare module "../../Schema.ts" { export interface WithHeadersAnnotation { readonly body: Schema.Top readonly headers: Schema.Top + /** + * Codecs from the body and headers types to their response representation. + * `HttpApiEndpoint` replaces them with the response encoding codecs. + */ + readonly bodyCodec: Schema.Top readonly headersCodec: Schema.Top + /** + * The user mapping between the `{ body, headers }` pair and the schema type. + */ + readonly transformation: { + readonly decode: (pair: { readonly body: unknown; readonly headers: unknown }) => unknown + readonly encode: (value: unknown) => { readonly body: unknown; readonly headers: unknown } + } } /** * HTTP API body encoding metadata used by payloads and responses. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -62,6 +76,7 @@ export type Encoding = PayloadEncoding | ResponseEncoding /** * HTTP API request payload encoding metadata. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -80,6 +95,7 @@ export type PayloadEncoding = /** * HTTP API response body encoding metadata. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -88,11 +104,12 @@ export type ResponseEncoding = { readonly contentType: string } -const StreamSchemaTypeId = "~effect/httpapi/HttpApiSchema/Stream" +const StreamSchemaTypeId = "~effect/http-api/HttpApiSchema/Stream" /** * Common HTTP status code literals accepted by {@link status}. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -107,15 +124,22 @@ export type StatusLiteral = HttpStatus.Literal * schema. You can pass either a numeric status code (for example, `201`) or a * common literal name (for example, `"Created"`). * + * @stability unstable * @category schemas * @since 4.0.0 */ export function status(code: number): { (self: S): S["Rebuild"] } +/** + * @stability unstable + */ export function status(code: StatusLiteral): { (self: S): S["Rebuild"] } +/** + * @stability unstable + */ export function status(code: number | StatusLiteral) { const statusCode = typeof code === "string" ? HttpStatus.fromLiteral(code) : code return (self: S): S["Rebuild"] => self.annotate({ httpApiStatus: statusCode }) @@ -127,6 +151,7 @@ export function status(code: number | StatusLiteral) { * * @see {@link NoContent} for the predefined 204 no content schema. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -135,6 +160,7 @@ export const Empty = (code: number): Schema.Void => Schema.Void.pipe(status(code /** * Type of the `NoContent` schema, a void schema annotated with HTTP status code 204. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -143,6 +169,7 @@ export interface NoContent extends Schema.Void {} /** * Schema for empty HTTP responses with status code 204. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -151,6 +178,7 @@ export const NoContent: NoContent = Empty(204) /** * Type of the `Created` schema, a void schema annotated with HTTP status code 201. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -159,6 +187,7 @@ export interface Created extends Schema.Void {} /** * Schema for empty HTTP responses with status code 201. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -167,6 +196,7 @@ export const Created: Created = Empty(201) /** * Type of the `Accepted` schema, a void schema annotated with HTTP status code 202. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -175,6 +205,7 @@ export interface Accepted extends Schema.Void {} /** * Schema for empty HTTP responses with status code 202. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -183,6 +214,7 @@ export const Accepted: Accepted = Empty(202) /** * Schema type returned by `asNoContent`, encoding as `void` while decoding to the original schema type. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -199,6 +231,7 @@ export interface asNoContent extends Schema.decodeT * @see {@link NoContent} for a void schema with the status code 204. * @see {@link Empty} for creating a void schema with a specific status code. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -223,6 +256,7 @@ type StreamMode = "sse" | "uint8array" /** * Mode describing whether an SSE stream emits full events or raw data values. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -242,12 +276,13 @@ export type StreamSseMode = "events" | "data" * * **Gotchas** * - * The client treats `effect/httpapi/stream/failure` as a stream failure only + * The client treats `effect/http-api/stream/failure` as a stream failure only * when its decoded `data` is a `Cause`. If an event schema accepts that name * dynamically but decodes `data` to another value, the client emits it as an * application event. Endpoint construction rejects event schemas that declare * the reserved name statically. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -282,13 +317,14 @@ export interface StreamSse< /** * Event schema produced when {@link StreamSse} is constructed from a JSON data schema. * + * @stability unstable * @category models * @since 4.0.0 */ export interface SseEventFromData extends Schema.ConstraintCodec< { - readonly id: string | undefined + readonly id?: string | undefined readonly event: string readonly data: Data["Type"] }, @@ -311,6 +347,7 @@ export interface SseEventFromData extends * server, client, and OpenAPI integrations. It is intentionally separate from * the buffered `asUint8Array` response encoding. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -334,6 +371,7 @@ export interface StreamUint8Array extends /** * Schema for a streaming HTTP API success response. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -344,6 +382,7 @@ const streamSchema = Schema.declare(Stream.isStream) /** * Creates a Server-Sent Events streaming success response schema. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -365,8 +404,7 @@ export const StreamSse: { readonly error?: Schema.Constraint | undefined }): StreamSse => { const events = options.events ?? (options.data === undefined ? undefined : Schema.Struct({ - id: Schema.UndefinedOr(Schema.String), - event: Schema.String, + ...Sse.EventEncoded.fields, data: Schema.fromJsonString(options.data) })) if (events === undefined) { @@ -386,6 +424,7 @@ export const StreamSse: { /** * Creates a streaming `Uint8Array` success response schema. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -399,15 +438,21 @@ export const StreamUint8Array = (options?: { contentType: options?.contentType ?? defaultStreamContentType("uint8array") }) -/** @internal */ +/** + * @internal + */ export const isStreamSchema = (u: unknown): u is StreamSchema => Schema.isSchema(u) && Predicate.hasProperty(u, StreamSchemaTypeId) -/** @internal */ +/** + * @internal + */ export const isStreamSse = (u: unknown): u is StreamSse => isStreamSchema(u) && u._tag === "StreamSse" -/** @internal */ +/** + * @internal + */ export const isStreamUint8Array = (u: unknown): u is StreamUint8Array => isStreamSchema(u) && u._tag === "StreamUint8Array" @@ -423,14 +468,16 @@ function defaultStreamContentType(mode: StreamMode): string { /** * Runtime brand key used to mark `WithHeaders` response schemas. * + * @stability unstable * @category type IDs * @since 4.0.0 */ -export const WithHeadersTypeId = "~effect/httpapi/HttpApiSchema/WithHeaders" +export const WithHeadersTypeId = "~effect/http-api/HttpApiSchema/WithHeaders" /** * Type-level brand identifier used by `WithHeaders`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -439,14 +486,16 @@ export type WithHeadersTypeId = typeof WithHeadersTypeId /** * Runtime brand key used to mark `WithHeaders` response values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ -export const WithHeadersValueTypeId = "~effect/httpapi/HttpApiSchema/WithHeadersValue" +export const WithHeadersValueTypeId = "~effect/http-api/HttpApiSchema/WithHeadersValue" /** * Type-level brand identifier used by `WithHeaders` response values. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -479,6 +528,7 @@ export type WithHeadersValueTypeId = typeof WithHeadersValueTypeId * - `Rebuild` preserves the brand and both parts, so `.annotate` keeps the * wrapper intact. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -507,6 +557,7 @@ export interface WithHeaders extends * `body` is the inner success value. For stream success schemas it is the * `Stream` itself, so headers are decided before the body starts streaming. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -516,7 +567,9 @@ export interface withHeaders { readonly headers: H } -/** @internal */ +/** + * @internal + */ export const isWithHeadersValue = (u: unknown): u is withHeaders => Predicate.hasProperty(u, WithHeadersValueTypeId) @@ -534,7 +587,7 @@ const withHeadersValueSchema = Schema.declare(isWithHeadersValue) * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { HttpApiSchema } from "effect/unstable/httpapi" + * import { HttpApiSchema } from "effect/http-api" * * const schema = HttpApiSchema.WithHeaders(Schema.String, { * "x-total-count": Schema.FiniteFromString @@ -549,6 +602,7 @@ const withHeadersValueSchema = Schema.declare(isWithHeadersValue) * response.headers // => { "x-total-count": 1 } * ``` * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -556,10 +610,16 @@ export function WithHeaders> +/** + * @stability unstable + */ export function WithHeaders( schema: S, headers: H ): WithHeaders +/** + * @stability unstable + */ export function WithHeaders( schema: Schema.Top, headers: Schema.Top | Schema.Struct.Fields @@ -586,6 +646,7 @@ export function WithHeaders( * See {@link WithHeaders} for an example that constructs a schema and its * corresponding response value. * + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -605,7 +666,7 @@ export const withHeaders = (options: { * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { HttpApiSchema } from "effect/unstable/httpapi" + * import { HttpApiSchema } from "effect/http-api" * * const schema = HttpApiSchema.WithHeaders(Schema.String, { * "x-request-id": Schema.String @@ -615,13 +676,16 @@ export const withHeaders = (options: { * HttpApiSchema.isWithHeaders(Schema.String) // => false * ``` * + * @stability unstable * @category predicates * @since 4.0.0 */ export const isWithHeaders = (u: unknown): u is WithHeaders => Schema.isSchema(u) && Predicate.hasProperty(u, WithHeadersTypeId) -/** @internal */ +/** + * @internal + */ export function rebuildWithHeaders( self: WithHeaders, schema: Schema.Top, @@ -638,6 +702,7 @@ export function rebuildWithHeaders( * Schema type returned by `encodeToWithHeaders`, encoding as a `{ body, headers }` * pair while decoding to the source schema type. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -677,7 +742,7 @@ export interface encodeToWithHeaders< * * ```ts import.meta.vitest * import { Schema } from "effect" - * import { HttpApiSchema } from "effect/unstable/httpapi" + * import { HttpApiSchema } from "effect/http-api" * * class UserNotFound extends Schema.TaggedError()("UserNotFound", { * userId: Schema.Int @@ -705,6 +770,7 @@ export interface encodeToWithHeaders< * @see {@link WithHeaders} for the structural wrapper recommended for success * responses, including streams. * + * @stability unstable * @category encoding * @since 4.0.0 */ @@ -734,7 +800,13 @@ export function encodeToWithHeaders< SchemaTransformation.transform(transformation) ) ).annotate({ - "~httpApiWithHeaders": { body, headers, headersCodec: Schema.toEncoded(headers) }, + "~httpApiWithHeaders": { + body, + headers, + bodyCodec: body, + headersCodec: headers, + transformation + } as WithHeadersAnnotation, httpApiStatus: status, "~httpApiEncoding": encoding }) @@ -744,14 +816,16 @@ export function encodeToWithHeaders< /** * Runtime brand key used to mark schemas as buffered multipart payloads. * + * @stability unstable * @category type IDs * @since 4.0.0 */ -export const MultipartTypeId = "~effect/httpapi/HttpApiSchema/Multipart" +export const MultipartTypeId = "~effect/http-api/HttpApiSchema/Multipart" /** * Type-level brand identifier used by `asMultipart`. * + * @stability unstable * @category type IDs * @since 4.0.0 */ @@ -760,6 +834,7 @@ export type MultipartTypeId = typeof MultipartTypeId /** * Schema type returned by `asMultipart` for buffered multipart payloads. * + * @stability unstable * @category schemas * @since 4.0.0 */ @@ -770,6 +845,7 @@ export interface asMultipart extends Schema.brand extends Schema.brand { const resolveHttpApiEncoding = SchemaAST.resolveAt("~httpApiEncoding") -/** @internal */ +/** + * @internal + */ export const getWithHeadersAnnotation = SchemaAST.resolveAt("~httpApiWithHeaders") const resolveHttpApiStatus = SchemaAST.resolveAt("httpApiStatus") @@ -959,14 +1046,18 @@ function getEncoding(ast: SchemaAST.AST): Encoding { return resolveHttpApiEncoding(ast) ?? defaultJsonEncoding } -/** @internal */ +/** + * @internal + */ export function getPayloadEncoding(ast: SchemaAST.AST, method: HttpMethod): PayloadEncoding { const encoding = resolveHttpApiEncoding(ast) if (encoding) return encoding return hasBody(method) ? defaultJsonEncoding : defaultUrlEncodedEncoding } -/** @internal */ +/** + * @internal + */ export function getResponseEncoding(ast: SchemaAST.AST): ResponseEncoding { const out = getEncoding(ast) if (out._tag === "Multipart") { @@ -975,12 +1066,16 @@ export function getResponseEncoding(ast: SchemaAST.AST): ResponseEncoding { return out } -/** @internal */ +/** + * @internal + */ export function getStatusSuccess(self: SchemaAST.AST): number { return resolveHttpApiStatus(self) ?? 200 } -/** @internal */ +/** + * @internal + */ export function getStatusSuccessSchema(schema: Schema.Constraint): number { if (isWithHeaders(schema)) { return resolveHttpApiStatus(schema.ast) ?? getStatusSuccess(schema.schema.ast) @@ -988,7 +1083,9 @@ export function getStatusSuccessSchema(schema: Schema.Constraint): number { return getStatusSuccess(schema.ast) } -/** @internal */ +/** + * @internal + */ export function getResponseEncodingSchema(schema: Schema.Constraint): ResponseEncoding { if (isWithHeaders(schema) && resolveHttpApiEncoding(schema.ast) === undefined) { return getResponseEncoding(schema.schema.ast) @@ -996,12 +1093,16 @@ export function getResponseEncodingSchema(schema: Schema.Constraint): ResponseEn return getResponseEncoding(schema.ast) } -/** @internal */ +/** + * @internal + */ export function getStatusError(self: SchemaAST.AST): number { return resolveHttpApiStatus(self) ?? 500 } -/** @internal */ +/** + * @internal + */ export function getStatusErrorSchema(schema: Schema.Constraint): number { if (isWithHeaders(schema)) { return resolveHttpApiStatus(schema.ast) ?? getStatusError(schema.schema.ast) diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSecurity.ts b/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts similarity index 90% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiSecurity.ts rename to repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts index d508aee20b..37a223182b 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSecurity.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts @@ -6,19 +6,21 @@ * `HttpApiMiddleware.Service`, `HttpApiBuilder`, generated clients, and OpenAPI * generation, but they do not authenticate requests by themselves. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import { dual } from "../../Function.ts" -import { type Pipeable, pipeArguments } from "../../Pipeable.ts" -import type { Redacted } from "../../Redacted.ts" -import type { Covariant } from "../../Types.ts" +import * as Context from "../Context.ts" +import { dual } from "../Function.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import type { Redacted } from "../Redacted.ts" +import type { Covariant } from "../Types.ts" -const TypeId = "~effect/httpapi/HttpApiSecurity" +const TypeId = "~effect/http-api/HttpApiSecurity" /** * Union of security schemes supported by the HTTP API OpenAPI model. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -27,12 +29,14 @@ export type HttpApiSecurity = Http | ApiKey | Basic /** * Helper types for HTTP API security schemes. * + * @stability unstable * @since 4.0.0 */ export declare namespace HttpApiSecurity { /** * Common prototype for security schemes, carrying the credential type and OpenAPI annotations. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -46,6 +50,7 @@ export declare namespace HttpApiSecurity { /** * Extracts the credential type produced by a security scheme. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -55,19 +60,23 @@ export declare namespace HttpApiSecurity { /** * Http token security scheme whose decoded credential is a redacted token. * + * @stability unstable * @category models * @since 4.0.0 */ export interface Http extends HttpApiSecurity.Proto { readonly _tag: "Http" readonly scheme: string - /** @internal */ + /** + * @internal + */ readonly schemeLength: number } /** * API key security scheme identifying the key name and whether it is read from a header, query parameter, or cookie. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -80,6 +89,7 @@ export interface ApiKey extends HttpApiSecurity.Proto { /** * HTTP Basic authentication security scheme whose decoded credential is `Credentials`. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -90,6 +100,7 @@ export interface Basic extends HttpApiSecurity.Proto { /** * Decoded credentials for HTTP Basic authentication. * + * @stability unstable * @category models * @since 4.0.0 */ @@ -120,6 +131,7 @@ const Proto = { * * @see {@link apiKey} for an API-key security scheme * @see {@link basic} for an HTTP Basic security scheme + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -148,6 +160,7 @@ export const http = (options: { * * @see {@link apiKey} for an API-key security scheme * @see {@link basic} for an HTTP Basic security scheme + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -171,6 +184,7 @@ export const bearer: Http = http({ scheme: "Bearer" }) * * @see {@link bearer} for a Bearer token security scheme * @see {@link basic} for an HTTP Basic security scheme + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -199,6 +213,7 @@ export const apiKey = (options: { * * @see {@link bearer} for a Bearer token security scheme * @see {@link apiKey} for an API-key security scheme + * @stability unstable * @category constructors * @since 4.0.0 */ @@ -210,6 +225,7 @@ export const basic: Basic = Object.assign(Object.create(Proto), { /** * Merges OpenAPI annotations into a security scheme. * + * @stability unstable * @category annotations * @since 4.0.0 */ @@ -228,6 +244,7 @@ export const annotateMerge: { /** * Adds an OpenAPI annotation value to a security scheme. * + * @stability unstable * @category annotations * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSwagger.ts b/repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts similarity index 93% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiSwagger.ts rename to repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts index 126672de78..d25913a8c4 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSwagger.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts @@ -6,13 +6,14 @@ * running application can expose interactive API documentation without writing a * separate OpenAPI file. * + * @stability unstable * @since 4.0.0 */ -import * as Effect from "../../Effect.ts" -import * as Function from "../../Function.ts" -import type * as Layer from "../../Layer.ts" +import * as Effect from "../Effect.ts" +import * as Function from "../Function.ts" import * as HttpRouter from "../http/HttpRouter.ts" import * as HttpServerResponse from "../http/HttpServerResponse.ts" +import type * as Layer from "../Layer.ts" import type * as HttpApi from "./HttpApi.ts" import type * as HttpApiGroup from "./HttpApiGroup.ts" import * as Html from "./internal/html.ts" @@ -56,6 +57,7 @@ const makeHandler = ( * Mounts Swagger UI for an `HttpApi` at the configured path, defaulting to * `/docs`, using the OpenAPI specification generated from the API. * + * @stability unstable * @category layers * @since 4.0.0 */ diff --git a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiTest.ts b/repos/effect/packages/effect/src/http-api/HttpApiTest.ts similarity index 91% rename from repos/effect/packages/effect/src/unstable/httpapi/HttpApiTest.ts rename to repos/effect/packages/effect/src/http-api/HttpApiTest.ts index c8f507b35f..b7f6f55e65 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/HttpApiTest.ts +++ b/repos/effect/packages/effect/src/http-api/HttpApiTest.ts @@ -6,14 +6,12 @@ * but they do not start an HTTP server. This is useful for focused handler * tests, schema round trips, middleware behavior, and typed client calls. * + * @stability unstable * @since 4.0.0 */ -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" -import type { FileSystem } from "../../FileSystem.ts" -import * as Layer from "../../Layer.ts" -import type { Path } from "../../Path.ts" -import type { Scope } from "../../Scope.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import type { FileSystem } from "../FileSystem.ts" import type { Generator } from "../http/Etag.ts" import * as HttpClient from "../http/HttpClient.ts" import type { HttpPlatform } from "../http/HttpPlatform.ts" @@ -21,6 +19,9 @@ import * as HttpRouter from "../http/HttpRouter.ts" import * as HttpServerRequest from "../http/HttpServerRequest.ts" import * as HttpServerResponse from "../http/HttpServerResponse.ts" import * as preResponseHandler from "../http/internal/preResponseHandler.ts" +import * as Layer from "../Layer.ts" +import type { Path } from "../Path.ts" +import type { Scope } from "../Scope.ts" import type * as HttpApi from "./HttpApi.ts" import type { HandlerRuntime } from "./HttpApiBuilder.ts" import * as HttpApiBuilder from "./HttpApiBuilder.ts" @@ -36,6 +37,7 @@ import type * as HttpApiGroup from "./HttpApiGroup.ts" * Handlers for the selected groups are taken from the environment; unselected * groups are wired with placeholder handlers that fail if called. * + * @stability unstable * @category testing * @since 4.0.0 */ @@ -81,7 +83,7 @@ export const groups = Effect.fnUntraced(function*< uninterruptible: false } handlers.set(endpointIdentifier, handler) - routes.push(HttpApiBuilder.handlerToRoute(group as any, handler, context)) + routes.push(HttpApiBuilder.handlerToRoute(api, group as any, handler, context)) } context = Context.add(context, group as any, { handlers, routes }) } diff --git a/repos/effect/packages/effect/src/http-api/OpenApi.ts b/repos/effect/packages/effect/src/http-api/OpenApi.ts new file mode 100644 index 0000000000..0cdc5e5e7b --- /dev/null +++ b/repos/effect/packages/effect/src/http-api/OpenApi.ts @@ -0,0 +1,1371 @@ +/** + * Generates OpenAPI 3.1 documents from declarative `HttpApi` contracts. + * + * The generator reads API groups, endpoints, schemas, security definitions, and + * annotations, then produces an OpenAPI document. This module also provides the + * annotations used to shape that output and the TypeScript model for the + * OpenAPI objects it generates. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Arr from "../Array.ts" +import type { NonEmptyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import * as Equal from "../Equal.ts" +import { constFalse } from "../Function.ts" +import * as HttpMethod from "../http/HttpMethod.ts" +import * as InternalRecord from "../internal/record.ts" +import * as InternalToCodec from "../internal/schema/toCodec.ts" +import * as InternalToJsonSchemaDocument from "../internal/schema/toJsonSchemaDocument.ts" +import * as InternalToRepresentation from "../internal/schema/toRepresentation.ts" +import * as JsonSchema from "../JsonSchema.ts" +import * as Option from "../Option.ts" +import * as Schema from "../Schema.ts" +import * as SchemaAST from "../SchemaAST.ts" +import type * as SchemaRepresentation from "../SchemaRepresentation.ts" +import * as HttpApi from "./HttpApi.ts" +import * as HttpApiEndpoint from "./HttpApiEndpoint.ts" +import type * as HttpApiGroup from "./HttpApiGroup.ts" +import * as HttpApiMiddleware from "./HttpApiMiddleware.ts" +import * as HttpApiSchema from "./HttpApiSchema.ts" +import type { HttpApiSecurity } from "./HttpApiSecurity.ts" +import * as HttpApiPath from "./internal/path.ts" + +/** + * OpenAPI annotation for overriding generated identifiers, including operation ids. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Identifier extends Context.Service()("effect/http-api/OpenApi/Identifier") {} + +/** + * OpenAPI annotation for setting the API title or group tag name. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Title extends Context.Service()("effect/http-api/OpenApi/Title") {} + +/** + * OpenAPI annotation for setting the generated API version. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Version extends Context.Service()("effect/http-api/OpenApi/Version") {} + +/** + * OpenAPI annotation for setting generated descriptions on APIs, groups, endpoints, or security schemes. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Description extends Context.Service()("effect/http-api/OpenApi/Description") {} + +/** + * OpenAPI annotation for setting the generated API license metadata. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class License extends Context.Service()("effect/http-api/OpenApi/License") {} + +/** + * OpenAPI annotation for adding external documentation metadata to groups or endpoints. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class ExternalDocs + extends Context.Service()("effect/http-api/OpenApi/ExternalDocs") +{} + +/** + * OpenAPI annotation for setting the generated API server list. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Servers + extends Context.Service>()("effect/http-api/OpenApi/Servers") +{} + +/** + * OpenAPI annotation for setting the format metadata, such as a bearer token format on security schemes. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Format extends Context.Service()("effect/http-api/OpenApi/Format") {} + +/** + * OpenAPI annotation for setting generated summary text. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Summary extends Context.Service()("effect/http-api/OpenApi/Summary") {} + +/** + * OpenAPI annotation for marking a generated endpoint operation as deprecated. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Deprecated extends Context.Service()("effect/http-api/OpenApi/Deprecated") {} + +/** + * OpenAPI annotation for shallowly merging additional fields into a generated OpenAPI object. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Override + extends Context.Service>()("effect/http-api/OpenApi/Override") +{} + +/** + * Annotation that excludes an annotated group or endpoint from the generated + * OpenAPI specification. + * + * **When to use** + * + * Use to hide internal, experimental, or otherwise undocumented HTTP API groups + * and endpoints from generated OpenAPI output. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const Exclude = Context.Reference("effect/http-api/OpenApi/Exclude", { + defaultValue: constFalse +}) + +/** + * OpenAPI annotation for transforming a generated OpenAPI object. + * + * **Details** + * + * The function is applied during generation to the annotated API, group tag, or + * endpoint operation. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export class Transform extends Context.Service< + Transform, + (openApiSpec: Record) => Record +>()("effect/http-api/OpenApi/Transform") {} + +const servicesPartial = | Context.Key>>( + tags: Tags +): ( + options: { + readonly [K in keyof Tags]?: Context.Service.Shape | undefined + } +) => Context.Context => { + const entries = Object.entries(tags) + return (options) => { + let context = Context.empty() + for (const [key, tag] of entries) { + if (options[key] !== undefined) { + context = Context.add(context, tag as any, options[key]!) + } + } + return context + } +} + +/** + * Builds a `Context` containing OpenAPI annotations from the supplied options. + * + * @stability unstable + * @category annotations + * @since 4.0.0 + */ +export const annotations: ( + options: { + readonly identifier?: string | undefined + readonly title?: string | undefined + readonly version?: string | undefined + readonly description?: string | undefined + readonly license?: OpenAPISpecLicense | undefined + readonly summary?: string | undefined + readonly deprecated?: boolean | undefined + readonly externalDocs?: OpenAPISpecExternalDocs | undefined + readonly servers?: ReadonlyArray | undefined + readonly format?: string | undefined + readonly override?: Record | undefined + readonly exclude?: boolean | undefined + readonly transform?: ((openApiSpec: Record) => Record) | undefined + } +) => Context.Context = servicesPartial({ + identifier: Identifier, + title: Title, + version: Version, + description: Description, + license: License, + summary: Summary, + deprecated: Deprecated, + externalDocs: ExternalDocs, + servers: Servers, + format: Format, + override: Override, + exclude: Exclude, + transform: Transform +}) + +const defaultOptions: SchemaRepresentation.ToRepresentationOptions = {} +const apiCache = new WeakMap< + SchemaRepresentation.ToRepresentationOptions, + WeakMap +>() + +const cloneOpenAPISpec = (value: A): A => { + if (Array.isArray(value)) { + return value.map(cloneOpenAPISpec) as A + } + if (value !== null && typeof value === "object" && !Object.isFrozen(value)) { + const out: Record = {} + for (const key of Object.keys(value)) { + InternalRecord.assignProperty(out, key, cloneOpenAPISpec((value as Record)[key])) + } + return out as A + } + return value +} + +/** + * This function checks if a given tag exists within the provided context. If + * the tag is present, it retrieves the associated value and applies the given + * callback function to it. If the tag is not found, the function does nothing. + */ +function processAnnotation( + ctx: Context.Context, + annotation: Context.Key, + f: (s: S) => void +) { + const o = Context.getOption(ctx, annotation) + if (Option.isSome(o)) { + f(o.value) + } +} + +/** + * Generates an OpenAPI 3.1 specification from an `HttpApi`. + * + * **When to use** + * + * Use when you need a programmatic OpenAPI document for an API definition. + * + * **Details** + * + * This function takes an `HttpApi` instance, which defines a structured API, + * and generates an OpenAPI Specification (`OpenAPISpec`). The resulting spec + * adheres to the OpenAPI 3.1.0 standard and includes detailed metadata such as + * paths, operations, security schemes, and components. The function processes + * the API's annotations, middleware, groups, and endpoints to build a complete + * and accurate representation of the API in OpenAPI format. + * + * The function also deduplicates schemas, applies transformations, and + * integrates annotations like descriptions, summaries, external documentation, + * and overrides. The optional reference policy receives canonical JSON encoded + * ASTs and controls which schemas are extracted into components. By default, only candidates with resolved identifiers + * become references; anonymous non-recursive schemas remain inline. + * + * **Gotchas** + * + * Cached results are keyed by both the `HttpApi` instance and the identity of the options object. Reuse the same immutable + * options object to reuse a cached result; mutating an options object after its first use does not invalidate the cached + * specification. Each call returns a copy of the cached specification. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export function fromApi( + api: HttpApi.HttpApi, + options?: SchemaRepresentation.ToRepresentationOptions +): OpenAPISpec { + const resolvedOptions = options ?? defaultOptions + let cache = apiCache.get(resolvedOptions) + if (cache === undefined) { + cache = new WeakMap() + apiCache.set(resolvedOptions, cache) + } + const cached = cache.get(api) + if (cached !== undefined) return cloneOpenAPISpec(cached) + const spec = makeOpenApi(api, resolvedOptions) + cache.set(api, cloneOpenAPISpec(spec)) + return spec +} + +type SchemaOp = + | { + readonly _tag: "schema" + readonly ast: SchemaAST.AST + readonly target: JsonSchema.JsonSchema + } + | { + readonly _tag: "properties" + readonly ast: SchemaAST.AST + readonly select?: ((property: SchemaRepresentation.PropertySignature) => boolean) | undefined + readonly apply: (property: SchemaRepresentation.PropertySignature, schema: JsonSchema.JsonSchema) => void + } + +function makeOpenApi( + api: HttpApi.HttpApi, + options: SchemaRepresentation.ToRepresentationOptions +): OpenAPISpec { + let spec: OpenAPISpec = { + openapi: "3.1.0", + info: { + title: "Api", + version: "0.0.1" + }, + paths: {}, + components: { + schemas: {}, + securitySchemes: {} + }, + security: [], + tags: [] + } + + const schemaOps: Array = [] + const pathOperations = new Set() + const operationIds = new Set() + const finalizeOperations: Array<() => void> = [] + + function registerSchema( + ast: SchemaAST.AST, + encoding: HttpApiSchema.Encoding["_tag"] = "Json" + ): JsonSchema.JsonSchema { + // Text bodies remain strings even when an opaque schema compiles to {}. + const target: JsonSchema.JsonSchema = encoding === "Text" ? { type: "string" } : {} + schemaOps.push({ _tag: "schema", ast: toEncodingAST(ast, encoding), target }) + return target + } + + processAnnotation(api.annotations, Title, (title) => { + spec.info.title = title + }) + processAnnotation(api.annotations, Version, (version) => { + spec.info.version = version + }) + processAnnotation(api.annotations, Description, (description) => { + spec.info.description = description + }) + processAnnotation(api.annotations, License, (license) => { + spec.info.license = license + }) + processAnnotation(api.annotations, Summary, (summary) => { + spec.info.summary = summary + }) + processAnnotation(api.annotations, Servers, (servers) => { + spec.servers = [...servers] + }) + + HttpApi.reflect(api, { + onGroup({ group }) { + if (Context.get(group.annotations, Exclude)) { + return + } + let tag: OpenAPISpecTag = { + name: Context.getOrElse(group.annotations, Title, () => group.identifier) + } + processAnnotation(group.annotations, Description, (description) => { + tag.description = description + }) + processAnnotation(group.annotations, ExternalDocs, (externalDocs) => { + tag.externalDocs = externalDocs + }) + processAnnotation(group.annotations, Override, (override) => { + // OpenAPI documents are JSON, so symbol keys are intentionally ignored. + for (const [key, value] of Object.entries(override)) { + InternalRecord.assignProperty(tag as any, key, value) + } + }) + processAnnotation(group.annotations, Transform, (transformFn) => { + tag = transformFn(tag) as OpenAPISpecTag + }) + + spec.tags.push(tag) + }, + onEndpoint({ endpoint, group, mergedAnnotations, middleware }) { + if (Context.get(mergedAnnotations, Exclude)) { + return + } + const op: OpenAPISpecOperation = { + tags: [Context.getOrElse(group.annotations, Title, () => group.identifier)], + operationId: Context.getOrElse( + endpoint.annotations, + Identifier, + () => group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}` + ), + parameters: [], + security: [], + responses: {} + } + + const paramNames = HttpApiPath.getParamNames(endpoint.params) + const path = endpoint.path.replace( + /:(\w+)\??/g, + (match, key: string) => paramNames === undefined || paramNames.has(key) ? `{${key}}` : match + ) + const method = endpoint.method.toLowerCase() as Lowercase + const isQuery = method === "query" + const operationKey = isQuery ? "QUERY" : method + + function processResponseBodies(bodies: ResponseBodies, defaultDescription: () => string) { + for (const [status, { content, descriptions, headers, streamContent }] of bodies) { + const description = descriptions.size > 0 ? Array.from(descriptions).join(" | ") : defaultDescription() + InternalRecord.assignProperty(op.responses, status, { + description + }) + for (const schema of headers) { + schemaOps.push({ + _tag: "properties", + ast: schema.ast, + select: (property) => String(property.name).toLowerCase() !== "content-type", + apply(property, schema) { + const name = String(property.name).toLowerCase() + op.responses[status].headers ??= {} + InternalRecord.assignProperty(op.responses[status].headers, name, { + schema, + required: !property.isOptional + }) + } + }) + } + if (content !== undefined) { + content.forEach((map, encoding) => { + map.forEach((schemas, contentType) => { + const asts = Array.from(schemas, SchemaAST.getAST) + const ast = asts.length === 1 ? asts[0] : new SchemaAST.Union(asts) + op.responses[status].content ??= {} + InternalRecord.assignProperty(op.responses[status].content, contentType, { + schema: registerSchema(ast, encoding) + }) + }) + }) + } + if (streamContent !== undefined) { + streamContent.forEach((stream, contentType) => { + op.responses[status].content ??= {} + if (HttpApiSchema.isStreamSse(stream)) { + InternalRecord.assignProperty(op.responses[status].content, contentType, { + schema: registerSchema(stream.events.ast), + "x-effect-stream": { + encoding: "sse", + causeSchema: registerSchema(Schema.Cause(stream.error, Schema.Defect()).ast), + errorSchema: registerSchema(stream.error.ast), + failureEvent: reservedStreamFailureEvent + } + }) + } else { + InternalRecord.assignProperty(op.responses[status].content, contentType, { + schema: { + type: "string", + format: "binary" + }, + "x-effect-stream": { + encoding: "uint8array" + } + }) + } + }) + } + } + } + + function processParameters(schema: Schema.Constraint | undefined, i: OpenAPISpecParameter["in"]) { + if (schema) { + schemaOps.push({ + _tag: "properties", + ast: schema.ast, + apply(property, schema) { + op.parameters.push({ + name: String(property.name), + in: i, + schema, + required: i === "path" || !property.isOptional + }) + } + }) + } + } + + processAnnotation(endpoint.annotations, Description, (description) => { + op.description = description + }) + processAnnotation(endpoint.annotations, Summary, (summary) => { + op.summary = summary + }) + processAnnotation(endpoint.annotations, Deprecated, (deprecated) => { + op.deprecated = deprecated + }) + processAnnotation(endpoint.annotations, ExternalDocs, (externalDocs) => { + op.externalDocs = externalDocs + }) + + middleware.forEach((middleware) => { + if (!HttpApiMiddleware.isSecurity(middleware)) { + return + } + for (const [name, security] of Object.entries(middleware.security)) { + processHttpApiSecurity(name, security) + op.security.push({ [name]: [] }) + } + }) + + function processHttpApiSecurity( + name: string, + security: HttpApiSecurity + ) { + const scheme = makeSecurityScheme(security) + if (!Object.hasOwn(spec.components.securitySchemes, name)) { + InternalRecord.assignProperty(spec.components.securitySchemes, name, scheme) + return + } + if ( + !Equal.equals( + securitySchemeForComparison(spec.components.securitySchemes[name]), + securitySchemeForComparison(scheme) + ) + ) { + throw new globalThis.Error(`Conflicting OpenAPI security scheme: ${name}`) + } + } + + const hasBody = HttpMethod.hasBody(endpoint.method) + if (hasBody) { + const schemasByContentType = new Map + }>() + for (const schema of HttpApiEndpoint.getPayloadSchemas(endpoint)) { + if (HttpApiSchema.isNoContent(schema.ast)) continue + const encoding = HttpApiSchema.getPayloadEncoding(schema.ast, endpoint.method) + const existing = schemasByContentType.get(encoding.contentType) + if (existing === undefined) { + schemasByContentType.set(encoding.contentType, { encoding, schemas: [schema] }) + } else { + existing.schemas.push(schema) + } + } + if (schemasByContentType.size > 0) { + const content: OpenApiSpecContent = {} + for (const [contentType, { encoding, schemas }] of schemasByContentType) { + const asts = schemas.map(SchemaAST.getAST) + const ast = asts.length === 1 ? asts[0] : new SchemaAST.Union(asts) + InternalRecord.assignProperty(content, contentType, { + schema: registerSchema(ast, encoding._tag) + }) + } + op.requestBody = { content, required: true } + } + } + + processParameters(endpoint.params, "path") + if (!hasBody && endpoint.payload.size === 1) { + const entry = endpoint.payload.values().next().value! + processParameters(entry.schemas[0], "query") + } + processParameters(endpoint.headers, "header") + processParameters(endpoint.query, "query") + + processResponseBodies( + extractSuccessResponseBodies(endpoint), + () => "Success" + ) + processResponseBodies( + extractResponseBodies( + HttpApiEndpoint.getErrorSchemas(endpoint), + HttpApiSchema.getStatusErrorSchema, + resolveDescriptionOrIdentifier + ), + () => "Error" + ) + + const pathOperation = `${method} ${path.replace(/\{[^}]+\}/g, "{}")}` + if (pathOperations.has(pathOperation)) { + throw new globalThis.Error(`Duplicate OpenAPI operation for ${endpoint.method} ${path}`) + } + pathOperations.add(pathOperation) + if (!Object.hasOwn(spec.paths, path)) { + InternalRecord.assignProperty(spec.paths, path, {}) + } + const getOperations = (): Partial> => { + const pathItem = spec.paths[path] + return isQuery ? (pathItem["x-oai-additionalOperations"] ??= {}) : pathItem + } + getOperations()[operationKey] = op + finalizeOperations.push(() => { + const operations = getOperations() + let op = operations[operationKey]! + processAnnotation(endpoint.annotations, Override, (override) => { + // OpenAPI documents are JSON, so symbol keys are intentionally ignored. + for (const [key, value] of Object.entries(override)) { + InternalRecord.assignProperty(op as any, key, value) + } + }) + processAnnotation(endpoint.annotations, Transform, (transformFn) => { + op = transformFn(op) as OpenAPISpecOperation + }) + const operationId = op.operationId + if (operationId !== undefined) { + if (operationIds.has(operationId)) { + throw new globalThis.Error(`Duplicate OpenAPI operationId: ${operationId}`) + } + operationIds.add(operationId) + } + operations[operationKey] = op + }) + } + }) + + processAnnotation(api.annotations, HttpApi.AdditionalSchemas, (componentSchemas) => { + componentSchemas.forEach((componentSchema) => { + const identifier = SchemaAST.resolveIdentifier(componentSchema.ast) + if (identifier !== undefined) { + if (Object.hasOwn(spec.components.schemas, identifier)) { + throw new globalThis.Error(`Duplicate component schema identifier: ${identifier}`) + } + InternalRecord.assignProperty(spec.components.schemas, identifier, registerSchema(componentSchema.ast)) + } + }) + }) + + compileSchemaOps(schemaOps, options, spec.components.schemas) + + for (const finalize of finalizeOperations) { + finalize() + } + + Object.keys(spec.components.schemas).forEach((key) => { + if (!JsonSchema.VALID_OPEN_API_COMPONENTS_SCHEMAS_KEY_REGEXP.test(key)) { + throw new globalThis.Error(`Invalid component schema key: ${key}`) + } + }) + + processAnnotation(api.annotations, Override, (override) => { + // OpenAPI documents are JSON, so symbol keys are intentionally ignored. + for (const [key, value] of Object.entries(override)) { + InternalRecord.assignProperty(spec as any, key, value) + } + }) + processAnnotation(api.annotations, Transform, (transformFn) => { + spec = transformFn(spec) as OpenAPISpec + }) + + return spec +} + +function compileSchemaOps( + schemaOps: Array, + options: SchemaRepresentation.ToRepresentationOptions, + components: JsonSchema.Definitions +): void { + if (!Arr.isArrayNonEmpty(schemaOps)) return + + const document = InternalToRepresentation.toRepresentations( + Arr.map(schemaOps, (op) => op.ast), + options + ) + const representations = Arr.map(document.representations, (representation, index) => { + const op = schemaOps[index] + return op._tag === "schema" + ? representation + : makePropertiesRepresentation(representation, document.references, op.select) + }) + const references = selectReferences(representations, document.references) + const jsonSchemaMultiDocument = JsonSchema.toMultiDocumentOpenApi3_1( + InternalToJsonSchemaDocument.toJsonSchemaMultiDocument( + { representations, references }, + { onExcessProperty: "error" } + ) + ) + + for (let index = 0; index < schemaOps.length; index++) { + const op = schemaOps[index] + const schema = jsonSchemaMultiDocument.schemas[index] + if (op._tag === "schema") { + InternalRecord.assignProperties(op.target, schema) + continue + } + const representation = representations[index] + if (representation._tag !== "Objects") { + throw new globalThis.Error("Expected an object representation for HTTP properties") + } + // These schemas come from the synthetic Objects below, without root checks or annotations. + const properties = schema.properties as Record + for (const property of representation.propertySignatures) { + op.apply(property, properties[String(property.name)]) + } + } + + for (const [name, definition] of Object.entries(jsonSchemaMultiDocument.definitions)) { + InternalRecord.assignProperty(components, name, definition) + } +} + +function makePropertiesRepresentation( + representation: SchemaRepresentation.Representation, + references: SchemaRepresentation.References, + select: ((property: SchemaRepresentation.PropertySignature) => boolean) | undefined +): SchemaRepresentation.Objects { + const resolved = resolveRepresentation(representation, references) + return { + _tag: "Objects", + propertySignatures: resolved._tag === "Objects" + ? resolved.propertySignatures + .filter((property) => select?.(property) ?? true) + .map((property) => ({ + ...property, + name: String(property.name) + })) + : [], + indexSignatures: [], + checks: [] + } +} + +function resolveRepresentation( + representation: SchemaRepresentation.Representation, + references: SchemaRepresentation.References, + seen = new Set() +): SchemaRepresentation.Representation { + if (representation._tag === "Suspend") { + return resolveRepresentation(representation.thunk, references, seen) + } + if (representation._tag !== "Reference" || seen.has(representation.$ref)) return representation + const target = references[representation.$ref] + return target === undefined + ? representation + : resolveRepresentation(target, references, new Set(seen).add(representation.$ref)) +} + +function selectReferences( + representations: ReadonlyArray, + references: SchemaRepresentation.References +): SchemaRepresentation.References { + // The JSON Schema compiler emits every supplied reference. Keep only references reachable from the projected + // roots so named parameter containers and excluded properties do not leak into OpenAPI components. + const selected: Record = {} + const visited = new WeakSet() + const stack = [...representations] + + function pushChecks(checks: ReadonlyArray): void { + for (const check of checks) { + if (check.representation?.schemas !== undefined) stack.push(...check.representation.schemas) + if (check._tag === "FilterGroup") pushChecks(check.checks) + } + } + + while (stack.length > 0) { + const representation = stack.pop()! + if (visited.has(representation)) continue + visited.add(representation) + if (representation._tag === "Reference") { + if (Object.hasOwn(selected, representation.$ref)) continue + const target = references[representation.$ref] + if (target === undefined) continue + InternalRecord.assignProperty(selected, representation.$ref, target) + stack.push(target) + continue + } + + pushChecks(representation.checks) + switch (representation._tag) { + case "Declaration": + stack.push(...representation.typeParameters) + break + case "Suspend": + stack.push(representation.thunk) + break + case "TemplateLiteral": + stack.push(...representation.parts) + break + case "Arrays": + for (const element of representation.elements) stack.push(element.type) + stack.push(...representation.rest) + break + case "Objects": + for (const property of representation.propertySignatures) stack.push(property.type) + for (const signature of representation.indexSignatures) { + stack.push(signature.parameter, signature.type) + } + break + case "Union": + stack.push(...representation.types) + break + } + } + return selected +} + +type ResponseBodies = Map< + number, // status + { + descriptions: Set + content: Content | undefined // undefined means no content + headers: Array + streamContent: StreamContent | undefined + } +> + +const reservedStreamFailureEvent = "effect/http-api/stream/failure" + +function extractSuccessResponseBodies(endpoint: HttpApiEndpoint.Top): ResponseBodies { + return extractResponseBodies( + HttpApiEndpoint.getSuccessSchemas(endpoint), + HttpApiSchema.getStatusSuccessSchema, + resolveDescriptionOrIdentifier + ) +} + +function extractResponseBodies( + schemas: Array, + getStatus: (schema: Schema.Constraint) => number, + getDescription: (ast: SchemaAST.AST) => string | undefined +): ResponseBodies { + const map = new Map + content: Content | undefined + headers: Array + streamContent: StreamContent | undefined + }>() + + schemas.forEach(process) + + return map + + function process(schema: Schema.Constraint) { + const annotation = HttpApiSchema.getWithHeadersAnnotation(schema.ast) + const body = HttpApiSchema.isWithHeaders(schema) ? schema.schema : annotation?.body ?? schema + const headers = HttpApiSchema.isWithHeaders(schema) ? schema.headers : annotation?.headersCodec + const status = getStatus(schema) + const ast = body.ast + if (HttpApiSchema.isStreamSchema(body)) { + addStreamContent(body, status) + } else if (HttpApiSchema.isNoContent(ast)) { + addNoContent(status, getDescription(schema.ast) ?? getDescription(ast) ?? "") + } else { + addContent( + body, + status, + HttpApiSchema.getResponseEncodingSchema(schema), + getDescription(schema.ast) ?? getDescription(ast) + ) + } + if (headers !== undefined) { + map.get(status)!.headers.push(headers) + } + } + + function addNoContent(status: number, description: string) { + const statusMap = map.get(status) + if (statusMap === undefined) { + map.set(status, { + descriptions: new Set([description]), + content: undefined, + headers: [], + streamContent: undefined + }) + } else { + if (description !== undefined) { + statusMap.descriptions.add(description) + } + } + } + + function addContent( + schema: Schema.Constraint, + status: number, + encoding: HttpApiSchema.Encoding, + description: string | undefined + ) { + const statusMap = map.get(status) + const { _tag, contentType } = encoding + if (statusMap === undefined) { + map.set(status, { + descriptions: new Set(description !== undefined ? [description] : []), + content: new Map([[_tag, new Map([[contentType, new Set([schema])]])]]), + headers: [], + streamContent: undefined + }) + } else { + // concat descriptions + if (description !== undefined) { + statusMap.descriptions.add(description) + } + + if (statusMap.content === undefined) { + statusMap.content = new Map([[_tag, new Map([[contentType, new Set([schema])]])]]) + } else { + const schemasByContentType = statusMap.content.get(_tag) + if (schemasByContentType === undefined) { + statusMap.content.set(_tag, new Map([[contentType, new Set([schema])]])) + } else { + const set = schemasByContentType.get(contentType) + if (set === undefined) { + schemasByContentType.set(contentType, new Set([schema])) + } else { + set.add(schema) + } + } + } + } + } + + function addStreamContent( + stream: HttpApiSchema.StreamSchema, + status: number + ) { + const statusMap = map.get(status) + if (statusMap === undefined) { + map.set(status, { + descriptions: new Set(), + content: undefined, + headers: [], + streamContent: new Map([[stream.contentType, stream]]) + }) + } else { + if (statusMap.streamContent === undefined) { + statusMap.streamContent = new Map([[stream.contentType, stream]]) + } else { + statusMap.streamContent.set(stream.contentType, stream) + } + } + } +} + +function resolveDescriptionOrIdentifier(ast: SchemaAST.AST): string | undefined { + return SchemaAST.resolveDescription(ast) ?? SchemaAST.resolveIdentifier(ast) +} + +type Content = Map< + HttpApiSchema.Encoding["_tag"], + Map< + string, // contentType + Set + > +> + +type StreamContent = Map + +const Uint8ArrayEncoding = Schema.String.annotate({ + format: "binary" +}) + +function toEncodingAST(ast: SchemaAST.AST, _tag: HttpApiSchema.Encoding["_tag"]): SchemaAST.AST { + switch (_tag) { + case "Uint8Array": + return Uint8ArrayEncoding.ast + case "Text": + return SchemaAST.isDeclaration(ast) && ast.encoding === undefined ? Schema.String.ast : ast + case "Multipart": + ast = persistedFileToBinaryEncoding(ast) + } + return InternalToCodec.toCodecJsonAST(ast) +} + +function persistedFileToBinaryEncoding(ast: SchemaAST.AST): SchemaAST.AST { + if ( + SchemaAST.isDeclaration(ast) && + ((ast.annotations as (Schema.Annotations.Declaration | undefined))?.representation?.id === + "effect/http/PersistedFile") + ) { + return Uint8ArrayEncoding.ast + } + + if (typeof (ast as any)?.recur === "function") { + return (ast as any).recur(persistedFileToBinaryEncoding) + } + + return ast +} + +const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme => { + const meta: Partial = {} + processAnnotation(security.annotations, Description, (description) => { + meta.description = description + }) + switch (security._tag) { + case "Basic": { + return { + ...meta, + type: "http", + scheme: "basic" + } + } + case "Http": { + const format = Context.getOption(security.annotations, Format).pipe( + Option.map((format) => ({ bearerFormat: format })), + Option.getOrUndefined + ) + return { + ...meta, + type: "http", + scheme: security.scheme, + ...format + } + } + case "ApiKey": { + return { + ...meta, + type: "apiKey", + name: security.key, + in: security.in + } + } + } +} + +const securitySchemeForComparison = (scheme: OpenAPISecurityScheme): OpenAPISecurityScheme => { + if (scheme.type === "http") { + return { ...scheme, scheme: scheme.scheme.toLowerCase() } + } + if (scheme.in === "header") { + return { ...scheme, name: scheme.name.toLowerCase() } + } + return scheme +} + +/** + * This model describes the OpenAPI specification (version 3.1.0) returned by + * {@link fromApi}. It is not intended to describe the entire OpenAPI + * specification, only the output of `fromApi`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpec { + openapi: "3.1.0" + info: OpenAPISpecInfo + paths: OpenAPISpecPaths + components: OpenAPIComponents + security: Array + tags: Array + servers?: Array +} + +/** + * OpenAPI `info` object generated by `fromApi`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecInfo { + title: string + version: string + description?: string + license?: OpenAPISpecLicense + summary?: string +} + +/** + * OpenAPI tag object generated for an HTTP API group. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecTag { + name: string + description?: string + externalDocs?: OpenAPISpecExternalDocs +} + +/** + * OpenAPI external documentation metadata. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecExternalDocs { + url: string + description?: string +} + +/** + * OpenAPI license metadata used in the generated `info` object. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecLicense { + name: string + url?: string + [key: string]: unknown +} + +/** + * OpenAPI server object used in the generated `servers` array. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecServer { + url: string + description?: string + variables?: Record +} + +/** + * OpenAPI variable definition for templated server URLs. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecServerVariable { + default: string + enum?: NonEmptyArray + description?: string +} + +/** + * Generated OpenAPI `paths` object, keyed by route path. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISpecPaths = Record + +/** + * Lowercase HTTP method names used as keys in generated OpenAPI path items. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISpecMethodName = + | "get" + | "put" + | "post" + | "delete" + | "options" + | "head" + | "patch" + | "trace" + +/** + * Generated OpenAPI path item mapping HTTP methods to operations for a single route path. + * Parameters declared here are shared by every operation on the path. + * `QUERY` operations are emitted under `x-oai-additionalOperations`. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISpecPathItem = + & { + [K in OpenAPISpecMethodName]?: OpenAPISpecOperation + } + & { + parameters?: Array + "x-oai-additionalOperations"?: Partial> + } + +/** + * Generated OpenAPI parameter object for path, query, header, or cookie parameters. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecParameter { + name: string + in: "query" | "header" | "path" | "cookie" + schema: object + required: boolean + description?: string +} + +/** + * Generated OpenAPI responses object, keyed by HTTP status code. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISpecResponses = Record + +/** + * Generated OpenAPI content object, keyed by media type. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenApiSpecContent = { + [K in string]: OpenApiSpecMediaType +} + +/** + * Generated OpenAPI response object for an endpoint success or error schema. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenApiSpecResponse { + description: string + content?: OpenApiSpecContent + headers?: Record +} + +/** + * Generated OpenAPI response header object. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISpecHeader = Omit + +/** + * Generated OpenAPI media type object containing the JSON Schema for a request or response body. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenApiSpecMediaType { + schema: JsonSchema.JsonSchema + "x-effect-stream"?: OpenApiSpecEffectStream +} + +/** + * Effect-specific metadata for generated streaming response media types. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenApiSpecEffectStream = + | { + encoding: "sse" + causeSchema: JsonSchema.JsonSchema + errorSchema: JsonSchema.JsonSchema + failureEvent: "effect/http-api/stream/failure" + } + | { + encoding: "uint8array" + } + +/** + * Generated OpenAPI request body object for endpoint payloads. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecRequestBody { + content: OpenApiSpecContent + required: true +} + +/** + * Generated OpenAPI components containing shared schemas and security schemes. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPIComponents { + schemas: JsonSchema.Definitions + securitySchemes: Record +} + +/** + * Generated OpenAPI HTTP security scheme, such as bearer or basic authentication. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPIHTTPSecurityScheme { + readonly type: "http" + scheme: "bearer" | "basic" | string + description?: string + /* only for scheme: 'bearer' */ + bearerFormat?: string +} + +/** + * Generated OpenAPI API key security scheme. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPIApiKeySecurityScheme { + readonly type: "apiKey" + name: string + in: "query" | "header" | "cookie" + description?: string +} + +/** + * Union of security scheme objects emitted in generated OpenAPI components. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISecurityScheme = + | OpenAPIHTTPSecurityScheme + | OpenAPIApiKeySecurityScheme + +/** + * Generated OpenAPI security requirement, keyed by security scheme name. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type OpenAPISecurityRequirement = Record> + +/** + * Generated OpenAPI operation object for an HTTP API endpoint. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface OpenAPISpecOperation { + operationId: string + parameters: Array + responses: OpenAPISpecResponses + /** Always contains at least the title annotation or the group identifier */ + tags: NonEmptyArray + security: Array + requestBody?: OpenAPISpecRequestBody + description?: string + summary?: string + deprecated?: boolean + externalDocs?: OpenAPISpecExternalDocs +} diff --git a/repos/effect/packages/effect/src/http-api/index.ts b/repos/effect/packages/effect/src/http-api/index.ts new file mode 100644 index 0000000000..b48cc40bc7 --- /dev/null +++ b/repos/effect/packages/effect/src/http-api/index.ts @@ -0,0 +1,84 @@ +/** + * @stability unstable + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApi from "./HttpApi.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiBuilder from "./HttpApiBuilder.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiClient from "./HttpApiClient.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiEndpoint from "./HttpApiEndpoint.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiError from "./HttpApiError.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiGroup from "./HttpApiGroup.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiMiddleware from "./HttpApiMiddleware.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiScalar from "./HttpApiScalar.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiSchema from "./HttpApiSchema.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiSecurity from "./HttpApiSecurity.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiSwagger from "./HttpApiSwagger.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as HttpApiTest from "./HttpApiTest.ts" + +/** + * @stability unstable + * @since 4.0.0 + */ +export * as OpenApi from "./OpenApi.ts" diff --git a/repos/effect/packages/effect/src/unstable/httpapi/internal/html.ts b/repos/effect/packages/effect/src/http-api/internal/html.ts similarity index 89% rename from repos/effect/packages/effect/src/unstable/httpapi/internal/html.ts rename to repos/effect/packages/effect/src/http-api/internal/html.ts index 9326d0104b..d030fdd592 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/internal/html.ts +++ b/repos/effect/packages/effect/src/http-api/internal/html.ts @@ -2,14 +2,18 @@ const ESCAPE_SCRIPT_DATA = / c === "\u2028" ? "\\u2028" : "\\u2029") } -/** @internal */ +/** + * @internal + */ export function escape(str: string): string { return str .replace(/&/g, "&") @@ -17,7 +21,9 @@ export function escape(str: string): string { .replace(/>/g, ">") } -/** @internal */ +/** + * @internal + */ export function escapeAttribute(str: string): string { return escape(str) .replace(/"/g, """) diff --git a/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiScalar.ts b/repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts similarity index 99% rename from repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiScalar.ts rename to repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts index 2bbac2be39..0e48b51704 100644 --- a/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiScalar.ts +++ b/repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts @@ -1,5 +1,7 @@ /* oxlint-disable no-undef */ -/** @internal */ +/** + * @internal + */ export const javascript = "/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@scalar/api-reference@1.43.5/dist/browser/standalone.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n!function(){\"use strict\";try{if(\"undefined\"!=typeof document){var e=document.createElement(\"style\");e.appendChild(document.createTextNode('.references-classic-header[data-v-9198d025]{display:flex;align-items:center;gap:12px;max-width:var(--refs-content-max-width);margin:auto;padding:12px 0}.references-classic-header-content[data-v-9198d025]{display:flex;gap:12px;flex-grow:1}.references-classic-header-container[data-v-9198d025]{padding:0 60px}@container narrow-references-container (max-width: 900px){.references-classic-header[data-v-9198d025]{padding:12px 24px}.references-classic-header-container[data-v-9198d025]{padding:0}}.references-classic-header-icon[data-v-9198d025]{height:24px;color:var(--scalar-color-1)}.client-libraries-content[data-v-6a49c111]{container:client-libraries-content / inline-size;display:flex;justify-content:center;overflow:hidden;padding:0 12px;background-color:var(--scalar-background-1);border-left:var(--scalar-border-width) solid var(--scalar-border-color);border-right:var(--scalar-border-width) solid var(--scalar-border-color)}.client-libraries[data-v-6a49c111]{display:flex;align-items:center;justify-content:center;width:100%;position:relative;cursor:pointer;white-space:nowrap;padding:8px 2px;gap:6px;color:var(--scalar-color-3);border-bottom:1px solid transparent;-webkit-user-select:none;user-select:none}.client-libraries[data-v-6a49c111]:not(.client-libraries__active):hover:before{content:\"\";position:absolute;width:calc(100% - 4px);height:calc(100% - 4px);background:var(--scalar-background-2);left:2px;top:2px;z-index:0;border-radius:var(--scalar-radius)}.client-libraries[data-v-6a49c111]:active{color:var(--scalar-color-1)}.client-libraries[data-v-6a49c111]:focus-visible{outline:none;box-shadow:inset 0 0 0 1px var(--scalar-color-accent)}@media screen and (max-width:450px){.client-libraries[data-v-6a49c111]:nth-of-type(4),.client-libraries[data-v-6a49c111]:nth-of-type(5){display:none}}.client-libraries-icon[data-v-6a49c111]{max-width:14px;max-height:14px;min-width:14px;width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;position:relative;box-sizing:border-box;color:currentColor}.client-libraries-icon__more svg[data-v-6a49c111]{height:initial}@container client-libraries-content (width < 400px){.client-libraries__select[data-v-6a49c111]{width:fit-content}.client-libraries__select .client-libraries-icon__more+span[data-v-6a49c111]{display:none}}@container client-libraries-content (width < 380px){.client-libraries[data-v-6a49c111]{width:100%}.client-libraries span[data-v-6a49c111]{display:none}}.client-libraries__active[data-v-6a49c111]{color:var(--scalar-color-1);border-bottom:1px solid var(--scalar-color-1)}@keyframes codeloader-6a49c111{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.client-libraries .client-libraries-text[data-v-6a49c111]{font-size:var(--scalar-small);position:relative;display:flex;align-items:center}.client-libraries__active .client-libraries-text[data-v-6a49c111]{color:var(--scalar-color-1);font-weight:var(--scalar-semibold)}@media screen and (max-width:600px){.references-classic .client-libraries[data-v-6a49c111]{flex-direction:column}}.selected-client[data-v-e59e2ca0]{color:var(--scalar-color-1);font-size:var(--scalar-small);font-family:var(--scalar-font-code);padding:9px 12px;border-top:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:var(--scalar-background-1);border:var(--scalar-border-width) solid var(--scalar-border-color);border-bottom-left-radius:var(--scalar-radius-lg);border-bottom-right-radius:var(--scalar-radius-lg);min-height:fit-content}.client-libraries-heading[data-v-e59e2ca0]{font-size:var(--scalar-small);font-weight:var(--scalar-font-medium);color:var(--scalar-color-1);padding:9px 12px;background-color:var(--scalar-background-2);display:flex;align-items:center;max-height:32px;border:var(--scalar-border-width) solid var(--scalar-border-color);border-top-left-radius:var(--scalar-radius-lg);border-top-right-radius:var(--scalar-radius-lg)}[data-v-e59e2ca0] .scalar-codeblock-pre .hljs{margin-top:8px}.badge[data-v-3dedb7e4]{color:var(--badge-text-color, var(--scalar-color-2));font-size:var(--scalar-mini);background:var(--badge-background-color, var(--scalar-background-2));border:var(--scalar-border-width) solid var(--badge-border-color, var(--scalar-border-color));padding:2px 6px;border-radius:12px;display:inline-block}.badge.text-orange[data-v-3dedb7e4]{background:color-mix(in srgb,var(--scalar-color-orange),transparent 90%);border:transparent}.badge.text-yellow[data-v-3dedb7e4]{background:color-mix(in srgb,var(--scalar-color-yellow),transparent 90%);border:transparent}.badge.text-red[data-v-3dedb7e4]{background:color-mix(in srgb,var(--scalar-color-red),transparent 90%);border:transparent}.badge.text-purple[data-v-3dedb7e4]{background:color-mix(in srgb,var(--scalar-color-purple),transparent 90%);border:transparent}.badge.text-green[data-v-3dedb7e4]{background:color-mix(in srgb,var(--scalar-color-green),transparent 90%);border:transparent}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){[data-v-59b5011b],[data-v-59b5011b]:before,[data-v-59b5011b]:after,[data-v-59b5011b]::backdrop{--tw-outline-style:solid}}}.download-container[data-v-59b5011b]{z-index:1;flex-direction:column;gap:16px;width:fit-content;margin:0 .5px 8px;display:flex;position:relative}.download-container[data-v-59b5011b]:has(:focus-visible):before,.download-container.download-both[data-v-59b5011b]:hover:before{content:\"\";border-radius:var(--scalar-radius-lg);width:calc(100% + 24px);height:90px;box-shadow:var(--scalar-shadow-2);pointer-events:none;background:var(--scalar-background-1);position:absolute;top:-11px;left:-12px}.download-button[data-v-59b5011b]{color:var(--scalar-link-color);cursor:pointer;outline:none;justify-content:center;align-items:center;gap:4px;height:fit-content;padding:0;display:flex;position:relative;white-space:nowrap!important}.download-button[data-v-59b5011b]:before{border-radius:var(--scalar-radius);content:\"\";width:calc(100% + 18px);height:calc(100% + 16px);position:absolute;top:-8px;left:-9px}.download-button[data-v-59b5011b]:last-of-type:before{width:calc(100% + 15px)}.download-button[data-v-59b5011b]:hover:before{background:var(--scalar-background-2);border:var(--scalar-border-width)solid var(--scalar-border-color)}.download-button[data-v-59b5011b]:focus-visible:before{background:var(--scalar-background-2);border:var(--scalar-border-width)solid var(--scalar-border-color);outline-style:var(--tw-outline-style);outline-width:1px}.download-button span[data-v-59b5011b]{--font-color:var(--scalar-link-color,var(--scalar-color-accent));--font-visited:var(--scalar-link-color-visited,var(--scalar-color-2));-webkit-text-decoration:var(--scalar-text-decoration);text-decoration:var(--scalar-text-decoration);color:var(--font-color);font-weight:var(--scalar-link-font-weight,var(--scalar-semibold));text-underline-offset:.25rem;text-decoration-thickness:1px;-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.download-button span[data-v-59b5011b]{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent);text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}.download-button span[data-v-59b5011b]{z-index:1;align-items:center;gap:6px;line-height:1.625;display:flex}.download-button:hover span[data-v-59b5011b]{-webkit-text-decoration-color:var(--scalar-color-1,currentColor);text-decoration-color:var(--scalar-color-1,currentColor);color:var(--scalar-link-color-hover,var(--scalar-color-accent));-webkit-text-decoration:var(--scalar-text-decoration-hover);text-decoration:var(--scalar-text-decoration-hover)}.download-button[data-v-59b5011b]:nth-of-type(2){clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.download-container:has(:focus-visible) .download-button[data-v-59b5011b]:nth-of-type(2),.download-container:hover .download-button[data-v-59b5011b]:nth-of-type(2){clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:absolute;top:42px;overflow:visible}.extension[data-v-59b5011b]{z-index:1;background:var(--scalar-link-color,var(--scalar-color-accent));color:var(--scalar-background-1)}.download-container:has(:focus-visible) .extension[data-v-59b5011b],.download-container:hover .extension[data-v-59b5011b]{opacity:1}.download-link[data-v-59b5011b]{--font-color:var(--scalar-link-color,var(--scalar-color-accent));--font-visited:var(--scalar-link-color-visited,var(--scalar-color-2));-webkit-text-decoration:var(--scalar-text-decoration);text-decoration:var(--scalar-text-decoration);color:var(--font-color);font-weight:var(--scalar-link-font-weight,var(--scalar-semibold));text-underline-offset:.25rem;text-decoration-thickness:1px;-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.download-link[data-v-59b5011b]{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent);text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}.download-link[data-v-59b5011b]:hover{--font-color:var(--scalar-link-color,var(--scalar-color-accent));-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}.introduction-card[data-v-a8605b85]{display:flex;flex-direction:column}.introduction-card-row[data-v-a8605b85]{gap:24px}@media(min-width:600px){.introduction-card-row[data-v-a8605b85]{flex-flow:row wrap}}.introduction-card-row[data-v-a8605b85]>*{flex:1}@media(min-width:600px){.introduction-card-row[data-v-a8605b85]>*{min-width:min-content}}@media(max-width:600px){.introduction-card-row[data-v-a8605b85]>*{max-width:100%}}@container (max-width: 900px){.introduction-card-row[data-v-a8605b85]{flex-direction:column;align-items:stretch;gap:0px}}.introduction-card[data-v-a8605b85] .security-scheme-label{text-transform:uppercase;font-weight:var(--scalar-semibold)}.introduction-card-row[data-v-a8605b85] .scalar-card:nth-of-type(2) .scalar-card-header{display:none}.introduction-card-row[data-v-a8605b85] .scalar-card:nth-of-type(2) .scalar-card-header.scalar-card--borderless+.scalar-card-content{margin-top:0}.section[data-v-be4443e9]{position:relative;display:flex;flex-direction:column;max-width:var(--refs-content-max-width);margin:auto;padding:90px 0;scroll-margin-top:var(--refs-viewport-offset)}.section[data-v-be4443e9]:has(~div.contents){border-bottom:var(--scalar-border-width) solid var(--scalar-border-color)}.references-classic .section[data-v-be4443e9]{padding:48px 0;gap:24px}@container narrow-references-container (max-width: 900px){.references-classic .section[data-v-be4443e9],.section[data-v-be4443e9]{padding:48px 24px}}.section[data-v-be4443e9]:not(:last-of-type){border-bottom:var(--scalar-border-width) solid var(--scalar-border-color)}.section-wrapper[data-v-ee08c887]{color:var(--scalar-color-1);padding-top:12px;margin-top:-12px}.section-accordion[data-v-ee08c887]{display:flex;flex-direction:column;border-radius:var(--scalar-radius-lg);background:var(--scalar-background-2)}.section-accordion-transparent[data-v-ee08c887]{background:transparent;border:var(--scalar-border-width) solid var(--scalar-border-color)}.section-accordion-button[data-v-ee08c887]{padding:6px}.section-accordion-button[data-v-ee08c887]{display:flex;align-items:center;gap:6px;cursor:pointer}.section-accordion-button-content[data-v-ee08c887]{flex:1;min-width:0}.section-accordion-button-actions[data-v-ee08c887]{display:flex;align-items:center;gap:6px;color:var(--scalar-color-3)}.section-accordion-chevron[data-v-ee08c887]{margin-right:4px;cursor:pointer;opacity:1;color:var(--scalar-color-3)}.section-accordion-button:hover .section-accordion-chevron[data-v-ee08c887]{color:var(--scalar-color-1)}.section-accordion-content[data-v-ee08c887]{border-top:var(--scalar-border-width) solid var(--scalar-border-color);display:flex;flex-direction:column}.section-accordion-description[data-v-ee08c887]{font-weight:var(--scalar-semibold);font-size:var(--scalar-mini);color:var(--scalar-color--1);padding:10px 12px 0}.section-accordion-content-card[data-v-ee08c887] .property:last-of-type{padding-bottom:9px}.section-column[data-v-699c28e3]{flex:1;min-width:0}@container narrow-references-container (max-width: 900px){.section-column[data-v-699c28e3]:nth-of-type(2){padding-top:0}}.section-columns[data-v-8b9602bf]{display:flex;gap:48px}@container narrow-references-container (max-width: 900px){.section-columns[data-v-8b9602bf]{flex-direction:column;gap:24px}}.section-container[data-v-20a1472a]{position:relative;padding:0 60px;width:100%;border-top:var(--scalar-border-width) solid var(--scalar-border-color)}.section-container[data-v-20a1472a]:has(.introduction-section){border-top:none}@container narrow-references-container (max-width: 900px){.section-container[data-v-20a1472a]{padding:0}}.section-accordion-wrapper[data-v-9419dd23]{padding:0 60px}.section-accordion[data-v-9419dd23]{position:relative;width:100%;max-width:var(--refs-content-max-width);margin:auto}.section-accordion-content[data-v-9419dd23]{display:flex;flex-direction:column;gap:12px;padding-top:12px}.section-accordion-button[data-v-9419dd23]{width:100%;display:flex;cursor:pointer;padding:6px 0;margin:-6px 0;border-radius:var(--scalar-radius)}.section-accordion-chevron[data-v-9419dd23]{position:absolute;left:-22px;top:12px;color:var(--scalar-color-3)}.section-accordion-button:hover .section-accordion-chevron[data-v-9419dd23]{color:var(--scalar-color-1)}.section-accordion-title[data-v-9419dd23]{display:flex;flex-direction:column;align-items:flex-start;flex:1;padding:0 6px}.section-accordion-title[data-v-9419dd23] .section-header-wrapper{grid-template-columns:1fr}.section-accordion-title[data-v-9419dd23] .section-header{margin-bottom:0}@container narrow-references-container (max-width: 900px){.section-accordion-chevron[data-v-9419dd23]{width:16px;left:-16px;top:14px}.section-accordion-wrapper[data-v-9419dd23]{padding:calc(var(--refs-viewport-offset)) 24px 0 24px}}.loading[data-v-8e0226d7]{background:var(--scalar-background-3);animation:loading-skeleton-8e0226d7 1.5s infinite alternate;border-radius:var(--scalar-radius-lg);min-height:1.6em;margin:.6em 0;max-width:100%}.loading[data-v-8e0226d7]:first-of-type{min-height:3em;margin-bottom:24px;margin-top:0}.loading[data-v-8e0226d7]:last-of-type{width:60%}.loading.single-line[data-v-8e0226d7]{min-height:3em;margin:.6em 0;max-width:80%}@keyframes loading-skeleton-8e0226d7{0%{opacity:1}to{opacity:.33}}@container narrow-references-container (max-width: 900px){.section-content--with-columns[data-v-9735459e]{flex-direction:column;gap:24px}}.section-header-wrapper[data-v-465a7a78]{grid-template-columns:1fr;display:grid}@media(min-width:1200px){.section-header-wrapper[data-v-465a7a78]{grid-template-columns:repeat(2,1fr)}}.section-header[data-v-465a7a78]{font-size:var(--font-size,var(--scalar-heading-1));font-weight:var(--font-weight,var(--scalar-bold));color:var(--scalar-color-1);word-wrap:break-word;margin-top:0;margin-bottom:12px;line-height:1.45}.section-header.tight[data-v-465a7a78]{margin-bottom:6px}.section-header.loading[data-v-465a7a78]{width:80%}.section-header-label[data-v-f1ac6c38]{display:inline}.screenreader-only[data-v-df2e1026]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.collapsible-section[data-v-999a158a]{border-top:var(--scalar-border-width) solid var(--scalar-border-color);position:relative}.collapsible-section-header[data-v-999a158a]{color:var(--scalar-color-1)}.collapsible-section .collapsible-section-trigger[data-v-999a158a]{display:flex;align-items:center;cursor:pointer;padding:10px 0;font-size:var(--scalar-font-size-3);z-index:1;position:relative}.collapsible-section-trigger svg[data-v-999a158a]{color:var(--scalar-color-3);position:absolute;left:-19px}.collapsible-section:hover .collapsible-section-trigger svg[data-v-999a158a]{color:var(--scalar-color-1)}.collapsible-section .collapsible-section-trigger[data-v-999a158a] .anchor-copy{line-height:18.5px}.collapsible-section-content[data-v-999a158a]{padding:0;margin:0 0 10px;scroll-margin-top:140px}.references-classic .introduction-description[data-v-fe80002d] img{max-width:720px}.icons-only[data-v-0939d4d9] span{display:none}.sticky-cards[data-v-0b1e2255]{display:flex;flex-direction:column;position:sticky;top:calc(var(--refs-viewport-offset) + 24px)}.introduction-card-item[data-v-708aae59]{display:flex;margin-bottom:12px;flex-direction:column;justify-content:start}.introduction-card-item[data-v-708aae59]:has(.description) .server-form-container{border-bottom-left-radius:0;border-bottom-right-radius:0}.introduction-card-item[data-v-708aae59] .request-item{border-bottom:0}.schema-type-icon[data-v-70cb5c13]{color:var(--scalar-color-1);display:none}.schema-type[data-v-70cb5c13]{font-family:var(--scalar-font-code);color:var(--scalar-color-1)}.property-enum-value[data-v-88bc950e]{color:var(--scalar-color-3);line-height:1.5;overflow-wrap:break-word;display:flex;align-items:stretch;position:relative;--decorator-width: 1px;--decorator-color: color-mix( in srgb, var(--scalar-background-1), var(--scalar-color-1) 25% )}.property-enum-value-content[data-v-88bc950e]{display:flex;flex-direction:column;padding:3px 0}.property-enum-value-label[data-v-88bc950e]{display:flex;font-family:var(--scalar-font-code);color:var(--scalar-color-1);position:relative}.property-enum-value:last-of-type .property-enum-value-label[data-v-88bc950e]{padding-bottom:0}.property-enum-value[data-v-88bc950e]:before{content:\"\";margin-right:12px;width:var(--decorator-width);display:block;background-color:var(--decorator-color)}.property-enum-value[data-v-88bc950e]:last-of-type:before,.property-enum-values:has(.enum-toggle-button) .property-enum-value[data-v-88bc950e]:nth-last-child(2):before{height:calc(.5lh + 4px)}.property-enum-value-label[data-v-88bc950e]:after{content:\"\";position:absolute;top:.5lh;left:-12px;width:8px;height:var(--decorator-width);background-color:var(--decorator-color)}.property-enum-value[data-v-88bc950e]:last-of-type:after{bottom:0;height:50%;background:var(--scalar-background-1);border-top:var(--scalar-border-width) solid var(--decorator-color)}.property-enum-value-description[data-v-88bc950e]{color:var(--scalar-color-3)}.property-heading:empty+.property-description[data-v-d4946030]:last-of-type,.property-description[data-v-d4946030]:first-of-type:last-of-type{margin-top:0}.property-list[data-v-d4946030]{border:var(--scalar-border-width) solid var(--scalar-border-color);border-radius:var(--scalar-radius);margin-top:10px}.property-list .property[data-v-d4946030]:last-of-type{padding-bottom:10px}.property-enum-values[data-v-d4946030]{font-size:var(--scalar-font-size-3);list-style:none;margin-top:8px;padding-left:2px}.enum-toggle-button[data-v-d4946030]:hover{color:var(--scalar-color-1)}.property-detail[data-v-827ea49d]{display:inline-flex}.property-detail+.property-detail[data-v-827ea49d]:before{display:block;content:\"·\";margin:0 .5ch}.property-detail-truncate[data-v-827ea49d]{overflow:hidden}.property-detail-truncate>.property-detail-value[data-v-827ea49d]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.property-detail-prefix[data-v-827ea49d]{color:var(--scalar-color-2)}code.property-detail-value[data-v-827ea49d]{font-family:var(--scalar-font-code);font-size:var(--scalar-font-size-3);color:var(--scalar-color-2);background:var(--scalar-background-3);padding:0 4px;border:.5px solid var(--scalar-border-color);border-radius:var(--scalar-radius)}.property-example[data-v-72def0ea]{display:flex;flex-direction:column;font-size:var(--scalar-mini);position:relative}.property-example[data-v-72def0ea]:hover:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:20px;border-radius:var(--scalar-radius)}.property-example:hover .property-example-label span[data-v-72def0ea]{color:var(--scalar-color-1)}.property-example-label span[data-v-72def0ea]{color:var(--scalar-color-3);position:relative;border-bottom:var(--scalar-border-width) dotted currentColor}.property-example-value[data-v-72def0ea]{font-family:var(--scalar-font-code);display:flex;gap:8px;align-items:center;width:100%;padding:6px}.property-example-value span[data-v-72def0ea]{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.property-example-value[data-v-72def0ea] svg{color:var(--scalar-color-3)}.property-example-value[data-v-72def0ea]:hover svg{color:var(--scalar-color-1)}.property-example-value[data-v-72def0ea]{background:var(--scalar-background-2);border:var(--scalar-border-width) solid var(--scalar-border-color);border-radius:var(--scalar-radius)}.property-example-value-list[data-v-72def0ea]{position:absolute;top:18px;left:50%;transform:translate3d(-50%,0,0);overflow:auto;background-color:var(--scalar-background-1);box-shadow:var(--scalar-shadow-1);border-radius:var(--scalar-radius-lg);border:var(--scalar-border-width) solid var(--scalar-border-color);padding:9px;min-width:200px;max-width:300px;flex-direction:column;gap:3px;display:none;z-index:2}.property-example:hover .property-example-value-list[data-v-72def0ea],.property-example:focus-within .property-example-value-list[data-v-72def0ea]{display:flex}.property-heading[data-v-5d03e993]{display:flex;flex-wrap:wrap;align-items:baseline;row-gap:9px;white-space:nowrap}.property-heading[data-v-5d03e993]:has(+.children),.property-heading[data-v-5d03e993]:has(+.property-rule){margin-bottom:9px}.property-heading[data-v-5d03e993]>*{margin-right:9px}.property-heading[data-v-5d03e993]:last-child{margin-right:0}.property-heading>.property-detail[data-v-5d03e993]:not(:last-of-type){margin-right:0}.property-name[data-v-5d03e993]{max-width:100%;font-family:var(--scalar-font-code);font-weight:var(--scalar-semibold);font-size:var(--scalar-font-size-3);white-space:normal;overflow-wrap:break-word}.property-additional[data-v-5d03e993]{font-family:var(--scalar-font-code)}.property-required[data-v-5d03e993],.property-optional[data-v-5d03e993]{color:var(--scalar-color-2)}.property-required[data-v-5d03e993]{font-size:var(--scalar-mini);color:var(--scalar-color-orange)}.property-read-only[data-v-5d03e993]{font-size:var(--scalar-mini);color:var(--scalar-color-blue)}.property-write-only[data-v-5d03e993]{font-size:var(--scalar-mini);color:var(--scalar-color-green)}.property-discriminator[data-v-5d03e993]{font-size:var(--scalar-mini);color:var(--scalar-color-purple)}.property-detail[data-v-5d03e993]{font-size:var(--scalar-mini);color:var(--scalar-color-2);display:flex;align-items:center;min-width:0}.property-const[data-v-5d03e993]{color:var(--scalar-color-1)}.deprecated[data-v-5d03e993]{text-decoration:line-through}.property[data-v-a4c1ff0a]{color:var(--scalar-color-1);display:flex;flex-direction:column;padding:8px;font-size:var(--scalar-small);position:relative}.property.property--level-0[data-v-a4c1ff0a]:has(>.property-rule>.schema-card>.schema-properties.schema-properties-open>ul>li.property){padding-top:0}.property--compact.property--level-0[data-v-a4c1ff0a],.property--compact.property--level-1[data-v-a4c1ff0a]{padding:8px 0}.composition-panel .property.property.property.property--level-0[data-v-a4c1ff0a]{padding:0}.property--compact.property--level-0 .composition-panel .property--compact.property--level-1[data-v-a4c1ff0a]{padding:8px}.property[data-v-a4c1ff0a]:has(>.property-rule:nth-of-type(1)):not(.property--compact){padding-top:8px;padding-bottom:8px}.property--deprecated[data-v-a4c1ff0a]{background:repeating-linear-gradient(-45deg,var(--scalar-background-2) 0,var(--scalar-background-2) 2px,transparent 2px,transparent 5px);background-size:100%}.property--deprecated[data-v-a4c1ff0a]>*{opacity:.75}.property-description[data-v-a4c1ff0a]{margin-top:6px;line-height:1.4;font-size:var(--scalar-small)}.property-description[data-v-a4c1ff0a]:has(+.property-rule){margin-bottom:9px}[data-v-a4c1ff0a] .property-description *{color:var(--scalar-color-2)!important}.property[data-v-a4c1ff0a]:not(:last-of-type){border-bottom:var(--scalar-border-width) solid var(--scalar-border-color)}.property-description+.children[data-v-a4c1ff0a],.children+.property-rule[data-v-a4c1ff0a]{margin-top:9px}.children[data-v-a4c1ff0a]{display:flex;flex-direction:column}.children .property--compact.property--level-1[data-v-a4c1ff0a]{padding:12px}.property-example-value[data-v-a4c1ff0a]{all:unset;font-family:var(--scalar-font-code);padding:6px;border-top:var(--scalar-border-width) solid var(--scalar-border-color)}.property-rule[data-v-a4c1ff0a]{border-radius:var(--scalar-radius-lg);display:flex;flex-direction:column}.property-rule[data-v-a4c1ff0a] .composition-panel .schema-card .schema-properties.schema-properties-open{border-top-left-radius:0;border-top-right-radius:0}.property-rule[data-v-a4c1ff0a] .composition-panel>.schema-card>.schema-card-description{padding-left:8px;padding-right:8px;border-left:1px solid var(--scalar-border-color);border-right:1px solid var(--scalar-border-color)}.property-rule[data-v-a4c1ff0a] .composition-panel>.schema-card>.schema-card-description+.schema-properties{margin-top:0}.property-example[data-v-a4c1ff0a]{background:transparent;border:none;display:flex;flex-direction:row;gap:8px}.property-example-label[data-v-a4c1ff0a],.property-example-value[data-v-a4c1ff0a]{padding:3px 0 0}.property-example-value[data-v-a4c1ff0a]{background:var(--scalar-background-2);border-top:0;border-radius:var(--scalar-radius);padding:3px 4px}.property-name[data-v-a4c1ff0a]{font-family:var(--scalar-font-code);font-weight:var(--scalar-semibold)}.property-name-additional-properties[data-v-a4c1ff0a]:before,.property-name-pattern-properties[data-v-a4c1ff0a]:before{text-transform:uppercase;font-size:var(--scalar-micro);display:inline-block;padding:2px 4px;border-radius:var(--scalar-radius);color:var(--scalar-color-1);border:1px solid var(--scalar-border-color);background-color:var(--scalar-background-2);margin-right:4px}.property-name-pattern-properties[data-v-a4c1ff0a]:before{content:\"regex\"}.property-name-additional-properties[data-v-a4c1ff0a]:before{content:\"unknown\"}.error[data-v-4d81600f]{background-color:var(--scalar-color-red)}.schema-card[data-v-4d81600f]{font-size:var(--scalar-font-size-4);color:var(--scalar-color-1)}.schema-card-title[data-v-4d81600f]{height:var(--schema-title-height);padding:6px 8px;display:flex;align-items:center;gap:4px;color:var(--scalar-color-2);font-weight:var(--scalar-semibold);font-size:var(--scalar-mini);border-bottom:var(--scalar-border-width) solid transparent}button.schema-card-title[data-v-4d81600f]{cursor:pointer}button.schema-card-title[data-v-4d81600f]:hover{color:var(--scalar-color-1)}.schema-card-title-icon--open[data-v-4d81600f]{transform:rotate(45deg)}.schema-properties-open>.schema-card-title[data-v-4d81600f]{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:var(--scalar-border-width) solid var(--scalar-border-color)}.schema-properties-open>.schema-properties[data-v-4d81600f]{width:fit-content}.schema-card-description[data-v-4d81600f]{color:var(--scalar-color-2)}.schema-card-description+.schema-properties[data-v-4d81600f]{width:fit-content}.schema-card-description+.schema-properties[data-v-4d81600f]{margin-top:8px}.schema-properties-open.schema-properties[data-v-4d81600f],.schema-properties-open>.schema-card--open[data-v-4d81600f]{width:100%}.schema-properties[data-v-4d81600f]{display:flex;flex-direction:column;border:var(--scalar-border-width) solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg);width:fit-content}.schema-properties-name[data-v-4d81600f]{width:100%}.schema-properties .schema-properties[data-v-4d81600f]{border-radius:13.5px}.schema-properties .schema-properties.schema-properties-open[data-v-4d81600f]{border-radius:var(--scalar-radius-lg)}.schema-properties-open[data-v-4d81600f]{width:100%}.schema-card--compact[data-v-4d81600f]{align-self:flex-start}.schema-card--compact.schema-card--open[data-v-4d81600f]{align-self:initial}.schema-card-title--compact[data-v-4d81600f]{color:var(--scalar-color-2);padding:6px 10px 6px 8px;height:auto;border-bottom:none}.schema-card-title--compact>.schema-card-title-icon[data-v-4d81600f]{margin:0}.schema-card-title--compact>.schema-card-title-icon--open[data-v-4d81600f]{transform:rotate(45deg)}.schema-properties-open>.schema-card-title--compact[data-v-4d81600f]{position:static}.property--level-0>.schema-properties>.schema-card--level-0>.schema-properties[data-v-4d81600f]{border:none}.property--level-0 .schema-card--level-0:not(.schema-card--compact) .property--level-1[data-v-4d81600f]{padding:0 0 8px}:not(.composition-panel)>.schema-card--compact.schema-card--level-0>.schema-properties[data-v-4d81600f]{border:none}[data-v-4d81600f] .schema-card-description p{font-size:var(--scalar-small, var(--scalar-paragraph));color:var(--scalar-color-2);line-height:1.5;display:block;margin-bottom:6px}.children .schema-card-description[data-v-4d81600f]:first-of-type{padding-top:0}.reference-models-anchor[data-v-161968a4]{display:flex;align-items:center;font-size:20px;padding-left:6px;color:var(--scalar-color-1)}.reference-models-label[data-v-161968a4]{display:block;font-size:var(--scalar-mini)}.reference-models-label[data-v-161968a4] em{font-weight:var(--scalar-bold)}.show-more[data-v-d1c2b649]{appearance:none;border:none;border:var(--scalar-border-width) solid var(--scalar-border-color);margin:auto;padding:8px 12px 8px 16px;border-radius:30px;color:var(--scalar-color-1);font-weight:var(--scalar-semibold);font-size:var(--scalar-small);display:flex;align-items:center;justify-content:center;position:relative;gap:6px;top:-48px}.show-more[data-v-d1c2b649]:hover{background:var(--scalar-background-2);cursor:pointer}.show-more[data-v-d1c2b649]:active{box-shadow:0 0 0 1px var(--scalar-border-color)}@container narrow-references-container (max-width: 900px){.show-more[data-v-d1c2b649]{top:-24px}}.tag-section[data-v-1124be5d]{margin-bottom:48px}.tag-name[data-v-1124be5d]{text-transform:capitalize}.tag-description[data-v-1124be5d]{padding-bottom:4px;text-align:left}.endpoint[data-v-ad8530a6]{display:flex;white-space:nowrap;cursor:pointer;text-decoration:none}.endpoint:hover .endpoint-path[data-v-ad8530a6],.endpoint:focus-visible .endpoint-path[data-v-ad8530a6]{text-decoration:underline}.endpoint .post[data-v-ad8530a6],.endpoint .get[data-v-ad8530a6],.endpoint .delete[data-v-ad8530a6],.endpoint .put[data-v-ad8530a6]{white-space:nowrap}.endpoint-method[data-v-ad8530a6],.endpoint-path[data-v-ad8530a6]{color:var(--scalar-color-1);min-width:62px;display:inline-flex;line-height:1.55;font-family:var(--scalar-font-code);font-size:var(--scalar-small);cursor:pointer}.endpoint-method[data-v-ad8530a6]{text-align:right}.endpoint-path[data-v-ad8530a6]{margin-left:12px;text-transform:initial}.deprecated[data-v-ad8530a6]{text-decoration:line-through}.endpoints-card[data-v-f726f753]{position:sticky;top:calc(var(--refs-viewport-offset) + 24px);font-size:var(--scalar-font-size-3)}.endpoints[data-v-f726f753]{overflow:auto;background:var(--scalar-background-2);padding:10px 12px;width:100%}.section-container[data-v-d207e09e]{border-top:var(--scalar-border-width) solid var(--scalar-border-color)}.section-container[data-v-d207e09e]:has(.show-more){background-color:color-mix(in srgb,var(--scalar-background-2),transparent)}.operation-path[data-v-ec6c8861]{overflow:hidden;word-wrap:break-word;font-weight:var(--scalar-semibold);line-break:anywhere}.deprecated[data-v-ec6c8861]{text-decoration:line-through}.empty-state[data-v-ab9ce344]{margin:10px 0 10px 12px;text-align:center;font-size:var(--scalar-mini);min-height:56px;display:flex;align-items:center;justify-content:center;border-radius:var(--scalar-radius-lg);color:var(--scalar-color-2)}.rule-title[data-v-ab9ce344]{font-family:var(--scalar-font-code);color:var(--scalar-color-1);display:inline-block;margin:12px 0 6px;border-radius:var(--scalar-radius)}.rule[data-v-ab9ce344]{margin:0 12px;border-radius:var(--scalar-radius-lg)}.rule-items[data-v-ab9ce344]{counter-reset:list-number;display:flex;flex-direction:column;gap:12px;border-left:1px solid var(--scalar-border-color);padding:12px 0}.rule-item[data-v-ab9ce344]{counter-increment:list-number;border:1px solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg);overflow:hidden;margin-left:24px}.rule-item[data-v-ab9ce344]:before{border:1px solid var(--scalar-border-color);border-top:0;border-right:0;content:\" \";display:block;width:24px;height:6px;border-radius:0 0 0 var(--scalar-radius-lg);margin-top:6px;color:var(--scalar-color-2);transform:translate(-25px);color:var(--scalar-color-1);position:absolute}.tab[data-v-c8207e48]{background:none;border:none;font-size:var(--scalar-small);font-family:var(--scalar-font);font-weight:var(--scalar-font-normal);color:var(--scalar-color-2);line-height:calc(var(--scalar-small) + 2px);white-space:nowrap;cursor:pointer;padding:0;margin-right:3px;text-transform:uppercase;position:relative;line-height:22px}.tab[data-v-c8207e48]:before{content:\"\";position:absolute;z-index:0;left:-6px;top:-6px;width:calc(100% + 12px);height:calc(100% + 12px);border-radius:var(--scalar-radius);background:var(--scalar-background-3);opacity:0}.tab[data-v-c8207e48]:hover:before,.tab[data-v-c8207e48]:focus-visible:before{opacity:1}.tab[data-v-c8207e48]:focus-visible:before{outline:1px solid var(--scalar-color-accent)}.tab span[data-v-c8207e48]{z-index:1;position:relative}.tab-selected[data-v-c8207e48]{color:var(--scalar-color-1);font-weight:var(--scalar-semibold)}.tab-selected[data-v-c8207e48]:after{content:\"\";position:absolute;background:currentColor;width:100%;left:0;height:1px;bottom:calc(var(--tab-list-padding-y) * -1)}.tab-list[data-v-fec8fbbb]{display:flex;gap:6px;position:relative;flex:1;--tab-list-padding-y: 7px;--tab-list-padding-x: 12px;padding:var(--tab-list-padding-y) var(--tab-list-padding-x);overflow:auto}.scalar-card-header.scalar-card-header-tabs[data-v-fec8fbbb]{padding:0}.response-card[data-v-b2851076]{font-size:var(--scalar-font-size-3)}.markdown[data-v-b2851076] *{margin:0}.code-copy[data-v-b2851076]{display:flex;align-items:center;justify-content:center;appearance:none;-webkit-appearance:none;outline:none;background:transparent;cursor:pointer;color:var(--scalar-color-3);border:none;padding:0;margin-right:12px}.code-copy[data-v-b2851076]:hover{color:var(--scalar-color-1)}.code-copy svg[data-v-b2851076]{width:13px;height:13px}.response-card-footer[data-v-b2851076]{display:flex;flex-direction:row;justify-content:space-between;flex-shrink:0;padding:7px 12px;gap:8px}.response-example-selector[data-v-b2851076]{align-self:flex-start;margin:-4px}.response-description[data-v-b2851076]{font-weight:var(--scalar-semibold);font-size:var(--scalar-small);color:var(--scalar-color--1);display:flex;align-items:center;box-sizing:border-box}.schema-type[data-v-b2851076]{font-size:var(--scalar-micro);color:var(--scalar-color-2);font-weight:var(--scalar-semibold);background:var(--scalar-background-3);padding:2px 4px;border-radius:4px;margin-right:4px}.schema-example[data-v-b2851076]{font-size:var(--scalar-micro);color:var(--scalar-color-2);font-weight:var(--scalar-semibold)}.example-response-tab[data-v-b2851076]{display:block;margin:6px}.scalar-card-checkbox[data-v-b2851076]{display:flex;align-items:center;justify-content:center;position:relative;min-height:17px;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:var(--scalar-small);font-weight:var(--scalar-font-normal);color:var(--scalar-color-2);width:fit-content;white-space:nowrap;gap:6px;padding:7px 6px}.scalar-card-checkbox:has(.scalar-card-checkbox-input:focus-visible) .scalar-card-checkbox-checkmark[data-v-b2851076]{outline:1px solid var(--scalar-color-accent)}.scalar-card-checkbox[data-v-b2851076]:hover{color:var(--scalar-color--1)}.scalar-card-checkbox .scalar-card-checkbox-input[data-v-b2851076]{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.scalar-card-checkbox-checkmark[data-v-b2851076]{height:16px;width:16px;border-radius:var(--scalar-radius);background-color:transparent;background-color:var(--scalar-background-3);box-shadow:inset 0 0 0 var(--scalar-border-width) var(--scalar-border-color)}.scalar-card-checkbox[data-v-b2851076]:has(.scalar-card-checkbox-input:checked){color:var(--scalar-color-1);font-weight:var(--scalar-semibold)}.scalar-card-checkbox .scalar-card-checkbox-input:checked~.scalar-card-checkbox-checkmark[data-v-b2851076]{background-color:var(--scalar-button-1);box-shadow:none}.scalar-card-checkbox-checkmark[data-v-b2851076]:after{content:\"\";position:absolute;display:none}.scalar-card-checkbox .scalar-card-checkbox-input:checked~.scalar-card-checkbox-checkmark[data-v-b2851076]:after{display:block}.scalar-card-checkbox .scalar-card-checkbox-checkmark[data-v-b2851076]:after{right:11.5px;top:12.5px;width:5px;height:9px;border:solid 1px var(--scalar-button-1-color);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}.headers-card[data-v-6fb09984]{z-index:0;margin-top:12px;margin-bottom:6px;position:relative;font-size:var(--scalar-font-size-4);color:var(--scalar-color-1);align-self:flex-start}.headers-card.headers-card--open[data-v-6fb09984]{align-self:initial}.headers-card-title[data-v-6fb09984]{padding:6px 10px;display:flex;align-items:center;gap:4px;color:var(--scalar-color-3);font-weight:var(--scalar-semibold);font-size:var(--scalar-micro);border-radius:13.5px}button.headers-card-title[data-v-6fb09984]{cursor:pointer}button.headers-card-title[data-v-6fb09984]:hover{color:var(--scalar-color-1)}.headers-card-title-icon--open[data-v-6fb09984]{transform:rotate(45deg)}.headers-properties[data-v-6fb09984]{display:flex;flex-direction:column;border:var(--scalar-border-width) solid var(--scalar-border-color);border-radius:13.5px;width:fit-content}.headers-properties-open>.headers-card-title[data-v-6fb09984]{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:var(--scalar-border-width) solid var(--scalar-border-color)}.headers-properties-open[data-v-6fb09984]{border-radius:var(--scalar-radius-lg);width:100%}.headers-card .property[data-v-6fb09984]:last-of-type{padding-bottom:10px}.headers-card-title>.headers-card-title-icon[data-v-6fb09984]{width:10px;height:10px;margin:0}.headers-card-title>.headers-card-title-icon--open[data-v-6fb09984]{transform:rotate(45deg)}.parameter-item[data-v-2b6dfab9]{display:flex;flex-direction:column;border-top:var(--scalar-border-width) solid var(--scalar-border-color)}.parameter-item:last-of-type .parameter-schema[data-v-2b6dfab9]{padding-bottom:0}.parameter-item-container[data-v-2b6dfab9]{padding:0}.parameter-item-headers[data-v-2b6dfab9]{border:var(--scalar-border-width) solid var(--scalar-border-color)}.parameter-item-name[data-v-2b6dfab9]{position:relative;font-weight:var(--scalar-semibold);font-size:var(--scalar-font-size-3);font-family:var(--scalar-font-code);color:var(--scalar-color-1);overflow-wrap:break-word}.parameter-item-description[data-v-2b6dfab9],.parameter-item-description-summary[data-v-2b6dfab9]{font-size:var(--scalar-mini);color:var(--scalar-color-2)}.parameter-item-description-summary.parameter-item-description-summary[data-v-2b6dfab9]>*{--markdown-line-height: 1}.parameter-item-trigger+.parameter-item-container[data-v-2b6dfab9] .property--level-0>.property-heading .property-detail-value{font-size:var(--scalar-micro)}.parameter-item-required-optional[data-v-2b6dfab9]{color:var(--scalar-color-2);font-weight:var(--scalar-semibold);margin-right:6px;position:relative}.parameter-item--required[data-v-2b6dfab9]{text-transform:uppercase;font-size:var(--scalar-micro);font-weight:var(--scalar-semibold);color:var(--scalar-color-orange)}.parameter-item-description[data-v-2b6dfab9],.parameter-item-description[data-v-2b6dfab9] p{margin-top:4px;font-size:var(--scalar-small);color:var(--scalar-color-2);line-height:1.4}.parameter-schema[data-v-2b6dfab9]{padding-bottom:9px;margin-top:3px}.parameter-item-trigger[data-v-2b6dfab9]{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;padding:12px 0;outline:none}.parameter-item-trigger-open[data-v-2b6dfab9]{padding-bottom:0}.parameter-item-trigger[data-v-2b6dfab9]:after{content:\"\";position:absolute;height:10px;width:100%;bottom:0}.parameter-item-icon[data-v-2b6dfab9]{color:var(--scalar-color-3);left:-19px;top:.5lh;translate:0 -50%;position:absolute}.parameter-item-trigger:hover .parameter-item-icon[data-v-2b6dfab9],.parameter-item-trigger:focus-visible .parameter-item-icon[data-v-2b6dfab9]{color:var(--scalar-color-1)}.parameter-item-trigger:focus-visible .parameter-item-icon[data-v-2b6dfab9]{outline:1px solid var(--scalar-color-accent);outline-offset:2px;border-radius:var(--scalar-radius)}.request-body[data-v-e9972a68]{margin-top:24px}.request-body-header[data-v-e9972a68]{display:flex;align-items:center;justify-content:space-between;padding-bottom:12px;border-bottom:var(--scalar-border-width) solid var(--scalar-border-color);flex-flow:wrap}.request-body-title[data-v-e9972a68]{display:flex;align-items:center;gap:8px;font-size:var(--scalar-font-size-2);font-weight:var(--scalar-semibold);color:var(--scalar-color-1)}.request-body-required[data-v-e9972a68]{font-size:var(--scalar-micro);color:var(--scalar-color-orange);font-weight:400}.request-body-description[data-v-e9972a68]{margin-top:6px;font-size:var(--scalar-small);width:100%}.request-body-header+.request-body-schema[data-v-e9972a68]:has(>.schema-card>.schema-card-description),.request-body-header+.request-body-schema[data-v-e9972a68]:has(>.schema-card>.schema-properties>*>.property--level-0){padding-top:8px}.request-body-description[data-v-e9972a68] .markdown *{color:var(--scalar-color-2)!important}.callback-sticky-offset[data-v-102d2096]{top:var(--refs-viewport-offset, 0px);z-index:1}.callback-operation-container[data-v-102d2096] .request-body,.callback-operation-container[data-v-102d2096] .request-body-description,.callback-operation-container[data-v-102d2096] .request-body-header{margin-top:0}.callback-operation-container[data-v-102d2096] .request-body-header{--scalar-font-size-2: var(--scalar-font-size-4);padding:8px;border-bottom:none;border:.5px solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg) var(--scalar-radius-lg) 0 0;background:color-mix(in srgb,var(--scalar-background-2) 50%,transparent)}.callback-operation-container[data-v-102d2096] .request-body-schema>.schema-card>.schema-card-description{padding-inline:8px}.callback-operation-container[data-v-102d2096] ul li.property.property--level-1{padding:8px}.callback-operation-container[data-v-102d2096] .request-body-schema{background-color:var(--scalar-background-1);border:var(--scalar-border-width) solid var(--scalar-border-color);border-top:none;overflow:hidden;border-radius:0 0 var(--scalar-radius-lg) var(--scalar-radius-lg)}.callback-operation-container[data-v-102d2096] .parameter-list{margin-top:0}.callback-operation-container[data-v-102d2096] .parameter-list-title{background:color-mix(in srgb,var(--scalar-background-2) 50%,transparent);border-radius:var(--scalar-radius-lg) var(--scalar-radius-lg) 0 0;padding:8px;margin-bottom:0;border:var(--scalar-border-width) solid var(--scalar-border-color);border-bottom:none;--scalar-font-size-2: var(--scalar-font-size-4)}.callback-operation-container[data-v-102d2096] .parameter-list-items{border:var(--scalar-border-width) solid var(--scalar-border-color);border-radius:0 0 var(--scalar-radius-lg) var(--scalar-radius-lg)}.callback-operation-container[data-v-102d2096] .parameter-list-items>li:first-of-type{border-top:none}.callback-operation-container[data-v-102d2096] .parameter-list-items>li{padding:0 8px}.show-api-client-button[data-v-e851edeb]{appearance:none;border:none;padding:1px 6px;white-space:nowrap;border-radius:var(--scalar-radius);display:flex;justify-content:center;align-items:center;font-weight:var(--scalar-semibold);font-size:var(--scalar-small);line-height:22px;color:var(--scalar-background-2);font-family:var(--scalar-font);background:var(--scalar-button-1);position:relative;cursor:pointer;box-sizing:border-box;box-shadow:inset 0 0 0 1px #0000001a;outline-offset:2px}.show-api-client-button span[data-v-e851edeb],.show-api-client-button svg[data-v-e851edeb]{fill:currentColor;color:var(--scalar-button-1-color);z-index:1}.show-api-client-button[data-v-e851edeb]:hover{background:var(--scalar-button-1-hover)}.show-api-client-button svg[data-v-e851edeb]{margin-right:4px}.operation-title[data-v-727857de]{justify-content:space-between;display:flex}.operation-details[data-v-727857de]{flex-shrink:1;align-items:center;gap:9px;min-width:0;margin-top:0;display:flex}.operation-details[data-v-727857de] .endpoint-anchor .scalar-button svg{width:16px;height:16px}.endpoint-type[data-v-727857de]{z-index:0;width:60px;font-size:var(--scalar-small);text-transform:uppercase;font-weight:var(--scalar-bold);font-family:var(--scalar-font);flex-shrink:0;justify-content:center;align-items:center;gap:6px;padding:6px;display:flex;position:relative}.endpoint-type[data-v-727857de]:after{content:\"\";z-index:-1;opacity:.15;border-radius:var(--scalar-radius);background:currentColor;position:absolute;inset:0}.endpoint-anchor[data-v-727857de]{flex-shrink:1;align-items:center;min-width:0;display:flex}.endpoint-anchor.label[data-v-727857de]{display:flex}.endpoint-label[data-v-727857de]{min-width:0;color:var(--scalar-color-1);flex-shrink:1;align-items:baseline;gap:9px;display:flex}.endpoint-label-path[data-v-727857de]{font-family:var(--scalar-font-code);font-size:var(--scalar-mini);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.endpoint-label-path[data-v-727857de] em{color:var(--scalar-color-2)}.endpoint-label-name[data-v-727857de]{color:var(--scalar-color-2);font-size:var(--scalar-small);text-overflow:ellipsis;white-space:nowrap;flex-shrink:1000000000;overflow:hidden}.endpoint-try-hint[data-v-727857de]{flex-shrink:0;padding:2px}.endpoint-copy[data-v-727857de]{color:currentColor}.endpoint-copy[data-v-727857de] svg{stroke-width:2px}.endpoint-content[data-v-727857de]{grid-auto-columns:1fr;grid-auto-flow:row;gap:9px;padding:9px;display:grid}@media(min-width:1000px){.endpoint-content[data-v-727857de]{grid-auto-flow:column}}@container (max-width:900px){.endpoint-content[data-v-727857de]{grid-template-columns:1fr}}.endpoint-content[data-v-727857de]>*{min-width:0}.operation-details-card[data-v-727857de]{flex-direction:column;gap:12px;min-width:0;display:flex}:is(.operation-details-card-item[data-v-727857de] .parameter-list,.operation-details-card-item[data-v-727857de] .callbacks-list){border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg);margin-top:0}.operation-details-card-item[data-v-727857de]{flex-direction:column;gap:12px;display:flex}.operation-details-card-item[data-v-727857de] .parameter-list-items{margin-bottom:0}.operation-details-card[data-v-727857de] .parameter-item:last-of-type .parameter-schema{padding-bottom:12px}.operation-details-card[data-v-727857de] .parameter-list .parameter-list{margin-bottom:12px}.operation-details-card[data-v-727857de] .parameter-item{margin:0;padding:0}.operation-details-card[data-v-727857de] .property{margin:0;padding:9px}:is(.operation-details-card[data-v-727857de] .parameter-list-title,.operation-details-card[data-v-727857de] .request-body-title,.operation-details-card[data-v-727857de] .callbacks-title){text-transform:uppercase;font-weight:var(--scalar-bold);font-size:var(--scalar-mini);color:var(--scalar-color-2);margin:0;padding:9px;line-height:1.33}.operation-details-card[data-v-727857de] .callback-list-item-title{padding-left:28px;padding-right:12px}.operation-details-card[data-v-727857de] .callback-list-item-icon{left:6px}.operation-details-card[data-v-727857de] .callback-operation-container{padding-inline:9px;padding-bottom:9px}:is(.operation-details-card[data-v-727857de] .callback-operation-container>.request-body,.operation-details-card[data-v-727857de] .callback-operation-container>.parameter-list){border:none}.operation-details-card[data-v-727857de] .callback-operation-container>.request-body>.request-body-header{border-bottom:var(--scalar-border-width)solid var(--scalar-border-color);padding:0 0 9px}.operation-details-card[data-v-727857de] .request-body-description{border-top:var(--scalar-border-width)solid var(--scalar-border-color);margin-top:0;padding:9px 9px 0}.operation-details-card[data-v-727857de] .request-body{border-radius:var(--scalar-radius-lg);border:var(--scalar-border-width)solid var(--scalar-border-color);margin-top:0}.operation-details-card[data-v-727857de] .request-body .schema-card--level-0>.schema-card-description{padding-inline:9px}.operation-details-card[data-v-727857de] .request-body-header{border-bottom:0;padding-bottom:0}.operation-details-card[data-v-727857de] .contents button{margin-right:9px}.operation-details-card[data-v-727857de] .schema-card--open+.schema-card:not(.schema-card--open){margin-inline:9px;margin-bottom:9px}.operation-details-card[data-v-727857de] .request-body-schema .property--level-0{padding:0}.operation-details-card[data-v-727857de] .selected-content-type{margin-right:9px}.operation-example-card[data-v-727857de]{top:calc(var(--refs-viewport-offset) + 24px);max-height:calc(var(--refs-viewport-height) - 48px);position:sticky}@media(max-width:600px){.operation-example-card[data-v-727857de]{max-height:unset;position:static}}.examples[data-v-6e4b7777]{position:sticky;top:calc(var(--refs-viewport-offset) + 24px)}.examples[data-v-6e4b7777]>*{max-height:calc((var(--refs-viewport-height) - 60px) / 2);position:relative}@media(max-width:600px){.examples[data-v-6e4b7777]>*{max-height:unset}}.deprecated[data-v-6e4b7777] *{text-decoration:line-through}.section-flare[data-v-2a9c8c02]{top:0;right:0;pointer-events:none}.narrow-references-container{container-name:narrow-references-container;container-type:inline-size}.ref-search-meta[data-v-c1c368f9]{background:var(--scalar-background-1);border-bottom-left-radius:var(--scalar-radius-lg);border-bottom-right-radius:var(--scalar-radius-lg);padding:6px 12px;font-size:var(--scalar-font-size-4);color:var(--scalar-color-3);font-weight:var(--scalar-semibold);display:flex;gap:12px;border-top:var(--scalar-border-width) solid var(--scalar-border-color)}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:\"\";--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-x-reverse:0}}}@layer scalar-base{@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}:root,:host{--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}}.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}}.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}}.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}}.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}}:root,:host{--leading-snug:1.375;--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1)}@supports (color:color-mix(in lab,red,red)){@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}}.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}}.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}}.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}}.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}}body{line-height:inherit;margin:0}:root{--scalar-border-width:.5px;--scalar-radius:3px;--scalar-radius-lg:6px;--scalar-radius-xl:8px;--scalar-font:\"Inter\",-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen,Ubuntu,Cantarell,\"Open Sans\",\"Helvetica Neue\",sans-serif;--scalar-font-code:\"JetBrains Mono\",ui-monospace,Menlo,Monaco,\"Cascadia Mono\",\"Segoe UI Mono\",\"Roboto Mono\",\"Oxygen Mono\",\"Ubuntu Monospace\",\"Source Code Pro\",\"Fira Mono\",\"Droid Sans Mono\",\"Courier New\",monospace;--scalar-heading-1:24px;--scalar-page-description:16px;--scalar-heading-2:20px;--scalar-heading-3:16px;--scalar-heading-4:16px;--scalar-heading-5:16px;--scalar-heading-6:16px;--scalar-paragraph:16px;--scalar-small:14px;--scalar-mini:13px;--scalar-micro:12px;--scalar-bold:600;--scalar-semibold:500;--scalar-regular:400;--scalar-font-size-1:21px;--scalar-font-size-2:16px;--scalar-font-size-3:14px;--scalar-font-size-4:13px;--scalar-font-size-5:12px;--scalar-font-size-6:12px;--scalar-font-size-7:10px;--scalar-line-height-1:32px;--scalar-line-height-2:24px;--scalar-line-height-3:20px;--scalar-line-height-4:18px;--scalar-line-height-5:16px;--scalar-font-normal:400;--scalar-font-medium:500;--scalar-font-bold:700;--scalar-text-decoration:none;--scalar-text-decoration-hover:underline;--scalar-link-font-weight:inherit;--scalar-sidebar-indent:20px}.dark-mode{color-scheme:dark;--scalar-scrollbar-color:#ffffff2e;--scalar-scrollbar-color-active:#ffffff5c;--scalar-button-1:#fff;--scalar-button-1-hover:#ffffffe6;--scalar-button-1-color:black;--scalar-shadow-1:0 1px 3px 0 #0000001a;--scalar-shadow-2:0 0 0 .5px var(--scalar-border-color),#0f0f0f33 0px 3px 6px,#0f0f0f66 0px 9px 24px;--scalar-lifted-brightness:1.45;--scalar-backdrop-brightness:.5;--scalar-text-decoration-color:currentColor;--scalar-text-decoration-color-hover:currentColor}.light-mode{color-scheme:light;--scalar-scrollbar-color-active:#0000005c;--scalar-scrollbar-color:#0000002e;--scalar-button-1:#000;--scalar-button-1-hover:#000c;--scalar-button-1-color:#ffffffe6;--scalar-shadow-1:0 1px 3px 0 #0000001c;--scalar-shadow-2:#00000014 0px 13px 20px 0px,#00000014 0px 3px 8px 0px,#eeeeed 0px 0 0 .5px;--scalar-lifted-brightness:1;--scalar-backdrop-brightness:1;--scalar-text-decoration-color:currentColor;--scalar-text-decoration-color-hover:currentColor}.light-mode .dark-mode{color-scheme:dark!important}@media(max-width:460px){:root{--scalar-font-size-1:22px;--scalar-font-size-2:14px;--scalar-font-size-3:12px}}@media(max-width:720px){:root{--scalar-heading-1:24px;--scalar-page-description:20px}}:root{--scalar-text-decoration:underline;--scalar-text-decoration-hover:underline}.light-mode{--scalar-background-1:#fff;--scalar-background-2:#f6f6f6;--scalar-background-3:#e7e7e7;--scalar-background-accent:#8ab4f81f;--scalar-color-1:#1b1b1b;--scalar-color-2:#757575;--scalar-color-3:#8e8e8e;--scalar-color-accent:#09f;--scalar-border-color:#dfdfdf}.dark-mode{--scalar-background-1:#0f0f0f;--scalar-background-2:#1a1a1a;--scalar-background-3:#272727;--scalar-color-1:#e7e7e7;--scalar-color-2:#a4a4a4;--scalar-color-3:#797979;--scalar-color-accent:#00aeff;--scalar-background-accent:#3ea6ff1f;--scalar-border-color:#2d2d2d}.light-mode,.dark-mode{--scalar-sidebar-background-1:var(--scalar-background-1);--scalar-sidebar-color-1:var(--scalar-color-1);--scalar-sidebar-color-2:var(--scalar-color-2);--scalar-sidebar-border-color:var(--scalar-border-color);--scalar-sidebar-item-hover-background:var(--scalar-background-2);--scalar-sidebar-item-hover-color:var(--scalar-sidebar-color-2);--scalar-sidebar-item-active-background:var(--scalar-background-2);--scalar-sidebar-color-active:var(--scalar-sidebar-color-1);--scalar-sidebar-indent-border:var(--scalar-sidebar-border-color);--scalar-sidebar-indent-border-hover:var(--scalar-sidebar-border-color);--scalar-sidebar-indent-border-active:var(--scalar-sidebar-border-color);--scalar-sidebar-search-background:transparent;--scalar-sidebar-search-color:var(--scalar-color-3);--scalar-sidebar-search-border-color:var(--scalar-border-color)}.light-mode{--scalar-color-green:#069061;--scalar-color-red:#ef0006;--scalar-color-yellow:#edbe20;--scalar-color-blue:#0082d0;--scalar-color-orange:#ff5800;--scalar-color-purple:#5203d1;--scalar-link-color:var(--scalar-color-1);--scalar-link-color-hover:var(--scalar-link-color);--scalar-button-1:#000;--scalar-button-1-hover:#000c;--scalar-button-1-color:#ffffffe6;--scalar-tooltip-background:#1a1a1ae6;--scalar-tooltip-color:#ffffffd9;--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-color-1)20%)}}}}}.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-color-1)20%)}}}}}.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}}}.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.light-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}}}.dark-mode{--scalar-color-green:#00b648;--scalar-color-red:#dc1b19;--scalar-color-yellow:#ffc90d;--scalar-color-blue:#4eb3ec;--scalar-color-orange:#ff8d4d;--scalar-color-purple:#b191f9;--scalar-link-color:var(--scalar-color-1);--scalar-link-color-hover:var(--scalar-link-color);--scalar-button-1:#fff;--scalar-button-1-hover:#ffffffe6;--scalar-button-1-color:black;--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-tooltip-background:color-mix(in srgb,var(--scalar-background-1),#fff 10%)}}}}}.dark-mode{--scalar-tooltip-color:#fffffff2;--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-color-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)20%)}}}}}.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-alert:color-mix(in srgb,var(--scalar-color-orange),var(--scalar-background-1)95%)}}}}}.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.dark-mode{--scalar-background-danger:color-mix(in srgb,var(--scalar-color-red),var(--scalar-background-1)95%)}}}}}@supports (color:color(display-p3 1 1 1)){.light-mode{--scalar-color-accent:color(display-p3 0 .6 1);--scalar-color-green:color(display-p3 .023529 .564706 .380392);--scalar-color-red:color(display-p3 .937255 0 .023529);--scalar-color-yellow:color(display-p3 .929412 .745098 .12549);--scalar-color-blue:color(display-p3 0 .509804 .815686);--scalar-color-orange:color(display-p3 1 .4 .02);--scalar-color-purple:color(display-p3 .321569 .011765 .819608)}.dark-mode{--scalar-color-accent:color(display-p3 .07 .67 1);--scalar-color-green:color(display-p3 0 .713725 .282353);--scalar-color-red:color(display-p3 .862745 .105882 .098039);--scalar-color-yellow:color(display-p3 1 .788235 .05098);--scalar-color-blue:color(display-p3 .305882 .701961 .92549);--scalar-color-orange:color(display-p3 1 .552941 .301961);--scalar-color-purple:color(display-p3 .694118 .568627 .976471)}}:root,:host{--leading-snug:1.375;--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--leading-normal:1.5}body{background-color:var(--scalar-background-1);margin:0}}@layer scalar-theme;.scalar-app .\\\\@container{container-type:inline-size}.scalar-app .-top-2{top:-8px}.scalar-app .top-3\\\\.5{top:14px}.scalar-app .-left-4\\\\.5{left:-18px}.scalar-app .-left-5{left:-20px}.scalar-app .z-1000{z-index:1000}.scalar-app .order-789{order:789}.scalar-app .-m-1{margin:-4px}.scalar-app .-m-2{margin:-8px}.scalar-app .-mx-2{margin-inline:-8px}.scalar-app .my-2{margin-block:8px}.scalar-app .my-3{margin-block:12px}.scalar-app .-mt-1{margin-top:-4px}.scalar-app .mt-6{margin-top:24px}.scalar-app .mb-3{margin-bottom:12px}.scalar-app .size-4\\\\.5{width:18px;height:18px}.scalar-app .h-\\\\[calc\\\\(100\\\\%\\\\+16px\\\\)\\\\]{height:calc(100% + 16px)}.scalar-app .h-\\\\[var\\\\(--scalar-header-height\\\\)\\\\]{height:var(--scalar-header-height)}.scalar-app .max-h-\\\\[60vh\\\\]{max-height:60vh}.scalar-app .min-h-3{min-height:12px}.scalar-app .min-h-7{min-height:28px}.scalar-app .min-h-dvh{min-height:100dvh}.scalar-app .w-0{width:0}.scalar-app .w-4\\\\.5{width:18px}.scalar-app .w-96{width:384px}.scalar-app .w-110{width:440px}.scalar-app .w-120{width:480px}.scalar-app .max-w-\\\\(--refs-content-max-width\\\\){max-width:var(--refs-content-max-width)}.scalar-app .max-w-64{max-width:256px}.scalar-app .min-w-3{min-width:12px}.scalar-app .min-w-7{min-width:28px}.scalar-app .rotate-45{rotate:45deg}.scalar-app .scroll-mt-16{scroll-margin-top:64px}.scalar-app .scroll-mt-24{scroll-margin-top:96px}.scalar-app .list-none{list-style-type:none}.scalar-app .content-end{align-content:flex-end}.scalar-app .gap-7{gap:28px}.scalar-app .overflow-x-scroll{overflow-x:scroll}.scalar-app .rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.scalar-app .p-7{padding:28px}.scalar-app .px-15{padding-inline:60px}.scalar-app .py-2\\\\.25{padding-block:9px}.scalar-app .pt-1\\\\.5{padding-top:6px}.scalar-app .pb-12{padding-bottom:48px}.scalar-app .leading-\\\\[1\\\\.45\\\\]{--tw-leading:1.45;line-height:1.45}.scalar-app .leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.scalar-app .text-current{color:currentColor}.scalar-app .italic{font-style:italic}.scalar-app .\\\\[--scalar-address-bar-height\\\\:0px\\\\]{--scalar-address-bar-height:0px}.scalar-app .\\\\[grid-area\\\\:header\\\\]{grid-area:header}.scalar-app .\\\\[grid-area\\\\:navigation\\\\]{grid-area:navigation}:is(.scalar-app .\\\\*\\\\:\\\\!p-0>*){padding:0!important}.scalar-app .group-last\\\\:mr-0:is(:where(.group):last-child *){margin-right:0}.scalar-app .group-open\\\\:rotate-90:is(:where(.group):is([open],:popover-open,:open) *){rotate:90deg}.scalar-app .group-open\\\\:flex-wrap:is(:where(.group):is([open],:popover-open,:open) *){flex-wrap:wrap}.scalar-app .group-open\\\\:whitespace-normal:is(:where(.group):is([open],:popover-open,:open) *){white-space:normal}.scalar-app .group-focus-within\\\\/parameter-item\\\\:w-auto:is(:where(.group\\\\/parameter-item):focus-within *){width:auto}@media(hover:hover){.scalar-app .group-hover\\\\:flex:is(:where(.group):hover *){display:flex}.scalar-app .group-hover\\\\:text-c-1:is(:where(.group):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\:opacity-100:is(:where(.group):hover *),.scalar-app .group-hover\\\\/heading\\\\:opacity-100:is(:where(.group\\\\/heading):hover *){opacity:1}.scalar-app .group-hover\\\\/parameter-item\\\\:w-auto:is(:where(.group\\\\/parameter-item):hover *){width:auto}}.scalar-app .group-has-focus-visible\\\\/heading\\\\:opacity-100:is(:where(.group\\\\/heading):has(:focus-visible) *){opacity:1}.scalar-app .empty\\\\:hidden:empty{display:none}@media(hover:hover){.scalar-app .hover\\\\:bg-b-2:hover{background-color:var(--scalar-background-2)}.scalar-app .hover\\\\:text-c-1:hover{color:var(--scalar-color-1)}}.scalar-app .has-focus\\\\:outline:has(:focus){outline-style:var(--tw-outline-style);outline-width:1px}@media(min-width:1200px){.scalar-app .xl\\\\:mb-1\\\\.5{margin-bottom:6px}.scalar-app .xl\\\\:gap-12{gap:48px}.scalar-app .xl\\\\:border-r{border-right-style:var(--tw-border-style);border-right-width:var(--scalar-border-width)}.scalar-app .xl\\\\:border-none{--tw-border-style:none;border-style:none}.scalar-app .xl\\\\:first\\\\:ml-auto:first-child{margin-left:auto}}.scalar-app .\\\\[\\\\&_a\\\\]\\\\:underline a{text-decoration-line:underline}.scalar-app .\\\\[\\\\&_a\\\\:hover\\\\]\\\\:text-c-1 a:hover{color:var(--scalar-color-1)}.scalar-app .\\\\[\\\\&_code\\\\]\\\\:font-code code{font-family:var(--scalar-font-code)}.scalar-app .\\\\[\\\\&_em\\\\]\\\\:text-c-1 em{color:var(--scalar-color-1)}.scalar-app .\\\\[\\\\&_em\\\\]\\\\:not-italic em{font-style:normal}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:color-mix(in srgb,var(--scalar-tooltip-color),var(--scalar-tooltip-background))}}}@property --tw-divide-x-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-gradient-position{syntax:\"*\";inherits:false}@property --tw-gradient-from{syntax:\"\";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:\"\";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:\"\";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:\"*\";inherits:false}@property --tw-gradient-via-stops{syntax:\"*\";inherits:false}@property --tw-gradient-from-position{syntax:\"\";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:\"\";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:\"\";inherits:false;initial-value:100%}@property --tw-ease{syntax:\"*\";inherits:false}@keyframes fade-in-27df5cd8{0%{opacity:0}70%{opacity:0}to{opacity:1}}@keyframes rotate-27df5cd8{0%{transform:scale(3.5)rotate(0)}to{transform:scale(3.5)rotate(360deg)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent);text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert{background-color:color-mix(in srgb,var(--scalar-background-2),transparent)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:color-mix(in srgb,var(--scalar-color-blue),transparent 97%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-blue),transparent 50%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:color-mix(in srgb,var(--scalar-color-2),transparent 97%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-2),transparent 50%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:color-mix(in srgb,var(--scalar-color-orange),transparent 97%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-orange),transparent 50%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:color-mix(in srgb,var(--scalar-color-red),transparent 97%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-red),transparent 50%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:color-mix(in srgb,var(--scalar-color-green),transparent 97%)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-green),transparent 50%)}}@property --tw-backdrop-blur{syntax:\"*\";inherits:false}@property --tw-backdrop-brightness{syntax:\"*\";inherits:false}@property --tw-backdrop-contrast{syntax:\"*\";inherits:false}@property --tw-backdrop-grayscale{syntax:\"*\";inherits:false}@property --tw-backdrop-hue-rotate{syntax:\"*\";inherits:false}@property --tw-backdrop-invert{syntax:\"*\";inherits:false}@property --tw-backdrop-opacity{syntax:\"*\";inherits:false}@property --tw-backdrop-saturate{syntax:\"*\";inherits:false}@property --tw-backdrop-sepia{syntax:\"*\";inherits:false}@keyframes fadein-layout-c1e93776{0%{opacity:0}to{opacity:1}}@keyframes fadein-modal-c1e93776{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translate(0)}}@media(hover:hover){.scalar-app .group-hover\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):hover *){opacity:0}.scalar-app .group-hover\\\\/group-button\\\\:flex:is(:where(.group\\\\/group-button):hover *){display:flex}.scalar-app .group-hover\\\\/group-button\\\\:hidden:is(:where(.group\\\\/group-button):hover *){display:none}.scalar-app .peer-hover\\\\/button\\\\:opacity-100:is(:where(.peer\\\\/button):hover~*),.scalar-app .hover\\\\:opacity-100:hover{opacity:1}}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:color-mix(in srgb,var(--scalar-tooltip-color),var(--scalar-tooltip-background))}}}}@property --tw-content{syntax:\"*\";inherits:false;initial-value:\"\"}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:color-mix(in srgb,var(--scalar-tooltip-color),var(--scalar-tooltip-background))}}}}@media(hover:hover){.scalar-app .group-hover\\\\:text-c-1:is(:where(.group):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\/button\\\\:bg-sidebar-indent-border-hover:is(:where(.group\\\\/button):hover *){background-color:var(--scalar-sidebar-indent-border-hover,var(--scalar-border-color))}.scalar-app .group-hover\\\\/button\\\\:text-c-1:is(:where(.group\\\\/button):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\/code-block\\\\:opacity-100:is(:where(.group\\\\/code-block):hover *){opacity:1}.scalar-app .hover\\\\:bg-b-2:hover{background-color:var(--scalar-background-2)}.scalar-app .hover\\\\:bg-b-3:hover{background-color:var(--scalar-background-3)}.scalar-app .hover\\\\:bg-h-btn:hover{background-color:var(--scalar-button-1-hover)}.scalar-app .hover\\\\:bg-sidebar-b-1:hover{background-color:var(--scalar-sidebar-background-1,var(--scalar-background-1))}.scalar-app .hover\\\\:bg-sidebar-b-hover:hover{background-color:var(--scalar-sidebar-item-hover-background,var(--scalar-background-2))}.scalar-app .hover\\\\:bg-linear-to-b:hover{--tw-gradient-position:to bottom}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .hover\\\\:bg-linear-to-b:hover{--tw-gradient-position:to bottom in oklab}}.scalar-app .hover\\\\:bg-linear-to-b:hover{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .hover\\\\:bg-linear-to-t:hover{--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .hover\\\\:bg-linear-to-t:hover{--tw-gradient-position:to top in oklab}}.scalar-app .hover\\\\:bg-linear-to-t:hover{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .hover\\\\:text-c-1:hover{color:var(--scalar-color-1)}.scalar-app .hover\\\\:text-sidebar-c-1:hover{color:var(--scalar-sidebar-color-1,var(--scalar-color-1))}.scalar-app .hover\\\\:text-sidebar-c-hover:hover{color:var(--scalar-sidebar-item-hover-color,var(--scalar-sidebar-color-2))}.scalar-app .hover\\\\:underline:hover{text-decoration-line:underline}.scalar-app .hover\\\\:brightness-90:hover{--tw-brightness:brightness(90%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent);text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}.scalar-app .markdown .markdown-alert{background-color:var(--scalar-background-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert{background-color:color-mix(in srgb,var(--scalar-background-2),transparent)}}.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:color-mix(in srgb,var(--scalar-color-blue),transparent 97%)}}.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-blue),transparent 50%)}}.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:color-mix(in srgb,var(--scalar-color-2),transparent 97%)}}.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-2),transparent 50%)}}.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:color-mix(in srgb,var(--scalar-color-orange),transparent 97%)}}.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-orange),transparent 50%)}}.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:color-mix(in srgb,var(--scalar-color-red),transparent 97%)}}.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-red),transparent 50%)}}.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:color-mix(in srgb,var(--scalar-color-green),transparent 97%)}}.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-green),transparent 50%)}}}.scalar-app .right-0\\\\.75{right:3px}.scalar-app .ml-2{margin-left:8px}.scalar-app .self-start{align-self:flex-start}.scalar-app .line-through{text-decoration-line:line-through}@media(hover:hover){.scalar-app .group-hover\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):hover *){opacity:0}.scalar-app .group-hover\\\\/group-button\\\\:flex:is(:where(.group\\\\/group-button):hover *){display:flex}.scalar-app .group-hover\\\\/group-button\\\\:hidden:is(:where(.group\\\\/group-button):hover *){display:none}}.scalar-app .group-focus-visible\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):focus-visible *),.scalar-app .group-has-\\\\[\\\\~\\\\*_\\\\[aria-expanded\\\\=true\\\\]\\\\]\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):has(~* [aria-expanded=true]) *),.scalar-app .group-has-\\\\[\\\\~\\\\*\\\\:focus-within\\\\]\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):has(~:focus-within) *),.scalar-app .group-has-\\\\[\\\\~\\\\*\\\\:hover\\\\]\\\\/button\\\\:opacity-0:is(:where(.group\\\\/button):has(~:hover) *){opacity:0}@media(hover:hover){.scalar-app .peer-hover\\\\/button\\\\:opacity-100:is(:where(.peer\\\\/button):hover~*){opacity:1}}.scalar-app .peer-focus-visible\\\\/button\\\\:opacity-100:is(:where(.peer\\\\/button):focus-visible~*){opacity:1}.scalar-app .after\\\\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.scalar-app .after\\\\:absolute:after{content:var(--tw-content);position:absolute}.scalar-app .after\\\\:inset-0:after{content:var(--tw-content);inset:0}.scalar-app .after\\\\:inset-x-0:after{content:var(--tw-content);inset-inline:0}.scalar-app .after\\\\:-top-0\\\\.5:after{content:var(--tw-content);top:-2px}.scalar-app .after\\\\:-bottom-0\\\\.5:after{content:var(--tw-content);bottom:-2px}.scalar-app .after\\\\:block:after{content:var(--tw-content);display:block}.scalar-app .after\\\\:h-0\\\\.75:after{content:var(--tw-content);height:3px}.scalar-app .after\\\\:rounded:after{content:var(--tw-content);border-radius:var(--scalar-radius)}.scalar-app .after\\\\:bg-blue:after{content:var(--tw-content);background-color:var(--scalar-color-blue)}.scalar-app .after\\\\:opacity-15:after{content:var(--tw-content);opacity:.15}.scalar-app .focus-within\\\\:opacity-100:focus-within{opacity:1}@media(hover:hover){.scalar-app .hover\\\\:opacity-100:hover{opacity:1}}.scalar-app .has-\\\\[\\\\&\\\\[aria-expanded\\\\=true\\\\]\\\\]\\\\:opacity-100:has([aria-expanded=true]){opacity:1}:where(.scalar-app){font-family:var(--scalar-font);color:var(--scalar-color-1);-webkit-text-size-adjust:100%;tab-size:4;line-height:1.15}:where(.scalar-app) *,:where(.scalar-app) :before,:where(.scalar-app) :after{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--scalar-border-color);outline-width:1px;outline-style:none;outline-color:var(--scalar-color-accent);font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;font-style:inherit;-webkit-text-decoration:inherit;text-decoration:inherit;text-align:inherit;line-height:inherit;color:inherit;margin:unset;padding:unset;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:where(.scalar-app) :before,:where(.scalar-app) :after{--tw-content:\"\"}:where(.scalar-app) button,:where(.scalar-app) input,:where(.scalar-app) optgroup,:where(.scalar-app) select,:where(.scalar-app) textarea{background:0 0}:where(.scalar-app) ::file-selector-button{background:0 0}:where(.scalar-app) ol,:where(.scalar-app) ul,:where(.scalar-app) menu{list-style:none}:where(.scalar-app) input:where(:not([type=button],[type=reset],[type=submit])),:where(.scalar-app) select,:where(.scalar-app) textarea{border-radius:var(--scalar-radius);border-width:1px}:where(.scalar-app) input::placeholder{color:var(--scalar-color-3);font-family:var(--scalar-font)}:where(.scalar-app) input[type=search]::-webkit-search-cancel-button{appearance:none}:where(.scalar-app) input[type=search]::-webkit-search-decoration{appearance:none}:where(.scalar-app) summary::-webkit-details-marker{display:none}:where(.scalar-app) input:-webkit-autofill{-webkit-background-clip:text!important;background-clip:text!important}:where(.scalar-app) :focus-visible{border-radius:var(--scalar-radius);outline-style:solid}:where(.scalar-app) button:focus-visible,:where(.scalar-app) [role=button]:focus-visible{outline-offset:-1px}:where(.scalar-app) button,:where(.scalar-app) [role=button]{cursor:pointer}:where(.scalar-app) :disabled{cursor:default}:where(.scalar-app) img,:where(.scalar-app) svg,:where(.scalar-app) video,:where(.scalar-app) canvas,:where(.scalar-app) audio,:where(.scalar-app) iframe,:where(.scalar-app) embed,:where(.scalar-app) object{vertical-align:middle;display:block}:where(.scalar-app) [hidden]{display:none}.scalar-app .cm-scroller,.scalar-app .custom-scroll{scrollbar-color:transparent transparent;scrollbar-width:thin;-webkit-overflow-scrolling:touch;overflow-y:auto}.scalar-app .custom-scroll-self-contain-overflow{overscroll-behavior:contain}.scalar-app .cm-scroller:hover,.scalar-app .custom-scroll:hover,.scalar-app.scalar-scrollbars-obtrusive .cm-scroller,.scalar-app.scalar-scrollbars-obtrusive .custom-scroll{scrollbar-color:var(--scalar-scrollbar-color,transparent)transparent}.scalar-app .cm-scroller:hover::-webkit-scrollbar-thumb{background:var(--scalar-scrollbar-color);background-clip:content-box;border:3px solid #0000}.scalar-app .custom-scroll:hover::-webkit-scrollbar-thumb{background:var(--scalar-scrollbar-color);background-clip:content-box;border:3px solid #0000}.scalar-app .cm-scroller::-webkit-scrollbar-thumb:active{background:var(--scalar-scrollbar-color-active);background-clip:content-box;border:3px solid #0000}.scalar-app .custom-scroll::-webkit-scrollbar-thumb:active{background:var(--scalar-scrollbar-color-active);background-clip:content-box;border:3px solid #0000}.scalar-app .cm-scroller::-webkit-scrollbar-corner{background:0 0}.scalar-app .custom-scroll::-webkit-scrollbar-corner{background:0 0}.scalar-app .cm-scroller::-webkit-scrollbar{width:12px;height:12px}.scalar-app .custom-scroll::-webkit-scrollbar{width:12px;height:12px}.scalar-app .cm-scroller::-webkit-scrollbar-track{background:0 0}.scalar-app .custom-scroll::-webkit-scrollbar-track{background:0 0}.scalar-app .cm-scroller::-webkit-scrollbar-thumb{background:padding-box content-box;border:3px solid #0000;border-radius:20px}.scalar-app .custom-scroll::-webkit-scrollbar-thumb{background:padding-box content-box;border:3px solid #0000;border-radius:20px}@media(pointer:coarse){.scalar-app .cm-scroller,.scalar-app .custom-scroll{padding-right:12px}}.scalar-app .invisible{visibility:hidden}.scalar-app .inset-y-0{inset-block:0}.scalar-app .top-\\\\(--nested-items-offset\\\\){top:var(--nested-items-offset)}.scalar-app .top-0\\\\.5{top:2px}.scalar-app .top-1\\\\/2{top:50%}.scalar-app .top-22{top:88px}.scalar-app .top-\\\\[1lh\\\\]{top:1lh}.scalar-app .top-px{top:1px}.scalar-app .left-2{left:8px}.scalar-app .left-2\\\\.5{left:10px}.scalar-app .left-4{left:16px}.scalar-app .left-10{left:40px}.scalar-app .left-\\\\[calc\\\\(4px\\\\+var\\\\(--scalar-sidebar-indent\\\\)\\\\*var\\\\(--scalar-sidebar-level\\\\)\\\\)\\\\]{left:calc(4px + var(--scalar-sidebar-indent)*var(--scalar-sidebar-level))}.scalar-app .left-border{left:var(--scalar-border-width)}.scalar-app .left-px{left:1px}.scalar-app .z-tooltip{z-index:99999}.scalar-app .-m-1\\\\.5{margin:-6px}.scalar-app .-m-px{margin:-1px}.scalar-app .m-1{margin:4px}.scalar-app .-mx-0\\\\.75{margin-inline:-3px}.scalar-app .-mx-px{margin-inline:-1px}.scalar-app .mx-px{margin-inline:1px}.scalar-app .-my-1\\\\.5{margin-block:-6px}.scalar-app .-my-2{margin-block:-8px}.scalar-app .my-0\\\\.75{margin-block:3px}.scalar-app .-mt-1\\\\.5{margin-top:-6px}.scalar-app .mt-0{margin-top:0}.scalar-app .mt-\\\\[15svh\\\\]{margin-top:15svh}.scalar-app .mt-\\\\[20svh\\\\]{margin-top:20svh}.scalar-app .-mr-0\\\\.25{margin-right:-1px}.scalar-app .mr-0{margin-right:0}.scalar-app .mr-\\\\[calc\\\\(20px-var\\\\(--scalar-sidebar-indent\\\\)\\\\)\\\\]{margin-right:calc(20px - var(--scalar-sidebar-indent))}.scalar-app .-mb-1{margin-bottom:-4px}.scalar-app .-ml-0\\\\.75{margin-left:-3px}.scalar-app .line-clamp-\\\\(--markdown-clamp\\\\){-webkit-line-clamp:var(--markdown-clamp);-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.scalar-app .\\\\!hidden{display:none!important}.scalar-app .size-2{width:8px;height:8px}.scalar-app .size-2\\\\.75{width:11px;height:11px}.scalar-app .size-3\\\\.25{width:13px;height:13px}.scalar-app .size-60{width:240px;height:240px}.scalar-app .size-\\\\[23px\\\\]{width:23px;height:23px}.scalar-app .h-0{height:0}.scalar-app .h-1{height:4px}.scalar-app .h-24{height:96px}.scalar-app .h-32{height:128px}.scalar-app .h-\\\\[1lh\\\\]{height:1lh}.scalar-app .h-border{height:var(--scalar-border-width)}.scalar-app .h-dvh{height:100dvh}.scalar-app .max-h-20{max-height:80px}.scalar-app .max-h-\\\\[80svh\\\\]{max-height:80svh}.scalar-app .max-h-\\\\[90svh\\\\]{max-height:90svh}.scalar-app .max-h-dvh{max-height:100dvh}.scalar-app .max-h-radix-popper{max-height:calc(var(--radix-popper-available-height) - 8px)}.scalar-app .min-h-96{min-height:384px}.scalar-app .min-h-header{min-height:48px}.scalar-app .w-12{width:48px}.scalar-app .w-24{width:96px}.scalar-app .w-32{width:128px}.scalar-app .w-40{width:160px}.scalar-app .w-48{width:192px}.scalar-app .w-\\\\[38px\\\\]{width:38px}.scalar-app .w-\\\\[calc\\\\(100vw-12px\\\\)\\\\]{width:calc(100vw - 12px)}.scalar-app .w-\\\\[var\\\\(--scalar-sidebar-indent\\\\)\\\\]{width:var(--scalar-sidebar-indent)}.scalar-app .w-border{width:var(--scalar-border-width)}.scalar-app .w-min{width:min-content}.scalar-app .w-screen{width:100vw}.scalar-app .max-w-\\\\[360px\\\\]{max-width:360px}.scalar-app .max-w-\\\\[480px\\\\]{max-width:480px}.scalar-app .max-w-\\\\[540px\\\\]{max-width:540px}.scalar-app .max-w-\\\\[640px\\\\]{max-width:640px}.scalar-app .max-w-\\\\[800px\\\\]{max-width:800px}.scalar-app .max-w-\\\\[1000px\\\\]{max-width:1000px}.scalar-app .max-w-\\\\[inherit\\\\]{max-width:inherit}.scalar-app .max-w-xs{max-width:320px}.scalar-app .min-w-6{min-width:24px}.scalar-app .min-w-40{min-width:160px}.scalar-app .min-w-min{min-width:min-content}.scalar-app .flex-shrink,.scalar-app .shrink{flex-shrink:1}.scalar-app .-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-x-2\\\\.5{--tw-translate-x:10px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-x-\\\\[14px\\\\]{--tw-translate-x:14px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .-translate-y-1\\\\/2{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .appearance-none{appearance:none}.scalar-app .grid-flow-col{grid-auto-flow:column}.scalar-app .\\\\!items-end{align-items:flex-end!important}.scalar-app .\\\\!items-start{align-items:flex-start!important}.scalar-app .items-baseline{align-items:baseline}.scalar-app .\\\\!justify-end{justify-content:flex-end!important}.scalar-app .\\\\!justify-start{justify-content:flex-start!important}.scalar-app .gap-2\\\\.25{gap:9px}.scalar-app .gap-x-4{column-gap:16px}.scalar-app .gap-y-8{row-gap:32px}:where(.scalar-app .divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(var(--scalar-border-width)*var(--tw-divide-x-reverse));border-inline-end-width:calc(var(--scalar-border-width)*calc(1 - var(--tw-divide-x-reverse)))}.scalar-app .self-end{align-self:flex-end}.scalar-app .overflow-x-clip{overflow-x:clip}.scalar-app .overflow-y-scroll{overflow-y:scroll}.scalar-app .overscroll-contain{overscroll-behavior:contain}.scalar-app .rounded-none{border-radius:0}.scalar-app .rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.scalar-app .border-1{border-style:var(--tw-border-style);border-width:1px}.scalar-app .border-solid{--tw-border-style:solid;border-style:solid}.scalar-app .border-\\\\(--scalar-background-3\\\\){border-color:var(--scalar-background-3)}.scalar-app .border-border{border-color:var(--scalar-border-color)}.scalar-app .border-c-alert{border-color:var(--scalar-color-alert)}.scalar-app .border-red{border-color:var(--scalar-color-red)}.scalar-app .border-sidebar-border{border-color:var(--scalar-sidebar-border-color,var(--scalar-border-color))}.scalar-app .border-sidebar-border-search{border-color:var(--scalar-sidebar-search-border-color,var(--scalar-border-color))}.scalar-app .bg-\\\\(--bg-light\\\\){background-color:var(--bg-light)}.scalar-app .bg-b-1,.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-b-1\\\\.5{background-color:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}}}.scalar-app .bg-b-alert{background-color:var(--scalar-background-alert)}.scalar-app .bg-b-btn{background-color:var(--scalar-button-1)}.scalar-app .bg-b-tooltip{background-color:var(--scalar-tooltip-background)}.scalar-app .bg-backdrop{background-color:#00000038}.scalar-app .bg-border{background-color:var(--scalar-border-color)}.scalar-app .bg-c-danger{background-color:var(--scalar-color-danger)}.scalar-app .bg-inherit{background-color:inherit}.scalar-app .bg-red{background-color:var(--scalar-color-red)}.scalar-app .bg-sidebar-b-search{background-color:var(--scalar-sidebar-search-background,var(--scalar-background-2))}.scalar-app .bg-sidebar-indent-border{background-color:var(--scalar-sidebar-indent-border,var(--scalar-border-color))}.scalar-app .bg-sidebar-indent-border-active{background-color:var(--scalar-sidebar-indent-border-active,var(--scalar-color-accent))}.scalar-app .bg-transparent{background-color:#0000}.scalar-app .bg-linear-to-b{--tw-gradient-position:to bottom}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .bg-linear-to-b{--tw-gradient-position:to bottom in oklab}}.scalar-app .bg-linear-to-b{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .from-b-1{--tw-gradient-from:var(--scalar-background-1);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.scalar-app .to-b-1\\\\.5{--tw-gradient-to:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}}}}.scalar-app .to-b-1\\\\.5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.scalar-app .to-b-2{--tw-gradient-to:var(--scalar-background-2);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.scalar-app .mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.scalar-app .p-0\\\\.25{padding:1px}.scalar-app .p-2\\\\.5{padding:10px}.scalar-app .p-6{padding:24px}.scalar-app .px-3\\\\.5{padding-inline:14px}.scalar-app .px-9{padding-inline:36px}.scalar-app .py-4{padding-block:16px}.scalar-app .py-\\\\[6\\\\.75px\\\\]{padding-block:6.75px}.scalar-app .pl-8{padding-left:32px}.scalar-app .text-base\\\\/5{font-size:var(--scalar-font-size-3);line-height:var(--scalar-line-height-5)}.scalar-app .text-sm\\\\/5{font-size:var(--scalar-font-size-4);line-height:var(--scalar-line-height-5)}.scalar-app .text-lg{font-size:var(--scalar-font-size-2)}.scalar-app .leading-5{--tw-leading:var(--scalar-line-height-5);line-height:var(--scalar-line-height-5)}.scalar-app .font-sidebar{--tw-font-weight:var(--scalar-sidebar-font-weight,var(--scalar-regular));font-weight:var(--scalar-sidebar-font-weight,var(--scalar-regular))}.scalar-app .font-sidebar-active{--tw-font-weight:var(--scalar-sidebar-font-weight-active,var(--scalar-semibold));font-weight:var(--scalar-sidebar-font-weight-active,var(--scalar-semibold))}.scalar-app .text-nowrap{text-wrap:nowrap}.scalar-app .text-wrap{text-wrap:wrap}.scalar-app .break-words,.scalar-app .wrap-break-word{overflow-wrap:break-word}.scalar-app .text-c-accent{color:var(--scalar-color-accent)}.scalar-app .text-c-alert{color:var(--scalar-color-alert)}.scalar-app .text-c-tooltip{color:var(--scalar-tooltip-color)}.scalar-app .text-sidebar-c-1{color:var(--scalar-sidebar-color-1,var(--scalar-color-1))}.scalar-app .text-sidebar-c-search{color:var(--scalar-sidebar-search-color,var(--scalar-color-3))}.scalar-app .text-white{color:#fff}.scalar-app .opacity-40{opacity:.4}.scalar-app .outline-offset-1{outline-offset:1px}.scalar-app .outline-offset-\\\\[-1px\\\\]{outline-offset:-1px}.scalar-app .backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}:is(.scalar-app .\\\\*\\\\:size-3>*){width:12px;height:12px}:is(.scalar-app .\\\\*\\\\:size-4>*){width:16px;height:16px}:is(.scalar-app .\\\\*\\\\:h-5>*){height:20px}:is(.scalar-app .\\\\*\\\\:min-w-5>*){min-width:20px}:is(.scalar-app .\\\\*\\\\:flex-1>*){flex:1}:is(.scalar-app .\\\\*\\\\:justify-center>*){justify-content:center}:is(.scalar-app .\\\\*\\\\:gap-px>*){gap:1px}:is(.scalar-app .\\\\*\\\\:rounded>*){border-radius:var(--scalar-radius)}:is(.scalar-app .\\\\*\\\\:border>*){border-style:var(--tw-border-style);border-width:var(--scalar-border-width)}:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:var(--scalar-tooltip-color)}@supports (color:color-mix(in lab,red,red)){:is(.scalar-app .\\\\*\\\\:border-border-tooltip>*){border-color:color-mix(in srgb,var(--scalar-tooltip-color),var(--scalar-tooltip-background))}}}}}:is(.scalar-app .\\\\*\\\\:px-1>*){padding-inline:4px}:is(.scalar-app .\\\\*\\\\:text-xs>*){font-size:var(--scalar-font-size-5)}@media(hover:hover){.scalar-app .group-hover\\\\:text-c-1:is(:where(.group):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\/button\\\\:bg-sidebar-indent-border-hover:is(:where(.group\\\\/button):hover *){background-color:var(--scalar-sidebar-indent-border-hover,var(--scalar-border-color))}.scalar-app .group-hover\\\\/button\\\\:text-c-1:is(:where(.group\\\\/button):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\/code-block\\\\:opacity-100:is(:where(.group\\\\/code-block):hover *){opacity:1}}.scalar-app .group-focus-visible\\\\/toggle\\\\:outline:is(:where(.group\\\\/toggle):focus-visible *){outline-style:var(--tw-outline-style);outline-width:1px}.scalar-app .placeholder\\\\:font-\\\\[inherit\\\\]::placeholder{font-family:inherit}.scalar-app .first\\\\:rounded-t-\\\\[inherit\\\\]:first-child,:is(.scalar-app .\\\\*\\\\:first\\\\:rounded-t-\\\\[inherit\\\\]>*):first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.scalar-app .last\\\\:rounded-b-\\\\[inherit\\\\]:last-child,:is(.scalar-app .\\\\*\\\\:last\\\\:rounded-b-\\\\[inherit\\\\]>*):last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.scalar-app .focus-within\\\\:outline-none:focus-within{--tw-outline-style:none;outline-style:none}@media(hover:hover){.scalar-app .hover\\\\:bg-b-2:hover{background-color:var(--scalar-background-2)}.scalar-app .hover\\\\:bg-b-3:hover{background-color:var(--scalar-background-3)}.scalar-app .hover\\\\:bg-h-btn:hover{background-color:var(--scalar-button-1-hover)}.scalar-app .hover\\\\:bg-sidebar-b-1:hover{background-color:var(--scalar-sidebar-background-1,var(--scalar-background-1))}.scalar-app .hover\\\\:bg-sidebar-b-hover:hover{background-color:var(--scalar-sidebar-item-hover-background,var(--scalar-background-2))}.scalar-app .hover\\\\:bg-linear-to-b:hover{--tw-gradient-position:to bottom}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .hover\\\\:bg-linear-to-b:hover{--tw-gradient-position:to bottom in oklab}}.scalar-app .hover\\\\:bg-linear-to-b:hover{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .hover\\\\:bg-linear-to-t:hover{--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .hover\\\\:bg-linear-to-t:hover{--tw-gradient-position:to top in oklab}}.scalar-app .hover\\\\:bg-linear-to-t:hover{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .hover\\\\:text-c-1:hover{color:var(--scalar-color-1)}.scalar-app .hover\\\\:text-sidebar-c-1:hover{color:var(--scalar-sidebar-color-1,var(--scalar-color-1))}.scalar-app .hover\\\\:text-sidebar-c-hover:hover{color:var(--scalar-sidebar-item-hover-color,var(--scalar-sidebar-color-2))}.scalar-app .hover\\\\:underline:hover{text-decoration-line:underline}.scalar-app .hover\\\\:brightness-90:hover{--tw-brightness:brightness(90%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}.scalar-app .focus-visible\\\\:border-c-btn:focus-visible{border-color:var(--scalar-button-1-color)}.scalar-app .focus-visible\\\\:opacity-100:focus-visible{opacity:1}.scalar-app .focus-visible\\\\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.scalar-app .active\\\\:bg-b-btn:active{background-color:var(--scalar-button-1)}.scalar-app .active\\\\:brightness-90:active{--tw-brightness:brightness(90%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:bg-sidebar-b-1:has(:focus-visible){background-color:var(--scalar-sidebar-background-1,var(--scalar-background-1))}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:outline:has(:focus-visible),.scalar-app .has-\\\\[input\\\\:focus-visible\\\\]\\\\:outline:has(:is(input:focus-visible)){outline-style:var(--tw-outline-style);outline-width:1px}@media(min-width:800px){.scalar-app .md\\\\:w-\\\\[calc\\\\(100vw-16px\\\\)\\\\]{width:calc(100vw - 16px)}}@media(min-width:1000px){.scalar-app .lg\\\\:w-\\\\[calc\\\\(100vw-32px\\\\)\\\\]{width:calc(100vw - 32px)}.scalar-app .lg\\\\:w-full{width:100%}}.scalar-app .dark\\\\:bg-\\\\(--bg-dark\\\\):where(.dark-mode,.dark-mode *){background-color:var(--bg-dark)}.scalar-app .dark\\\\:bg-b-3:where(.dark-mode,.dark-mode *){background-color:var(--scalar-background-3)}.scalar-app .dark\\\\:bg-backdrop-dark:where(.dark-mode,.dark-mode *){background-color:#00000073}.scalar-app .dark\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *){--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .dark\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *){--tw-gradient-position:to top in oklab}}.scalar-app .dark\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *){background-image:linear-gradient(var(--tw-gradient-stops))}@media(hover:hover){.scalar-app .dark\\\\:hover\\\\:bg-b-3:where(.dark-mode,.dark-mode *):hover{background-color:var(--scalar-background-3)}.scalar-app .dark\\\\:hover\\\\:bg-linear-to-b:where(.dark-mode,.dark-mode *):hover{--tw-gradient-position:to bottom}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .dark\\\\:hover\\\\:bg-linear-to-b:where(.dark-mode,.dark-mode *):hover{--tw-gradient-position:to bottom in oklab}}.scalar-app .dark\\\\:hover\\\\:bg-linear-to-b:where(.dark-mode,.dark-mode *):hover{background-image:linear-gradient(var(--tw-gradient-stops))}.scalar-app .dark\\\\:hover\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *):hover{--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab,red,red)){.scalar-app .dark\\\\:hover\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *):hover{--tw-gradient-position:to top in oklab}}.scalar-app .dark\\\\:hover\\\\:bg-linear-to-t:where(.dark-mode,.dark-mode *):hover{background-image:linear-gradient(var(--tw-gradient-stops))}}@media(max-width:720px)and (max-height:480px){.scalar-app .zoomed\\\\:\\\\!whitespace-normal{white-space:normal!important}}.loader-wrapper[data-v-27df5cd8]{--loader-size:50%;justify-content:center;align-items:center;display:flex;position:relative}.svg-loader[data-v-27df5cd8]{width:var(--loader-size);height:var(--loader-size);fill:none;stroke:currentColor;background-color:#0000;top:1rem;right:.9rem;overflow:visible}.svg-path[data-v-27df5cd8]{stroke-width:12px;fill:none;transition:all .3s}.svg-x-mark[data-v-27df5cd8]{stroke-dasharray:57;stroke-dashoffset:57px;transition-delay:0s}.svg-check-mark[data-v-27df5cd8]{stroke-dasharray:149;stroke-dashoffset:149px;transition-delay:0s}.icon-is-invalid .svg-x-mark[data-v-27df5cd8],.icon-is-valid .svg-check-mark[data-v-27df5cd8]{stroke-dashoffset:0;transition-delay:.3s}.circular-loader[data-v-27df5cd8]{transform-origin:50%;background:0 0;animation:.7s linear infinite rotate-27df5cd8,.4s fade-in-27df5cd8;transform:scale(3.5)}.loader-path[data-v-27df5cd8]{stroke-dasharray:50 200;stroke-dashoffset:-100px;stroke-linecap:round}.loader-path-off[data-v-27df5cd8]{stroke-dasharray:50 200;stroke-dashoffset:-100px;opacity:0;transition:opacity .3s}.scalar-code-block:hover .scalar-code-copy[data-v-e1870266]{opacity:100}.copy-icon[data-v-e1870266],.check-icon[data-v-e1870266]{transition:transform .15s ease-in-out;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)scale(1)}.copy-icon.copied[data-v-e1870266],.check-icon[data-v-e1870266]{transform:translate(-50%,-50%)scale(0)}.check-icon.visible[data-v-e1870266]{transform:translate(-50%,-50%)scale(1)}.scalar-code-block{background:inherit;padding:10px 8px 12px 12px;position:relative;overflow:auto}.scalar-codeblock-pre{all:unset;text-wrap:nowrap;white-space-collapse:preserve;background:0 0;border-radius:0;width:fit-content;margin:0}.toggle-icon-ellipse[data-v-60be8692]{background:var(--scalar-background-1);border-radius:50%;width:7px;height:7px;transition:width .3s ease-in-out,height .3s ease-in-out;display:inline-block;position:relative;overflow:hidden;box-shadow:inset 0 0 0 1px}.toggle-icon-moon-mask[data-v-60be8692]{background:var(--scalar-background-1);border:1px solid;border-radius:50%;width:100%;height:100%;transition:transform .3s ease-in-out;display:block;position:absolute;bottom:2.5px;left:2.5px;transform:translate(4px,-4px)}.toggle-icon-sun-ray[data-v-60be8692]{background:currentColor;border-radius:8px;width:12px;height:1px;transition:transform .3s ease-in-out;position:absolute}.toggle-icon-sun-ray[data-v-60be8692]:nth-of-type(2){transform:rotate(90deg)}.toggle-icon-sun-ray[data-v-60be8692]:nth-of-type(3){transform:rotate(45deg)}.toggle-icon-sun-ray[data-v-60be8692]:nth-of-type(4){transform:rotate(-45deg)}.toggle-icon-dark .toggle-icon-ellipse[data-v-60be8692]{width:10px;height:10px;-webkit-mask-image:radial-gradient(circle at 0 100%,pink 10px,#0000 12px);mask-image:radial-gradient(circle at 0 100%,pink 10px,#0000 12px)}.toggle-icon-dark .toggle-icon-sun-ray[data-v-60be8692]{transform:scale(0)}.toggle-icon-dark .toggle-icon-moon-mask[data-v-60be8692]{transform:translateZ(0)}.scalar-icon[data-v-b651bb23],.scalar-icon[data-v-b651bb23] *{stroke-width:var(--c07589c2)}.scalar-app :where(code.hljs) *{font-size:inherit;font-family:var(--scalar-font-code);text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;tab-size:4;line-height:1.4}.scalar-app code.hljs{all:unset;font-size:inherit;color:var(--scalar-color-2);font-family:var(--scalar-font-code);counter-reset:linenumber}.scalar-app .hljs{color:var(--scalar-color-2);background:0 0}.scalar-app .hljs .line:before{color:var(--scalar-color-3);counter-increment:linenumber;content:counter(linenumber);min-width:calc(var(--line-digits)*1ch);text-align:right;margin-right:.875rem;display:inline-block}.scalar-app .hljs-comment,.scalar-app .hljs-quote{color:var(--scalar-color-3);font-style:italic}.scalar-app .hljs-number{color:var(--scalar-color-orange)}.scalar-app .hljs-regexp,.scalar-app .hljs-string,.scalar-app .hljs-built_in{color:var(--scalar-color-blue)}.scalar-app .hljs-title.class_{color:var(--scalar-color-1)}.scalar-app .hljs-keyword{color:var(--scalar-color-purple)}.scalar-app .hljs-title.function_{color:var(--scalar-color-orange)}.scalar-app .hljs-subst,.scalar-app .hljs-name{color:var(--scalar-color-blue)}.scalar-app .hljs-attr,.scalar-app .hljs-attribute{color:var(--scalar-color-1)}.scalar-app .hljs-addition,.scalar-app .hljs-literal,.scalar-app .hljs-selector-tag,.scalar-app .hljs-type{color:var(--scalar-color-green)}.scalar-app .hljs-selector-attr,.scalar-app .hljs-selector-pseudo{color:var(--scalar-color-orange)}.scalar-app .hljs-doctag,.scalar-app .hljs-section,.scalar-app .hljs-title{color:var(--scalar-color-blue)}.scalar-app .hljs-selector-id,.scalar-app .hljs-template-variable,.scalar-app .hljs-variable{color:var(--scalar-color-1)}.scalar-app .hljs-name,.scalar-app .hljs-section,.scalar-app .hljs-strong{font-weight:var(--scalar-semibold)}.scalar-app .hljs-bullet,.scalar-app .hljs-link,.scalar-app .hljs-meta,.scalar-app .hljs-symbol{color:var(--scalar-color-blue)}.scalar-app .hljs-deletion{color:var(--scalar-color-red)}.scalar-app .hljs-formula{background:var(--scalar-color-1)}.scalar-app .hljs-emphasis{font-style:italic}.scalar-app .credential .credential-value{color:#0000;font-size:0}.scalar-app .credential:after{content:\"·····\";color:var(--scalar-color-3);-webkit-user-select:none;user-select:none}.hljs.language-html{color:var(--scalar-color-1)}.hljs.language-html .hljs-attr{color:var(--scalar-color-2)}.hljs.language-curl .hljs-string{color:var(--scalar-color-blue)}.hljs.language-curl .hljs-literal{color:var(--scalar-color-1)}.hljs.language-php .hljs-variable{color:var(--scalar-color-blue)}.hljs.language-objectivec .hljs-meta{color:var(--scalar-color-1)}.hljs.language-objectivec .hljs-built_in,.hljs-built_in{color:var(--scalar-color-orange)}.scalar-app .markdown{--scalar-refs-heading-spacing:24px;--markdown-border:var(--scalar-border-width)solid var(--scalar-border-color);--markdown-spacing-sm:12px;--markdown-spacing-md:16px;--markdown-line-height:1.625;--markdown-heading-line-height:1.15;font-family:var(--scalar-font);word-break:break-word;line-height:var(--markdown-line-height)}.scalar-app .markdown>*{margin-bottom:var(--markdown-spacing-md)}.scalar-app .markdown>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):last-child{margin-bottom:0}.scalar-app .markdown h1,.scalar-app .markdown h2,.scalar-app .markdown h3,.scalar-app .markdown h4,.scalar-app .markdown h5,.scalar-app .markdown h6{font-weight:var(--scalar-bold);margin-top:var(--scalar-refs-heading-spacing);margin-bottom:var(--markdown-spacing-sm);line-height:var(--markdown-heading-line-height,1.15);scroll-margin-top:1rem;display:block}.scalar-app .markdown h1{font-size:1.5rem}.scalar-app .markdown h2,.scalar-app .markdown h3{font-size:1.25rem}.scalar-app .markdown h4,.scalar-app .markdown h5,.scalar-app .markdown h6{font-size:1rem}.scalar-app .markdown b,.scalar-app .markdown strong{font-weight:var(--scalar-bold)}.scalar-app .markdown p{color:inherit;line-height:var(--markdown-line-height);display:block}.scalar-app .markdown img{border-radius:var(--scalar-radius);max-width:100%;display:inline-block;overflow:hidden}.scalar-app .markdown ul,.scalar-app .markdown ol{line-height:var(--markdown-line-height);flex-direction:column;gap:2px;padding-left:1.6em;display:flex}.scalar-app .markdown li{margin-top:2px;padding-left:7px}.scalar-app ol>li::marker{font:var(--scalar-font);font-variant-numeric:tabular-nums;font-weight:var(--scalar-semibold);white-space:nowrap}.scalar-app ol>*>li::marker{font:var(--scalar-font);font-variant-numeric:tabular-nums;font-weight:var(--scalar-semibold);white-space:nowrap}.scalar-app .markdown ol{list-style-type:decimal}.scalar-app .markdown ol ol{list-style-type:lower-alpha}.scalar-app .markdown ol ol ol ol,.scalar-app .markdown ol ol ol ol ol ol ol{list-style-type:decimal}.scalar-app .markdown ol ol ol ol ol,.scalar-app .markdown ol ol ol ol ol ol ol ol{list-style-type:lower-alpha}.scalar-app .markdown ol ol ol,.scalar-app .markdown ol ol ol ol ol ol,.scalar-app .markdown ol ol ol ol ol ol ol ol ol{list-style-type:lower-roman}.scalar-app .markdown ul>li,.scalar-app .markdown ul>*>li{list-style-type:disc}.scalar-app .markdown table{table-layout:fixed;border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:var(--scalar-radius);border-spacing:0;width:100%;margin:1em 0;display:table;position:relative;overflow-x:auto}.scalar-app .markdown tbody,.scalar-app .markdown thead{vertical-align:middle}.scalar-app .markdown tbody{display:table-row-group}.scalar-app .markdown thead{display:table-header-group}.scalar-app .markdown tr{border-color:inherit;vertical-align:inherit;display:table-row}.scalar-app .markdown td,.scalar-app .markdown th{vertical-align:top;min-width:1em;line-height:var(--markdown-line-height);word-break:break-word;font-size:var(--scalar-small);color:var(--scalar-color-1);border-right:var(--markdown-border);border-bottom:var(--markdown-border);padding:8.5px 16px;display:table-cell;position:relative}.scalar-app .markdown td>*,.scalar-app .markdown th>*{margin-bottom:0}.scalar-app .markdown th:empty{display:none}.scalar-app .markdown td:first-of-type,.scalar-app .markdown th:first-of-type{border-left:none}.scalar-app .markdown td:last-of-type,.scalar-app .markdown th:last-of-type{border-right:none}.scalar-app .markdown tr:last-of-type td{border-bottom:none}.scalar-app .markdown th{font-weight:var(--scalar-bold);text-align:left;background:var(--scalar-background-2);border-left-color:#0000}.scalar-app .markdown th:first-of-type{border-top-left-radius:var(--scalar-radius)}.scalar-app .markdown th:last-of-type{border-top-right-radius:var(--scalar-radius)}.scalar-app .markdown tr>[align=left]{text-align:left}.scalar-app .markdown tr>[align=right]{text-align:right}.scalar-app .markdown tr>[align=center]{text-align:center}.scalar-app .markdown details{border:var(--markdown-border);border-radius:var(--scalar-radius);color:var(--scalar-color-1)}.scalar-app .markdown details>:not(summary){margin:var(--markdown-spacing-md);margin-bottom:0}.scalar-app .markdown details>p:has(>strong):not(:has(:not(strong))){margin-bottom:8px}.scalar-app .markdown details>p:has(>strong):not(:has(:not(strong)))+*{margin-top:0}.scalar-app .markdown details>table{width:calc(100% - calc(var(--markdown-spacing-md)*2))}.scalar-app .markdown summary{min-height:40px;font-weight:var(--scalar-semibold);line-height:var(--markdown-line-height);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:2.5px;align-items:flex-start;gap:8px;padding:7px 14px;display:flex;position:relative}.scalar-app .markdown summary:hover{background-color:var(--scalar-background-2)}.scalar-app .markdown details[open]{padding-bottom:var(--markdown-spacing-md)}.scalar-app .markdown details[open]>summary{border-bottom:var(--markdown-border);border-bottom-right-radius:0;border-bottom-left-radius:0}.scalar-app .markdown summary:before{content:\"\";width:var(--markdown-spacing-md);height:var(--markdown-spacing-md);background-color:var(--scalar-color-3);flex-shrink:0;margin-top:5px;display:block;-webkit-mask-image:url(\\'data:image/svg+xml,\\');mask-image:url(\\'data:image/svg+xml,\\')}.scalar-app .markdown summary:hover:before{background-color:var(--scalar-color-1)}.scalar-app .markdown details[open]>summary:before{transition:transform .1s ease-in-out;transform:rotate(90deg)}.scalar-app .markdown details:has(+details){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0;margin-bottom:0}.scalar-app .markdown details:has(+details)+details,.scalar-app .markdown details:has(+details)+details>summary{border-top-left-radius:0;border-top-right-radius:0}.scalar-app .markdown a{--font-color:var(--scalar-link-color,var(--scalar-color-accent));--font-visited:var(--scalar-link-color-visited,var(--scalar-color-2));-webkit-text-decoration:var(--scalar-text-decoration);text-decoration:var(--scalar-text-decoration);color:var(--font-color);font-weight:var(--scalar-link-font-weight,var(--scalar-semibold));text-underline-offset:.25rem;text-decoration-thickness:1px;-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}}.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}.scalar-app .markdown a{-webkit-text-decoration-color:var(--font-color);text-decoration-color:var(--font-color)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown a{-webkit-text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent);text-decoration-color:color-mix(in srgb,var(--font-color)30%,transparent)}}}}.scalar-app .markdown a:hover{-webkit-text-decoration-color:var(--scalar-color-1,currentColor);text-decoration-color:var(--scalar-color-1,currentColor);color:var(--scalar-link-color-hover,var(--scalar-color-accent));-webkit-text-decoration:var(--scalar-text-decoration-hover);text-decoration:var(--scalar-text-decoration-hover)}.scalar-app .markdown a:visited{color:var(--font-visited)}.scalar-app .markdown em{font-style:italic}.scalar-app .markdown sup,.scalar-app .markdown sub{font-size:var(--scalar-micro);font-weight:450}.scalar-app .markdown sup{vertical-align:super}.scalar-app .markdown sub{vertical-align:sub}.scalar-app .markdown del{text-decoration:line-through}.scalar-app .markdown code{font-family:var(--scalar-font-code);background-color:var(--scalar-background-2);box-shadow:0 0 0 var(--scalar-border-width) var(--scalar-border-color);font-size:var(--scalar-micro);border-radius:2px;padding:0 3px}.scalar-app .markdown .hljs{font-size:var(--scalar-small)}.scalar-app .markdown pre code{white-space:pre;padding:var(--markdown-spacing-sm);margin:var(--markdown-spacing-sm)0;-webkit-overflow-scrolling:touch;min-width:100px;max-width:100%;line-height:1.5;display:block;overflow-x:auto}.scalar-app .markdown hr{border:none;border-bottom:var(--markdown-border)}.scalar-app .markdown blockquote{border-left:1px solid var(--scalar-color-1);padding-left:var(--markdown-spacing-md);font-weight:var(--scalar-bold);font-size:var(--scalar-font-size-2);margin:0;display:block}.scalar-app .markdown li.task-list-item{list-style:none;position:relative}.scalar-app .markdown li.task-list-item>input{appearance:none;width:var(--markdown-spacing-md);height:var(--markdown-spacing-md);border:1px solid var(--scalar-color-3);border-radius:var(--scalar-radius);display:inline;position:absolute;top:.225em;left:-1.4em}.scalar-app .markdown li.task-list-item>input[type=checkbox]:checked{background-color:var(--scalar-color-1);border-color:var(--scalar-color-1)}.scalar-app .markdown li.task-list-item>input[type=checkbox]:before{content:\"\";border:solid var(--scalar-background-1);opacity:0;border-width:0 1.5px 1.5px 0;width:5px;height:10px;position:absolute;top:1px;left:5px;transform:rotate(45deg)}.scalar-app .markdown li.task-list-item>input[type=checkbox]:checked:before{opacity:1}.scalar-app .markdown .markdown-alert{border-radius:var(--scalar-radius);background-color:var(--scalar-background-2);align-items:stretch}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert{background-color:var(--scalar-background-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert{background-color:var(--scalar-background-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert{background-color:color-mix(in srgb,var(--scalar-background-2),transparent)}}}}.scalar-app .markdown .markdown-alert{border:var(--markdown-border);gap:var(--markdown-spacing-sm);padding:10px 14px;display:flex;position:relative}.scalar-app .markdown .markdown-alert .markdown-alert-icon:before{content:\"\";background-color:currentColor;flex-shrink:0;width:18px;height:18px;margin-top:3px;display:block;-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{background-color:color-mix(in srgb,var(--scalar-color-blue),transparent 97%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid var(--scalar-color-blue)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-note{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-blue),transparent 50%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{background-color:color-mix(in srgb,var(--scalar-color-2),transparent 97%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid var(--scalar-color-2)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-tip{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-2),transparent 50%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-note .markdown-alert-icon:before,.scalar-app .markdown .markdown-alert.markdown-alert-tip .markdown-alert-icon:before{color:var(--scalar-color-blue);-webkit-mask-image:url(\\'data:image/svg+xml,\\');mask-image:url(\\'data:image/svg+xml,\\')}.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{background-color:color-mix(in srgb,var(--scalar-color-orange),transparent 97%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid var(--scalar-color-orange)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-important,.scalar-app .markdown .markdown-alert.markdown-alert-warning{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-orange),transparent 50%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-important .markdown-alert-icon:before,.scalar-app .markdown .markdown-alert.markdown-alert-warning .markdown-alert-icon:before{-webkit-mask-image:url(\\'data:image/svg+xml,\\');mask-image:url(\\'data:image/svg+xml,\\')}.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{background-color:color-mix(in srgb,var(--scalar-color-red),transparent 97%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid var(--scalar-color-red)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-caution{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-red),transparent 50%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-caution .markdown-alert-icon:before{color:var(--scalar-color-red);-webkit-mask-image:url(\\'data:image/svg+xml,\\');mask-image:url(\\'data:image/svg+xml,\\')}.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{background-color:color-mix(in srgb,var(--scalar-color-green),transparent 97%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid var(--scalar-color-green)}@supports (color:color-mix(in lab,red,red)){.scalar-app .markdown .markdown-alert.markdown-alert-success{border:var(--scalar-border-width)solid color-mix(in srgb,var(--scalar-color-green),transparent 50%)}}}}.scalar-app .markdown .markdown-alert.markdown-alert-success .markdown-alert-icon:before{color:var(--scalar-color-green);-webkit-mask-image:url(\\'data:image/svg+xml,\\');mask-image:url(\\'data:image/svg+xml,\\')}.scalar-app .markdown .markdown-alert.markdown-alert-note .markdown-alert-icon:before{color:var(--scalar-color-blue)}.scalar-app .markdown .markdown-alert.markdown-alert-tip .markdown-alert-icon:before{color:var(--scalar-color-2)}.scalar-app .markdown .markdown-alert.markdown-alert-important .markdown-alert-icon:before{color:var(--scalar-color-purple)}.scalar-app .markdown .markdown-alert.markdown-alert-warning .markdown-alert-icon:before{color:var(--scalar-color-orange)}.scalar-app .markdown .markdown-alert .markdown-alert-content{line-height:var(--markdown-line-height);margin:0}.scalar-app .markdown.markdown-summary.markdown-summary :before,.scalar-app .markdown.markdown-summary.markdown-summary :after{content:none}.scalar-app .markdown.markdown-summary.markdown-summary :not(strong,em,a){font-size:inherit;font-weight:inherit;line-height:var(--markdown-line-height);display:contents}.scalar-app .markdown.markdown-summary.markdown-summary img,.scalar-app .markdown.markdown-summary.markdown-summary svg,.scalar-app .markdown.markdown-summary.markdown-summary hr,.scalar-app .markdown.markdown-summary.markdown-summary pre{display:none}.dark-mode .scalar-dropdown-item[data-v-6660bbc5]:hover{filter:brightness(1.1)}.group\\\\/item>*>.scalar-sidebar-indent .scalar-sidebar-indent-border[data-v-3e080c68]{inset-block:-1px}.group\\\\/item:first-child>*>.scalar-sidebar-indent .scalar-sidebar-indent-border[data-v-3e080c68]{top:0}.group\\\\/item:last-child>*>.scalar-sidebar-indent .scalar-sidebar-indent-border[data-v-3e080c68]{bottom:0}.group\\\\/items.-translate-x-full .group\\\\/button{transition-behavior:allow-discrete;max-height:0;transition-property:display,max-height;transition-duration:0s;transition-delay:.3s;display:none}.group\\\\/item.group\\\\/nested-items-open>*>.group\\\\/items.translate-x-0 .group\\\\/button{max-height:3.40282e38px;display:flex}.group\\\\/sidebar-section:first-of-type>.group\\\\/spacer-before,.group\\\\/sidebar-section:last-of-type>.group\\\\/spacer-after{height:0}.group\\\\/sidebar-section:has(+.group\\\\/sidebar-section)>.group\\\\/spacer-after{height:0;margin-bottom:-1px}:where(body)>.scalar-tooltip{--scalar-tooltip-padding:8px;padding:calc(var(--scalar-tooltip-padding) + var(--scalar-tooltip-offset));z-index:99999;max-width:320px;font-size:var(--scalar-font-size-5);--tw-leading:var(--scalar-line-height-5);line-height:var(--scalar-line-height-5);--tw-font-weight:var(--scalar-semibold);font-weight:var(--scalar-semibold);overflow-wrap:break-word;color:var(--scalar-tooltip-color)}:where(body)>.scalar-tooltip:before{content:\"\";inset:var(--scalar-tooltip-offset);z-index:-1;border-radius:var(--scalar-radius);background-color:var(--scalar-tooltip-background);--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);position:absolute}:where(body.dark-mode)>.scalar-tooltip:before{--tw-shadow:inset 0 0 0 var(--tw-shadow-color,calc(var(--scalar-border-width)*2))var(--scalar-border-color);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark-mode .scalar-dropdown-item[data-v-3402682d]:hover{filter:brightness(1.1)}.scalar-modal-layout[data-v-c1e93776]{animation:.3s ease-in-out forwards fadein-layout-c1e93776}.scalar-modal[data-v-c1e93776]{box-shadow:var(--scalar-shadow-2);animation:.3s ease-in-out .1s forwards fadein-modal-c1e93776;transform:translateY(10px)}.scalar-modal-layout-full[data-v-c1e93776]{opacity:1!important;background:0 0!important}.modal-content-search .modal-body[data-v-c1e93776]{flex-direction:column;max-height:440px;padding:0;display:flex;overflow:hidden}@media(max-width:720px)and (max-height:480px){.scalar-modal-layout .scalar-modal[data-v-c1e93776]{max-height:90svh;margin-top:5svh}}.full-size-styles[data-v-c1e93776]{margin:initial;border-right:var(--scalar-border-width)solid var(--scalar-border-color);animation:.3s ease-in-out forwards fadein-layout-c1e93776;left:0;transform:translate(0);background-color:var(--scalar-background-1)!important;max-height:100%!important;box-shadow:none!important;border-radius:0!important;position:absolute!important;top:0!important}@media(min-width:800px){.full-size-styles[data-v-c1e93776]{width:50dvw!important}}.full-size-styles[data-v-c1e93776]:after{content:\"\";width:50dvw;height:100dvh;position:absolute;top:0;right:-50dvw}.sidebar-heading-type[data-v-1857170e]{text-transform:uppercase;color:var(--method-color,var(--scalar-color-1));font-size:10px;line-height:14px;font-weight:var(--scalar-bold);font-family:var(--scalar-font-code);white-space:nowrap;flex-shrink:0;align-items:center;gap:4px;display:inline-flex;overflow:hidden}.scalar-app .pointer-events-auto{pointer-events:auto}.scalar-app .pointer-events-none{pointer-events:none}.scalar-app .collapse{visibility:collapse}.scalar-app .visible{visibility:visible}.scalar-app .floating-bg:before{background-color:var(--scalar-background-2);border-radius:var(--scalar-radius);content:\"\";opacity:0;z-index:1;width:calc(100% + 8px);height:calc(100% - 4px);transition:opacity .2s ease-in-out;position:absolute;top:2.5px;left:-4px}.scalar-app .floating-bg:hover:before{opacity:1}.scalar-app .centered{--tw-translate-y:-50%;--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y);position:absolute;top:50%;left:50%}.scalar-app .centered-y{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y);position:absolute;top:50%}.scalar-app .centered-x{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y);position:absolute;left:50%}.scalar-app .sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.scalar-app .absolute{position:absolute}.scalar-app .fixed{position:fixed}.scalar-app .relative{position:relative}.scalar-app .static{position:static}.scalar-app .sticky{position:sticky}.scalar-app .inset-0{inset:0}.scalar-app .inset-x-0{inset-inline:0}.scalar-app .inset-x-1{inset-inline:4px}.scalar-app .-top-\\\\(--scalar-address-bar-height\\\\){top:calc(var(--scalar-address-bar-height)*-1)}.scalar-app .-top-\\\\[104px\\\\]{top:-104px}.scalar-app .top-0{top:0}.scalar-app .top-2{top:8px}.scalar-app .top-12{top:48px}.scalar-app .top-\\\\[calc\\\\(100\\\\%\\\\+4px\\\\)\\\\]{top:calc(100% + 4px)}.scalar-app .-right-\\\\[30px\\\\]{right:-30px}.scalar-app .right-0{right:0}.scalar-app .right-1{right:4px}.scalar-app .right-1\\\\.5{right:6px}.scalar-app .right-1\\\\/2{right:50%}.scalar-app .right-2{right:8px}.scalar-app .right-4{right:16px}.scalar-app .right-7{right:28px}.scalar-app .right-14{right:56px}.scalar-app .right-16{right:64px}.scalar-app .bottom-0{bottom:0}.scalar-app .bottom-1{bottom:4px}.scalar-app .bottom-1\\\\/2{bottom:50%}.scalar-app .bottom-\\\\[var\\\\(--scalar-border-width\\\\)\\\\]{bottom:var(--scalar-border-width)}.scalar-app .left-0{left:0}.scalar-app .left-1\\\\/2{left:50%}.scalar-app .left-3{left:12px}.scalar-app .-z-1{z-index:-1}.scalar-app .z-0{z-index:0}.scalar-app .z-1{z-index:1}.scalar-app .z-10{z-index:10}.scalar-app .z-20{z-index:20}.scalar-app .z-50{z-index:50}.scalar-app .z-\\\\[1\\\\]{z-index:1}.scalar-app .z-\\\\[1002\\\\]{z-index:1002}.scalar-app .z-\\\\[10000\\\\]{z-index:10000}.scalar-app .z-\\\\[10001\\\\]{z-index:10001}.scalar-app .z-context{z-index:1000}.scalar-app .z-context-plus{z-index:1001}.scalar-app .z-overlay{z-index:10000}.scalar-app .order-last{order:9999}.scalar-app .col-span-full{grid-column:1/-1}.scalar-app .container{width:100%}@media(min-width:400px){.scalar-app .container{max-width:400px}}@media(min-width:600px){.scalar-app .container{max-width:600px}}@media(min-width:800px){.scalar-app .container{max-width:800px}}@media(min-width:1000px){.scalar-app .container{max-width:1000px}}@media(min-width:1200px){.scalar-app .container{max-width:1200px}}@media(min-width:96rem){.scalar-app .container{max-width:96rem}}.scalar-app .\\\\!m-0{margin:0!important}.scalar-app .-m-0\\\\.5{margin:-2px}.scalar-app .m-0{margin:0}.scalar-app .m-4{margin:16px}.scalar-app .m-auto{margin:auto}.scalar-app .m-header{margin:48px}.scalar-app .-mx-0\\\\.25{margin-inline:-1px}.scalar-app .mx-1{margin-inline:4px}.scalar-app .mx-auto{margin-inline:auto}.scalar-app .-my-1{margin-block:-4px}.scalar-app .my-12{margin-block:48px}.scalar-app .-mt-\\\\[\\\\.5px\\\\]{margin-top:-.5px}.scalar-app .mt-0\\\\.25{margin-top:1px}.scalar-app .mt-1{margin-top:4px}.scalar-app .mt-1\\\\.5{margin-top:6px}.scalar-app .mt-2{margin-top:8px}.scalar-app .mt-3{margin-top:12px}.scalar-app .mt-5{margin-top:20px}.scalar-app .mt-10{margin-top:40px}.scalar-app .mt-\\\\[0\\\\.5px\\\\]{margin-top:.5px}.scalar-app .mt-auto{margin-top:auto}.scalar-app .\\\\!mr-0{margin-right:0!important}.scalar-app .-mr-0\\\\.5{margin-right:-2px}.scalar-app .-mr-1{margin-right:-4px}.scalar-app .-mr-1\\\\.5{margin-right:-6px}.scalar-app .-mr-3{margin-right:-12px}.scalar-app .mr-0\\\\.5{margin-right:2px}.scalar-app .mr-0\\\\.75{margin-right:3px}.scalar-app .mr-1{margin-right:4px}.scalar-app .mr-1\\\\.5{margin-right:6px}.scalar-app .mr-1\\\\.25{margin-right:5px}.scalar-app .mr-2{margin-right:8px}.scalar-app .mr-2\\\\.5{margin-right:10px}.scalar-app .mr-3{margin-right:12px}.scalar-app .mr-\\\\[6\\\\.25px\\\\]{margin-right:6.25px}.scalar-app .mr-auto{margin-right:auto}.scalar-app .\\\\!mb-0{margin-bottom:0!important}.scalar-app .-mb-\\\\[var\\\\(--scalar-border-width\\\\)\\\\]{margin-bottom:calc(var(--scalar-border-width)*-1)}.scalar-app .mb-0{margin-bottom:0}.scalar-app .mb-1{margin-bottom:4px}.scalar-app .mb-1\\\\.5{margin-bottom:6px}.scalar-app .mb-2{margin-bottom:8px}.scalar-app .mb-4{margin-bottom:16px}.scalar-app .mb-\\\\[\\\\.5px\\\\]{margin-bottom:.5px}.scalar-app .-ml-0\\\\.5{margin-left:-2px}.scalar-app .-ml-0\\\\.25{margin-left:-1px}.scalar-app .-ml-1{margin-left:-4px}.scalar-app .-ml-2{margin-left:-8px}.scalar-app .-ml-12{margin-left:-48px}.scalar-app .ml-0\\\\.5{margin-left:2px}.scalar-app .ml-0\\\\.75{margin-left:3px}.scalar-app .ml-1{margin-left:4px}.scalar-app .ml-1\\\\.25{margin-left:5px}.scalar-app .ml-3{margin-left:12px}.scalar-app .ml-auto{margin-left:auto}.scalar-app .box-border{box-sizing:border-box}.scalar-app .box-content{box-sizing:content-box}.scalar-app .flex-center{justify-content:center;align-items:center;display:flex}.scalar-app .line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.scalar-app .\\\\!block{display:block!important}.scalar-app .\\\\!flex{display:flex!important}.scalar-app .block{display:block}.scalar-app .contents{display:contents}.scalar-app .flex{display:flex}.scalar-app .grid{display:grid}.scalar-app .hidden{display:none}.scalar-app .inline{display:inline}.scalar-app .inline-block{display:inline-block}.scalar-app .inline-flex{display:inline-flex}.scalar-app .table{display:table}.scalar-app .aspect-\\\\[4\\\\/3\\\\]{aspect-ratio:4/3}.scalar-app .aspect-square{aspect-ratio:1}.scalar-app .size-2\\\\.5{width:10px;height:10px}.scalar-app .size-3{width:12px;height:12px}.scalar-app .size-3\\\\.5{width:14px;height:14px}.scalar-app .size-3\\\\/4{width:75%;height:75%}.scalar-app .size-4{width:16px;height:16px}.scalar-app .size-5{width:20px;height:20px}.scalar-app .size-6{width:24px;height:24px}.scalar-app .size-7{width:28px;height:28px}.scalar-app .size-8{width:32px;height:32px}.scalar-app .size-10{width:40px;height:40px}.scalar-app .size-full{width:100%;height:100%}.scalar-app .h-\\\\(--scalar-address-bar-height\\\\){height:var(--scalar-address-bar-height)}.scalar-app .h-1\\\\.5{height:6px}.scalar-app .h-2\\\\.5{height:10px}.scalar-app .h-2\\\\.25{height:9px}.scalar-app .h-3{height:12px}.scalar-app .h-3\\\\.5{height:14px}.scalar-app .h-4{height:16px}.scalar-app .h-5{height:20px}.scalar-app .h-6{height:24px}.scalar-app .h-7{height:28px}.scalar-app .h-8{height:32px}.scalar-app .h-9{height:36px}.scalar-app .h-10{height:40px}.scalar-app .h-12{height:48px}.scalar-app .h-16{height:64px}.scalar-app .h-64{height:256px}.scalar-app .h-\\\\[68px\\\\]{height:68px}.scalar-app .h-\\\\[calc\\\\(100\\\\%-273\\\\.5px\\\\)\\\\]{height:calc(100% - 273.5px)}.scalar-app .h-\\\\[calc\\\\(100\\\\%_-_50px\\\\)\\\\]{height:calc(100% - 50px)}.scalar-app .h-auto{height:auto}.scalar-app .h-fit{height:fit-content}.scalar-app .h-full{height:100%}.scalar-app .h-header{height:48px}.scalar-app .h-min{height:min-content}.scalar-app .h-px{height:1px}.scalar-app .h-screen{height:100vh}.scalar-app .\\\\!max-h-\\\\[initial\\\\]{max-height:initial!important}.scalar-app .max-h-8{max-height:32px}.scalar-app .max-h-40{max-height:160px}.scalar-app .max-h-80{max-height:320px}.scalar-app .max-h-\\\\[40dvh\\\\]{max-height:40dvh}.scalar-app .max-h-\\\\[50dvh\\\\]{max-height:50dvh}.scalar-app .max-h-\\\\[60svh\\\\]{max-height:60svh}.scalar-app .max-h-\\\\[auto\\\\]{max-height:auto}.scalar-app .max-h-\\\\[calc\\\\(100\\\\%-32px\\\\)\\\\]{max-height:calc(100% - 32px)}.scalar-app .max-h-\\\\[inherit\\\\]{max-height:inherit}.scalar-app .max-h-fit{max-height:fit-content}.scalar-app .max-h-screen{max-height:100vh}.scalar-app .\\\\!min-h-full{min-height:100%!important}.scalar-app .min-h-0{min-height:0}.scalar-app .min-h-8{min-height:32px}.scalar-app .min-h-10{min-height:40px}.scalar-app .min-h-11{min-height:44px}.scalar-app .min-h-12{min-height:48px}.scalar-app .min-h-16{min-height:64px}.scalar-app .min-h-20{min-height:80px}.scalar-app .min-h-\\\\[64px\\\\]{min-height:64px}.scalar-app .min-h-\\\\[65px\\\\]{min-height:65px}.scalar-app .min-h-\\\\[calc\\\\(1rem\\\\*4\\\\)\\\\]{min-height:4rem}.scalar-app .min-h-\\\\[calc\\\\(4rem\\\\+0\\\\.5px\\\\)\\\\]{min-height:calc(4rem + .5px)}.scalar-app .min-h-\\\\[calc\\\\(4rem\\\\+1px\\\\)\\\\]{min-height:calc(4rem + 1px)}.scalar-app .min-h-fit{min-height:fit-content}.scalar-app .\\\\!w-fit{width:fit-content!important}.scalar-app .w-0\\\\.5{width:2px}.scalar-app .w-1\\\\.5{width:6px}.scalar-app .w-1\\\\/2{width:50%}.scalar-app .w-2\\\\.5{width:10px}.scalar-app .w-2\\\\.25{width:9px}.scalar-app .w-3{width:12px}.scalar-app .w-3\\\\.5{width:14px}.scalar-app .w-4{width:16px}.scalar-app .w-5{width:20px}.scalar-app .w-6{width:24px}.scalar-app .w-7{width:28px}.scalar-app .w-8{width:32px}.scalar-app .w-10{width:40px}.scalar-app .w-16{width:64px}.scalar-app .w-20{width:80px}.scalar-app .w-56{width:224px}.scalar-app .w-64{width:256px}.scalar-app .w-72{width:288px}.scalar-app .w-\\\\[60px\\\\]{width:60px}.scalar-app .w-\\\\[100px\\\\]{width:100px}.scalar-app .w-\\\\[150px\\\\]{width:150px}.scalar-app .w-\\\\[calc\\\\(100\\\\%-10px\\\\)\\\\]{width:calc(100% - 10px)}.scalar-app .w-\\\\[calc\\\\(100\\\\%_-_8px\\\\)\\\\]{width:calc(100% - 8px)}.scalar-app .w-\\\\[inherit\\\\]{width:inherit}.scalar-app .w-auto{width:auto}.scalar-app .w-dvw{width:100dvw}.scalar-app .w-fit{width:fit-content}.scalar-app .w-full{width:100%}.scalar-app .w-max{width:max-content}.scalar-app .max-w-8{max-width:32px}.scalar-app .max-w-40{max-width:160px}.scalar-app .max-w-\\\\[14px\\\\]{max-width:14px}.scalar-app .max-w-\\\\[16rem\\\\]{max-width:16rem}.scalar-app .max-w-\\\\[37px\\\\]{max-width:37px}.scalar-app .max-w-\\\\[100\\\\%\\\\]{max-width:100%}.scalar-app .max-w-\\\\[150px\\\\]{max-width:150px}.scalar-app .max-w-\\\\[380px\\\\]{max-width:380px}.scalar-app .max-w-\\\\[420px\\\\]{max-width:420px}.scalar-app .max-w-\\\\[720px\\\\]{max-width:720px}.scalar-app .max-w-\\\\[calc\\\\(100dvw-24px\\\\)\\\\]{max-width:calc(100dvw - 24px)}.scalar-app .max-w-full{max-width:100%}.scalar-app .min-w-0{min-width:0}.scalar-app .min-w-2\\\\.25{min-width:9px}.scalar-app .min-w-3\\\\.5{min-width:14px}.scalar-app .min-w-4{min-width:16px}.scalar-app .min-w-8{min-width:32px}.scalar-app .min-w-32{min-width:128px}.scalar-app .min-w-48{min-width:192px}.scalar-app .min-w-\\\\[37px\\\\]{min-width:37px}.scalar-app .min-w-\\\\[100px\\\\]{min-width:100px}.scalar-app .min-w-\\\\[150px\\\\]{min-width:150px}.scalar-app .min-w-\\\\[296px\\\\]{min-width:296px}.scalar-app .min-w-fit{min-width:fit-content}.scalar-app .min-w-full{min-width:100%}.scalar-app .flex-1{flex:1}.scalar-app .flex-shrink{flex-shrink:1}.scalar-app .shrink-0{flex-shrink:0}.scalar-app .flex-grow{flex-grow:1}.scalar-app .-translate-x-1\\\\/2{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-x-1\\\\/2{--tw-translate-x:50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .translate-y-1\\\\/2{--tw-translate-y:50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scalar-app .rotate-90{rotate:90deg}.scalar-app .rotate-180{rotate:180deg}.scalar-app .transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.scalar-app .cursor-auto{cursor:auto}.scalar-app .cursor-default{cursor:default}.scalar-app .cursor-grab{cursor:grab}.scalar-app .cursor-help{cursor:help}.scalar-app .cursor-not-allowed{cursor:not-allowed}.scalar-app .cursor-pointer{cursor:pointer}.scalar-app .cursor-text{cursor:text}.scalar-app .resize{resize:both}.scalar-app .resize-none{resize:none}.scalar-app .auto-rows-\\\\[32px\\\\]{grid-auto-rows:32px}.scalar-app .auto-rows-auto{grid-auto-rows:auto}.scalar-app .grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.scalar-app .grid-cols-\\\\[44px_1fr_repeat\\\\(3\\\\,auto\\\\)\\\\]{grid-template-columns:44px 1fr repeat(3,auto)}.scalar-app .grid-cols-\\\\[auto_1fr\\\\]{grid-template-columns:auto 1fr}.scalar-app .grid-cols-\\\\[repeat\\\\(auto-fill\\\\,minmax\\\\(32px\\\\,1fr\\\\)\\\\)\\\\]{grid-template-columns:repeat(auto-fill,minmax(32px,1fr))}.scalar-app .flex-col{flex-direction:column}.scalar-app .flex-row{flex-direction:row}.scalar-app .flex-wrap{flex-wrap:wrap}.scalar-app .content-between{align-content:space-between}.scalar-app .content-start{align-content:flex-start}.scalar-app .items-center{align-items:center}.scalar-app .items-end{align-items:flex-end}.scalar-app .items-start{align-items:flex-start}.scalar-app .items-stretch{align-items:stretch}.scalar-app .justify-between{justify-content:space-between}.scalar-app .justify-center{justify-content:center}.scalar-app .justify-end{justify-content:flex-end}.scalar-app .justify-start{justify-content:flex-start}.scalar-app .justify-stretch{justify-content:stretch}.scalar-app .\\\\!gap-2{gap:8px!important}.scalar-app .gap-0\\\\.5{gap:2px}.scalar-app .gap-0\\\\.75{gap:3px}.scalar-app .gap-1{gap:4px}.scalar-app .gap-1\\\\.5{gap:6px}.scalar-app .gap-1\\\\.75{gap:7px}.scalar-app .gap-2{gap:8px}.scalar-app .gap-2\\\\.5{gap:10px}.scalar-app .gap-3{gap:12px}.scalar-app .gap-4{gap:16px}.scalar-app .gap-6{gap:24px}.scalar-app .gap-8{gap:32px}.scalar-app .gap-10{gap:40px}.scalar-app .gap-12{gap:48px}.scalar-app .gap-\\\\[1\\\\.5px\\\\]{gap:1.5px}.scalar-app .gap-px{gap:1px}.scalar-app .gap-x-2\\\\.5{column-gap:10px}:where(.scalar-app .space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(4px*var(--tw-space-x-reverse));margin-inline-end:calc(4px*calc(1 - var(--tw-space-x-reverse)))}:where(.scalar-app .divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(var(--scalar-border-width)*var(--tw-divide-y-reverse));border-bottom-width:calc(var(--scalar-border-width)*calc(1 - var(--tw-divide-y-reverse)))}.scalar-app .self-center{align-self:center}.scalar-app .truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.scalar-app .overflow-auto{overflow:auto}.scalar-app .overflow-hidden{overflow:hidden}.scalar-app .overflow-visible{overflow:visible}.scalar-app .overflow-x-auto{overflow-x:auto}.scalar-app .overflow-y-auto{overflow-y:auto}.scalar-app .overflow-y-hidden{overflow-y:hidden}.scalar-app .\\\\!rounded-none{border-radius:0!important}.scalar-app .rounded{border-radius:var(--scalar-radius)}.scalar-app .rounded-\\\\[10px\\\\]{border-radius:10px}.scalar-app .rounded-full{border-radius:9999px}.scalar-app .rounded-lg{border-radius:var(--scalar-radius-lg)}.scalar-app .rounded-md{border-radius:var(--scalar-radius)}.scalar-app .rounded-px{border-radius:1px}.scalar-app .rounded-xl{border-radius:var(--scalar-radius-xl)}.scalar-app .rounded-t{border-top-left-radius:var(--scalar-radius);border-top-right-radius:var(--scalar-radius)}.scalar-app .rounded-t-lg{border-top-left-radius:var(--scalar-radius-lg);border-top-right-radius:var(--scalar-radius-lg)}.scalar-app .rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.scalar-app .rounded-b{border-bottom-right-radius:var(--scalar-radius);border-bottom-left-radius:var(--scalar-radius)}.scalar-app .rounded-b-lg{border-bottom-right-radius:var(--scalar-radius-lg);border-bottom-left-radius:var(--scalar-radius-lg)}.scalar-app .\\\\!border-0{border-style:var(--tw-border-style)!important;border-width:0!important}.scalar-app .border{border-style:var(--tw-border-style);border-width:var(--scalar-border-width)}.scalar-app .border-0{border-style:var(--tw-border-style);border-width:0}.scalar-app .border-\\\\[1\\\\.5px\\\\]{border-style:var(--tw-border-style);border-width:1.5px}.scalar-app .border-\\\\[1px\\\\]{border-style:var(--tw-border-style);border-width:1px}.scalar-app .border-x{border-inline-style:var(--tw-border-style);border-inline-width:var(--scalar-border-width)}.scalar-app .border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.scalar-app .border-y{border-block-style:var(--tw-border-style);border-block-width:var(--scalar-border-width)}.scalar-app .border-t{border-top-style:var(--tw-border-style);border-top-width:var(--scalar-border-width)}.scalar-app .border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.scalar-app .\\\\!border-r{border-right-style:var(--tw-border-style)!important;border-right-width:var(--scalar-border-width)!important}.scalar-app .border-r{border-right-style:var(--tw-border-style);border-right-width:var(--scalar-border-width)}.scalar-app .border-r-0{border-right-style:var(--tw-border-style);border-right-width:0}.scalar-app .border-r-1{border-right-style:var(--tw-border-style);border-right-width:1px}.scalar-app .border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:var(--scalar-border-width)}.scalar-app .border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.scalar-app .border-b-\\\\[1px\\\\]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.scalar-app .border-l{border-left-style:var(--tw-border-style);border-left-width:var(--scalar-border-width)}.scalar-app .border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.scalar-app .\\\\!border-none{--tw-border-style:none!important;border-style:none!important}.scalar-app .border-dashed{--tw-border-style:dashed;border-style:dashed}.scalar-app .border-none{--tw-border-style:none;border-style:none}.scalar-app .\\\\!border-current{border-color:currentColor!important}.scalar-app .border-c-1{border-color:var(--scalar-color-1)}.scalar-app .border-c-3{border-color:var(--scalar-color-3)}.scalar-app .border-c-danger{border-color:var(--scalar-color-danger)}.scalar-app .border-transparent{border-color:#0000}.scalar-app .border-r-transparent{border-right-color:#0000}.scalar-app .bg-b-1{background-color:var(--scalar-background-1)}.scalar-app .bg-b-2{background-color:var(--scalar-background-2)}.scalar-app .bg-b-3{background-color:var(--scalar-background-3)}.scalar-app .bg-b-danger{background-color:var(--scalar-background-danger)}.scalar-app .bg-c-3\\\\/5{background-color:var(--scalar-color-3)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-c-3\\\\/5{background-color:var(--scalar-color-3)}@supports (color:color-mix(in lab,red,red)){.scalar-app .bg-c-3\\\\/5{background-color:color-mix(in oklab,var(--scalar-color-3)5%,transparent)}}}.scalar-app .bg-c-accent{background-color:var(--scalar-color-accent)}.scalar-app .bg-current{background-color:currentColor}.scalar-app .bg-grey{background-color:var(--scalar-color-3)}.scalar-app .bg-sidebar-b-1{background-color:var(--scalar-sidebar-background-1,var(--scalar-background-1))}.scalar-app .bg-sidebar-b-active{background-color:var(--scalar-sidebar-item-active-background,var(--scalar-background-2))}.scalar-app .bg-none{background-image:none}.scalar-app .fill-current{fill:currentColor}.scalar-app .stroke-2{stroke-width:2px}.scalar-app .stroke-\\\\[1\\\\.5\\\\]{stroke-width:1.5px}.scalar-app .stroke-\\\\[1\\\\.75\\\\]{stroke-width:1.75px}.scalar-app .stroke-\\\\[2\\\\.25\\\\]{stroke-width:2.25px}.scalar-app .object-contain{object-fit:contain}.scalar-app .\\\\!p-0{padding:0!important}.scalar-app .p-0{padding:0}.scalar-app .p-0\\\\.5{padding:2px}.scalar-app .p-0\\\\.75{padding:3px}.scalar-app .p-1{padding:4px}.scalar-app .p-1\\\\.5{padding:6px}.scalar-app .p-1\\\\.25{padding:5px}.scalar-app .p-1\\\\.75{padding:7px}.scalar-app .p-2{padding:8px}.scalar-app .p-3{padding:12px}.scalar-app .p-4{padding:16px}.scalar-app .p-\\\\[3px\\\\]{padding:3px}.scalar-app .p-\\\\[5px\\\\]{padding:5px}.scalar-app .p-px{padding:1px}.scalar-app .\\\\!px-3{padding-inline:12px!important}.scalar-app .px-0{padding-inline:0}.scalar-app .px-0\\\\.5{padding-inline:2px}.scalar-app .px-0\\\\.75{padding-inline:3px}.scalar-app .px-1{padding-inline:4px}.scalar-app .px-1\\\\.5{padding-inline:6px}.scalar-app .px-1\\\\.25{padding-inline:5px}.scalar-app .px-2{padding-inline:8px}.scalar-app .px-2\\\\.5{padding-inline:10px}.scalar-app .px-3{padding-inline:12px}.scalar-app .px-4{padding-inline:16px}.scalar-app .px-5{padding-inline:20px}.scalar-app .px-6{padding-inline:24px}.scalar-app .px-8{padding-inline:32px}.scalar-app .\\\\!py-1\\\\.5{padding-block:6px!important}.scalar-app .py-0{padding-block:0}.scalar-app .py-0\\\\.5{padding-block:2px}.scalar-app .py-0\\\\.25{padding-block:1px}.scalar-app .py-0\\\\.75{padding-block:3px}.scalar-app .py-1{padding-block:4px}.scalar-app .py-1\\\\.5{padding-block:6px}.scalar-app .py-1\\\\.25{padding-block:5px}.scalar-app .py-1\\\\.75{padding-block:7px}.scalar-app .py-2{padding-block:8px}.scalar-app .py-2\\\\.5{padding-block:10px}.scalar-app .py-3{padding-block:12px}.scalar-app .py-5{padding-block:20px}.scalar-app .py-8{padding-block:32px}.scalar-app .py-px{padding-block:1px}.scalar-app .\\\\!pt-0{padding-top:0!important}.scalar-app .pt-0{padding-top:0}.scalar-app .pt-2{padding-top:8px}.scalar-app .pt-3{padding-top:12px}.scalar-app .pt-4{padding-top:16px}.scalar-app .pt-6{padding-top:24px}.scalar-app .pt-8{padding-top:32px}.scalar-app .pt-px{padding-top:1px}.scalar-app .pr-0{padding-right:0}.scalar-app .pr-0\\\\.75{padding-right:3px}.scalar-app .pr-1{padding-right:4px}.scalar-app .pr-1\\\\.5{padding-right:6px}.scalar-app .pr-2{padding-right:8px}.scalar-app .pr-2\\\\.5{padding-right:10px}.scalar-app .pr-2\\\\.25{padding-right:9px}.scalar-app .pr-3{padding-right:12px}.scalar-app .pr-6{padding-right:24px}.scalar-app .pr-8{padding-right:32px}.scalar-app .pr-9{padding-right:36px}.scalar-app .pr-10{padding-right:40px}.scalar-app .pr-12{padding-right:48px}.scalar-app .pr-\\\\[26px\\\\]{padding-right:26px}.scalar-app .pb-0{padding-bottom:0}.scalar-app .pb-1\\\\.5{padding-bottom:6px}.scalar-app .pb-2{padding-bottom:8px}.scalar-app .pb-3{padding-bottom:12px}.scalar-app .pb-5{padding-bottom:20px}.scalar-app .pb-6{padding-bottom:24px}.scalar-app .pb-8{padding-bottom:32px}.scalar-app .pb-14{padding-bottom:56px}.scalar-app .pb-\\\\[75px\\\\]{padding-bottom:75px}.scalar-app .\\\\!pl-3{padding-left:12px!important}.scalar-app .pl-1{padding-left:4px}.scalar-app .pl-1\\\\.5{padding-left:6px}.scalar-app .pl-1\\\\.25{padding-left:5px}.scalar-app .pl-2{padding-left:8px}.scalar-app .pl-3{padding-left:12px}.scalar-app .pl-5{padding-left:20px}.scalar-app .pl-6{padding-left:24px}.scalar-app .pl-8\\\\.5{padding-left:34px}.scalar-app .pl-9{padding-left:36px}.scalar-app .pl-12{padding-left:48px}.scalar-app .pl-px{padding-left:1px}.scalar-app .text-center{text-align:center}.scalar-app .text-left{text-align:left}.scalar-app .text-right{text-align:right}.scalar-app .font-code{font-family:var(--scalar-font-code)}.scalar-app .font-sans{font-family:var(--scalar-font)}.scalar-app .text-3xs{font-size:var(--scalar-font-size-7)}.scalar-app .text-\\\\[6px\\\\]{font-size:6px}.scalar-app .text-\\\\[11px\\\\]{font-size:11px}.scalar-app .text-\\\\[21px\\\\]{font-size:21px}.scalar-app .text-base{font-size:var(--scalar-font-size-3)}.scalar-app .text-sm{font-size:var(--scalar-font-size-4)}.scalar-app .text-xl{font-size:var(--scalar-font-size-1)}.scalar-app .text-xs{font-size:var(--scalar-font-size-5)}.scalar-app .text-xxs{font-size:var(--scalar-font-size-6)}.scalar-app .\\\\!leading-\\\\[6px\\\\]{--tw-leading:6px!important;line-height:6px!important}.scalar-app .leading-2{--tw-leading:var(--scalar-line-height-2);line-height:var(--scalar-line-height-2)}.scalar-app .leading-3{--tw-leading:var(--scalar-line-height-3);line-height:var(--scalar-line-height-3)}.scalar-app .leading-\\\\[1\\\\.44\\\\]{--tw-leading:1.44;line-height:1.44}.scalar-app .leading-\\\\[7px\\\\]{--tw-leading:7px;line-height:7px}.scalar-app .leading-\\\\[20px\\\\]{--tw-leading:20px;line-height:20px}.scalar-app .leading-\\\\[21px\\\\]{--tw-leading:21px;line-height:21px}.scalar-app .leading-\\\\[22px\\\\]{--tw-leading:22px;line-height:22px}.scalar-app .leading-\\\\[normal\\\\]{--tw-leading:normal;line-height:normal}.scalar-app .leading-none{--tw-leading:1;line-height:1}.scalar-app .leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.scalar-app .leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.scalar-app .font-bold{--tw-font-weight:var(--scalar-bold);font-weight:var(--scalar-bold)}.scalar-app .font-medium{--tw-font-weight:var(--scalar-semibold);font-weight:var(--scalar-semibold)}.scalar-app .font-normal{--tw-font-weight:var(--scalar-regular);font-weight:var(--scalar-regular)}.scalar-app .text-balance{text-wrap:balance}.scalar-app .text-pretty{text-wrap:pretty}.scalar-app .break-words{overflow-wrap:break-word}.scalar-app .break-all{word-break:break-all}.scalar-app .text-ellipsis{text-overflow:ellipsis}.scalar-app .whitespace-nowrap{white-space:nowrap}.scalar-app .whitespace-pre{white-space:pre}.scalar-app .whitespace-pre-wrap{white-space:pre-wrap}.scalar-app .\\\\!text-c-1{color:var(--scalar-color-1)!important}.scalar-app .text-b-1{color:var(--scalar-background-1)}.scalar-app .text-blue{color:var(--scalar-color-blue)}.scalar-app .text-border{color:var(--scalar-border-color)}.scalar-app .text-c-1{color:var(--scalar-color-1)}.scalar-app .text-c-2{color:var(--scalar-color-2)}.scalar-app .text-c-3{color:var(--scalar-color-3)}.scalar-app .text-c-btn{color:var(--scalar-button-1-color)}.scalar-app .text-c-danger{color:var(--scalar-color-danger)}.scalar-app .text-green{color:var(--scalar-color-green)}.scalar-app .text-grey{color:var(--scalar-color-3)}.scalar-app .text-orange{color:var(--scalar-color-orange)}.scalar-app .text-purple{color:var(--scalar-color-purple)}.scalar-app .text-red{color:var(--scalar-color-red)}.scalar-app .text-sidebar-c-2{color:var(--scalar-sidebar-color-2,var(--scalar-color-2))}.scalar-app .text-sidebar-c-active{color:var(--scalar-sidebar-color-active,var(--scalar-sidebar-color-1))}.scalar-app .text-transparent{color:#0000}.scalar-app .text-yellow{color:var(--scalar-color-yellow)}.scalar-app .capitalize{text-transform:capitalize}.scalar-app .lowercase{text-transform:lowercase}.scalar-app .uppercase{text-transform:uppercase}.scalar-app .no-underline{text-decoration-line:none}.scalar-app .underline{text-decoration-line:underline}.scalar-app .decoration-c-3{-webkit-text-decoration-color:var(--scalar-color-3);text-decoration-color:var(--scalar-color-3)}.scalar-app .underline-offset-2{text-underline-offset:2px}.scalar-app .opacity-0{opacity:0}.scalar-app .opacity-50{opacity:.5}.scalar-app .opacity-100{opacity:1}.scalar-app .bg-blend-normal{background-blend-mode:normal}.scalar-app .mix-blend-luminosity{mix-blend-mode:luminosity}.scalar-app .shadow{--tw-shadow:var(--scalar-shadow-1);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .shadow-\\\\[-8px_0_4px_var\\\\(--scalar-background-1\\\\)\\\\]{--tw-shadow:-8px 0 4px var(--tw-shadow-color,var(--scalar-background-1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .shadow-\\\\[0_-8px_0_8px_var\\\\(--scalar-background-1\\\\)\\\\,0_0_8px_8px_var\\\\(--scalar-background-1\\\\)\\\\]{--tw-shadow:0 -8px 0 8px var(--tw-shadow-color,var(--scalar-background-1)),0 0 8px 8px var(--tw-shadow-color,var(--scalar-background-1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .shadow-border{--tw-shadow:inset 0 0 0 var(--tw-shadow-color,calc(var(--scalar-border-width)*2))var(--scalar-border-color);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .shadow-lg{--tw-shadow:var(--scalar-shadow-2);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .outline{outline-style:var(--tw-outline-style);outline-width:1px}.scalar-app .-outline-offset-1{outline-offset:-1px}.scalar-app .-outline-offset-2{outline-offset:-2px}.scalar-app .outline-offset-2{outline-offset:2px}.scalar-app .outline-b-3{outline-color:var(--scalar-background-3)}.scalar-app .outline-c-danger{outline-color:var(--scalar-color-danger)}.scalar-app .blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .brightness-90{--tw-brightness:brightness(90%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .brightness-\\\\[\\\\.9\\\\]{--tw-brightness:brightness(.9);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .brightness-lifted{--tw-brightness:brightness(var(--scalar-lifted-brightness));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.scalar-app .backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.scalar-app .transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.scalar-app .transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.scalar-app .transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.scalar-app .transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.scalar-app .transition-none{transition-property:none}.scalar-app .duration-100{--tw-duration:.1s;transition-duration:.1s}.scalar-app .duration-150{--tw-duration:.15s;transition-duration:.15s}.scalar-app .duration-200{--tw-duration:.2s;transition-duration:.2s}.scalar-app .duration-300{--tw-duration:.3s;transition-duration:.3s}.scalar-app .ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.scalar-app .outline-none{--tw-outline-style:none;outline-style:none}.scalar-app .select-none{-webkit-user-select:none;user-select:none}.scalar-app .\\\\[--scalar-address-bar-height\\\\:32px\\\\]{--scalar-address-bar-height:32px}.scalar-app .app-drag-region{-webkit-app-region:drag}.scalar-app .app-no-drag-region{-webkit-app-region:no-drag}:is(.scalar-app .\\\\*\\\\:flex>*){display:flex}:is(.scalar-app .\\\\*\\\\:h-8>*){height:32px}:is(.scalar-app .\\\\*\\\\:cursor-pointer>*){cursor:pointer}:is(.scalar-app .\\\\*\\\\:items-center>*){align-items:center}:is(.scalar-app .\\\\*\\\\:rounded-none>*){border-radius:0}:is(.scalar-app .\\\\*\\\\:border-t>*){border-top-style:var(--tw-border-style);border-top-width:var(--scalar-border-width)}:is(.scalar-app .\\\\*\\\\:border-b-0>*){border-bottom-style:var(--tw-border-style);border-bottom-width:0}:is(.scalar-app .\\\\*\\\\:px-1\\\\.5>*){padding-inline:6px}:is(.scalar-app .\\\\*\\\\:pl-4>*){padding-left:16px}.scalar-app .group-first\\\\/row\\\\:border-t-0:is(:where(.group\\\\/row):first-child *){border-top-style:var(--tw-border-style);border-top-width:0}.scalar-app .group-last\\\\:border-b-transparent:is(:where(.group):last-child *){border-bottom-color:#0000}.scalar-app .group-last\\\\/label\\\\:rounded-br-lg:is(:where(.group\\\\/label):last-child *){border-bottom-right-radius:var(--scalar-radius-lg)}.scalar-app .group-focus-within\\\\:flex:is(:where(.group):focus-within *){display:flex}@media(hover:hover){.scalar-app .group-hover\\\\:block:is(:where(.group):hover *){display:block}.scalar-app .group-hover\\\\:flex:is(:where(.group):hover *){display:flex}.scalar-app .group-hover\\\\:hidden:is(:where(.group):hover *){display:none}.scalar-app .group-hover\\\\:inline:is(:where(.group):hover *){display:inline}.scalar-app .group-hover\\\\:pr-5:is(:where(.group):hover *){padding-right:20px}.scalar-app .group-hover\\\\:pr-6:is(:where(.group):hover *){padding-right:24px}.scalar-app .group-hover\\\\:pr-10:is(:where(.group):hover *){padding-right:40px}.scalar-app .group-hover\\\\:text-c-1:is(:where(.group):hover *){color:var(--scalar-color-1)}.scalar-app .group-hover\\\\:opacity-80:is(:where(.group):hover *){opacity:.8}.scalar-app .group-hover\\\\:opacity-100:is(:where(.group):hover *){opacity:1}.scalar-app .group-hover\\\\/auth\\\\:absolute:is(:where(.group\\\\/auth):hover *){position:absolute}.scalar-app .group-hover\\\\/auth\\\\:h-auto:is(:where(.group\\\\/auth):hover *){height:auto}.scalar-app .group-hover\\\\/auth\\\\:border-b:is(:where(.group\\\\/auth):hover *){border-bottom-style:var(--tw-border-style);border-bottom-width:var(--scalar-border-width)}.scalar-app .group-hover\\\\/cell\\\\:opacity-100:is(:where(.group\\\\/cell):hover *){opacity:1}.scalar-app .group-hover\\\\/item\\\\:flex:is(:where(.group\\\\/item):hover *){display:flex}.scalar-app .group-hover\\\\/item\\\\:opacity-100:is(:where(.group\\\\/item):hover *),.scalar-app .group-hover\\\\/params\\\\:opacity-100:is(:where(.group\\\\/params):hover *){opacity:1}.scalar-app .group-hover\\\\/row\\\\:flex:is(:where(.group\\\\/row):hover *){display:flex}.scalar-app .group-hover\\\\/scopes-accordion\\\\:text-c-2:is(:where(.group\\\\/scopes-accordion):hover *){color:var(--scalar-color-2)}.scalar-app .group-hover\\\\/upload\\\\:block:is(:where(.group\\\\/upload):hover *){display:block}}.scalar-app .group-focus-visible\\\\:opacity-100:is(:where(.group):focus-visible *){opacity:1}.scalar-app .group-focus-visible\\\\:outline:is(:where(.group):focus-visible *){outline-style:var(--tw-outline-style);outline-width:1px}.scalar-app .group-has-\\\\[\\\\.cm-focused\\\\]\\\\:z-1:is(:where(.group):has(.cm-focused) *){z-index:1}.scalar-app .group-has-\\\\[\\\\.cm-focused\\\\]\\\\:flex:is(:where(.group):has(.cm-focused) *){display:flex}.scalar-app .group-has-\\\\[\\\\.cm-focused\\\\]\\\\:pr-6:is(:where(.group):has(.cm-focused) *){padding-right:24px}.scalar-app .group-has-\\\\[\\\\.cm-focused\\\\]\\\\:pr-10:is(:where(.group):has(.cm-focused) *){padding-right:40px}.scalar-app .group-has-\\\\[\\\\:focus-visible\\\\]\\\\:hidden:is(:where(.group):has(:focus-visible) *){display:none}.scalar-app .group-has-\\\\[\\\\:focus-visible\\\\]\\\\:opacity-100:is(:where(.group):has(:focus-visible) *){opacity:1}.scalar-app .group-has-\\\\[\\\\:focus-visible\\\\]\\\\/cell\\\\:border-c-accent:is(:where(.group\\\\/cell):has(:focus-visible) *){border-color:var(--scalar-color-accent)}.scalar-app .group-has-\\\\[\\\\:focus-visible\\\\]\\\\/cell\\\\:opacity-100:is(:where(.group\\\\/cell):has(:focus-visible) *){opacity:1}.scalar-app .group-has-\\\\[\\\\:focus-visible\\\\]\\\\/input\\\\:block:is(:where(.group\\\\/input):has(:focus-visible) *){display:block}.scalar-app .group-has-\\\\[input\\\\]\\\\/label\\\\:mr-0:is(:where(.group\\\\/label):has(:is(input)) *){margin-right:0}.scalar-app .group-aria-expanded\\\\/button\\\\:rotate-180:is(:where(.group\\\\/button)[aria-expanded=true] *),.scalar-app .group-aria-expanded\\\\/combobox-button\\\\:rotate-180:is(:where(.group\\\\/combobox-button)[aria-expanded=true] *){rotate:180deg}.scalar-app .group-\\\\[\\\\.alert\\\\]\\\\:bg-b-alert:is(:where(.group).alert *){background-color:var(--scalar-background-alert)}.scalar-app .group-\\\\[\\\\.alert\\\\]\\\\:bg-transparent:is(:where(.group).alert *){background-color:#0000}.scalar-app .group-\\\\[\\\\.alert\\\\]\\\\:shadow-none:is(:where(.group).alert *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .group-\\\\[\\\\.alert\\\\]\\\\:outline-orange:is(:where(.group).alert *){outline-color:var(--scalar-color-orange)}.scalar-app .group-\\\\[\\\\.error\\\\]\\\\:bg-b-danger:is(:where(.group).error *){background-color:var(--scalar-background-danger)}.scalar-app .group-\\\\[\\\\.error\\\\]\\\\:bg-transparent:is(:where(.group).error *){background-color:#0000}.scalar-app .group-\\\\[\\\\.error\\\\]\\\\:text-red:is(:where(.group).error *){color:var(--scalar-color-red)}.scalar-app .group-\\\\[\\\\.error\\\\]\\\\:shadow-none:is(:where(.group).error *){--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.scalar-app .group-\\\\[\\\\.error\\\\]\\\\:outline-red:is(:where(.group).error *){outline-color:var(--scalar-color-red)}.scalar-app .peer-checked\\\\:text-c-1:is(:where(.peer):checked~*){color:var(--scalar-color-1)}.scalar-app .peer-has-\\\\[\\\\.cm-focused\\\\]\\\\:opacity-0:is(:where(.peer):has(.cm-focused)~*){opacity:0}.scalar-app .peer-has-\\\\[\\\\.color-selector\\\\]\\\\:hidden:is(:where(.peer):has(.color-selector)~*){display:none}.scalar-app .before\\\\:pointer-events-none:before{content:var(--tw-content);pointer-events:none}.scalar-app .before\\\\:absolute:before{content:var(--tw-content);position:absolute}.scalar-app .before\\\\:top-0:before{content:var(--tw-content);top:0}.scalar-app .before\\\\:left-3:before{content:var(--tw-content);left:12px}.scalar-app .before\\\\:left-\\\\[calc\\\\(\\\\.75rem_\\\\+_\\\\.5px\\\\)\\\\]:before{content:var(--tw-content);left:calc(.75rem + .5px)}.scalar-app .before\\\\:z-1:before{content:var(--tw-content);z-index:1}.scalar-app .before\\\\:h-\\\\[calc\\\\(100\\\\%_\\\\+_\\\\.5px\\\\)\\\\]:before{content:var(--tw-content);height:calc(100% + .5px)}.scalar-app .before\\\\:w-\\\\[\\\\.5px\\\\]:before{content:var(--tw-content);width:.5px}.scalar-app .before\\\\:bg-border:before{content:var(--tw-content);background-color:var(--scalar-border-color)}.scalar-app .after\\\\:content-\\\\[\\\\\\'\\\\:\\\\\\'\\\\]:after{--tw-content:\":\";content:var(--tw-content)}:is(.scalar-app .\\\\*\\\\:first\\\\:line-clamp-1>*):first-child{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.scalar-app .\\\\*\\\\:first\\\\:rounded-l>*):first-child{border-top-left-radius:var(--scalar-radius);border-bottom-left-radius:var(--scalar-radius)}:is(.scalar-app .\\\\*\\\\:first\\\\:border-t-0>*):first-child,:is(.scalar-app .first\\\\:\\\\*\\\\:border-t-0:first-child>*){border-top-style:var(--tw-border-style);border-top-width:0}:is(.scalar-app .\\\\*\\\\:first\\\\:text-ellipsis>*):first-child{text-overflow:ellipsis}@media(hover:hover){:is(.scalar-app .group-hover\\\\/auth\\\\:\\\\*\\\\:first\\\\:line-clamp-none:is(:where(.group\\\\/auth):hover *)>*):first-child{-webkit-line-clamp:unset;-webkit-box-orient:horizontal;display:block;overflow:visible}}.scalar-app .last\\\\:mb-0:last-child{margin-bottom:0}.scalar-app .last\\\\:rounded-b-lg:last-child{border-bottom-right-radius:var(--scalar-radius-lg);border-bottom-left-radius:var(--scalar-radius-lg)}.scalar-app .last\\\\:border-r-0:last-child{border-right-style:var(--tw-border-style);border-right-width:0}:is(.scalar-app .\\\\*\\\\:last\\\\:rounded-r>*):last-child{border-top-right-radius:var(--scalar-radius);border-bottom-right-radius:var(--scalar-radius)}.scalar-app .last\\\\:before\\\\:h-full:last-child:before{content:var(--tw-content);height:100%}.scalar-app .last-of-type\\\\:first-of-type\\\\:border-b-0:last-of-type:first-of-type{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.scalar-app .focus-within\\\\:z-20:focus-within{z-index:20}.scalar-app .focus-within\\\\:border-\\\\(--scalar-background-3\\\\):focus-within{border-color:var(--scalar-background-3)}.scalar-app .focus-within\\\\:bg-b-1:focus-within{background-color:var(--scalar-background-1)}.scalar-app .focus-within\\\\:text-c-1:focus-within{color:var(--scalar-color-1)}@media(hover:hover){.scalar-app .hover\\\\:cursor-default:hover{cursor:default}.scalar-app .hover\\\\:border-\\\\(--scalar-background-3\\\\):hover{border-color:var(--scalar-background-3)}.scalar-app .hover\\\\:border-inherit:hover{border-color:inherit}.scalar-app .hover\\\\:bg-b-2:hover{background-color:var(--scalar-background-2)}.scalar-app .hover\\\\:bg-b-3:hover{background-color:var(--scalar-background-3)}.scalar-app .hover\\\\:bg-inherit:hover{background-color:inherit}.scalar-app .hover\\\\:bg-sidebar-b-active:hover{background-color:var(--scalar-sidebar-item-active-background,var(--scalar-background-2))}.scalar-app .hover\\\\:whitespace-normal:hover{white-space:normal}.scalar-app .hover\\\\:text-c-1:hover{color:var(--scalar-color-1)}.scalar-app .hover\\\\:text-c-2:hover{color:var(--scalar-color-2)}.scalar-app .hover\\\\:underline:hover{text-decoration-line:underline}.scalar-app .hover\\\\:brightness-75:hover{--tw-brightness:brightness(75%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}.scalar-app .focus\\\\:border-b-1:focus{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--scalar-background-1)}.scalar-app .focus\\\\:text-c-1:focus{color:var(--scalar-color-1)}.scalar-app .focus\\\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.scalar-app .focus-visible\\\\:z-10:focus-visible{z-index:10}.scalar-app .active\\\\:text-c-1:active{color:var(--scalar-color-1)}.scalar-app .disabled\\\\:cursor-default:disabled{cursor:default}.scalar-app .disabled\\\\:text-c-2:disabled{color:var(--scalar-color-2)}.scalar-app .has-\\\\[\\\\.empty-sidebar-item\\\\]\\\\:border-t:has(.empty-sidebar-item){border-top-style:var(--tw-border-style);border-top-width:var(--scalar-border-width)}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:absolute:has(:focus-visible){position:absolute}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:z-1:has(:focus-visible){z-index:1}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:rounded-\\\\[4px\\\\]:has(:focus-visible){border-radius:4px}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:bg-b-1:has(:focus-visible){background-color:var(--scalar-background-1)}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:opacity-100:has(:focus-visible){opacity:1}.scalar-app .has-\\\\[\\\\:focus-visible\\\\]\\\\:outline:has(:focus-visible){outline-style:var(--tw-outline-style);outline-width:1px}@media not all and (min-width:800px){.scalar-app .max-md\\\\:absolute\\\\!{position:absolute!important}.scalar-app .max-md\\\\:w-full\\\\!{width:100%!important}}@media(min-width:600px){.scalar-app .sm\\\\:not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.scalar-app .sm\\\\:order-none{order:0}.scalar-app .sm\\\\:mr-1\\\\.5{margin-right:6px}.scalar-app .sm\\\\:mb-1\\\\.5{margin-bottom:6px}.scalar-app .sm\\\\:ml-1\\\\.5{margin-left:6px}.scalar-app .sm\\\\:flex{display:flex}.scalar-app .sm\\\\:hidden{display:none}.scalar-app .sm\\\\:max-w-max{max-width:max-content}.scalar-app .sm\\\\:min-w-max{min-width:max-content}.scalar-app .sm\\\\:flex-col{flex-direction:column}.scalar-app .sm\\\\:flex-row{flex-direction:row}.scalar-app .sm\\\\:justify-between{justify-content:space-between}.scalar-app .sm\\\\:gap-px{gap:1px}.scalar-app .sm\\\\:rounded{border-radius:var(--scalar-radius)}.scalar-app .sm\\\\:rounded-lg{border-radius:var(--scalar-radius-lg)}.scalar-app .sm\\\\:px-2{padding-inline:8px}.scalar-app .sm\\\\:px-3{padding-inline:12px}.scalar-app .sm\\\\:py-1\\\\.5{padding-block:6px}:is(.scalar-app .sm\\\\:\\\\*\\\\:rounded-lg>*){border-radius:var(--scalar-radius-lg)}}@media(min-width:800px){.scalar-app .md\\\\:right-10{right:40px}.scalar-app .md\\\\:bottom-10{bottom:40px}.scalar-app .md\\\\:mx-auto{margin-inline:auto}.scalar-app .md\\\\:-ml-1\\\\.25{margin-left:-5px}.scalar-app .md\\\\:ml-1\\\\.5{margin-left:6px}.scalar-app .md\\\\:block{display:block}.scalar-app .md\\\\:flex{display:flex}.scalar-app .md\\\\:grid{display:grid}.scalar-app .md\\\\:w-full{width:100%}.scalar-app .md\\\\:max-w-\\\\[720px\\\\]{max-width:720px}.scalar-app .md\\\\:min-w-fit{min-width:fit-content}.scalar-app .md\\\\:flex-none{flex:none}.scalar-app .md\\\\:translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .md\\\\:translate-y-0{--tw-translate-y:0px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scalar-app .md\\\\:grid-cols-\\\\[1fr_720px_1fr\\\\]{grid-template-columns:1fr 720px 1fr}.scalar-app .md\\\\:flex-row{flex-direction:row}.scalar-app .md\\\\:border-r{border-right-style:var(--tw-border-style);border-right-width:var(--scalar-border-width)}.scalar-app .md\\\\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.scalar-app .md\\\\:p-1\\\\.5{padding:6px}.scalar-app .md\\\\:px-0{padding-inline:0}.scalar-app .md\\\\:px-1\\\\.5{padding-inline:6px}.scalar-app .md\\\\:px-2{padding-inline:8px}.scalar-app .md\\\\:px-2\\\\.5{padding-inline:10px}.scalar-app .md\\\\:px-4{padding-inline:16px}.scalar-app .md\\\\:px-\\\\[18px\\\\]{padding-inline:18px}.scalar-app .md\\\\:py-2\\\\.5{padding-block:10px}.scalar-app .md\\\\:pb-2\\\\.5{padding-bottom:10px}.scalar-app .md\\\\:pb-\\\\[37px\\\\]{padding-bottom:37px}.scalar-app .md\\\\:pl-0{padding-left:0}:is(.scalar-app .md\\\\:\\\\*\\\\:border-t-0>*){border-top-style:var(--tw-border-style);border-top-width:0}}@media(min-width:1000px){.scalar-app .lg\\\\:order-none{order:0}.scalar-app .lg\\\\:-mr-1{margin-right:-4px}.scalar-app .lg\\\\:mb-0{margin-bottom:0}.scalar-app .lg\\\\:flex{display:flex}.scalar-app .lg\\\\:min-h-header{min-height:48px}.scalar-app .lg\\\\:w-auto{width:auto}.scalar-app .lg\\\\:max-w-\\\\[580px\\\\]{max-width:580px}.scalar-app .lg\\\\:min-w-\\\\[580px\\\\]{min-width:580px}.scalar-app .lg\\\\:flex-1{flex:1}.scalar-app .lg\\\\:p-0{padding:0}.scalar-app .lg\\\\:p-1{padding:4px}.scalar-app .lg\\\\:px-1{padding-inline:4px}.scalar-app .lg\\\\:px-2\\\\.5{padding-inline:10px}.scalar-app .lg\\\\:pt-1{padding-top:4px}.scalar-app .lg\\\\:pr-24{padding-right:96px}}@media(min-width:1200px){.scalar-app .xl\\\\:\\\\!flex{display:flex!important}.scalar-app .xl\\\\:flex{display:flex}.scalar-app .xl\\\\:hidden{display:none}.scalar-app .xl\\\\:h-fit{height:fit-content}.scalar-app .xl\\\\:h-full{height:100%}.scalar-app .xl\\\\:min-h-header{min-height:48px}.scalar-app .xl\\\\:max-w-\\\\[720px\\\\]{max-width:720px}.scalar-app .xl\\\\:min-w-0{min-width:0}.scalar-app .xl\\\\:min-w-\\\\[720px\\\\]{min-width:720px}.scalar-app .xl\\\\:flex-row{flex-direction:row}.scalar-app .xl\\\\:overflow-auto{overflow:auto}.scalar-app .xl\\\\:overflow-hidden{overflow:hidden}.scalar-app .xl\\\\:rounded-none{border-radius:0}.scalar-app .xl\\\\:pr-0\\\\.5{padding-right:2px}.scalar-app .xl\\\\:pl-2{padding-left:8px}:is(.scalar-app .\\\\*\\\\:xl\\\\:border-t-0>*){border-top-style:var(--tw-border-style);border-top-width:0}:is(.scalar-app .\\\\*\\\\:xl\\\\:border-l>*){border-left-style:var(--tw-border-style);border-left-width:var(--scalar-border-width)}:is(.scalar-app .\\\\*\\\\:first\\\\:xl\\\\:border-l-0>*):first-child{border-left-style:var(--tw-border-style);border-left-width:0}}.scalar-app .dark\\\\:bg-b-2:where(.dark-mode,.dark-mode *){background-color:var(--scalar-background-2)}@media(hover:hover){.scalar-app .hover\\\\:dark\\\\:bg-b-2:hover:where(.dark-mode,.dark-mode *){background-color:var(--scalar-background-2)}}.scalar-app .ui-open\\\\:rotate-90[data-headlessui-state~=open],:where([data-headlessui-state~=open]) :is(.scalar-app .ui-open\\\\:rotate-90){rotate:90deg}.scalar-app .ui-open\\\\:rotate-180[data-headlessui-state~=open],:where([data-headlessui-state~=open]) :is(.scalar-app .ui-open\\\\:rotate-180){rotate:180deg}.scalar-app .last\\\\:ui-open\\\\:border-b-0:last-child[data-headlessui-state~=open],:where([data-headlessui-state~=open]) .scalar-app .last\\\\:ui-open\\\\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.scalar-app .ui-not-open\\\\:hidden[data-headlessui-state]:not([data-headlessui-state~=open]),:where([data-headlessui-state]:not([data-headlessui-state~=open])) :is(.scalar-app .ui-not-open\\\\:hidden):not([data-headlessui-state]){display:none}.scalar-app .ui-not-open\\\\:rotate-0[data-headlessui-state]:not([data-headlessui-state~=open]),:where([data-headlessui-state]:not([data-headlessui-state~=open])) :is(.scalar-app .ui-not-open\\\\:rotate-0):not([data-headlessui-state]){rotate:none}.scalar-app .ui-checked\\\\:bg-b-3[data-headlessui-state~=checked],:where([data-headlessui-state~=checked]) :is(.scalar-app .ui-checked\\\\:bg-b-3){background-color:var(--scalar-background-3)}.scalar-app .ui-active\\\\:bg-b-2[data-headlessui-state~=active],:where([data-headlessui-state~=active]) :is(.scalar-app .ui-active\\\\:bg-b-2),:is(.scalar-app .ui-active\\\\:\\\\*\\\\:bg-b-2[data-headlessui-state~=active]>*),:is(:where([data-headlessui-state~=active]) :is(.scalar-app .ui-active\\\\:\\\\*\\\\:bg-b-2)>*){background-color:var(--scalar-background-2)}@media(max-width:720px)and (max-height:480px){.scalar-app .zoomed\\\\:static{position:static}.scalar-app .zoomed\\\\:p-1{padding:4px}}.app-platform-mac :is(.scalar-app .mac\\\\:pl-\\\\[72px\\\\]){padding-left:72px}@property --tw-scale-x{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-y{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-z{syntax:\"*\";inherits:false;initial-value:1}@property --tw-space-x-reverse{syntax:\"*\";inherits:false;initial-value:0}.nav-item[data-v-507381a3]{cursor:pointer;border-radius:var(--scalar-radius-lg);background:var(--scalar-background-3);border:var(--scalar-border-width)solid var(--scalar-background-2);color:var(--scalar-color-3);flex:1;justify-content:center;align-items:center;min-width:0;padding:4.5px;display:flex;position:relative;overflow:hidden}.dark-mode .nav-item[data-v-507381a3]{background:var(--scalar-background-2)}@supports (color:color-mix(in lab,red,red)){.dark-mode .nav-item[data-v-507381a3]{background:color-mix(in srgb,var(--scalar-background-2),transparent)}}.nav-item-icon-copy[data-v-507381a3]{white-space:nowrap;max-width:100%;-webkit-mask-image:linear-gradient(to left,transparent 0,var(--scalar-background-2)20px);mask-image:linear-gradient(to left,transparent 0,var(--scalar-background-2)20px);overflow:hidden}.nav-item:hover .nav-item-icon-copy[data-v-507381a3]{-webkit-mask-image:linear-gradient(to left,transparent 20px,var(--scalar-background-2)40px);mask-image:linear-gradient(to left,transparent 20px,var(--scalar-background-2)40px)}.nav-item-copy[data-v-507381a3]{max-width:calc(100% - 20px)}.nav-item[data-v-507381a3]:hover{color:var(--scalar-color-1)}.nav-item__active[data-v-507381a3]{background-color:var(--scalar-background-1);color:var(--scalar-color-1);border-color:var(--scalar-border-color)}.dark-mode .nav-item__active[data-v-507381a3]{background-color:var(--scalar-background-2)}.nav-item-close[data-v-507381a3]{border-radius:var(--scalar-radius);stroke-width:1.5px;max-width:20px;color:var(--scalar-color-3);opacity:0;background:0 0;margin-left:-20px;padding:2px;position:absolute;right:3px}.nav-item:hover .nav-item-close[data-v-507381a3]{opacity:1}.nav-item-close[data-v-507381a3]:hover{background-color:var(--scalar-background-4)}.nav-item__active .nav-item-close[data-v-507381a3]:hover{background-color:var(--scalar-background-2)}.download-app-button[data-v-cb45fa05]{box-shadow:0 0 0 .5px var(--scalar-border-color);background:linear-gradient(#ffffffbf,#00000009)}.dark-mode .download-app-button[data-v-cb45fa05]{background:linear-gradient(#ffffff1a,#00000026)}.download-app-button[data-v-cb45fa05]:hover{background:linear-gradient(#00000009,#ffffffbf)}.dark-mode .download-app-button[data-v-cb45fa05]:hover{background:linear-gradient(#00000026,#ffffff1a)}.http-bg-gradient[data-v-076b14a1]{background:linear-gradient(#ffffffbf,#00000009)}.http-bg-gradient[data-v-076b14a1]:hover{background:linear-gradient(#00000009,#ffffffbf)}.dark-mode .http-bg-gradient[data-v-076b14a1]{background:linear-gradient(#ffffff09,#00000026)}.dark-mode .http-bg-gradient[data-v-076b14a1]:hover{background:linear-gradient(#00000026,#ffffff09)}.scroll-timeline-x[data-v-e0578855]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;-ms-overflow-style:none;scrollbar-width:none;overflow:auto}.commandmenu[data-v-f2bbd082]{box-shadow:var(--scalar-shadow-2);border-radius:var(--scalar-radius-lg);background-color:var(--scalar-background-1);opacity:0;width:100%;max-width:580px;max-height:60dvh;margin:12px;animation:.3s ease-in-out .1s forwards fadeincommandmenu-f2bbd082;position:fixed;top:150px;left:50%;transform:translate(-50%,10px)}.commandmenu-overlay[data-v-f2bbd082]{cursor:pointer;background:#0003;animation:.3s ease-in-out forwards fadeincommand-f2bbd082;position:fixed;inset:0}@keyframes fadeincommand-f2bbd082{0%{opacity:0}to{opacity:1}}@keyframes fadeincommandmenu-f2bbd082{0%{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%)}}.scalar .scalar-app-layout[data-v-45e9730e]{background:var(--scalar-background-1);opacity:0;border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:8px;width:100%;max-width:1390px;height:calc(100% - 120px);margin:auto;animation:.35s forwards scalarapiclientfadein-45e9730e;position:relative;overflow:hidden}@media(max-width:720px)and (max-height:480px){.scalar .scalar-app-layout[data-v-45e9730e]{height:100%;max-height:90svh}}@keyframes scalarapiclientfadein-45e9730e{0%{opacity:0}to{opacity:1}}.scalar .scalar-app-exit[data-v-45e9730e]{cursor:pointer;z-index:-1;background:#00000038;width:100vw;height:100vh;transition:all .3s ease-in-out;animation:.35s forwards scalardrawerexitfadein-45e9730e;position:fixed;top:0;left:0}.dark-mode .scalar .scalar-app-exit[data-v-45e9730e]{background:#00000073}.scalar .scalar-app-exit[data-v-45e9730e]:before{text-align:center;color:#fff;opacity:.6;font-family:sans-serif;font-size:30px;font-weight:100;line-height:50px;position:absolute;top:0;right:12px}.scalar .scalar-app-exit[data-v-45e9730e]:hover:before{opacity:1}@keyframes scalardrawerexitfadein-45e9730e{0%{opacity:0}to{opacity:1}}.scalar-container[data-v-45e9730e]{visibility:visible;z-index:10000;justify-content:center;align-items:center;width:100%;height:100%;display:flex;position:fixed;top:0;bottom:0;left:0;overflow:hidden}.scalar .url-form-input[data-v-45e9730e]{min-height:auto!important}.scalar .scalar-container[data-v-45e9730e]{line-height:normal}.scalar .scalar-app-header span[data-v-45e9730e]{color:var(--scalar-color-3)}.scalar .scalar-app-header a[data-v-45e9730e]{color:var(--scalar-color-1)}.scalar .scalar-app-header a[data-v-45e9730e]:hover{text-decoration:underline}.scalar-activate[data-v-45e9730e]{cursor:pointer;align-items:center;gap:6px;width:fit-content;margin:0 .75rem .75rem auto;font-size:.875rem;font-weight:600;line-height:24px;display:flex}.scalar-activate-button[data-v-45e9730e]{color:var(--scalar-color-blue);appearance:none;background:0 0;border:none;outline:none;align-items:center;gap:6px;padding:0 .5rem;display:flex}.scalar-activate:hover .scalar-activate-button[data-v-45e9730e]{background:var(--scalar-background-3);border-radius:3px}.open-api-client-button[data-v-f016469d]{cursor:pointer;text-align:center;white-space:nowrap;width:100%;height:31px;font-size:var(--scalar-mini);font-weight:var(--scalar-semibold);border-radius:var(--scalar-radius);box-shadow:0 0 0 .5px var(--scalar-border-color);color:var(--scalar-sidebar-color-1);justify-content:center;align-items:center;gap:6px;padding:9px 12px;line-height:1.385;text-decoration:none;display:flex}.open-api-client-button[data-v-f016469d]:hover{background:var(--scalar-sidebar-item-hover-background,var(--scalar-background-2))}[data-v-103d9d56] .cm-editor{background:0 0;outline:none;height:100%;padding:0}[data-v-103d9d56] .cm-placeholder{color:var(--scalar-color-3)}[data-v-103d9d56] .cm-content{font-family:var(--scalar-font-code);font-size:var(--scalar-small);max-height:20px;padding:8px 0}[data-v-103d9d56] .cm-tooltip{filter:brightness(var(--scalar-lifted-brightness));border-radius:var(--scalar-radius);box-shadow:var(--scalar-shadow-2);background:0 0!important;border:none!important;outline:none!important;overflow:hidden!important}[data-v-103d9d56] .cm-tooltip-autocomplete ul li{padding:3px 6px!important}[data-v-103d9d56] .cm-completionIcon-type:after{color:var(--scalar-color-3)!important}[data-v-103d9d56] .cm-tooltip-autocomplete ul li[aria-selected]{background:var(--scalar-background-2)!important;color:var(--scalar-color-1)!important}[data-v-103d9d56] .cm-tooltip-autocomplete ul{position:relative;padding:6px!important}[data-v-103d9d56] .cm-tooltip-autocomplete ul li:hover{border-radius:3px;color:var(--scalar-color-1)!important;background:var(--scalar-background-3)!important}[data-v-103d9d56] .cm-activeLine,[data-v-103d9d56] .cm-activeLineGutter{background-color:#0000}[data-v-103d9d56] .cm-selectionMatch,[data-v-103d9d56] .cm-matchingBracket{border-radius:var(--scalar-radius);background:var(--scalar-background-4)!important}[data-v-103d9d56] .cm-css-color-picker-wrapper{outline:1px solid var(--scalar-background-3);border-radius:3px;display:inline-flex;overflow:hidden}[data-v-103d9d56] .cm-gutters{color:var(--scalar-color-3);font-size:var(--scalar-small);background-color:#0000;border-right:none;border-radius:0 0 0 3px;line-height:22px}[data-v-103d9d56] .cm-gutters:before{content:\"\";border-radius:var(--scalar-radius)0 0 var(--scalar-radius);background-color:var(--scalar-background-1);width:calc(100% - 2px);height:calc(100% - 4px);position:absolute;top:2px;left:2px}[data-v-103d9d56] .cm-gutterElement{justify-content:flex-end;align-items:center;display:flex;position:relative;font-family:var(--scalar-font-code)!important;padding-left:0!important;padding-right:6px!important}[data-v-103d9d56] .cm-lineNumbers .cm-gutterElement{min-width:fit-content}[data-v-103d9d56] .cm-gutter+.cm-gutter :not(.cm-foldGutter) .cm-gutterElement{padding-left:0!important}[data-v-103d9d56] .cm-scroller{overflow:auto}.line-wrapping[data-v-103d9d56]:focus-within .cm-content{white-space:break-spaces;word-break:break-all;min-height:fit-content;padding:3px 6px;display:inline-table}.address-bar-history-button[data-v-a93fa60f]:hover{background:var(--scalar-background-3)}.address-bar-history-button[data-v-a93fa60f]:focus-within{background:var(--scalar-background-2)}.description[data-v-92012388] .markdown{font-weight:var(--scalar-semibold);color:var(--scalar-color--1);padding:0;display:block}.description[data-v-92012388] .markdown>:first-child{margin-top:0}[data-v-cb2a35da] .cm-editor{outline:none;width:100%;height:100%}[data-v-cb2a35da] .cm-line{padding:0}[data-v-cb2a35da] .cm-content{font-size:var(--scalar-small);align-items:center;padding:0;display:flex}.scroll-timeline-x[data-v-cb2a35da]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;-ms-overflow-style:none}.scroll-timeline-x-hidden[data-v-cb2a35da]{overflow-x:auto}.scroll-timeline-x-hidden[data-v-cb2a35da] .cm-scroller{scrollbar-width:none;-ms-overflow-style:none;padding-right:20px;overflow:auto}.scroll-timeline-x-hidden[data-v-cb2a35da]::-webkit-scrollbar{width:0;height:0;display:none}.scroll-timeline-x-hidden[data-v-cb2a35da] .cm-scroller::-webkit-scrollbar{width:0;height:0;display:none}.scroll-timeline-x-address[data-v-cb2a35da]{scrollbar-width:none;line-height:27px}.scroll-timeline-x-address[data-v-cb2a35da]:after{content:\"\";cursor:text;width:24px;height:100%;position:absolute;right:0}.scroll-timeline-x-address[data-v-cb2a35da]:empty:before{content:\"Enter URL or cURL request\";color:var(--scalar-color-3);pointer-events:none}.fade-left[data-v-cb2a35da],.fade-right[data-v-cb2a35da]{content:\"\";pointer-events:none;z-index:1;height:100%;animation-name:fadein-cb2a35da;animation-duration:1ms;animation-direction:reverse;animation-timeline:--scroll-timeline;position:sticky}.fade-left[data-v-cb2a35da]{background:linear-gradient(-90deg,var(--scalar-address-bar-bg)0%,var(--scalar-address-bar-bg)30%,var(--scalar-address-bar-bg)100%)}@supports (color:color-mix(in lab,red,red)){.fade-left[data-v-cb2a35da]{background:linear-gradient(-90deg,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 100%)0%,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 20%)30%,var(--scalar-address-bar-bg)100%)}}.fade-left[data-v-cb2a35da]{min-width:6px;animation-direction:normal;left:-1px}.fade-right[data-v-cb2a35da]{background:linear-gradient(90deg,var(--scalar-address-bar-bg)0%,var(--scalar-address-bar-bg)30%,var(--scalar-address-bar-bg)100%)}@supports (color:color-mix(in lab,red,red)){.fade-right[data-v-cb2a35da]{background:linear-gradient(90deg,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 100%)0%,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 20%)30%,var(--scalar-address-bar-bg)100%)}}.fade-right[data-v-cb2a35da]{min-width:24px;right:-1px}@keyframes fadein-cb2a35da{0%{opacity:0}1%{opacity:1}}.address-bar-bg-states[data-v-cb2a35da]{--scalar-address-bar-bg:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.address-bar-bg-states[data-v-cb2a35da]{--scalar-address-bar-bg:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.address-bar-bg-states[data-v-cb2a35da]{background:var(--scalar-address-bar-bg)}.address-bar-bg-states[data-v-cb2a35da]:has(.cm-focused){--scalar-address-bar-bg:var(--scalar-background-1);border-color:var(--scalar-border-color);outline:1px solid var(--scalar-color-accent)}.address-bar-bg-states:has(.cm-focused) .fade-left[data-v-cb2a35da],.address-bar-bg-states:has(.cm-focused) .fade-right[data-v-cb2a35da]{--scalar-address-bar-bg:var(--scalar-background-1)}.sidebar-height[data-v-dcff7b49]{min-height:100%}@media(min-width:800px){.sidebar-mask[data-v-dcff7b49]{-webkit-mask-image:linear-gradient(0,transparent 0,transparent 0,var(--scalar-background-2)30px);mask-image:linear-gradient(0,transparent 0,transparent 0,var(--scalar-background-2)30px)}}.resizer[data-v-dcff7b49]{cursor:col-resize;border-right:2px solid #0000;width:5px;transition:border-right-color .3s;position:absolute;top:0;bottom:0;right:0}.resizer[data-v-dcff7b49]:hover,.dragging .resizer[data-v-dcff7b49]{border-right-color:var(--scalar-background-3)}.dragging[data-v-dcff7b49]{cursor:col-resize}.dragging[data-v-dcff7b49]:before{content:\"\";width:100%;height:100%;display:block;position:absolute}[data-v-c1a50a6e] .cm-editor{padding:0}[data-v-c1a50a6e] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-c1a50a6e] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-c1a50a6e] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-c1a50a6e] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-c1a50a6e] .cm-line{text-overflow:ellipsis;word-break:break-word;padding:0;overflow:hidden}.required[data-v-c1a50a6e]:after{content:\"Required\"}input[data-v-c1a50a6e]::placeholder{color:var(--scalar-color-3)}.scalar-password-input[data-v-c1a50a6e]{text-security:disc;-webkit-text-security:disc;-moz-text-security:disc}@media(min-width:800px){.has-no-import-url,.has-import-url{contain:paint;max-width:100dvw;overflow-x:hidden}.has-no-import-url .scalar-client>main{opacity:1;background:var(--scalar-background-1);animation:.3s ease-in-out forwards transform-restore-layout}.has-import-url .scalar-client>main{opacity:0;border:var(--scalar-border-width)solid var(--scalar-border-color);z-index:10000;border-radius:12px;animation:.3s ease-in-out forwards transform-fade-layout;overflow:hidden;transform:scale(.85)translate(calc(50dvw + 80px))}.has-import-url .scalar-client .sidenav{display:none}.has-no-import-url .scalar-app,.has-import-url .scalar-app{background:var(--scalar-background-1)!important}}@keyframes transform-fade-layout{0%{opacity:0;transform:scale(.85)translate(calc(50dvw + 80px),10px)}to{opacity:1;transform:scale(.85)translate(calc(50dvw + 80px))}}@keyframes transform-restore-layout{0%{opacity:1;transform:scale(.85)translate(calc(50dvw + 80px))}to{opacity:1;transform:scale(1)translate(0)}}.openapi-color{color:var(--scalar-color-green)}.section-flare{position:fixed;top:0;right:-50dvw}.address-bar-history-button[data-v-8cf04803]:hover{background:var(--scalar-background-3)}.address-bar-history-button[data-v-8cf04803]:focus-within{background:var(--scalar-background-2)}.description[data-v-1b7a32a4] .markdown{font-weight:var(--scalar-semibold);color:var(--scalar-color--1);padding:0;display:block}.description[data-v-1b7a32a4] .markdown>:first-child{margin-top:0}[data-v-d6129762] .cm-editor{background:0 0;outline:none;height:100%;padding:0}[data-v-d6129762] .cm-placeholder{color:var(--scalar-color-3)}[data-v-d6129762] .cm-content{font-family:var(--scalar-font-code);font-size:var(--scalar-small);max-height:20px;padding:8px 0}[data-v-d6129762] .cm-tooltip{filter:brightness(var(--scalar-lifted-brightness));border-radius:var(--scalar-radius);box-shadow:var(--scalar-shadow-2);background:0 0!important;border:none!important;outline:none!important;overflow:hidden!important}[data-v-d6129762] .cm-tooltip-autocomplete ul li{padding:3px 6px!important}[data-v-d6129762] .cm-completionIcon-type:after{color:var(--scalar-color-3)!important}[data-v-d6129762] .cm-tooltip-autocomplete ul li[aria-selected]{background:var(--scalar-background-2)!important;color:var(--scalar-color-1)!important}[data-v-d6129762] .cm-tooltip-autocomplete ul{position:relative;padding:6px!important}[data-v-d6129762] .cm-tooltip-autocomplete ul li:hover{border-radius:3px;color:var(--scalar-color-1)!important;background:var(--scalar-background-3)!important}[data-v-d6129762] .cm-activeLine,[data-v-d6129762] .cm-activeLineGutter{background-color:#0000}[data-v-d6129762] .cm-selectionMatch,[data-v-d6129762] .cm-matchingBracket{border-radius:var(--scalar-radius);background:var(--scalar-background-4)!important}[data-v-d6129762] .cm-css-color-picker-wrapper{outline:1px solid var(--scalar-background-3);border-radius:3px;display:inline-flex;overflow:hidden}[data-v-d6129762] .cm-gutters{color:var(--scalar-color-3);font-size:var(--scalar-small);background-color:#0000;border-right:none;border-radius:0 0 0 3px;line-height:22px}[data-v-d6129762] .cm-gutters:before{content:\"\";border-radius:var(--scalar-radius)0 0 var(--scalar-radius);background-color:var(--scalar-background-1);width:calc(100% - 2px);height:calc(100% - 4px);position:absolute;top:2px;left:2px}[data-v-d6129762] .cm-gutterElement{justify-content:flex-end;align-items:center;display:flex;position:relative;font-family:var(--scalar-font-code)!important;padding-left:0!important;padding-right:6px!important}[data-v-d6129762] .cm-lineNumbers .cm-gutterElement{min-width:fit-content}[data-v-d6129762] .cm-gutter+.cm-gutter :not(.cm-foldGutter) .cm-gutterElement{padding-left:0!important}[data-v-d6129762] .cm-scroller{overflow:auto}.line-wrapping[data-v-d6129762]:focus-within .cm-content{white-space:break-spaces;word-break:break-all;min-height:fit-content;padding:3px 6px;display:inline-table}.cm-pill{--tw-bg-base:var(--scalar-color-1);color:var(--tw-bg-base);font-size:var(--scalar-small);border-radius:30px;padding:0 9px;display:inline-block;background:var(--tw-bg-base)!important}@supports (color:color-mix(in lab,red,red)){.cm-pill{background:color-mix(in srgb,var(--tw-bg-base),transparent 94%)!important}}.cm-pill.bg-grey{background:var(--scalar-background-3)!important}.dark-mode .cm-pill{background:var(--tw-bg-base)!important}@supports (color:color-mix(in lab,red,red)){.dark-mode .cm-pill{background:color-mix(in srgb,var(--tw-bg-base),transparent 90%)!important}}.cm-pill:first-of-type{margin-left:0}.cm-editor .cm-widgetBuffer{display:none}.cm-foldPlaceholder:hover{color:var(--scalar-color-1)}.cm-foldGutter .cm-gutterElement{font-size:var(--scalar-heading-4);padding:2px!important}.cm-foldGutter .cm-gutterElement:first-of-type{display:none}.cm-foldGutter .cm-gutterElement .cm-foldMarker{padding:2px}.cm-foldGutter .cm-gutterElement:hover .cm-foldMarker{background:var(--scalar-background-2);border-radius:var(--scalar-radius);color:var(--scalar-color-1)}[data-v-a165ba6c] .cm-editor{outline:none;width:100%;height:100%}[data-v-a165ba6c] .cm-line{padding:0}[data-v-a165ba6c] .cm-content{font-size:var(--scalar-small);align-items:center;padding:0;display:flex}.scroll-timeline-x[data-v-a165ba6c]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;-ms-overflow-style:none}.scroll-timeline-x-hidden[data-v-a165ba6c]{overflow-x:auto}.scroll-timeline-x-hidden[data-v-a165ba6c] .cm-scroller{scrollbar-width:none;-ms-overflow-style:none;padding-right:20px;overflow:auto}.scroll-timeline-x-hidden[data-v-a165ba6c]::-webkit-scrollbar{width:0;height:0;display:none}.scroll-timeline-x-hidden[data-v-a165ba6c] .cm-scroller::-webkit-scrollbar{width:0;height:0;display:none}.scroll-timeline-x-address[data-v-a165ba6c]{scrollbar-width:none;line-height:27px}.scroll-timeline-x-address[data-v-a165ba6c]:after{content:\"\";cursor:text;width:24px;height:100%;position:absolute;right:0}.scroll-timeline-x-address[data-v-a165ba6c]:empty:before{content:\"Enter URL or cURL request\";color:var(--scalar-color-3);pointer-events:none}.fade-left[data-v-a165ba6c],.fade-right[data-v-a165ba6c]{content:\"\";pointer-events:none;z-index:1;height:100%;animation-name:fadein-a165ba6c;animation-duration:1ms;animation-direction:reverse;animation-timeline:--scroll-timeline;position:sticky}.fade-left[data-v-a165ba6c]{background:linear-gradient(-90deg,var(--scalar-address-bar-bg)0%,var(--scalar-address-bar-bg)30%,var(--scalar-address-bar-bg)100%)}@supports (color:color-mix(in lab,red,red)){.fade-left[data-v-a165ba6c]{background:linear-gradient(-90deg,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 100%)0%,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 20%)30%,var(--scalar-address-bar-bg)100%)}}.fade-left[data-v-a165ba6c]{min-width:6px;animation-direction:normal;left:-1px}.fade-right[data-v-a165ba6c]{background:linear-gradient(90deg,var(--scalar-address-bar-bg)0%,var(--scalar-address-bar-bg)30%,var(--scalar-address-bar-bg)100%)}@supports (color:color-mix(in lab,red,red)){.fade-right[data-v-a165ba6c]{background:linear-gradient(90deg,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 100%)0%,color-mix(in srgb,var(--scalar-address-bar-bg),transparent 20%)30%,var(--scalar-address-bar-bg)100%)}}.fade-right[data-v-a165ba6c]{min-width:24px;right:-1px}@keyframes fadein-a165ba6c{0%{opacity:0}1%{opacity:1}}.address-bar-bg-states[data-v-a165ba6c]{--scalar-address-bar-bg:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.address-bar-bg-states[data-v-a165ba6c]{--scalar-address-bar-bg:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.address-bar-bg-states[data-v-a165ba6c]{background:var(--scalar-address-bar-bg)}.address-bar-bg-states[data-v-a165ba6c]:has(.cm-focused){--scalar-address-bar-bg:var(--scalar-background-1);border-color:var(--scalar-border-color);outline-width:1px;outline-style:solid}.address-bar-bg-states:has(.cm-focused) .fade-left[data-v-a165ba6c],.address-bar-bg-states:has(.cm-focused) .fade-right[data-v-a165ba6c]{--scalar-address-bar-bg:var(--scalar-background-1)}.app-exit-button[data-v-d73c7e3d]{color:#fff;background:#0000001a}.app-exit-button[data-v-d73c7e3d]:hover{background:#ffffff1a}.fade-request-section-content[data-v-f97cc68c]{background:linear-gradient(to left,var(--scalar-background-1)64%,transparent)}.filter-hover[data-v-f97cc68c]{height:100%;padding-left:24px;padding-right:39px;transition:width 0s ease-in-out .2s;position:absolute;right:0;overflow:hidden}.filter-hover[data-v-f97cc68c]:hover,.filter-hover[data-v-f97cc68c]:has(:focus-visible){z-index:10;width:100%}.filter-hover[data-v-f97cc68c]:before{content:\"\";background-color:var(--scalar-background-1);opacity:0;pointer-events:none;width:100%;height:fit-content;transition:all .3s ease-in-out;position:absolute;top:0;left:0}.filter-hover-item[data-v-f97cc68c]{opacity:0}.filter-hover-item[data-v-f97cc68c]:not(:last-of-type){transform:translateY(3px)}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]{transition:opacity .2s ease-in-out,transform .2s ease-in-out}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:last-of-type{transition-delay:50ms}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(2){transition-delay:.1s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(3){transition-delay:.15s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(4){transition-delay:.2s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(5){transition-delay:.25s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(6){transition-delay:.3s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c]:nth-last-of-type(7){transition-delay:.35s}.filter-hover:hover .filter-hover-item[data-v-f97cc68c],.filter-hover:has(:focus-visible) .filter-hover-item[data-v-f97cc68c]{opacity:1;transform:translateZ(0)}.filter-hover[data-v-f97cc68c]:hover:before,.filter-hover[data-v-f97cc68c]:has(:focus-visible):before{opacity:.9;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.filter-button[data-v-f97cc68c]{top:50%;transform:translateY(-50%)}.context-bar-group:hover .context-bar-group-hover\\\\:text-c-1[data-v-f97cc68c],.context-bar-group:has(:focus-visible) .context-bar-group-hover\\\\:text-c-1[data-v-f97cc68c]{--tw-text-opacity:1;color:rgb(var(--scalar-color-1)/var(--tw-text-opacity))}.context-bar-group:hover .context-bar-group-hover\\\\:hidden[data-v-f97cc68c],.context-bar-group:has(:focus-visible) .context-bar-group-hover\\\\:hidden[data-v-f97cc68c]{display:none}.schema>span[data-v-f2ab7aa3]:not(:first-child):before{content:\"·\";margin:0 .5ch;display:block}.schema>span[data-v-f2ab7aa3]{white-space:nowrap;display:flex}[data-v-1434cda7] .cm-editor{padding:0}[data-v-1434cda7] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-1434cda7] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-1434cda7] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-1434cda7] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-1434cda7] .cm-line{text-overflow:ellipsis;padding:0;overflow:hidden}.filemask[data-v-1434cda7]{-webkit-mask-image:linear-gradient(to right,transparent 0,var(--scalar-background-2)20px);mask-image:linear-gradient(to right,transparent 0,var(--scalar-background-2)20px)}[data-v-cbed7b55] .cm-content{font-size:var(--scalar-small)}.form-group[data-v-43df1726]{margin-bottom:1rem}.modal-actions[data-v-43df1726]{justify-content:flex-end;gap:1rem;display:flex}[data-v-3157c3c7] .cm-editor{padding:0}[data-v-3157c3c7] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-3157c3c7] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-3157c3c7] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-3157c3c7] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-3157c3c7] .cm-line{text-overflow:ellipsis;word-break:break-word;padding:0;overflow:hidden}.required[data-v-3157c3c7]:after{content:\"Required\"}input[data-v-3157c3c7]::placeholder{color:var(--scalar-color-3)}.scalar-password-input[data-v-3157c3c7]{text-security:disc;-webkit-text-security:disc;-moz-text-security:disc}.request-section-content[data-v-13f5983a]{--scalar-border-width:.5px}.request-section-content-filter[data-v-13f5983a]{box-shadow:0 -10px 0 10px var(--scalar-background-1)}.request-item:focus-within .request-meta-buttons[data-v-13f5983a]{opacity:1}.group-hover-input[data-v-13f5983a]{border-width:var(--scalar-border-width);border-color:#0000}.group:hover .group-hover-input[data-v-13f5983a]{background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.group:hover .group-hover-input[data-v-13f5983a]{background:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.group:hover .group-hover-input[data-v-13f5983a]{border-color:var(--scalar-border-color)}.group-hover-input[data-v-13f5983a]:focus{border-color:var(--scalar-border-color)!important;background:0 0!important}.light-mode .bg-preview[data-v-c02b5bb8]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' width=\\'16\\' height=\\'16\\' fill=\\'%23000\\' fill-opacity=\\'10%25\\'%3E%3Crect width=\\'8\\' height=\\'8\\' /%3E%3Crect x=\\'8\\' y=\\'8\\' width=\\'8\\' height=\\'8\\' /%3E%3C/svg%3E\")}.dark-mode .bg-preview[data-v-c02b5bb8]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' width=\\'16\\' height=\\'16\\' fill=\\'%23FFF\\' fill-opacity=\\'10%25\\'%3E%3Crect width=\\'8\\' height=\\'8\\' /%3E%3Crect x=\\'8\\' y=\\'8\\' width=\\'8\\' height=\\'8\\' /%3E%3C/svg%3E\")}[data-v-10022578] .cm-editor{font-size:var(--scalar-small);background-color:#0000;outline:none}[data-v-10022578] .cm-gutters{background-color:var(--scalar-background-1);border-radius:var(--scalar-radius)0 0 var(--scalar-radius)}.body-raw[data-v-10022578] .cm-scroller{min-width:100%;overflow:auto}.scalar-code-block[data-v-32d6d6ca] .hljs *{font-size:var(--scalar-small)}.ascii-art-animate .ascii-art-line[data-v-69ebd973]{border-right:1ch solid #0000;animation:4s step-end 1s both typewriter-69ebd973,.5s step-end infinite blinkTextCursor-69ebd973}@keyframes typewriter-69ebd973{0%{width:0}to{width:100%}}@keyframes blinkTextCursor-69ebd973{0%{border-right-color:currentColor}50%{border-right-color:#0000}}.keycap-n[data-v-b1211b87]{background:-webkit-linear-gradient(5deg,transparent 30%,var(--scalar-color-3)50%);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text}.keycap-hotkey[data-v-b1211b87]{line-height:26px;position:absolute;top:32px}.scalar-version-number[data-v-159bd9b3]{width:76px;height:76px;font-size:8px;font-family:var(--scalar-font-code);box-shadow:inset 2px 0 0 2px var(--scalar-background-2);text-align:center;text-transform:initial;-webkit-text-decoration-color:var(--scalar-color-3);text-decoration-color:var(--scalar-color-3);border-radius:9px 9px 16px 12px;flex-direction:column;justify-content:center;align-items:center;margin-top:-113px;margin-left:-36px;line-height:11px;display:flex;position:absolute;transform:skewY(13deg)}.scalar-version-number a[data-v-159bd9b3]{background:var(--scalar-background-2);border:.5px solid var(--scalar-border-color);border-radius:3px;padding:2px 4px;font-weight:700;text-decoration:none}.gitbook-show[data-v-159bd9b3]{display:none}.v-enter-active[data-v-57ced68a]{transition:opacity .5s}.v-enter-from[data-v-57ced68a]{opacity:0}.animate-response-heading .response-heading[data-v-aaba9a18]{opacity:1;animation:.2s ease-in-out forwards push-response-aaba9a18}@keyframes push-response-aaba9a18{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}.animate-response-heading .animate-response-children[data-v-aaba9a18]{opacity:0;animation:.2s ease-in-out 50ms forwards response-spans-aaba9a18}@keyframes response-spans-aaba9a18{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.request-card[data-v-a3bf456d]{font-size:var(--scalar-font-size-3)}.request-method[data-v-a3bf456d]{font-family:var(--scalar-font-code);text-transform:uppercase;margin-right:6px}.request-card-footer[data-v-a3bf456d]{flex-shrink:0;justify-content:flex-end;padding:6px;display:flex}.request-card-footer-addon[data-v-a3bf456d]{flex:1;align-items:center;min-width:0;display:flex}.request-editor-section[data-v-a3bf456d]{flex:1;display:flex}.request-card-simple[data-v-a3bf456d]{font-size:var(--scalar-small);justify-content:space-between;align-items:center;padding:8px 8px 8px 12px;display:flex}.code-snippet[data-v-a3bf456d]{flex-direction:column;width:100%;display:flex}.resizer[data-v-e2c54c18]{cursor:col-resize;z-index:100;border-right:2px solid #0000;width:5px;transition:border-right-color .3s;position:absolute;top:0;bottom:0;right:0}.scalar-dragging{cursor:col-resize}.resizer:hover,.scalar-dragging .resizer{border-right-color:var(--scalar-background-3)}.scalar-dragging:after{content:\"\";display:block;position:absolute;inset:0}.ref-search-meta[data-v-0c30b37a]{background:var(--scalar-background-1);border-bottom-left-radius:var(--scalar-radius-lg);border-bottom-right-radius:var(--scalar-radius-lg);font-size:var(--scalar-font-size-4);color:var(--scalar-color-3);font-weight:var(--scalar-semibold);border-top:var(--scalar-border-width)solid var(--scalar-border-color);gap:12px;padding:6px 12px;display:flex}.splash-screen[data-v-af32615f]{opacity:0;animation:.5s ease-in-out forwards fadeIn-af32615f}.logo-icon[data-v-af32615f]{opacity:0;animation:.6s ease-in-out .2s forwards fadeInLogo-af32615f,2s ease-in-out .8s infinite pulse-af32615f}@keyframes fadeIn-af32615f{0%{opacity:0}to{opacity:.9}}@keyframes fadeInLogo-af32615f{0%{opacity:0;transform:scale(.9)}to{opacity:.8;transform:scale(1)}}@keyframes pulse-af32615f{0%,to{opacity:.8}50%{opacity:.6}}.scroll-timeline-x[data-v-981120f2]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;-ms-overflow-style:none;scrollbar-width:none;overflow:auto}.scroll-timeline-x[data-v-981120f2]::-webkit-scrollbar{display:none}.commandmenu[data-v-7f3e459b]{box-shadow:var(--scalar-shadow-2);border-radius:var(--scalar-radius-lg);background-color:var(--scalar-background-1);opacity:0;width:100%;max-width:580px;max-height:60dvh;margin:12px;animation:.3s ease-in-out .1s forwards fadeincommandmenu-7f3e459b;position:fixed;top:150px;left:50%;transform:translate(-50%,10px)}.commandmenu-overlay[data-v-7f3e459b]{cursor:pointer;background:#0003;animation:.3s ease-in-out forwards fadeincommand-7f3e459b;position:fixed;inset:0}@keyframes fadeincommand-7f3e459b{0%{opacity:0}to{opacity:1}}@keyframes fadeincommandmenu-7f3e459b{0%{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%)}}.empty-sidebar-item-content[data-v-9bec4555]{display:none}.empty-sidebar-item .empty-sidebar-item-content[data-v-9bec4555]{display:block}.rabbitjump[data-v-9bec4555]{opacity:0}.empty-sidebar-item:hover .rabbitjump[data-v-9bec4555]{opacity:1;animation:.5s step-end infinite rabbitAnimation-9bec4555}.empty-sidebar-item:hover .rabbitsit[data-v-9bec4555]{opacity:0;animation:.5s step-end infinite rabbitAnimation2-9bec4555}.empty-sidebar-item:hover .rabbit-ascii[data-v-9bec4555]{animation:8s linear infinite rabbitRun-9bec4555}@keyframes rabbitRun-9bec4555{0%{transform:translateZ(0)}25%{transform:translate(250px)}25.01%{transform:translate(-250px)}75%{transform:translate(250px)}75.01%{transform:translate(-250px)}to{transform:translateZ(0)}}@keyframes rabbitAnimation-9bec4555{0%,to{opacity:1}50%{opacity:0}}@keyframes rabbitAnimation2-9bec4555{0%,to{opacity:0}50%{opacity:1;transform:translateY(-8px)}}.nav-single-tab[data-v-2e741aab]{width:100%;height:100%;color:var(--scalar-color-1);justify-content:center;align-items:center;display:flex;overflow:hidden}.nav-item[data-v-2e741aab]{cursor:pointer;border-radius:var(--scalar-radius-lg);background:var(--scalar-background-3);border:var(--scalar-border-width)solid var(--scalar-background-2);color:var(--scalar-color-3);flex:1;justify-content:center;align-items:center;min-width:0;padding:4.5px 1rem;display:flex;position:relative;overflow:hidden}.dark-mode .nav-item[data-v-2e741aab]{background:var(--scalar-background-2)}@supports (color:color-mix(in lab,red,red)){.dark-mode .nav-item[data-v-2e741aab]{background:color-mix(in srgb,var(--scalar-background-2),transparent)}}.nav-item-icon-copy[data-v-2e741aab]{white-space:nowrap;max-width:100%;-webkit-mask-image:linear-gradient(to left,transparent 0,var(--scalar-background-2)20px);mask-image:linear-gradient(to left,transparent 0,var(--scalar-background-2)20px);overflow:hidden}.nav-item:hover .nav-item-icon-copy[data-v-2e741aab]{-webkit-mask-image:linear-gradient(to left,transparent 20px,var(--scalar-background-2)40px);mask-image:linear-gradient(to left,transparent 20px,var(--scalar-background-2)40px)}.nav-item-copy[data-v-2e741aab]{max-width:calc(100% - 20px)}.nav-item[data-v-2e741aab]:hover{color:var(--scalar-color-1)}.nav-item__active[data-v-2e741aab]{background-color:var(--scalar-background-1);color:var(--scalar-color-1);border-color:var(--scalar-border-color)}.dark-mode .nav-item__active[data-v-2e741aab]{background-color:var(--scalar-background-2)}.nav-item-close[data-v-2e741aab]{border-radius:var(--scalar-radius);stroke-width:1.5px;max-width:20px;color:var(--scalar-color-3);opacity:0;background:0 0;margin-left:-20px;padding:2px;position:absolute;right:3px}.nav-item:hover .nav-item-close[data-v-2e741aab]{opacity:1}.nav-item-close[data-v-2e741aab]:hover{background-color:var(--scalar-background-4)}.nav-item__active .nav-item-close[data-v-2e741aab]:hover{background-color:var(--scalar-background-2)}.download-app-button[data-v-d9bec97b]{box-shadow:0 0 0 .5px var(--scalar-border-color);background:linear-gradient(#ffffffbf,#00000009)}.dark-mode .download-app-button[data-v-d9bec97b]{background:linear-gradient(#ffffff1a,#00000026)}.download-app-button[data-v-d9bec97b]:hover{background:linear-gradient(#00000009,#ffffffbf)}.dark-mode .download-app-button[data-v-d9bec97b]:hover{background:linear-gradient(#00000026,#ffffff1a)}#scalar-client{background-color:var(--scalar-background-2);flex-direction:column;width:100dvw;height:100dvh;display:flex;position:relative}.dark-mode #scalar-client{background-color:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.dark-mode #scalar-client{background-color:color-mix(in srgb,var(--scalar-background-1)65%,black)}}.scalar-collection-auth[data-v-dc159f6f]{border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg);overflow:hidden}[data-v-6fc24fdf] .cm-editor{padding:0}[data-v-6fc24fdf] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-6fc24fdf] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-6fc24fdf] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-6fc24fdf] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-6fc24fdf] .cm-line{text-overflow:ellipsis;padding:0;overflow:hidden}[data-v-28c8509c] .cm-editor{padding:0}[data-v-28c8509c] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-28c8509c] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-28c8509c] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-28c8509c] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-28c8509c] .cm-line{text-overflow:ellipsis;padding:0;overflow:hidden}[data-v-7c1a2f6c] .cm-content{min-height:fit-content}[data-v-7c1a2f6c] .cm-scroller{max-width:100%;overflow:auto hidden}.group-hover-input[data-v-5a23cb87]{border-width:var(--scalar-border-width);border-color:#0000}.group:hover .group-hover-input[data-v-5a23cb87]{background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.group:hover .group-hover-input[data-v-5a23cb87]{background:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.group:hover .group-hover-input[data-v-5a23cb87]{border-color:var(--scalar-border-color)}.group-hover-input[data-v-5a23cb87]:focus{border-color:var(--scalar-border-color)!important;background:0 0!important}.scalar .scalar-app-layout[data-v-2d195805]{background:var(--scalar-background-1);opacity:0;border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:8px;width:100%;max-width:1390px;height:calc(100% - 120px);margin:auto;animation:.35s forwards scalarapiclientfadein-2d195805;position:relative;overflow:hidden}@media(max-width:720px)and (max-height:480px){.scalar .scalar-app-layout[data-v-2d195805]{height:100%;max-height:90svh}}@keyframes scalarapiclientfadein-2d195805{0%{opacity:0}to{opacity:1}}.scalar .scalar-app-exit[data-v-2d195805]{cursor:pointer;z-index:-1;background:#00000038;width:100vw;height:100vh;transition:all .3s ease-in-out;animation:.35s forwards scalardrawerexitfadein-2d195805;position:fixed;top:0;left:0}.dark-mode .scalar .scalar-app-exit[data-v-2d195805]{background:#00000073}.scalar .scalar-app-exit[data-v-2d195805]:before{text-align:center;color:#fff;opacity:.6;font-family:sans-serif;font-size:30px;font-weight:100;line-height:50px;position:absolute;top:0;right:12px}.scalar .scalar-app-exit[data-v-2d195805]:hover:before{opacity:1}@keyframes scalardrawerexitfadein-2d195805{0%{opacity:0}to{opacity:1}}.scalar-container[data-v-2d195805]{visibility:visible;z-index:10000;justify-content:center;align-items:center;width:100%;height:100%;display:flex;position:fixed;top:0;bottom:0;left:0;overflow:hidden}.scalar .url-form-input[data-v-2d195805]{min-height:auto!important}.scalar .scalar-container[data-v-2d195805]{line-height:normal}.scalar .scalar-app-header span[data-v-2d195805]{color:var(--scalar-color-3)}.scalar .scalar-app-header a[data-v-2d195805]{color:var(--scalar-color-1)}.scalar .scalar-app-header a[data-v-2d195805]:hover{text-decoration:underline}.scalar-activate[data-v-2d195805]{cursor:pointer;align-items:center;gap:6px;width:fit-content;margin:0 .75rem .75rem auto;font-size:.875rem;font-weight:600;line-height:24px;display:flex}.scalar-activate-button[data-v-2d195805]{color:var(--scalar-color-blue);appearance:none;background:0 0;border:none;outline:none;align-items:center;gap:6px;padding:0 .5rem;display:flex}.scalar-activate:hover .scalar-activate-button[data-v-2d195805]{background:var(--scalar-background-3);border-radius:3px}.schema>span[data-v-4df72868]:not(:first-child):before{content:\"·\";margin:0 .5ch;display:block}.schema>span[data-v-4df72868]{white-space:nowrap;display:flex}[data-v-04661eb4] .cm-editor{padding:0}[data-v-04661eb4] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-04661eb4] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-04661eb4] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-04661eb4] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-04661eb4] .cm-line{text-overflow:ellipsis;padding:0;overflow:hidden}.filemask[data-v-04661eb4]{-webkit-mask-image:linear-gradient(to right,transparent 0,var(--scalar-background-2)20px);mask-image:linear-gradient(to right,transparent 0,var(--scalar-background-2)20px)}[data-v-9aa4b63a] .cm-content{font-size:var(--scalar-small)}.auth-combobox-position[data-v-0bb98074]{margin-left:120px}.scroll-timeline-x[data-v-0bb98074]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;scrollbar-width:none;-ms-overflow-style:none;overflow:auto}.fade-left[data-v-0bb98074],.fade-right[data-v-0bb98074]{content:\"\";pointer-events:none;height:100%;min-height:24px;animation-name:fadein-0bb98074;animation-duration:1ms;animation-direction:reverse;animation-timeline:--scroll-timeline;position:sticky}.fade-left[data-v-0bb98074]{background:linear-gradient(-90deg,var(--scalar-background-1)0%,var(--scalar-background-1)60%,var(--scalar-background-1)100%)}@supports (color:color-mix(in lab,red,red)){.fade-left[data-v-0bb98074]{background:linear-gradient(-90deg,color-mix(in srgb,var(--scalar-background-1),transparent 100%)0%,color-mix(in srgb,var(--scalar-background-1),transparent 20%)60%,var(--scalar-background-1)100%)}}.fade-left[data-v-0bb98074]{min-width:3px;animation-direction:normal;left:-1px}.fade-right[data-v-0bb98074]{background:linear-gradient(90deg,var(--scalar-background-1)0%,var(--scalar-background-1)60%,var(--scalar-background-1)100%)}@supports (color:color-mix(in lab,red,red)){.fade-right[data-v-0bb98074]{background:linear-gradient(90deg,color-mix(in srgb,var(--scalar-background-1),transparent 100%)0%,color-mix(in srgb,var(--scalar-background-1),transparent 20%)60%,var(--scalar-background-1)100%)}}.fade-right[data-v-0bb98074]{min-width:24px;margin-left:-20px;top:0;right:-1px}@keyframes fadein-0bb98074{0%{opacity:0}15%{opacity:1}}.auth-combobox-position[data-v-3f1067a4]{margin-left:120px}.scroll-timeline-x[data-v-3f1067a4]{scroll-timeline:--scroll-timeline x;scroll-timeline:--scroll-timeline horizontal;scrollbar-width:none;-ms-overflow-style:none;overflow:auto}.fade-left[data-v-3f1067a4],.fade-right[data-v-3f1067a4]{content:\"\";pointer-events:none;height:100%;min-height:24px;animation-name:fadein-3f1067a4;animation-duration:1ms;animation-direction:reverse;animation-timeline:--scroll-timeline;position:sticky}.fade-left[data-v-3f1067a4]{background:linear-gradient(-90deg,var(--scalar-background-1)0%,var(--scalar-background-1)60%,var(--scalar-background-1)100%)}@supports (color:color-mix(in lab,red,red)){.fade-left[data-v-3f1067a4]{background:linear-gradient(-90deg,color-mix(in srgb,var(--scalar-background-1),transparent 100%)0%,color-mix(in srgb,var(--scalar-background-1),transparent 20%)60%,var(--scalar-background-1)100%)}}.fade-left[data-v-3f1067a4]{min-width:3px;animation-direction:normal;left:-1px}.fade-right[data-v-3f1067a4]{background:linear-gradient(90deg,var(--scalar-background-1)0%,var(--scalar-background-1)60%,var(--scalar-background-1)100%)}@supports (color:color-mix(in lab,red,red)){.fade-right[data-v-3f1067a4]{background:linear-gradient(90deg,color-mix(in srgb,var(--scalar-background-1),transparent 100%)0%,color-mix(in srgb,var(--scalar-background-1),transparent 20%)60%,var(--scalar-background-1)100%)}}.fade-right[data-v-3f1067a4]{min-width:24px;margin-left:-20px;top:0;right:-1px}@keyframes fadein-3f1067a4{0%{opacity:0}15%{opacity:1}}[data-v-2891f052] code.hljs *{font-size:var(--scalar-small)}.request-section-content[data-v-e85e2882]{--scalar-border-width:.5px}.request-section-content-filter[data-v-e85e2882]{box-shadow:0 -10px 0 10px var(--scalar-background-1)}.request-item:focus-within .request-meta-buttons[data-v-e85e2882]{opacity:1}.group-hover-input[data-v-e85e2882]{border-width:var(--scalar-border-width);border-color:#0000}.group:hover .group-hover-input[data-v-e85e2882]{background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.group:hover .group-hover-input[data-v-e85e2882]{background:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.group:hover .group-hover-input[data-v-e85e2882]{border-color:var(--scalar-border-color)}.group-hover-input[data-v-e85e2882]:focus{border-color:var(--scalar-border-color)!important;background:0 0!important}.light-mode .bg-preview[data-v-0956ad2d]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' width=\\'16\\' height=\\'16\\' fill=\\'%23000\\' fill-opacity=\\'10%25\\'%3E%3Crect width=\\'8\\' height=\\'8\\' /%3E%3Crect x=\\'8\\' y=\\'8\\' width=\\'8\\' height=\\'8\\' /%3E%3C/svg%3E\")}.dark-mode .bg-preview[data-v-0956ad2d]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' width=\\'16\\' height=\\'16\\' fill=\\'%23FFF\\' fill-opacity=\\'10%25\\'%3E%3Crect width=\\'8\\' height=\\'8\\' /%3E%3Crect x=\\'8\\' y=\\'8\\' width=\\'8\\' height=\\'8\\' /%3E%3C/svg%3E\")}[data-v-85d2902e] .cm-editor{font-size:var(--scalar-small);background-color:#0000;outline:none}[data-v-85d2902e] .cm-gutters{background-color:var(--scalar-background-1);border-radius:var(--scalar-radius)0 0 var(--scalar-radius)}.body-raw[data-v-85d2902e] .cm-scroller{min-width:100%;overflow:auto}.scalar-code-block[data-v-17966bf4] .hljs *{font-size:var(--scalar-small)}.response-body-virtual[data-headlessui-state=open],.response-body-virtual[data-headlessui-state=open] .diclosure-panel{flex-direction:column;flex-grow:1;display:flex}.scalar-version-number[data-v-6d2bdb61]{width:76px;height:76px;font-size:8px;font-family:var(--scalar-font-code);box-shadow:inset 2px 0 0 2px var(--scalar-background-2);text-align:center;text-transform:initial;-webkit-text-decoration-color:var(--scalar-color-3);text-decoration-color:var(--scalar-color-3);border-radius:9px 9px 16px 12px;flex-direction:column;justify-content:center;align-items:center;margin-top:-113px;margin-left:-36px;line-height:11px;display:flex;position:absolute;transform:skewY(13deg)}.scalar-version-number a[data-v-6d2bdb61]{background:var(--scalar-background-2);border:.5px solid var(--scalar-border-color);border-radius:3px;padding:2px 4px;font-weight:700;text-decoration:none}.gitbook-show[data-v-6d2bdb61]{display:none}.v-enter-active[data-v-7ec8af01]{transition:opacity .5s}.v-enter-from[data-v-7ec8af01]{opacity:0}.animate-response-heading .response-heading[data-v-7138ed84]{opacity:1;animation:.2s ease-in-out forwards push-response-7138ed84}@keyframes push-response-7138ed84{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}.animate-response-heading .animate-response-children[data-v-7138ed84]{opacity:0;animation:.2s ease-in-out 50ms forwards response-spans-7138ed84}@keyframes response-spans-7138ed84{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.ellipsis-position[data-v-01a1ab71]{transform:translate(calc(-100% - 4.5px))}.indent-border-line-offset[data-v-4f5a9d1f]:before{left:var(--0bed2d4e)}.indent-padding-left[data-v-4f5a9d1f]{padding-left:calc(var(--57ee1db0) + 6px)}.sidebar-folderitem[data-v-4f5a9d1f] .ellipsis-position{right:6px;transform:none}.search-button-fade[data-v-bca9c474]{background:linear-gradient(var(--scalar-background-1)32px,var(--scalar-background-1)38px,transparent)}@supports (color:color-mix(in lab,red,red)){.search-button-fade[data-v-bca9c474]{background:linear-gradient(var(--scalar-background-1)32px,color-mix(in srgb,var(--scalar-background-1),transparent)38px,transparent)}}.empty-sidebar-item-content[data-v-bca9c474]{display:none}.empty-sidebar-item .empty-sidebar-item-content[data-v-bca9c474]{display:block}.rabbitjump[data-v-bca9c474]{opacity:0}.empty-sidebar-item:hover .rabbitjump[data-v-bca9c474]{opacity:1;animation:.5s step-end infinite rabbitAnimation-bca9c474}.empty-sidebar-item:hover .rabbitsit[data-v-bca9c474]{opacity:0;animation:.5s step-end infinite rabbitAnimation2-bca9c474}.empty-sidebar-item:hover .rabbit-ascii[data-v-bca9c474]{animation:8s linear infinite rabbitRun-bca9c474}@keyframes rabbitRun-bca9c474{0%{transform:translateZ(0)}25%{transform:translate(250px)}25.01%{transform:translate(-250px)}75%{transform:translate(250px)}75.01%{transform:translate(-250px)}to{transform:translateZ(0)}}@keyframes rabbitAnimation-bca9c474{0%,to{opacity:1}50%{opacity:0}}@keyframes rabbitAnimation2-bca9c474{0%,to{opacity:0}50%{opacity:1;transform:translateY(-8px)}}.request-text-color-text[data-v-c508c571]{color:var(--scalar-color-1);background:linear-gradient(var(--scalar-background-1),var(--scalar-background-3));box-shadow:0 0 0 1px var(--scalar-border-color)}@media screen and (max-width:800px){.sidebar-active-hide-layout[data-v-c508c571]{display:none}.sidebar-active-width[data-v-c508c571]{width:100%}}.gitbook-show[data-v-c8df97c6]{display:none}.app-exit-button[data-v-c8df97c6]{color:#fff;background:#0000001a}.app-exit-button[data-v-c8df97c6]:hover{background:#ffffff1a}.request-text-color-text[data-v-57ae0d10]{color:var(--scalar-color-1);background:linear-gradient(var(--scalar-background-1),var(--scalar-background-3));box-shadow:0 0 0 1px var(--scalar-border-color)}@media screen and (max-width:800px){.sidebar-active-hide-layout[data-v-57ae0d10]{display:none}.sidebar-active-width[data-v-57ae0d10]{width:100%}}.group-hover-input[data-v-fced736a]{border-width:var(--scalar-border-width);border-color:#0000}.group:hover .group-hover-input[data-v-fced736a]{background:var(--scalar-background-1)}@supports (color:color-mix(in lab,red,red)){.group:hover .group-hover-input[data-v-fced736a]{background:color-mix(in srgb,var(--scalar-background-1),var(--scalar-background-2))}}.group:hover .group-hover-input[data-v-fced736a]{border-color:var(--scalar-border-color)}.group-hover-input[data-v-fced736a]:focus{border-color:var(--scalar-border-color)!important;background:0 0!important}[data-v-68d5218e] .markdown h2{font-size:var(--scalar-font-size-2)}[data-v-5997a667] .cm-content{min-height:fit-content}[data-v-5997a667] .cm-scroller{max-width:100%;overflow:auto hidden}[data-v-83bfcc8a] .cm-editor{padding:0}[data-v-83bfcc8a] .cm-content{font-family:var(--scalar-font);font-size:var(--scalar-small);background-color:#0000;align-items:center;width:100%;padding:5px 8px;display:flex}[data-v-83bfcc8a] .cm-content:has(.cm-pill){padding:5px 8px}[data-v-83bfcc8a] .cm-content .cm-pill:not(:last-of-type){margin-right:.5px}[data-v-83bfcc8a] .cm-content .cm-pill:not(:first-of-type){margin-left:.5px}[data-v-83bfcc8a] .cm-line{text-overflow:ellipsis;padding:0;overflow:hidden}.scalar-collection-auth[data-v-cc87292e]{border:var(--scalar-border-width)solid var(--scalar-border-color);border-radius:var(--scalar-radius-lg);overflow:hidden}.dragover-asChild[data-v-a89d6a6e],.dragover-above[data-v-a89d6a6e],.dragover-below[data-v-a89d6a6e]{position:relative}.dragover-above[data-v-a89d6a6e]:after,.dragover-below[data-v-a89d6a6e]:after{content:\"\";background:var(--scalar-color-blue);width:100%;height:3px;display:block;position:absolute;top:-1.5px}@supports (color:color-mix(in lab,red,red)){.dragover-above[data-v-a89d6a6e]:after,.dragover-below[data-v-a89d6a6e]:after{background:color-mix(in srgb,var(--scalar-color-blue),transparent 85%)}}.dragover-above[data-v-a89d6a6e]:after,.dragover-below[data-v-a89d6a6e]:after{pointer-events:none;border-radius:var(--scalar-radius)}.dragover-below[data-v-a89d6a6e]:after{top:initial;bottom:-1.5px}.dragover-asChild[data-v-a89d6a6e]:after{content:\"\";background:var(--scalar-color-blue);width:100%;height:100%;display:block;position:absolute;top:0;left:0}@supports (color:color-mix(in lab,red,red)){.dragover-asChild[data-v-a89d6a6e]:after{background:color-mix(in srgb,var(--scalar-color-blue),transparent 85%)}}.dragover-asChild[data-v-a89d6a6e]:after{pointer-events:none;border-radius:var(--scalar-radius)}.empty-variable-name[data-v-0b6c70e4]:empty:before{content:\"Untitled\";color:var(--scalar-color-3)}.form-group[data-v-694018d6]{margin-bottom:1rem}.modal-actions[data-v-694018d6]{justify-content:flex-end;gap:1rem;display:flex}:root{--scalar-loaded-api-reference:true}@property --tw-translate-x{syntax:\"*\";inherits:false;initial-value:0}@property --tw-translate-y{syntax:\"*\";inherits:false;initial-value:0}@property --tw-translate-z{syntax:\"*\";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:\"*\";inherits:false}@property --tw-rotate-y{syntax:\"*\";inherits:false}@property --tw-rotate-z{syntax:\"*\";inherits:false}@property --tw-skew-x{syntax:\"*\";inherits:false}@property --tw-skew-y{syntax:\"*\";inherits:false}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-leading{syntax:\"*\";inherits:false}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-blur{syntax:\"*\";inherits:false}@property --tw-brightness{syntax:\"*\";inherits:false}@property --tw-contrast{syntax:\"*\";inherits:false}@property --tw-grayscale{syntax:\"*\";inherits:false}@property --tw-hue-rotate{syntax:\"*\";inherits:false}@property --tw-invert{syntax:\"*\";inherits:false}@property --tw-opacity{syntax:\"*\";inherits:false}@property --tw-saturate{syntax:\"*\";inherits:false}@property --tw-sepia{syntax:\"*\";inherits:false}@property --tw-drop-shadow{syntax:\"*\";inherits:false}@property --tw-drop-shadow-color{syntax:\"*\";inherits:false}@property --tw-drop-shadow-alpha{syntax:\"\";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:\"*\";inherits:false}@property --tw-duration{syntax:\"*\";inherits:false}@layer scalar-config{.scalar-api-reference[data-v-8456284e]{--refs-sidebar-width: var(--scalar-sidebar-width, 0px);--refs-header-height: calc( var(--scalar-custom-header-height) + var(--scalar-header-height, 0px) );--refs-viewport-offset: calc( var(--refs-header-height, 0px) + var(--refs-content-offset, 0px) );--refs-viewport-height: calc( var(--full-height, 100dvh) - var(--refs-viewport-offset, 0px) );--refs-content-max-width: var(--scalar-content-max-width, 1540px)}.scalar-api-reference.references-classic[data-v-8456284e]{--refs-content-max-width: var(--scalar-content-max-width, 1420px);min-height:100dvh;--refs-sidebar-width: 0}.scalar-api-reference[data-v-8456284e]:has(.api-reference-toolbar){--refs-content-offset: 48px}}.references-layout[data-v-8456284e]{min-height:100dvh;min-width:100%;max-width:100%;flex:1;--full-height: 100dvh;display:grid;grid-template-rows:var(--scalar-header-height, 0px) repeat(2,auto);grid-template-columns:auto 1fr;grid-template-areas:\"header header\" \"navigation rendered\" \"footer footer\";background:var(--scalar-background-1)}.references-editor[data-v-8456284e]{grid-area:editor;display:flex;min-width:0;background:var(--scalar-background-1)}.references-rendered[data-v-8456284e]{position:relative;grid-area:rendered;min-width:0;background:var(--scalar-background-1)}.scalar-api-reference.references-classic[data-v-8456284e],.references-classic .references-rendered[data-v-8456284e]{height:initial!important;max-height:initial!important}@layer scalar-config{.references-sidebar[data-v-8456284e]{--refs-sidebar-width: var(--scalar-sidebar-width, 280px)}}.references-footer[data-v-8456284e]{grid-area:footer}@media(max-width:1000px){.references-layout[data-v-8456284e]{grid-template-columns:100%;grid-template-rows:var(--scalar-header-height, 0px) 0px auto auto;grid-template-areas:\"header\" \"navigation\" \"rendered\" \"footer\"}.references-editable[data-v-8456284e]{grid-template-areas:\"header\" \"navigation\" \"editor\"}.references-rendered[data-v-8456284e]{position:static}}@media(max-width:1000px){.scalar-api-references-standalone-mobile[data-v-8456284e]{--scalar-header-height: 50px}}.darklight-reference[data-v-8456284e]{width:100%;margin-top:auto}')),document.head.appendChild(e)}}catch(e){console.error(\"vite-plugin-css-injected-by-js\",e)}}(),function(e){\"function\"==typeof define&&define.amd?define(e):e()}((function(){\"use strict\";const e=Object.freeze({status:\"aborted\"});function t(e,t,n){function r(n,r){var a;Object.defineProperty(n,\"_zod\",{value:n._zod??{},enumerable:!1}),(a=n._zod).traits??(a.traits=new Set),n._zod.traits.add(e),t(n,r);for(const e in i.prototype)e in n||Object.defineProperty(n,e,{value:i.prototype[e].bind(n)});n._zod.constr=i,n._zod.def=r}const a=n?.Parent??Object;class o extends a{}function i(e){var t;const a=n?.Parent?new o:this;r(a,e),(t=a._zod).deferred??(t.deferred=[]);for(const e of a._zod.deferred)e();return a}return Object.defineProperty(o,\"name\",{value:e}),Object.defineProperty(i,\"init\",{value:r}),Object.defineProperty(i,Symbol.hasInstance,{value:t=>!!(n?.Parent&&t instanceof n.Parent)||t?._zod?.traits?.has(e)}),Object.defineProperty(i,\"name\",{value:e}),i}const n=Symbol(\"zod_brand\");class r extends Error{constructor(){super(\"Encountered Promise during synchronous parse. Use .parseAsync() instead.\")}}class a extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name=\"ZodEncodeError\"}}const o={};function i(e){return e&&Object.assign(o,e),o}function s(e){const t=Object.values(e).filter((e=>\"number\"==typeof e));return Object.entries(e).filter((([e,n])=>-1===t.indexOf(+e))).map((([e,t])=>t))}function l(e,t=\"|\"){return e.map((e=>C(e))).join(t)}function c(e,t){return\"bigint\"==typeof t?t.toString():t}function u(e){return{get value(){{const t=e();return Object.defineProperty(this,\"value\",{value:t}),t}}}}function d(e){return null==e}function p(e){const t=e.startsWith(\"^\")?1:0,n=e.endsWith(\"$\")?e.length-1:e.length;return e.slice(t,n)}function h(e,t){const n=(e.toString().split(\".\")[1]||\"\").length,r=t.toString();let a=(r.split(\".\")[1]||\"\").length;if(0===a&&/\\d?e-\\d?/.test(r)){const e=r.match(/\\d?e-(\\d?)/);e?.[1]&&(a=Number.parseInt(e[1]))}const o=n>a?n:a;return Number.parseInt(e.toFixed(o).replace(\".\",\"\"))%Number.parseInt(t.toFixed(o).replace(\".\",\"\"))/10**o}const f=Symbol(\"evaluating\");function m(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==f)return void 0===r&&(r=f,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}function g(e,t,n){Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})}function v(...e){const t={};for(const n of e){const e=Object.getOwnPropertyDescriptors(n);Object.assign(t,e)}return Object.defineProperties({},t)}function b(e){return JSON.stringify(e)}const y=\"captureStackTrace\"in Error?Error.captureStackTrace:(...e)=>{};function O(e){return\"object\"==typeof e&&null!==e&&!Array.isArray(e)}const w=u((()=>{if(\"undefined\"!=typeof navigator&&navigator?.userAgent?.includes(\"Cloudflare\"))return!1;try{return new Function(\"\"),!0}catch(e){return!1}}));function x(e){if(!1===O(e))return!1;const t=e.constructor;if(void 0===t)return!0;const n=t.prototype;return!1!==O(n)&&!1!==Object.prototype.hasOwnProperty.call(n,\"isPrototypeOf\")}function k(e){return x(e)?{...e}:Array.isArray(e)?[...e]:e}const S=new Set([\"string\",\"number\",\"symbol\"]),_=new Set([\"string\",\"number\",\"bigint\",\"boolean\",\"symbol\",\"undefined\"]);function T(e){return e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\")}function E(e,t,n){const r=new e._zod.constr(t??e._zod.def);return t&&!n?.parent||(r._zod.parent=e),r}function A(e){const t=e;if(!t)return{};if(\"string\"==typeof t)return{error:()=>t};if(void 0!==t?.message){if(void 0!==t?.error)throw new Error(\"Cannot specify both `message` and `error` params\");t.error=t.message}return delete t.message,\"string\"==typeof t.error?{...t,error:()=>t.error}:t}function C(e){return\"bigint\"==typeof e?e.toString()+\"n\":\"string\"==typeof e?`\"${e}\"`:`${e}`}function P(e){return Object.keys(e).filter((t=>\"optional\"===e[t]._zod.optin&&\"optional\"===e[t]._zod.optout))}const $={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]},D={int64:[BigInt(\"-9223372036854775808\"),BigInt(\"9223372036854775807\")],uint64:[BigInt(0),BigInt(\"18446744073709551615\")]};function I(e,t){const n=e._zod.def;return E(e,v(e._zod.def,{get shape(){const e={};for(const r in t){if(!(r in n.shape))throw new Error(`Unrecognized key: \"${r}\"`);t[r]&&(e[r]=n.shape[r])}return g(this,\"shape\",e),e},checks:[]}))}function M(e,t){const n=e._zod.def,r=v(e._zod.def,{get shape(){const r={...e._zod.def.shape};for(const e in t){if(!(e in n.shape))throw new Error(`Unrecognized key: \"${e}\"`);t[e]&&delete r[e]}return g(this,\"shape\",r),r},checks:[]});return E(e,r)}function N(e,t){if(!x(t))throw new Error(\"Invalid input to extend: expected a plain object\");const n=e._zod.def.checks;if(n&&n.length>0)throw new Error(\"Object schemas containing refinements cannot be extended. Use `.safeExtend()` instead.\");const r=v(e._zod.def,{get shape(){const n={...e._zod.def.shape,...t};return g(this,\"shape\",n),n},checks:[]});return E(e,r)}function R(e,t){if(!x(t))throw new Error(\"Invalid input to safeExtend: expected a plain object\");const n={...e._zod.def,get shape(){const n={...e._zod.def.shape,...t};return g(this,\"shape\",n),n},checks:e._zod.def.checks};return E(e,n)}function L(e,t){const n=v(e._zod.def,{get shape(){const n={...e._zod.def.shape,...t._zod.def.shape};return g(this,\"shape\",n),n},get catchall(){return t._zod.def.catchall},checks:[]});return E(e,n)}function B(e,t,n){const r=v(t._zod.def,{get shape(){const r=t._zod.def.shape,a={...r};if(n)for(const t in n){if(!(t in r))throw new Error(`Unrecognized key: \"${t}\"`);n[t]&&(a[t]=e?new e({type:\"optional\",innerType:r[t]}):r[t])}else for(const t in r)a[t]=e?new e({type:\"optional\",innerType:r[t]}):r[t];return g(this,\"shape\",a),a},checks:[]});return E(t,r)}function j(e,t,n){const r=v(t._zod.def,{get shape(){const r=t._zod.def.shape,a={...r};if(n)for(const t in n){if(!(t in a))throw new Error(`Unrecognized key: \"${t}\"`);n[t]&&(a[t]=new e({type:\"nonoptional\",innerType:r[t]}))}else for(const t in r)a[t]=new e({type:\"nonoptional\",innerType:r[t]});return g(this,\"shape\",a),a},checks:[]});return E(t,r)}function U(e,t=0){if(!0===e.aborted)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t}))}function F(e){return\"string\"==typeof e?e:e?.message}function Q(e,t,n){const r={...e,path:e.path??[]};if(!e.message){const a=F(e.inst?._zod.def?.error?.(e))??F(t?.error?.(e))??F(n.customError?.(e))??F(n.localeError?.(e))??\"Invalid input\";r.message=a}return delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}function Z(e){return e instanceof Set?\"set\":e instanceof Map?\"map\":e instanceof File?\"file\":\"unknown\"}function H(e){return Array.isArray(e)?\"array\":\"string\"==typeof e?\"string\":\"unknown\"}function V(...e){const[t,n,r]=e;return\"string\"==typeof t?{message:t,code:\"custom\",input:n,inst:r}:{...t}}function q(e){const t=atob(e),n=new Uint8Array(t.length);for(let e=0;eNumber.isNaN(Number.parseInt(e,10)))).map((e=>e[1]))},cleanRegex:p,clone:E,cloneDef:function(e){return v(e._zod.def)},createTransparentProxy:function(e){let t;return new Proxy({},{get:(n,r,a)=>(t??(t=e()),Reflect.get(t,r,a)),set:(n,r,a,o)=>(t??(t=e()),Reflect.set(t,r,a,o)),has:(n,r)=>(t??(t=e()),Reflect.has(t,r)),deleteProperty:(n,r)=>(t??(t=e()),Reflect.deleteProperty(t,r)),ownKeys:n=>(t??(t=e()),Reflect.ownKeys(t)),getOwnPropertyDescriptor:(n,r)=>(t??(t=e()),Reflect.getOwnPropertyDescriptor(t,r)),defineProperty:(n,r,a)=>(t??(t=e()),Reflect.defineProperty(t,r,a))})},defineLazy:m,esc:b,escapeRegex:T,extend:N,finalizeIssue:Q,floatSafeRemainder:h,getElementAtPath:function(e,t){return t?t.reduce(((e,t)=>e?.[t]),e):e},getEnumValues:s,getLengthableOrigin:H,getParsedType:e=>{const t=typeof e;switch(t){case\"undefined\":return\"undefined\";case\"string\":return\"string\";case\"number\":return Number.isNaN(e)?\"nan\":\"number\";case\"boolean\":return\"boolean\";case\"function\":return\"function\";case\"bigint\":return\"bigint\";case\"symbol\":return\"symbol\";case\"object\":return Array.isArray(e)?\"array\":null===e?\"null\":e.then&&\"function\"==typeof e.then&&e.catch&&\"function\"==typeof e.catch?\"promise\":\"undefined\"!=typeof Map&&e instanceof Map?\"map\":\"undefined\"!=typeof Set&&e instanceof Set?\"set\":\"undefined\"!=typeof Date&&e instanceof Date?\"date\":\"undefined\"!=typeof File&&e instanceof File?\"file\":\"object\";default:throw new Error(`Unknown data type: ${t}`)}},getSizableOrigin:Z,hexToUint8Array:function(e){const t=e.replace(/^0x/,\"\");if(t.length%2!=0)throw new Error(\"Invalid hex string length\");const n=new Uint8Array(t.length/2);for(let e=0;ee[t]));return Promise.all(n).then((e=>{const n={};for(let r=0;re.toString(16).padStart(2,\"0\"))).join(\"\")},unwrapMessage:F},Symbol.toStringTag,{value:\"Module\"})),G=(e,t)=>{e.name=\"$ZodError\",Object.defineProperty(e,\"_zod\",{value:e._zod,enumerable:!1}),Object.defineProperty(e,\"issues\",{value:t,enumerable:!1}),e.message=JSON.stringify(t,c,2),Object.defineProperty(e,\"toString\",{value:()=>e.message,enumerable:!1})},Y=t(\"$ZodError\",G),K=t(\"$ZodError\",G,{Parent:Error});function J(e,t=e=>e.message){const n={},r=[];for(const a of e.issues)a.path.length>0?(n[a.path[0]]=n[a.path[0]]||[],n[a.path[0]].push(t(a))):r.push(t(a));return{formErrors:r,fieldErrors:n}}function ee(e,t){const n=t||function(e){return e.message},r={_errors:[]},a=e=>{for(const t of e.issues)if(\"invalid_union\"===t.code&&t.errors.length)t.errors.map((e=>a({issues:e})));else if(\"invalid_key\"===t.code)a({issues:t.issues});else if(\"invalid_element\"===t.code)a({issues:t.issues});else if(0===t.path.length)r._errors.push(n(t));else{let e=r,a=0;for(;a{var o,i;for(const s of e.issues)if(\"invalid_union\"===s.code&&s.errors.length)s.errors.map((e=>a({issues:e},s.path)));else if(\"invalid_key\"===s.code)a({issues:s.issues},s.path);else if(\"invalid_element\"===s.code)a({issues:s.issues},s.path);else{const e=[...t,...s.path];if(0===e.length){r.errors.push(n(s));continue}let a=r,l=0;for(;l\"object\"==typeof e?e.key:e));for(const e of n)\"number\"==typeof e?t.push(`[${e}]`):\"symbol\"==typeof e?t.push(`[${JSON.stringify(String(e))}]`):/[^\\w$]/.test(e)?t.push(`[${JSON.stringify(e)}]`):(t.length&&t.push(\".\"),t.push(e));return t.join(\"\")}function re(e){const t=[],n=[...e.issues].sort(((e,t)=>(e.path??[]).length-(t.path??[]).length));for(const e of n)t.push(`✖ ${e.message}`),e.path?.length&&t.push(` → at ${ne(e.path)}`);return t.join(\"\\n\")}const ae=e=>(t,n,a,o)=>{const s=a?Object.assign(a,{async:!1}):{async:!1},l=t._zod.run({value:n,issues:[]},s);if(l instanceof Promise)throw new r;if(l.issues.length){const t=new(o?.Err??e)(l.issues.map((e=>Q(e,s,i()))));throw y(t,o?.callee),t}return l.value},oe=ae(K),ie=e=>async(t,n,r,a)=>{const o=r?Object.assign(r,{async:!0}):{async:!0};let s=t._zod.run({value:n,issues:[]},o);if(s instanceof Promise&&(s=await s),s.issues.length){const t=new(a?.Err??e)(s.issues.map((e=>Q(e,o,i()))));throw y(t,a?.callee),t}return s.value},se=ie(K),le=e=>(t,n,a)=>{const o=a?{...a,async:!1}:{async:!1},s=t._zod.run({value:n,issues:[]},o);if(s instanceof Promise)throw new r;return s.issues.length?{success:!1,error:new(e??Y)(s.issues.map((e=>Q(e,o,i()))))}:{success:!0,data:s.value}},ce=le(K),ue=e=>async(t,n,r)=>{const a=r?Object.assign(r,{async:!0}):{async:!0};let o=t._zod.run({value:n,issues:[]},a);return o instanceof Promise&&(o=await o),o.issues.length?{success:!1,error:new e(o.issues.map((e=>Q(e,a,i()))))}:{success:!0,data:o.value}},de=ue(K),pe=e=>(t,n,r)=>{const a=r?Object.assign(r,{direction:\"backward\"}):{direction:\"backward\"};return ae(e)(t,n,a)},he=pe(K),fe=e=>(t,n,r)=>ae(e)(t,n,r),me=fe(K),ge=e=>async(t,n,r)=>{const a=r?Object.assign(r,{direction:\"backward\"}):{direction:\"backward\"};return ie(e)(t,n,a)},ve=ge(K),be=e=>async(t,n,r)=>ie(e)(t,n,r),ye=be(K),Oe=e=>(t,n,r)=>{const a=r?Object.assign(r,{direction:\"backward\"}):{direction:\"backward\"};return le(e)(t,n,a)},we=Oe(K),xe=e=>(t,n,r)=>le(e)(t,n,r),ke=xe(K),Se=e=>async(t,n,r)=>{const a=r?Object.assign(r,{direction:\"backward\"}):{direction:\"backward\"};return ue(e)(t,n,a)},_e=Se(K),Te=e=>async(t,n,r)=>ue(e)(t,n,r),Ee=Te(K),Ae=/^[cC][^\\s-]{8,}$/,Ce=/^[0-9a-z]+$/,Pe=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,$e=/^[0-9a-vA-V]{20}$/,De=/^[A-Za-z0-9]{27}$/,Ie=/^[a-zA-Z0-9_-]{21}$/,Me=/^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$/,Ne=/^([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})$/,Re=e=>e?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${e}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,Le=Re(4),Be=Re(6),je=Re(7),Ue=/^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/,ze=/^[^\\s@\"]{1,64}@[^\\s@]{1,255}$/u,Fe=ze;function Qe(){return new RegExp(\"^(\\\\p{Extended_Pictographic}|\\\\p{Emoji_Component})+$\",\"u\")}const Ze=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,He=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,Ve=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$/,qe=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,We=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Xe=/^[A-Za-z0-9_-]*$/,Ge=/^(?=.{1,253}\\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\\.?$/,Ye=/^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/,Ke=/^\\+(?:[0-9]){6,14}[0-9]$/,Je=\"(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))\",et=new RegExp(`^${Je}$`);function tt(e){const t=\"(?:[01]\\\\d|2[0-3]):[0-5]\\\\d\";return\"number\"==typeof e.precision?-1===e.precision?`${t}`:0===e.precision?`${t}:[0-5]\\\\d`:`${t}:[0-5]\\\\d\\\\.\\\\d{${e.precision}}`:`${t}(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?`}function nt(e){return new RegExp(`^${tt(e)}$`)}function rt(e){const t=tt({precision:e.precision}),n=[\"Z\"];e.local&&n.push(\"\"),e.offset&&n.push(\"([+-](?:[01]\\\\d|2[0-3]):[0-5]\\\\d)\");const r=`${t}(?:${n.join(\"|\")})`;return new RegExp(`^${Je}T(?:${r})$`)}const at=e=>new RegExp(`^${e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??\"\"}}`:\"[\\\\s\\\\S]*\"}$`),ot=/^-?\\d+n?$/,it=/^-?\\d+$/,st=/^-?\\d+(?:\\.\\d+)?/,lt=/^(?:true|false)$/i,ct=/^null$/i,ut=/^undefined$/i,dt=/^[^A-Z]*$/,pt=/^[^a-z]*$/,ht=/^[0-9a-fA-F]*$/;function ft(e,t){return new RegExp(`^[A-Za-z0-9+/]{${e}}${t}$`)}function mt(e){return new RegExp(`^[A-Za-z0-9_-]{${e}}$`)}const gt=ft(22,\"==\"),vt=mt(22),bt=ft(27,\"=\"),yt=mt(27),Ot=ft(43,\"=\"),wt=mt(43),xt=ft(64,\"\"),kt=mt(64),St=ft(86,\"==\"),_t=mt(86),Tt=Object.freeze(Object.defineProperty({__proto__:null,base64:We,base64url:Xe,bigint:ot,boolean:lt,browserEmail:/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,cidrv4:Ve,cidrv6:qe,cuid:Ae,cuid2:Ce,date:et,datetime:rt,domain:Ye,duration:Me,e164:Ke,email:Ue,emoji:Qe,extendedDuration:/^[-+]?P(?!$)(?:(?:[-+]?\\d+Y)|(?:[-+]?\\d+[.,]\\d+Y$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:(?:[-+]?\\d+W)|(?:[-+]?\\d+[.,]\\d+W$))?(?:(?:[-+]?\\d+D)|(?:[-+]?\\d+[.,]\\d+D$))?(?:T(?=[\\d+-])(?:(?:[-+]?\\d+H)|(?:[-+]?\\d+[.,]\\d+H$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:[-+]?\\d+(?:[.,]\\d+)?S)?)??$/,guid:Ne,hex:ht,hostname:Ge,html5Email:/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,idnEmail:Fe,integer:it,ipv4:Ze,ipv6:He,ksuid:De,lowercase:dt,md5_base64:gt,md5_base64url:vt,md5_hex:/^[0-9a-fA-F]{32}$/,nanoid:Ie,null:ct,number:st,rfc5322Email:/^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/,sha1_base64:bt,sha1_base64url:yt,sha1_hex:/^[0-9a-fA-F]{40}$/,sha256_base64:Ot,sha256_base64url:wt,sha256_hex:/^[0-9a-fA-F]{64}$/,sha384_base64:xt,sha384_base64url:kt,sha384_hex:/^[0-9a-fA-F]{96}$/,sha512_base64:St,sha512_base64url:_t,sha512_hex:/^[0-9a-fA-F]{128}$/,string:at,time:nt,ulid:Pe,undefined:ut,unicodeEmail:ze,uppercase:pt,uuid:Re,uuid4:Le,uuid6:Be,uuid7:je,xid:$e},Symbol.toStringTag,{value:\"Module\"})),Et=t(\"$ZodCheck\",((e,t)=>{var n;e._zod??(e._zod={}),e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])})),At={number:\"number\",bigint:\"bigint\",object:\"date\"},Ct=t(\"$ZodCheckLessThan\",((e,t)=>{Et.init(e,t);const n=At[typeof t.value];e._zod.onattach.push((e=>{const n=e._zod.bag,r=(t.inclusive?n.maximum:n.exclusiveMaximum)??Number.POSITIVE_INFINITY;t.value{(t.inclusive?r.value<=t.value:r.value{Et.init(e,t);const n=At[typeof t.value];e._zod.onattach.push((e=>{const n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??Number.NEGATIVE_INFINITY;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)})),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:\"too_small\",minimum:t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}})),$t=t(\"$ZodCheckMultipleOf\",((e,t)=>{Et.init(e,t),e._zod.onattach.push((e=>{var n;(n=e._zod.bag).multipleOf??(n.multipleOf=t.value)})),e._zod.check=n=>{if(typeof n.value!=typeof t.value)throw new Error(\"Cannot mix number and bigint in multiple_of check.\");(\"bigint\"==typeof n.value?n.value%t.value===BigInt(0):0===h(n.value,t.value))||n.issues.push({origin:typeof n.value,code:\"not_multiple_of\",divisor:t.value,input:n.value,inst:e,continue:!t.abort})}})),Dt=t(\"$ZodCheckNumberFormat\",((e,t)=>{Et.init(e,t),t.format=t.format||\"float64\";const n=t.format?.includes(\"int\"),r=n?\"int\":\"number\",[a,o]=$[t.format];e._zod.onattach.push((e=>{const r=e._zod.bag;r.format=t.format,r.minimum=a,r.maximum=o,n&&(r.pattern=it)})),e._zod.check=i=>{const s=i.value;if(n){if(!Number.isInteger(s))return void i.issues.push({expected:r,format:t.format,code:\"invalid_type\",continue:!1,input:s,inst:e});if(!Number.isSafeInteger(s))return void(s>0?i.issues.push({input:s,code:\"too_big\",maximum:Number.MAX_SAFE_INTEGER,note:\"Integers must be within the safe integer range.\",inst:e,origin:r,continue:!t.abort}):i.issues.push({input:s,code:\"too_small\",minimum:Number.MIN_SAFE_INTEGER,note:\"Integers must be within the safe integer range.\",inst:e,origin:r,continue:!t.abort}))}so&&i.issues.push({origin:\"number\",input:s,code:\"too_big\",maximum:o,inst:e})}})),It=t(\"$ZodCheckBigIntFormat\",((e,t)=>{Et.init(e,t);const[n,r]=D[t.format];e._zod.onattach.push((e=>{const a=e._zod.bag;a.format=t.format,a.minimum=n,a.maximum=r})),e._zod.check=a=>{const o=a.value;or&&a.issues.push({origin:\"bigint\",input:o,code:\"too_big\",maximum:r,inst:e})}})),Mt=t(\"$ZodCheckMaxSize\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.size}),e._zod.onattach.push((e=>{const n=e._zod.bag.maximum??Number.POSITIVE_INFINITY;t.maximum{const r=n.value;r.size<=t.maximum||n.issues.push({origin:Z(r),code:\"too_big\",maximum:t.maximum,inclusive:!0,input:r,inst:e,continue:!t.abort})}})),Nt=t(\"$ZodCheckMinSize\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.size}),e._zod.onattach.push((e=>{const n=e._zod.bag.minimum??Number.NEGATIVE_INFINITY;t.minimum>n&&(e._zod.bag.minimum=t.minimum)})),e._zod.check=n=>{const r=n.value;r.size>=t.minimum||n.issues.push({origin:Z(r),code:\"too_small\",minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}})),Rt=t(\"$ZodCheckSizeEquals\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.size}),e._zod.onattach.push((e=>{const n=e._zod.bag;n.minimum=t.size,n.maximum=t.size,n.size=t.size})),e._zod.check=n=>{const r=n.value,a=r.size;if(a===t.size)return;const o=a>t.size;n.issues.push({origin:Z(r),...o?{code:\"too_big\",maximum:t.size}:{code:\"too_small\",minimum:t.size},inclusive:!0,exact:!0,input:n.value,inst:e,continue:!t.abort})}})),Lt=t(\"$ZodCheckMaxLength\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.length}),e._zod.onattach.push((e=>{const n=e._zod.bag.maximum??Number.POSITIVE_INFINITY;t.maximum{const r=n.value;if(r.length<=t.maximum)return;const a=H(r);n.issues.push({origin:a,code:\"too_big\",maximum:t.maximum,inclusive:!0,input:r,inst:e,continue:!t.abort})}})),Bt=t(\"$ZodCheckMinLength\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.length}),e._zod.onattach.push((e=>{const n=e._zod.bag.minimum??Number.NEGATIVE_INFINITY;t.minimum>n&&(e._zod.bag.minimum=t.minimum)})),e._zod.check=n=>{const r=n.value;if(r.length>=t.minimum)return;const a=H(r);n.issues.push({origin:a,code:\"too_small\",minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}})),jt=t(\"$ZodCheckLengthEquals\",((e,t)=>{var n;Et.init(e,t),(n=e._zod.def).when??(n.when=e=>{const t=e.value;return!d(t)&&void 0!==t.length}),e._zod.onattach.push((e=>{const n=e._zod.bag;n.minimum=t.length,n.maximum=t.length,n.length=t.length})),e._zod.check=n=>{const r=n.value,a=r.length;if(a===t.length)return;const o=H(r),i=a>t.length;n.issues.push({origin:o,...i?{code:\"too_big\",maximum:t.length}:{code:\"too_small\",minimum:t.length},inclusive:!0,exact:!0,input:n.value,inst:e,continue:!t.abort})}})),Ut=t(\"$ZodCheckStringFormat\",((e,t)=>{var n,r;Et.init(e,t),e._zod.onattach.push((e=>{const n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??(n.patterns=new Set),n.patterns.add(t.pattern))})),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,t.pattern.test(n.value)||n.issues.push({origin:\"string\",code:\"invalid_format\",format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})})),zt=t(\"$ZodCheckRegex\",((e,t)=>{Ut.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,t.pattern.test(n.value)||n.issues.push({origin:\"string\",code:\"invalid_format\",format:\"regex\",input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}})),Ft=t(\"$ZodCheckLowerCase\",((e,t)=>{t.pattern??(t.pattern=dt),Ut.init(e,t)})),Qt=t(\"$ZodCheckUpperCase\",((e,t)=>{t.pattern??(t.pattern=pt),Ut.init(e,t)})),Zt=t(\"$ZodCheckIncludes\",((e,t)=>{Et.init(e,t);const n=T(t.includes),r=new RegExp(\"number\"==typeof t.position?`^.{${t.position}}${n}`:n);t.pattern=r,e._zod.onattach.push((e=>{const t=e._zod.bag;t.patterns??(t.patterns=new Set),t.patterns.add(r)})),e._zod.check=n=>{n.value.includes(t.includes,t.position)||n.issues.push({origin:\"string\",code:\"invalid_format\",format:\"includes\",includes:t.includes,input:n.value,inst:e,continue:!t.abort})}})),Ht=t(\"$ZodCheckStartsWith\",((e,t)=>{Et.init(e,t);const n=new RegExp(`^${T(t.prefix)}.*`);t.pattern??(t.pattern=n),e._zod.onattach.push((e=>{const t=e._zod.bag;t.patterns??(t.patterns=new Set),t.patterns.add(n)})),e._zod.check=n=>{n.value.startsWith(t.prefix)||n.issues.push({origin:\"string\",code:\"invalid_format\",format:\"starts_with\",prefix:t.prefix,input:n.value,inst:e,continue:!t.abort})}})),Vt=t(\"$ZodCheckEndsWith\",((e,t)=>{Et.init(e,t);const n=new RegExp(`.*${T(t.suffix)}$`);t.pattern??(t.pattern=n),e._zod.onattach.push((e=>{const t=e._zod.bag;t.patterns??(t.patterns=new Set),t.patterns.add(n)})),e._zod.check=n=>{n.value.endsWith(t.suffix)||n.issues.push({origin:\"string\",code:\"invalid_format\",format:\"ends_with\",suffix:t.suffix,input:n.value,inst:e,continue:!t.abort})}}));function qt(e,t,n){e.issues.length&&t.issues.push(...z(n,e.issues))}const Wt=t(\"$ZodCheckProperty\",((e,t)=>{Et.init(e,t),e._zod.check=e=>{const n=t.schema._zod.run({value:e.value[t.property],issues:[]},{});if(n instanceof Promise)return n.then((n=>qt(n,e,t.property)));qt(n,e,t.property)}})),Xt=t(\"$ZodCheckMimeType\",((e,t)=>{Et.init(e,t);const n=new Set(t.mime);e._zod.onattach.push((e=>{e._zod.bag.mime=t.mime})),e._zod.check=r=>{n.has(r.value.type)||r.issues.push({code:\"invalid_value\",values:t.mime,input:r.value.type,inst:e,continue:!t.abort})}})),Gt=t(\"$ZodCheckOverwrite\",((e,t)=>{Et.init(e,t),e._zod.check=e=>{e.value=t.tx(e.value)}}));class Yt{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),this.indent-=1}write(e){if(\"function\"==typeof e)return e(this,{execution:\"sync\"}),void e(this,{execution:\"async\"});const t=e.split(\"\\n\").filter((e=>e)),n=Math.min(...t.map((e=>e.length-e.trimStart().length))),r=t.map((e=>e.slice(n))).map((e=>\" \".repeat(2*this.indent)+e));for(const e of r)this.content.push(e)}compile(){const e=Function,t=this?.args;return new e(...t,[...(this?.content??[\"\"]).map((e=>` ${e}`))].join(\"\\n\"))}}const Kt={major:4,minor:1,patch:11},Jt=t(\"$ZodType\",((e,t)=>{var n;e??(e={}),e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=Kt;const a=[...e._zod.def.checks??[]];e._zod.traits.has(\"$ZodCheck\")&&a.unshift(e);for(const t of a)for(const n of t._zod.onattach)n(e);if(0===a.length)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push((()=>{e._zod.run=e._zod.parse}));else{const t=(e,t,n)=>{let a,o=U(e);for(const i of t){if(i._zod.def.when){if(!i._zod.def.when(e))continue}else if(o)continue;const t=e.issues.length,s=i._zod.check(e);if(s instanceof Promise&&!1===n?.async)throw new r;if(a||s instanceof Promise)a=(a??Promise.resolve()).then((async()=>{await s,e.issues.length!==t&&(o||(o=U(e,t)))}));else{if(e.issues.length===t)continue;o||(o=U(e,t))}}return a?a.then((()=>e)):e},n=(n,o,i)=>{if(U(n))return n.aborted=!0,n;const s=t(o,a,i);if(s instanceof Promise){if(!1===i.async)throw new r;return s.then((t=>e._zod.parse(t,i)))}return e._zod.parse(s,i)};e._zod.run=(o,i)=>{if(i.skipChecks)return e._zod.parse(o,i);if(\"backward\"===i.direction){const t=e._zod.parse({value:o.value,issues:[]},{...i,skipChecks:!0});return t instanceof Promise?t.then((e=>n(e,o,i))):n(t,o,i)}const s=e._zod.parse(o,i);if(s instanceof Promise){if(!1===i.async)throw new r;return s.then((e=>t(e,a,i)))}return t(s,a,i)}}e[\"~standard\"]={validate:t=>{try{const n=ce(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch(n){return de(e,t).then((e=>e.success?{value:e.data}:{issues:e.error?.issues}))}},vendor:\"zod\",version:1}})),en=t(\"$ZodString\",((e,t)=>{Jt.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??at(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch(e){}return\"string\"==typeof n.value||n.issues.push({expected:\"string\",code:\"invalid_type\",input:n.value,inst:e}),n}})),tn=t(\"$ZodStringFormat\",((e,t)=>{Ut.init(e,t),en.init(e,t)})),nn=t(\"$ZodGUID\",((e,t)=>{t.pattern??(t.pattern=Ne),tn.init(e,t)})),rn=t(\"$ZodUUID\",((e,t)=>{if(t.version){const e={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[t.version];if(void 0===e)throw new Error(`Invalid UUID version: \"${t.version}\"`);t.pattern??(t.pattern=Re(e))}else t.pattern??(t.pattern=Re());tn.init(e,t)})),an=t(\"$ZodEmail\",((e,t)=>{t.pattern??(t.pattern=Ue),tn.init(e,t)})),on=t(\"$ZodURL\",((e,t)=>{tn.init(e,t),e._zod.check=n=>{try{const r=n.value.trim(),a=new URL(r);return t.hostname&&(t.hostname.lastIndex=0,t.hostname.test(a.hostname)||n.issues.push({code:\"invalid_format\",format:\"url\",note:\"Invalid hostname\",pattern:Ge.source,input:n.value,inst:e,continue:!t.abort})),t.protocol&&(t.protocol.lastIndex=0,t.protocol.test(a.protocol.endsWith(\":\")?a.protocol.slice(0,-1):a.protocol)||n.issues.push({code:\"invalid_format\",format:\"url\",note:\"Invalid protocol\",pattern:t.protocol.source,input:n.value,inst:e,continue:!t.abort})),void(t.normalize?n.value=a.href:n.value=r)}catch(r){n.issues.push({code:\"invalid_format\",format:\"url\",input:n.value,inst:e,continue:!t.abort})}}})),sn=t(\"$ZodEmoji\",((e,t)=>{t.pattern??(t.pattern=Qe()),tn.init(e,t)})),ln=t(\"$ZodNanoID\",((e,t)=>{t.pattern??(t.pattern=Ie),tn.init(e,t)})),cn=t(\"$ZodCUID\",((e,t)=>{t.pattern??(t.pattern=Ae),tn.init(e,t)})),un=t(\"$ZodCUID2\",((e,t)=>{t.pattern??(t.pattern=Ce),tn.init(e,t)})),dn=t(\"$ZodULID\",((e,t)=>{t.pattern??(t.pattern=Pe),tn.init(e,t)})),pn=t(\"$ZodXID\",((e,t)=>{t.pattern??(t.pattern=$e),tn.init(e,t)})),hn=t(\"$ZodKSUID\",((e,t)=>{t.pattern??(t.pattern=De),tn.init(e,t)})),fn=t(\"$ZodISODateTime\",((e,t)=>{t.pattern??(t.pattern=rt(t)),tn.init(e,t)})),mn=t(\"$ZodISODate\",((e,t)=>{t.pattern??(t.pattern=et),tn.init(e,t)})),gn=t(\"$ZodISOTime\",((e,t)=>{t.pattern??(t.pattern=nt(t)),tn.init(e,t)})),vn=t(\"$ZodISODuration\",((e,t)=>{t.pattern??(t.pattern=Me),tn.init(e,t)})),bn=t(\"$ZodIPv4\",((e,t)=>{t.pattern??(t.pattern=Ze),tn.init(e,t),e._zod.onattach.push((e=>{e._zod.bag.format=\"ipv4\"}))})),yn=t(\"$ZodIPv6\",((e,t)=>{t.pattern??(t.pattern=He),tn.init(e,t),e._zod.onattach.push((e=>{e._zod.bag.format=\"ipv6\"})),e._zod.check=n=>{try{new URL(`http://[${n.value}]`)}catch{n.issues.push({code:\"invalid_format\",format:\"ipv6\",input:n.value,inst:e,continue:!t.abort})}}})),On=t(\"$ZodCIDRv4\",((e,t)=>{t.pattern??(t.pattern=Ve),tn.init(e,t)})),wn=t(\"$ZodCIDRv6\",((e,t)=>{t.pattern??(t.pattern=qe),tn.init(e,t),e._zod.check=n=>{const r=n.value.split(\"/\");try{if(2!==r.length)throw new Error;const[e,t]=r;if(!t)throw new Error;const n=Number(t);if(`${n}`!==t)throw new Error;if(n<0||n>128)throw new Error;new URL(`http://[${e}]`)}catch{n.issues.push({code:\"invalid_format\",format:\"cidrv6\",input:n.value,inst:e,continue:!t.abort})}}}));function xn(e){if(\"\"===e)return!0;if(e.length%4!=0)return!1;try{return atob(e),!0}catch{return!1}}const kn=t(\"$ZodBase64\",((e,t)=>{t.pattern??(t.pattern=We),tn.init(e,t),e._zod.onattach.push((e=>{e._zod.bag.contentEncoding=\"base64\"})),e._zod.check=n=>{xn(n.value)||n.issues.push({code:\"invalid_format\",format:\"base64\",input:n.value,inst:e,continue:!t.abort})}}));function Sn(e){if(!Xe.test(e))return!1;const t=e.replace(/[-_]/g,(e=>\"-\"===e?\"+\":\"/\"));return xn(t.padEnd(4*Math.ceil(t.length/4),\"=\"))}const _n=t(\"$ZodBase64URL\",((e,t)=>{t.pattern??(t.pattern=Xe),tn.init(e,t),e._zod.onattach.push((e=>{e._zod.bag.contentEncoding=\"base64url\"})),e._zod.check=n=>{Sn(n.value)||n.issues.push({code:\"invalid_format\",format:\"base64url\",input:n.value,inst:e,continue:!t.abort})}})),Tn=t(\"$ZodE164\",((e,t)=>{t.pattern??(t.pattern=Ke),tn.init(e,t)}));function En(e,t=null){try{const n=e.split(\".\");if(3!==n.length)return!1;const[r]=n;if(!r)return!1;const a=JSON.parse(atob(r));return!(\"typ\"in a&&\"JWT\"!==a?.typ||!a.alg||t&&(!(\"alg\"in a)||a.alg!==t))}catch{return!1}}const An=t(\"$ZodJWT\",((e,t)=>{tn.init(e,t),e._zod.check=n=>{En(n.value,t.alg)||n.issues.push({code:\"invalid_format\",format:\"jwt\",input:n.value,inst:e,continue:!t.abort})}})),Cn=t(\"$ZodCustomStringFormat\",((e,t)=>{tn.init(e,t),e._zod.check=n=>{t.fn(n.value)||n.issues.push({code:\"invalid_format\",format:t.format,input:n.value,inst:e,continue:!t.abort})}})),Pn=t(\"$ZodNumber\",((e,t)=>{Jt.init(e,t),e._zod.pattern=e._zod.bag.pattern??st,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch(e){}const a=n.value;if(\"number\"==typeof a&&!Number.isNaN(a)&&Number.isFinite(a))return n;const o=\"number\"==typeof a?Number.isNaN(a)?\"NaN\":Number.isFinite(a)?void 0:\"Infinity\":void 0;return n.issues.push({expected:\"number\",code:\"invalid_type\",input:a,inst:e,...o?{received:o}:{}}),n}})),$n=t(\"$ZodNumber\",((e,t)=>{Dt.init(e,t),Pn.init(e,t)})),Dn=t(\"$ZodBoolean\",((e,t)=>{Jt.init(e,t),e._zod.pattern=lt,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Boolean(n.value)}catch(e){}const a=n.value;return\"boolean\"==typeof a||n.issues.push({expected:\"boolean\",code:\"invalid_type\",input:a,inst:e}),n}})),In=t(\"$ZodBigInt\",((e,t)=>{Jt.init(e,t),e._zod.pattern=ot,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=BigInt(n.value)}catch(e){}return\"bigint\"==typeof n.value||n.issues.push({expected:\"bigint\",code:\"invalid_type\",input:n.value,inst:e}),n}})),Mn=t(\"$ZodBigInt\",((e,t)=>{It.init(e,t),In.init(e,t)})),Nn=t(\"$ZodSymbol\",((e,t)=>{Jt.init(e,t),e._zod.parse=(t,n)=>{const r=t.value;return\"symbol\"==typeof r||t.issues.push({expected:\"symbol\",code:\"invalid_type\",input:r,inst:e}),t}})),Rn=t(\"$ZodUndefined\",((e,t)=>{Jt.init(e,t),e._zod.pattern=ut,e._zod.values=new Set([void 0]),e._zod.optin=\"optional\",e._zod.optout=\"optional\",e._zod.parse=(t,n)=>{const r=t.value;return void 0===r||t.issues.push({expected:\"undefined\",code:\"invalid_type\",input:r,inst:e}),t}})),Ln=t(\"$ZodNull\",((e,t)=>{Jt.init(e,t),e._zod.pattern=ct,e._zod.values=new Set([null]),e._zod.parse=(t,n)=>{const r=t.value;return null===r||t.issues.push({expected:\"null\",code:\"invalid_type\",input:r,inst:e}),t}})),Bn=t(\"$ZodAny\",((e,t)=>{Jt.init(e,t),e._zod.parse=e=>e})),jn=t(\"$ZodUnknown\",((e,t)=>{Jt.init(e,t),e._zod.parse=e=>e})),Un=t(\"$ZodNever\",((e,t)=>{Jt.init(e,t),e._zod.parse=(t,n)=>(t.issues.push({expected:\"never\",code:\"invalid_type\",input:t.value,inst:e}),t)})),zn=t(\"$ZodVoid\",((e,t)=>{Jt.init(e,t),e._zod.parse=(t,n)=>{const r=t.value;return void 0===r||t.issues.push({expected:\"void\",code:\"invalid_type\",input:r,inst:e}),t}})),Fn=t(\"$ZodDate\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=new Date(n.value)}catch(e){}const a=n.value,o=a instanceof Date;return o&&!Number.isNaN(a.getTime())||n.issues.push({expected:\"date\",code:\"invalid_type\",input:a,...o?{received:\"Invalid Date\"}:{},inst:e}),n}}));function Qn(e,t,n){e.issues.length&&t.issues.push(...z(n,e.issues)),t.value[n]=e.value}const Zn=t(\"$ZodArray\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,r)=>{const a=n.value;if(!Array.isArray(a))return n.issues.push({expected:\"array\",code:\"invalid_type\",input:a,inst:e}),n;n.value=Array(a.length);const o=[];for(let e=0;eQn(t,n,e)))):Qn(s,n,e)}return o.length?Promise.all(o).then((()=>n)):n}}));function Hn(e,t,n,r){e.issues.length&&t.issues.push(...z(n,e.issues)),void 0===e.value?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function Vn(e){const t=Object.keys(e.shape);for(const n of t)if(!e.shape?.[n]?._zod?.traits?.has(\"$ZodType\"))throw new Error(`Invalid element at key \"${n}\": expected a Zod schema`);const n=P(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function qn(e,t,n,r,a,o){const i=[],s=a.keySet,l=a.catchall._zod,c=l.def.type;for(const a of Object.keys(t)){if(s.has(a))continue;if(\"never\"===c){i.push(a);continue}const o=l.run({value:t[a],issues:[]},r);o instanceof Promise?e.push(o.then((e=>Hn(e,n,a,t)))):Hn(o,n,a,t)}return i.length&&n.issues.push({code:\"unrecognized_keys\",keys:i,input:t,inst:o}),e.length?Promise.all(e).then((()=>n)):n}const Wn=t(\"$ZodObject\",((e,t)=>{Jt.init(e,t);const n=Object.getOwnPropertyDescriptor(t,\"shape\");if(!n?.get){const e=t.shape;Object.defineProperty(t,\"shape\",{get:()=>{const n={...e};return Object.defineProperty(t,\"shape\",{value:n}),n}})}const r=u((()=>Vn(t)));m(e._zod,\"propValues\",(()=>{const e=t.shape,n={};for(const t in e){const r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(const e of r.values)n[t].add(e)}}return n}));const a=O,o=t.catchall;let i;e._zod.parse=(t,n)=>{i??(i=r.value);const s=t.value;if(!a(s))return t.issues.push({expected:\"object\",code:\"invalid_type\",input:s,inst:e}),t;t.value={};const l=[],c=i.shape;for(const e of i.keys){const r=c[e]._zod.run({value:s[e],issues:[]},n);r instanceof Promise?l.push(r.then((n=>Hn(n,t,e,s)))):Hn(r,t,e,s)}return o?qn(l,s,t,n,r.value,e):l.length?Promise.all(l).then((()=>t)):t}})),Xn=t(\"$ZodObjectJIT\",((e,t)=>{Wn.init(e,t);const n=e._zod.parse,r=u((()=>Vn(t)));let a;const i=O,s=!o.jitless,l=s&&w.value,c=t.catchall;let d;e._zod.parse=(o,u)=>{d??(d=r.value);const p=o.value;return i(p)?s&&l&&!1===u?.async&&!0!==u.jitless?(a||(a=(e=>{const t=new Yt([\"shape\",\"payload\",\"ctx\"]),n=r.value,a=e=>{const t=b(e);return`shape[${t}]._zod.run({ value: input[${t}], issues: [] }, ctx)`};t.write(\"const input = payload.value;\");const o=Object.create(null);let i=0;for(const e of n.keys)o[e]=\"key_\"+i++;t.write(\"const newResult = {};\");for(const e of n.keys){const n=o[e],r=b(e);t.write(`const ${n} = ${a(e)};`),t.write(`\\n if (${n}.issues.length) {\\n payload.issues = payload.issues.concat(${n}.issues.map(iss => ({\\n ...iss,\\n path: iss.path ? [${r}, ...iss.path] : [${r}]\\n })));\\n }\\n \\n \\n if (${n}.value === undefined) {\\n if (${r} in input) {\\n newResult[${r}] = undefined;\\n }\\n } else {\\n newResult[${r}] = ${n}.value;\\n }\\n \\n `)}t.write(\"payload.value = newResult;\"),t.write(\"return payload;\");const s=t.compile();return(t,n)=>s(e,t,n)})(t.shape)),o=a(o,u),c?qn([],p,o,u,d,e):o):n(o,u):(o.issues.push({expected:\"object\",code:\"invalid_type\",input:p,inst:e}),o)}}));function Gn(e,t,n,r){for(const n of e)if(0===n.issues.length)return t.value=n.value,t;const a=e.filter((e=>!U(e)));return 1===a.length?(t.value=a[0].value,a[0]):(t.issues.push({code:\"invalid_union\",input:t.value,inst:n,errors:e.map((e=>e.issues.map((e=>Q(e,r,i())))))}),t)}const Yn=t(\"$ZodUnion\",((e,t)=>{Jt.init(e,t),m(e._zod,\"optin\",(()=>t.options.some((e=>\"optional\"===e._zod.optin))?\"optional\":void 0)),m(e._zod,\"optout\",(()=>t.options.some((e=>\"optional\"===e._zod.optout))?\"optional\":void 0)),m(e._zod,\"values\",(()=>{if(t.options.every((e=>e._zod.values)))return new Set(t.options.flatMap((e=>Array.from(e._zod.values))))})),m(e._zod,\"pattern\",(()=>{if(t.options.every((e=>e._zod.pattern))){const e=t.options.map((e=>e._zod.pattern));return new RegExp(`^(${e.map((e=>p(e.source))).join(\"|\")})$`)}}));const n=1===t.options.length,r=t.options[0]._zod.run;e._zod.parse=(a,o)=>{if(n)return r(a,o);let i=!1;const s=[];for(const e of t.options){const t=e._zod.run({value:a.value,issues:[]},o);if(t instanceof Promise)s.push(t),i=!0;else{if(0===t.issues.length)return t;s.push(t)}}return i?Promise.all(s).then((t=>Gn(t,a,e,o))):Gn(s,a,e,o)}})),Kn=t(\"$ZodDiscriminatedUnion\",((e,t)=>{Yn.init(e,t);const n=e._zod.parse;m(e._zod,\"propValues\",(()=>{const e={};for(const n of t.options){const r=n._zod.propValues;if(!r||0===Object.keys(r).length)throw new Error(`Invalid discriminated union option at index \"${t.options.indexOf(n)}\"`);for(const[t,n]of Object.entries(r)){e[t]||(e[t]=new Set);for(const r of n)e[t].add(r)}}return e}));const r=u((()=>{const e=t.options,n=new Map;for(const r of e){const e=r._zod.propValues?.[t.discriminator];if(!e||0===e.size)throw new Error(`Invalid discriminated union option at index \"${t.options.indexOf(r)}\"`);for(const t of e){if(n.has(t))throw new Error(`Duplicate discriminator value \"${String(t)}\"`);n.set(t,r)}}return n}));e._zod.parse=(a,o)=>{const i=a.value;if(!O(i))return a.issues.push({code:\"invalid_type\",expected:\"object\",input:i,inst:e}),a;const s=r.value.get(i?.[t.discriminator]);return s?s._zod.run(a,o):t.unionFallback?n(a,o):(a.issues.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:t.discriminator,input:i,path:[t.discriminator],inst:e}),a)}})),Jn=t(\"$ZodIntersection\",((e,t)=>{Jt.init(e,t),e._zod.parse=(e,n)=>{const r=e.value,a=t.left._zod.run({value:r,issues:[]},n),o=t.right._zod.run({value:r,issues:[]},n);return a instanceof Promise||o instanceof Promise?Promise.all([a,o]).then((([t,n])=>tr(e,t,n))):tr(e,a,o)}}));function er(e,t){if(e===t)return{valid:!0,data:e};if(e instanceof Date&&t instanceof Date&&+e==+t)return{valid:!0,data:e};if(x(e)&&x(t)){const n=Object.keys(t),r=Object.keys(e).filter((e=>-1!==n.indexOf(e))),a={...e,...t};for(const n of r){const r=er(e[n],t[n]);if(!r.valid)return{valid:!1,mergeErrorPath:[n,...r.mergeErrorPath]};a[n]=r.data}return{valid:!0,data:a}}if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return{valid:!1,mergeErrorPath:[]};const n=[];for(let r=0;r{Jt.init(e,t);const n=t.items,r=n.length-[...n].reverse().findIndex((e=>\"optional\"!==e._zod.optin));e._zod.parse=(a,o)=>{const i=a.value;if(!Array.isArray(i))return a.issues.push({input:i,inst:e,expected:\"tuple\",code:\"invalid_type\"}),a;a.value=[];const s=[];if(!t.rest){const t=i.length>n.length,o=i.length=i.length&&l>=r)continue;const t=e._zod.run({value:i[l],issues:[]},o);t instanceof Promise?s.push(t.then((e=>rr(e,a,l)))):rr(t,a,l)}if(t.rest){const e=i.slice(n.length);for(const n of e){l++;const e=t.rest._zod.run({value:n,issues:[]},o);e instanceof Promise?s.push(e.then((e=>rr(e,a,l)))):rr(e,a,l)}}return s.length?Promise.all(s).then((()=>a)):a}}));function rr(e,t,n){e.issues.length&&t.issues.push(...z(n,e.issues)),t.value[n]=e.value}const ar=t(\"$ZodRecord\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,r)=>{const a=n.value;if(!x(a))return n.issues.push({expected:\"record\",code:\"invalid_type\",input:a,inst:e}),n;const o=[];if(t.keyType._zod.values){const i=t.keyType._zod.values;n.value={};for(const e of i)if(\"string\"==typeof e||\"number\"==typeof e||\"symbol\"==typeof e){const i=t.valueType._zod.run({value:a[e],issues:[]},r);i instanceof Promise?o.push(i.then((t=>{t.issues.length&&n.issues.push(...z(e,t.issues)),n.value[e]=t.value}))):(i.issues.length&&n.issues.push(...z(e,i.issues)),n.value[e]=i.value)}let s;for(const e in a)i.has(e)||(s=s??[],s.push(e));s&&s.length>0&&n.issues.push({code:\"unrecognized_keys\",input:a,inst:e,keys:s})}else{n.value={};for(const s of Reflect.ownKeys(a)){if(\"__proto__\"===s)continue;const l=t.keyType._zod.run({value:s,issues:[]},r);if(l instanceof Promise)throw new Error(\"Async schemas not supported in object keys currently\");if(l.issues.length){n.issues.push({code:\"invalid_key\",origin:\"record\",issues:l.issues.map((e=>Q(e,r,i()))),input:s,path:[s],inst:e}),n.value[l.value]=l.value;continue}const c=t.valueType._zod.run({value:a[s],issues:[]},r);c instanceof Promise?o.push(c.then((e=>{e.issues.length&&n.issues.push(...z(s,e.issues)),n.value[l.value]=e.value}))):(c.issues.length&&n.issues.push(...z(s,c.issues)),n.value[l.value]=c.value)}}return o.length?Promise.all(o).then((()=>n)):n}})),or=t(\"$ZodMap\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,r)=>{const a=n.value;if(!(a instanceof Map))return n.issues.push({expected:\"map\",code:\"invalid_type\",input:a,inst:e}),n;const o=[];n.value=new Map;for(const[i,s]of a){const l=t.keyType._zod.run({value:i,issues:[]},r),c=t.valueType._zod.run({value:s,issues:[]},r);l instanceof Promise||c instanceof Promise?o.push(Promise.all([l,c]).then((([t,o])=>{ir(t,o,n,i,a,e,r)}))):ir(l,c,n,i,a,e,r)}return o.length?Promise.all(o).then((()=>n)):n}}));function ir(e,t,n,r,a,o,s){e.issues.length&&(S.has(typeof r)?n.issues.push(...z(r,e.issues)):n.issues.push({code:\"invalid_key\",origin:\"map\",input:a,inst:o,issues:e.issues.map((e=>Q(e,s,i())))})),t.issues.length&&(S.has(typeof r)?n.issues.push(...z(r,t.issues)):n.issues.push({origin:\"map\",code:\"invalid_element\",input:a,inst:o,key:r,issues:t.issues.map((e=>Q(e,s,i())))})),n.value.set(e.value,t.value)}const sr=t(\"$ZodSet\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,r)=>{const a=n.value;if(!(a instanceof Set))return n.issues.push({input:a,inst:e,expected:\"set\",code:\"invalid_type\"}),n;const o=[];n.value=new Set;for(const e of a){const a=t.valueType._zod.run({value:e,issues:[]},r);a instanceof Promise?o.push(a.then((e=>lr(e,n)))):lr(a,n)}return o.length?Promise.all(o).then((()=>n)):n}}));function lr(e,t){e.issues.length&&t.issues.push(...e.issues),t.value.add(e.value)}const cr=t(\"$ZodEnum\",((e,t)=>{Jt.init(e,t);const n=s(t.entries),r=new Set(n);e._zod.values=r,e._zod.pattern=new RegExp(`^(${n.filter((e=>S.has(typeof e))).map((e=>\"string\"==typeof e?T(e):e.toString())).join(\"|\")})$`),e._zod.parse=(t,a)=>{const o=t.value;return r.has(o)||t.issues.push({code:\"invalid_value\",values:n,input:o,inst:e}),t}})),ur=t(\"$ZodLiteral\",((e,t)=>{if(Jt.init(e,t),0===t.values.length)throw new Error(\"Cannot create literal schema with no valid values\");e._zod.values=new Set(t.values),e._zod.pattern=new RegExp(`^(${t.values.map((e=>\"string\"==typeof e?T(e):e?T(e.toString()):String(e))).join(\"|\")})$`),e._zod.parse=(n,r)=>{const a=n.value;return e._zod.values.has(a)||n.issues.push({code:\"invalid_value\",values:t.values,input:a,inst:e}),n}})),dr=t(\"$ZodFile\",((e,t)=>{Jt.init(e,t),e._zod.parse=(t,n)=>{const r=t.value;return r instanceof File||t.issues.push({expected:\"file\",code:\"invalid_type\",input:r,inst:e}),t}})),pr=t(\"$ZodTransform\",((e,t)=>{Jt.init(e,t),e._zod.parse=(n,o)=>{if(\"backward\"===o.direction)throw new a(e.constructor.name);const i=t.transform(n.value,n);if(o.async)return(i instanceof Promise?i:Promise.resolve(i)).then((e=>(n.value=e,n)));if(i instanceof Promise)throw new r;return n.value=i,n}}));function hr(e,t){return e.issues.length&&void 0===t?{issues:[],value:void 0}:e}const fr=t(\"$ZodOptional\",((e,t)=>{Jt.init(e,t),e._zod.optin=\"optional\",e._zod.optout=\"optional\",m(e._zod,\"values\",(()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0)),m(e._zod,\"pattern\",(()=>{const e=t.innerType._zod.pattern;return e?new RegExp(`^(${p(e.source)})?$`):void 0})),e._zod.parse=(e,n)=>{if(\"optional\"===t.innerType._zod.optin){const r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then((t=>hr(t,e.value))):hr(r,e.value)}return void 0===e.value?e:t.innerType._zod.run(e,n)}})),mr=t(\"$ZodNullable\",((e,t)=>{Jt.init(e,t),m(e._zod,\"optin\",(()=>t.innerType._zod.optin)),m(e._zod,\"optout\",(()=>t.innerType._zod.optout)),m(e._zod,\"pattern\",(()=>{const e=t.innerType._zod.pattern;return e?new RegExp(`^(${p(e.source)}|null)$`):void 0})),m(e._zod,\"values\",(()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0)),e._zod.parse=(e,n)=>null===e.value?e:t.innerType._zod.run(e,n)})),gr=t(\"$ZodDefault\",((e,t)=>{Jt.init(e,t),e._zod.optin=\"optional\",m(e._zod,\"values\",(()=>t.innerType._zod.values)),e._zod.parse=(e,n)=>{if(\"backward\"===n.direction)return t.innerType._zod.run(e,n);if(void 0===e.value)return e.value=t.defaultValue,e;const r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then((e=>vr(e,t))):vr(r,t)}}));function vr(e,t){return void 0===e.value&&(e.value=t.defaultValue),e}const br=t(\"$ZodPrefault\",((e,t)=>{Jt.init(e,t),e._zod.optin=\"optional\",m(e._zod,\"values\",(()=>t.innerType._zod.values)),e._zod.parse=(e,n)=>(\"backward\"===n.direction||void 0===e.value&&(e.value=t.defaultValue),t.innerType._zod.run(e,n))})),yr=t(\"$ZodNonOptional\",((e,t)=>{Jt.init(e,t),m(e._zod,\"values\",(()=>{const e=t.innerType._zod.values;return e?new Set([...e].filter((e=>void 0!==e))):void 0})),e._zod.parse=(n,r)=>{const a=t.innerType._zod.run(n,r);return a instanceof Promise?a.then((t=>Or(t,e))):Or(a,e)}}));function Or(e,t){return e.issues.length||void 0!==e.value||e.issues.push({code:\"invalid_type\",expected:\"nonoptional\",input:e.value,inst:t}),e}const wr=t(\"$ZodSuccess\",((e,t)=>{Jt.init(e,t),e._zod.parse=(e,n)=>{if(\"backward\"===n.direction)throw new a(\"ZodSuccess\");const r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then((t=>(e.value=0===t.issues.length,e))):(e.value=0===r.issues.length,e)}})),xr=t(\"$ZodCatch\",((e,t)=>{Jt.init(e,t),m(e._zod,\"optin\",(()=>t.innerType._zod.optin)),m(e._zod,\"optout\",(()=>t.innerType._zod.optout)),m(e._zod,\"values\",(()=>t.innerType._zod.values)),e._zod.parse=(e,n)=>{if(\"backward\"===n.direction)return t.innerType._zod.run(e,n);const r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then((r=>(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map((e=>Q(e,n,i())))},input:e.value}),e.issues=[]),e))):(e.value=r.value,r.issues.length&&(e.value=t.catchValue({...e,error:{issues:r.issues.map((e=>Q(e,n,i())))},input:e.value}),e.issues=[]),e)}})),kr=t(\"$ZodNaN\",((e,t)=>{Jt.init(e,t),e._zod.parse=(t,n)=>(\"number\"==typeof t.value&&Number.isNaN(t.value)||t.issues.push({input:t.value,inst:e,expected:\"nan\",code:\"invalid_type\"}),t)})),Sr=t(\"$ZodPipe\",((e,t)=>{Jt.init(e,t),m(e._zod,\"values\",(()=>t.in._zod.values)),m(e._zod,\"optin\",(()=>t.in._zod.optin)),m(e._zod,\"optout\",(()=>t.out._zod.optout)),m(e._zod,\"propValues\",(()=>t.in._zod.propValues)),e._zod.parse=(e,n)=>{if(\"backward\"===n.direction){const r=t.out._zod.run(e,n);return r instanceof Promise?r.then((e=>_r(e,t.in,n))):_r(r,t.in,n)}const r=t.in._zod.run(e,n);return r instanceof Promise?r.then((e=>_r(e,t.out,n))):_r(r,t.out,n)}}));function _r(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues},n)}const Tr=t(\"$ZodCodec\",((e,t)=>{Jt.init(e,t),m(e._zod,\"values\",(()=>t.in._zod.values)),m(e._zod,\"optin\",(()=>t.in._zod.optin)),m(e._zod,\"optout\",(()=>t.out._zod.optout)),m(e._zod,\"propValues\",(()=>t.in._zod.propValues)),e._zod.parse=(e,n)=>{if(\"forward\"===(n.direction||\"forward\")){const r=t.in._zod.run(e,n);return r instanceof Promise?r.then((e=>Er(e,t,n))):Er(r,t,n)}{const r=t.out._zod.run(e,n);return r instanceof Promise?r.then((e=>Er(e,t,n))):Er(r,t,n)}}}));function Er(e,t,n){if(e.issues.length)return e.aborted=!0,e;if(\"forward\"===(n.direction||\"forward\")){const r=t.transform(e.value,e);return r instanceof Promise?r.then((r=>Ar(e,r,t.out,n))):Ar(e,r,t.out,n)}{const r=t.reverseTransform(e.value,e);return r instanceof Promise?r.then((r=>Ar(e,r,t.in,n))):Ar(e,r,t.in,n)}}function Ar(e,t,n,r){return e.issues.length?(e.aborted=!0,e):n._zod.run({value:t,issues:e.issues},r)}const Cr=t(\"$ZodReadonly\",((e,t)=>{Jt.init(e,t),m(e._zod,\"propValues\",(()=>t.innerType._zod.propValues)),m(e._zod,\"values\",(()=>t.innerType._zod.values)),m(e._zod,\"optin\",(()=>t.innerType._zod.optin)),m(e._zod,\"optout\",(()=>t.innerType._zod.optout)),e._zod.parse=(e,n)=>{if(\"backward\"===n.direction)return t.innerType._zod.run(e,n);const r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(Pr):Pr(r)}}));function Pr(e){return e.value=Object.freeze(e.value),e}const $r=t(\"$ZodTemplateLiteral\",((e,t)=>{Jt.init(e,t);const n=[];for(const e of t.parts)if(\"object\"==typeof e&&null!==e){if(!e._zod.pattern)throw new Error(`Invalid template literal part, no pattern found: ${[...e._zod.traits].shift()}`);const t=e._zod.pattern instanceof RegExp?e._zod.pattern.source:e._zod.pattern;if(!t)throw new Error(`Invalid template literal part: ${e._zod.traits}`);const r=t.startsWith(\"^\")?1:0,a=t.endsWith(\"$\")?t.length-1:t.length;n.push(t.slice(r,a))}else{if(null!==e&&!_.has(typeof e))throw new Error(`Invalid template literal part: ${e}`);n.push(T(`${e}`))}e._zod.pattern=new RegExp(`^${n.join(\"\")}$`),e._zod.parse=(n,r)=>\"string\"!=typeof n.value?(n.issues.push({input:n.value,inst:e,expected:\"template_literal\",code:\"invalid_type\"}),n):(e._zod.pattern.lastIndex=0,e._zod.pattern.test(n.value)||n.issues.push({input:n.value,inst:e,code:\"invalid_format\",format:t.format??\"template_literal\",pattern:e._zod.pattern.source}),n)})),Dr=t(\"$ZodFunction\",((e,t)=>(Jt.init(e,t),e._def=t,e._zod.def=t,e.implement=t=>{if(\"function\"!=typeof t)throw new Error(\"implement() must be called with a function\");return function(...n){const r=e._def.input?oe(e._def.input,n):n,a=Reflect.apply(t,this,r);return e._def.output?oe(e._def.output,a):a}},e.implementAsync=t=>{if(\"function\"!=typeof t)throw new Error(\"implementAsync() must be called with a function\");return async function(...n){const r=e._def.input?await se(e._def.input,n):n,a=await Reflect.apply(t,this,r);return e._def.output?await se(e._def.output,a):a}},e._zod.parse=(t,n)=>{if(\"function\"!=typeof t.value)return t.issues.push({code:\"invalid_type\",expected:\"function\",input:t.value,inst:e}),t;const r=e._def.output&&\"promise\"===e._def.output._zod.def.type;return t.value=r?e.implementAsync(t.value):e.implement(t.value),t},e.input=(...t)=>{const n=e.constructor;return Array.isArray(t[0])?new n({type:\"function\",input:new nr({type:\"tuple\",items:t[0],rest:t[1]}),output:e._def.output}):new n({type:\"function\",input:t[0],output:e._def.output})},e.output=t=>new(0,e.constructor)({type:\"function\",input:e._def.input,output:t}),e))),Ir=t(\"$ZodPromise\",((e,t)=>{Jt.init(e,t),e._zod.parse=(e,n)=>Promise.resolve(e.value).then((e=>t.innerType._zod.run({value:e,issues:[]},n)))})),Mr=t(\"$ZodLazy\",((e,t)=>{Jt.init(e,t),m(e._zod,\"innerType\",(()=>t.getter())),m(e._zod,\"pattern\",(()=>e._zod.innerType._zod.pattern)),m(e._zod,\"propValues\",(()=>e._zod.innerType._zod.propValues)),m(e._zod,\"optin\",(()=>e._zod.innerType._zod.optin??void 0)),m(e._zod,\"optout\",(()=>e._zod.innerType._zod.optout??void 0)),e._zod.parse=(t,n)=>e._zod.innerType._zod.run(t,n)})),Nr=t(\"$ZodCustom\",((e,t)=>{Et.init(e,t),Jt.init(e,t),e._zod.parse=(e,t)=>e,e._zod.check=n=>{const r=n.value,a=t.fn(r);if(a instanceof Promise)return a.then((t=>Rr(t,n,r,e)));Rr(a,n,r,e)}}));function Rr(e,t,n,r){if(!e){const e={code:\"custom\",input:n,inst:r,path:[...r._zod.def.path??[]],continue:!r._zod.def.abort};r._zod.def.params&&(e.params=r._zod.def.params),t.issues.push(V(e))}}const Lr=()=>{const e={string:{unit:\"حرف\",verb:\"أن يحوي\"},file:{unit:\"بايت\",verb:\"أن يحوي\"},array:{unit:\"عنصر\",verb:\"أن يحوي\"},set:{unit:\"عنصر\",verb:\"أن يحوي\"}};function t(t){return e[t]??null}const n={regex:\"مدخل\",email:\"بريد إلكتروني\",url:\"رابط\",emoji:\"إيموجي\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"تاريخ ووقت بمعيار ISO\",date:\"تاريخ بمعيار ISO\",time:\"وقت بمعيار ISO\",duration:\"مدة بمعيار ISO\",ipv4:\"عنوان IPv4\",ipv6:\"عنوان IPv6\",cidrv4:\"مدى عناوين بصيغة IPv4\",cidrv6:\"مدى عناوين بصيغة IPv6\",base64:\"نَص بترميز base64-encoded\",base64url:\"نَص بترميز base64url-encoded\",json_string:\"نَص على هيئة JSON\",e164:\"رقم هاتف بمعيار E.164\",jwt:\"JWT\",template_literal:\"مدخل\"};return e=>{switch(e.code){case\"invalid_type\":return`مدخلات غير مقبولة: يفترض إدخال ${e.expected}، ولكن تم إدخال ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`مدخلات غير مقبولة: يفترض إدخال ${C(e.values[0])}`:`اختيار غير مقبول: يتوقع انتقاء أحد هذه الخيارات: ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?` أكبر من اللازم: يفترض أن تكون ${e.origin??\"القيمة\"} ${n} ${e.maximum.toString()} ${r.unit??\"عنصر\"}`:`أكبر من اللازم: يفترض أن تكون ${e.origin??\"القيمة\"} ${n} ${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`أصغر من اللازم: يفترض لـ ${e.origin} أن يكون ${n} ${e.minimum.toString()} ${r.unit}`:`أصغر من اللازم: يفترض لـ ${e.origin} أن يكون ${n} ${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`نَص غير مقبول: يجب أن يبدأ بـ \"${e.prefix}\"`:\"ends_with\"===t.format?`نَص غير مقبول: يجب أن ينتهي بـ \"${t.suffix}\"`:\"includes\"===t.format?`نَص غير مقبول: يجب أن يتضمَّن \"${t.includes}\"`:\"regex\"===t.format?`نَص غير مقبول: يجب أن يطابق النمط ${t.pattern}`:`${n[t.format]??e.format} غير مقبول`}case\"not_multiple_of\":return`رقم غير مقبول: يجب أن يكون من مضاعفات ${e.divisor}`;case\"unrecognized_keys\":return`معرف${e.keys.length>1?\"ات\":\"\"} غريب${e.keys.length>1?\"ة\":\"\"}: ${l(e.keys,\"، \")}`;case\"invalid_key\":return`معرف غير مقبول في ${e.origin}`;case\"invalid_union\":default:return\"مدخل غير مقبول\";case\"invalid_element\":return`مدخل غير مقبول في ${e.origin}`}}},Br=()=>{const e={string:{unit:\"simvol\",verb:\"olmalıdır\"},file:{unit:\"bayt\",verb:\"olmalıdır\"},array:{unit:\"element\",verb:\"olmalıdır\"},set:{unit:\"element\",verb:\"olmalıdır\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"email address\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO datetime\",date:\"ISO date\",time:\"ISO time\",duration:\"ISO duration\",ipv4:\"IPv4 address\",ipv6:\"IPv6 address\",cidrv4:\"IPv4 range\",cidrv6:\"IPv6 range\",base64:\"base64-encoded string\",base64url:\"base64url-encoded string\",json_string:\"JSON string\",e164:\"E.164 number\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Yanlış dəyər: gözlənilən ${e.expected}, daxil olan ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Yanlış dəyər: gözlənilən ${C(e.values[0])}`:`Yanlış seçim: aşağıdakılardan biri olmalıdır: ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Çox böyük: gözlənilən ${e.origin??\"dəyər\"} ${n}${e.maximum.toString()} ${r.unit??\"element\"}`:`Çox böyük: gözlənilən ${e.origin??\"dəyər\"} ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Çox kiçik: gözlənilən ${e.origin} ${n}${e.minimum.toString()} ${r.unit}`:`Çox kiçik: gözlənilən ${e.origin} ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Yanlış mətn: \"${t.prefix}\" ilə başlamalıdır`:\"ends_with\"===t.format?`Yanlış mətn: \"${t.suffix}\" ilə bitməlidir`:\"includes\"===t.format?`Yanlış mətn: \"${t.includes}\" daxil olmalıdır`:\"regex\"===t.format?`Yanlış mətn: ${t.pattern} şablonuna uyğun olmalıdır`:`Yanlış ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Yanlış ədəd: ${e.divisor} ilə bölünə bilən olmalıdır`;case\"unrecognized_keys\":return`Tanınmayan açar${e.keys.length>1?\"lar\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`${e.origin} daxilində yanlış açar`;case\"invalid_union\":default:return\"Yanlış dəyər\";case\"invalid_element\":return`${e.origin} daxilində yanlış dəyər`}}};function jr(e,t,n,r){const a=Math.abs(e),o=a%10,i=a%100;return i>=11&&i<=19?r:1===o?t:o>=2&&o<=4?n:r}const Ur=()=>{const e={string:{unit:{one:\"сімвал\",few:\"сімвалы\",many:\"сімвалаў\"},verb:\"мець\"},array:{unit:{one:\"элемент\",few:\"элементы\",many:\"элементаў\"},verb:\"мець\"},set:{unit:{one:\"элемент\",few:\"элементы\",many:\"элементаў\"},verb:\"мець\"},file:{unit:{one:\"байт\",few:\"байты\",many:\"байтаў\"},verb:\"мець\"}};function t(t){return e[t]??null}const n={regex:\"увод\",email:\"email адрас\",url:\"URL\",emoji:\"эмодзі\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO дата і час\",date:\"ISO дата\",time:\"ISO час\",duration:\"ISO працягласць\",ipv4:\"IPv4 адрас\",ipv6:\"IPv6 адрас\",cidrv4:\"IPv4 дыяпазон\",cidrv6:\"IPv6 дыяпазон\",base64:\"радок у фармаце base64\",base64url:\"радок у фармаце base64url\",json_string:\"JSON радок\",e164:\"нумар E.164\",jwt:\"JWT\",template_literal:\"увод\"};return e=>{switch(e.code){case\"invalid_type\":return`Няправільны ўвод: чакаўся ${e.expected}, атрымана ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"лік\";case\"object\":if(Array.isArray(e))return\"масіў\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Няправільны ўвод: чакалася ${C(e.values[0])}`:`Няправільны варыянт: чакаўся адзін з ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);if(r){const t=jr(Number(e.maximum),r.unit.one,r.unit.few,r.unit.many);return`Занадта вялікі: чакалася, што ${e.origin??\"значэнне\"} павінна ${r.verb} ${n}${e.maximum.toString()} ${t}`}return`Занадта вялікі: чакалася, што ${e.origin??\"значэнне\"} павінна быць ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);if(r){const t=jr(Number(e.minimum),r.unit.one,r.unit.few,r.unit.many);return`Занадта малы: чакалася, што ${e.origin} павінна ${r.verb} ${n}${e.minimum.toString()} ${t}`}return`Занадта малы: чакалася, што ${e.origin} павінна быць ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Няправільны радок: павінен пачынацца з \"${t.prefix}\"`:\"ends_with\"===t.format?`Няправільны радок: павінен заканчвацца на \"${t.suffix}\"`:\"includes\"===t.format?`Няправільны радок: павінен змяшчаць \"${t.includes}\"`:\"regex\"===t.format?`Няправільны радок: павінен адпавядаць шаблону ${t.pattern}`:`Няправільны ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Няправільны лік: павінен быць кратным ${e.divisor}`;case\"unrecognized_keys\":return`Нераспазнаны ${e.keys.length>1?\"ключы\":\"ключ\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Няправільны ключ у ${e.origin}`;case\"invalid_union\":default:return\"Няправільны ўвод\";case\"invalid_element\":return`Няправільнае значэнне ў ${e.origin}`}}},zr=()=>{const e={string:{unit:\"caràcters\",verb:\"contenir\"},file:{unit:\"bytes\",verb:\"contenir\"},array:{unit:\"elements\",verb:\"contenir\"},set:{unit:\"elements\",verb:\"contenir\"}};function t(t){return e[t]??null}const n={regex:\"entrada\",email:\"adreça electrònica\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"data i hora ISO\",date:\"data ISO\",time:\"hora ISO\",duration:\"durada ISO\",ipv4:\"adreça IPv4\",ipv6:\"adreça IPv6\",cidrv4:\"rang IPv4\",cidrv6:\"rang IPv6\",base64:\"cadena codificada en base64\",base64url:\"cadena codificada en base64url\",json_string:\"cadena JSON\",e164:\"número E.164\",jwt:\"JWT\",template_literal:\"entrada\"};return e=>{switch(e.code){case\"invalid_type\":return`Tipus invàlid: s'esperava ${e.expected}, s'ha rebut ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Valor invàlid: s'esperava ${C(e.values[0])}`:`Opció invàlida: s'esperava una de ${l(e.values,\" o \")}`;case\"too_big\":{const n=e.inclusive?\"com a màxim\":\"menys de\",r=t(e.origin);return r?`Massa gran: s'esperava que ${e.origin??\"el valor\"} contingués ${n} ${e.maximum.toString()} ${r.unit??\"elements\"}`:`Massa gran: s'esperava que ${e.origin??\"el valor\"} fos ${n} ${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\"com a mínim\":\"més de\",r=t(e.origin);return r?`Massa petit: s'esperava que ${e.origin} contingués ${n} ${e.minimum.toString()} ${r.unit}`:`Massa petit: s'esperava que ${e.origin} fos ${n} ${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Format invàlid: ha de començar amb \"${t.prefix}\"`:\"ends_with\"===t.format?`Format invàlid: ha d'acabar amb \"${t.suffix}\"`:\"includes\"===t.format?`Format invàlid: ha d'incloure \"${t.includes}\"`:\"regex\"===t.format?`Format invàlid: ha de coincidir amb el patró ${t.pattern}`:`Format invàlid per a ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Número invàlid: ha de ser múltiple de ${e.divisor}`;case\"unrecognized_keys\":return`Clau${e.keys.length>1?\"s\":\"\"} no reconeguda${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Clau invàlida a ${e.origin}`;case\"invalid_union\":default:return\"Entrada invàlida\";case\"invalid_element\":return`Element invàlid a ${e.origin}`}}},Fr=()=>{const e={string:{unit:\"znaků\",verb:\"mít\"},file:{unit:\"bajtů\",verb:\"mít\"},array:{unit:\"prvků\",verb:\"mít\"},set:{unit:\"prvků\",verb:\"mít\"}};function t(t){return e[t]??null}const n={regex:\"regulární výraz\",email:\"e-mailová adresa\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"datum a čas ve formátu ISO\",date:\"datum ve formátu ISO\",time:\"čas ve formátu ISO\",duration:\"doba trvání ISO\",ipv4:\"IPv4 adresa\",ipv6:\"IPv6 adresa\",cidrv4:\"rozsah IPv4\",cidrv6:\"rozsah IPv6\",base64:\"řetězec zakódovaný ve formátu base64\",base64url:\"řetězec zakódovaný ve formátu base64url\",json_string:\"řetězec ve formátu JSON\",e164:\"číslo E.164\",jwt:\"JWT\",template_literal:\"vstup\"};return e=>{switch(e.code){case\"invalid_type\":return`Neplatný vstup: očekáváno ${e.expected}, obdrženo ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"číslo\";case\"string\":return\"řetězec\";case\"boolean\":return\"boolean\";case\"bigint\":return\"bigint\";case\"function\":return\"funkce\";case\"symbol\":return\"symbol\";case\"undefined\":return\"undefined\";case\"object\":if(Array.isArray(e))return\"pole\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Neplatný vstup: očekáváno ${C(e.values[0])}`:`Neplatná možnost: očekávána jedna z hodnot ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Hodnota je příliš velká: ${e.origin??\"hodnota\"} musí mít ${n}${e.maximum.toString()} ${r.unit??\"prvků\"}`:`Hodnota je příliš velká: ${e.origin??\"hodnota\"} musí být ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Hodnota je příliš malá: ${e.origin??\"hodnota\"} musí mít ${n}${e.minimum.toString()} ${r.unit??\"prvků\"}`:`Hodnota je příliš malá: ${e.origin??\"hodnota\"} musí být ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Neplatný řetězec: musí začínat na \"${t.prefix}\"`:\"ends_with\"===t.format?`Neplatný řetězec: musí končit na \"${t.suffix}\"`:\"includes\"===t.format?`Neplatný řetězec: musí obsahovat \"${t.includes}\"`:\"regex\"===t.format?`Neplatný řetězec: musí odpovídat vzoru ${t.pattern}`:`Neplatný formát ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Neplatné číslo: musí být násobkem ${e.divisor}`;case\"unrecognized_keys\":return`Neznámé klíče: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Neplatný klíč v ${e.origin}`;case\"invalid_union\":default:return\"Neplatný vstup\";case\"invalid_element\":return`Neplatná hodnota v ${e.origin}`}}},Qr=()=>{const e={string:{unit:\"tegn\",verb:\"havde\"},file:{unit:\"bytes\",verb:\"havde\"},array:{unit:\"elementer\",verb:\"indeholdt\"},set:{unit:\"elementer\",verb:\"indeholdt\"}},t={string:\"streng\",number:\"tal\",boolean:\"boolean\",array:\"liste\",object:\"objekt\",set:\"sæt\",file:\"fil\"};function n(t){return e[t]??null}function r(e){return t[e]??e}const a={regex:\"input\",email:\"e-mailadresse\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO dato- og klokkeslæt\",date:\"ISO-dato\",time:\"ISO-klokkeslæt\",duration:\"ISO-varighed\",ipv4:\"IPv4-område\",ipv6:\"IPv6-område\",cidrv4:\"IPv4-spektrum\",cidrv6:\"IPv6-spektrum\",base64:\"base64-kodet streng\",base64url:\"base64url-kodet streng\",json_string:\"JSON-streng\",e164:\"E.164-nummer\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Ugyldigt input: forventede ${r(e.expected)}, fik ${r((e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"tal\";case\"object\":return Array.isArray(e)?\"liste\":null===e?\"null\":Object.getPrototypeOf(e)!==Object.prototype&&e.constructor?e.constructor.name:\"objekt\"}return t})(e.input))}`;case\"invalid_value\":return 1===e.values.length?`Ugyldig værdi: forventede ${C(e.values[0])}`:`Ugyldigt valg: forventede en af følgende ${l(e.values,\"|\")}`;case\"too_big\":{const t=e.inclusive?\"<=\":\"<\",a=n(e.origin),o=r(e.origin);return a?`For stor: forventede ${o??\"value\"} ${a.verb} ${t} ${e.maximum.toString()} ${a.unit??\"elementer\"}`:`For stor: forventede ${o??\"value\"} havde ${t} ${e.maximum.toString()}`}case\"too_small\":{const t=e.inclusive?\">=\":\">\",a=n(e.origin),o=r(e.origin);return a?`For lille: forventede ${o} ${a.verb} ${t} ${e.minimum.toString()} ${a.unit}`:`For lille: forventede ${o} havde ${t} ${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ugyldig streng: skal starte med \"${t.prefix}\"`:\"ends_with\"===t.format?`Ugyldig streng: skal ende med \"${t.suffix}\"`:\"includes\"===t.format?`Ugyldig streng: skal indeholde \"${t.includes}\"`:\"regex\"===t.format?`Ugyldig streng: skal matche mønsteret ${t.pattern}`:`Ugyldig ${a[t.format]??e.format}`}case\"not_multiple_of\":return`Ugyldigt tal: skal være deleligt med ${e.divisor}`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Ukendte nøgler\":\"Ukendt nøgle\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Ugyldig nøgle i ${e.origin}`;case\"invalid_union\":return\"Ugyldigt input: matcher ingen af de tilladte typer\";case\"invalid_element\":return`Ugyldig værdi i ${e.origin}`;default:return\"Ugyldigt input\"}}},Zr=()=>{const e={string:{unit:\"Zeichen\",verb:\"zu haben\"},file:{unit:\"Bytes\",verb:\"zu haben\"},array:{unit:\"Elemente\",verb:\"zu haben\"},set:{unit:\"Elemente\",verb:\"zu haben\"}};function t(t){return e[t]??null}const n={regex:\"Eingabe\",email:\"E-Mail-Adresse\",url:\"URL\",emoji:\"Emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO-Datum und -Uhrzeit\",date:\"ISO-Datum\",time:\"ISO-Uhrzeit\",duration:\"ISO-Dauer\",ipv4:\"IPv4-Adresse\",ipv6:\"IPv6-Adresse\",cidrv4:\"IPv4-Bereich\",cidrv6:\"IPv6-Bereich\",base64:\"Base64-codierter String\",base64url:\"Base64-URL-codierter String\",json_string:\"JSON-String\",e164:\"E.164-Nummer\",jwt:\"JWT\",template_literal:\"Eingabe\"};return e=>{switch(e.code){case\"invalid_type\":return`Ungültige Eingabe: erwartet ${e.expected}, erhalten ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"Zahl\";case\"object\":if(Array.isArray(e))return\"Array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Ungültige Eingabe: erwartet ${C(e.values[0])}`:`Ungültige Option: erwartet eine von ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Zu groß: erwartet, dass ${e.origin??\"Wert\"} ${n}${e.maximum.toString()} ${r.unit??\"Elemente\"} hat`:`Zu groß: erwartet, dass ${e.origin??\"Wert\"} ${n}${e.maximum.toString()} ist`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Zu klein: erwartet, dass ${e.origin} ${n}${e.minimum.toString()} ${r.unit} hat`:`Zu klein: erwartet, dass ${e.origin} ${n}${e.minimum.toString()} ist`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ungültiger String: muss mit \"${t.prefix}\" beginnen`:\"ends_with\"===t.format?`Ungültiger String: muss mit \"${t.suffix}\" enden`:\"includes\"===t.format?`Ungültiger String: muss \"${t.includes}\" enthalten`:\"regex\"===t.format?`Ungültiger String: muss dem Muster ${t.pattern} entsprechen`:`Ungültig: ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Ungültige Zahl: muss ein Vielfaches von ${e.divisor} sein`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Unbekannte Schlüssel\":\"Unbekannter Schlüssel\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Ungültiger Schlüssel in ${e.origin}`;case\"invalid_union\":default:return\"Ungültige Eingabe\";case\"invalid_element\":return`Ungültiger Wert in ${e.origin}`}}},Hr=()=>{const e={string:{unit:\"characters\",verb:\"to have\"},file:{unit:\"bytes\",verb:\"to have\"},array:{unit:\"items\",verb:\"to have\"},set:{unit:\"items\",verb:\"to have\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"email address\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO datetime\",date:\"ISO date\",time:\"ISO time\",duration:\"ISO duration\",ipv4:\"IPv4 address\",ipv6:\"IPv6 address\",cidrv4:\"IPv4 range\",cidrv6:\"IPv6 range\",base64:\"base64-encoded string\",base64url:\"base64url-encoded string\",json_string:\"JSON string\",e164:\"E.164 number\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Invalid input: expected ${e.expected}, received ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Invalid input: expected ${C(e.values[0])}`:`Invalid option: expected one of ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Too big: expected ${e.origin??\"value\"} to have ${n}${e.maximum.toString()} ${r.unit??\"elements\"}`:`Too big: expected ${e.origin??\"value\"} to be ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Too small: expected ${e.origin} to have ${n}${e.minimum.toString()} ${r.unit}`:`Too small: expected ${e.origin} to be ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Invalid string: must start with \"${t.prefix}\"`:\"ends_with\"===t.format?`Invalid string: must end with \"${t.suffix}\"`:\"includes\"===t.format?`Invalid string: must include \"${t.includes}\"`:\"regex\"===t.format?`Invalid string: must match pattern ${t.pattern}`:`Invalid ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Invalid number: must be a multiple of ${e.divisor}`;case\"unrecognized_keys\":return`Unrecognized key${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Invalid key in ${e.origin}`;case\"invalid_union\":default:return\"Invalid input\";case\"invalid_element\":return`Invalid value in ${e.origin}`}}};function Vr(){return{localeError:Hr()}}const qr=()=>{const e={string:{unit:\"karaktrojn\",verb:\"havi\"},file:{unit:\"bajtojn\",verb:\"havi\"},array:{unit:\"elementojn\",verb:\"havi\"},set:{unit:\"elementojn\",verb:\"havi\"}};function t(t){return e[t]??null}const n={regex:\"enigo\",email:\"retadreso\",url:\"URL\",emoji:\"emoĝio\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO-datotempo\",date:\"ISO-dato\",time:\"ISO-tempo\",duration:\"ISO-daŭro\",ipv4:\"IPv4-adreso\",ipv6:\"IPv6-adreso\",cidrv4:\"IPv4-rango\",cidrv6:\"IPv6-rango\",base64:\"64-ume kodita karaktraro\",base64url:\"URL-64-ume kodita karaktraro\",json_string:\"JSON-karaktraro\",e164:\"E.164-nombro\",jwt:\"JWT\",template_literal:\"enigo\"};return e=>{switch(e.code){case\"invalid_type\":return`Nevalida enigo: atendiĝis ${e.expected}, riceviĝis ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"nombro\";case\"object\":if(Array.isArray(e))return\"tabelo\";if(null===e)return\"senvalora\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Nevalida enigo: atendiĝis ${C(e.values[0])}`:`Nevalida opcio: atendiĝis unu el ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Tro granda: atendiĝis ke ${e.origin??\"valoro\"} havu ${n}${e.maximum.toString()} ${r.unit??\"elementojn\"}`:`Tro granda: atendiĝis ke ${e.origin??\"valoro\"} havu ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Tro malgranda: atendiĝis ke ${e.origin} havu ${n}${e.minimum.toString()} ${r.unit}`:`Tro malgranda: atendiĝis ke ${e.origin} estu ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Nevalida karaktraro: devas komenciĝi per \"${t.prefix}\"`:\"ends_with\"===t.format?`Nevalida karaktraro: devas finiĝi per \"${t.suffix}\"`:\"includes\"===t.format?`Nevalida karaktraro: devas inkluzivi \"${t.includes}\"`:\"regex\"===t.format?`Nevalida karaktraro: devas kongrui kun la modelo ${t.pattern}`:`Nevalida ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Nevalida nombro: devas esti oblo de ${e.divisor}`;case\"unrecognized_keys\":return`Nekonata${e.keys.length>1?\"j\":\"\"} ŝlosilo${e.keys.length>1?\"j\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Nevalida ŝlosilo en ${e.origin}`;case\"invalid_union\":default:return\"Nevalida enigo\";case\"invalid_element\":return`Nevalida valoro en ${e.origin}`}}},Wr=()=>{const e={string:{unit:\"caracteres\",verb:\"tener\"},file:{unit:\"bytes\",verb:\"tener\"},array:{unit:\"elementos\",verb:\"tener\"},set:{unit:\"elementos\",verb:\"tener\"}},t={string:\"texto\",number:\"número\",boolean:\"booleano\",array:\"arreglo\",object:\"objeto\",set:\"conjunto\",file:\"archivo\",date:\"fecha\",bigint:\"número grande\",symbol:\"símbolo\",undefined:\"indefinido\",null:\"nulo\",function:\"función\",map:\"mapa\",record:\"registro\",tuple:\"tupla\",enum:\"enumeración\",union:\"unión\",literal:\"literal\",promise:\"promesa\",void:\"vacío\",never:\"nunca\",unknown:\"desconocido\",any:\"cualquiera\"};function n(t){return e[t]??null}function r(e){return t[e]??e}const a={regex:\"entrada\",email:\"dirección de correo electrónico\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"fecha y hora ISO\",date:\"fecha ISO\",time:\"hora ISO\",duration:\"duración ISO\",ipv4:\"dirección IPv4\",ipv6:\"dirección IPv6\",cidrv4:\"rango IPv4\",cidrv6:\"rango IPv6\",base64:\"cadena codificada en base64\",base64url:\"URL codificada en base64\",json_string:\"cadena JSON\",e164:\"número E.164\",jwt:\"JWT\",template_literal:\"entrada\"};return e=>{switch(e.code){case\"invalid_type\":return`Entrada inválida: se esperaba ${r(e.expected)}, recibido ${r((e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":return Array.isArray(e)?\"array\":null===e?\"null\":Object.getPrototypeOf(e)!==Object.prototype?e.constructor.name:\"object\"}return t})(e.input))}`;case\"invalid_value\":return 1===e.values.length?`Entrada inválida: se esperaba ${C(e.values[0])}`:`Opción inválida: se esperaba una de ${l(e.values,\"|\")}`;case\"too_big\":{const t=e.inclusive?\"<=\":\"<\",a=n(e.origin),o=r(e.origin);return a?`Demasiado grande: se esperaba que ${o??\"valor\"} tuviera ${t}${e.maximum.toString()} ${a.unit??\"elementos\"}`:`Demasiado grande: se esperaba que ${o??\"valor\"} fuera ${t}${e.maximum.toString()}`}case\"too_small\":{const t=e.inclusive?\">=\":\">\",a=n(e.origin),o=r(e.origin);return a?`Demasiado pequeño: se esperaba que ${o} tuviera ${t}${e.minimum.toString()} ${a.unit}`:`Demasiado pequeño: se esperaba que ${o} fuera ${t}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Cadena inválida: debe comenzar con \"${t.prefix}\"`:\"ends_with\"===t.format?`Cadena inválida: debe terminar en \"${t.suffix}\"`:\"includes\"===t.format?`Cadena inválida: debe incluir \"${t.includes}\"`:\"regex\"===t.format?`Cadena inválida: debe coincidir con el patrón ${t.pattern}`:`Inválido ${a[t.format]??e.format}`}case\"not_multiple_of\":return`Número inválido: debe ser múltiplo de ${e.divisor}`;case\"unrecognized_keys\":return`Llave${e.keys.length>1?\"s\":\"\"} desconocida${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Llave inválida en ${r(e.origin)}`;case\"invalid_union\":default:return\"Entrada inválida\";case\"invalid_element\":return`Valor inválido en ${r(e.origin)}`}}},Xr=()=>{const e={string:{unit:\"کاراکتر\",verb:\"داشته باشد\"},file:{unit:\"بایت\",verb:\"داشته باشد\"},array:{unit:\"آیتم\",verb:\"داشته باشد\"},set:{unit:\"آیتم\",verb:\"داشته باشد\"}};function t(t){return e[t]??null}const n={regex:\"ورودی\",email:\"آدرس ایمیل\",url:\"URL\",emoji:\"ایموجی\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"تاریخ و زمان ایزو\",date:\"تاریخ ایزو\",time:\"زمان ایزو\",duration:\"مدت زمان ایزو\",ipv4:\"IPv4 آدرس\",ipv6:\"IPv6 آدرس\",cidrv4:\"IPv4 دامنه\",cidrv6:\"IPv6 دامنه\",base64:\"base64-encoded رشته\",base64url:\"base64url-encoded رشته\",json_string:\"JSON رشته\",e164:\"E.164 عدد\",jwt:\"JWT\",template_literal:\"ورودی\"};return e=>{switch(e.code){case\"invalid_type\":return`ورودی نامعتبر: می‌بایست ${e.expected} می‌بود، ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"عدد\";case\"object\":if(Array.isArray(e))return\"آرایه\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)} دریافت شد`;case\"invalid_value\":return 1===e.values.length?`ورودی نامعتبر: می‌بایست ${C(e.values[0])} می‌بود`:`گزینه نامعتبر: می‌بایست یکی از ${l(e.values,\"|\")} می‌بود`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`خیلی بزرگ: ${e.origin??\"مقدار\"} باید ${n}${e.maximum.toString()} ${r.unit??\"عنصر\"} باشد`:`خیلی بزرگ: ${e.origin??\"مقدار\"} باید ${n}${e.maximum.toString()} باشد`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`خیلی کوچک: ${e.origin} باید ${n}${e.minimum.toString()} ${r.unit} باشد`:`خیلی کوچک: ${e.origin} باید ${n}${e.minimum.toString()} باشد`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`رشته نامعتبر: باید با \"${t.prefix}\" شروع شود`:\"ends_with\"===t.format?`رشته نامعتبر: باید با \"${t.suffix}\" تمام شود`:\"includes\"===t.format?`رشته نامعتبر: باید شامل \"${t.includes}\" باشد`:\"regex\"===t.format?`رشته نامعتبر: باید با الگوی ${t.pattern} مطابقت داشته باشد`:`${n[t.format]??e.format} نامعتبر`}case\"not_multiple_of\":return`عدد نامعتبر: باید مضرب ${e.divisor} باشد`;case\"unrecognized_keys\":return`کلید${e.keys.length>1?\"های\":\"\"} ناشناس: ${l(e.keys,\", \")}`;case\"invalid_key\":return`کلید ناشناس در ${e.origin}`;case\"invalid_union\":default:return\"ورودی نامعتبر\";case\"invalid_element\":return`مقدار نامعتبر در ${e.origin}`}}},Gr=()=>{const e={string:{unit:\"merkkiä\",subject:\"merkkijonon\"},file:{unit:\"tavua\",subject:\"tiedoston\"},array:{unit:\"alkiota\",subject:\"listan\"},set:{unit:\"alkiota\",subject:\"joukon\"},number:{unit:\"\",subject:\"luvun\"},bigint:{unit:\"\",subject:\"suuren kokonaisluvun\"},int:{unit:\"\",subject:\"kokonaisluvun\"},date:{unit:\"\",subject:\"päivämäärän\"}};function t(t){return e[t]??null}const n={regex:\"säännöllinen lauseke\",email:\"sähköpostiosoite\",url:\"URL-osoite\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO-aikaleima\",date:\"ISO-päivämäärä\",time:\"ISO-aika\",duration:\"ISO-kesto\",ipv4:\"IPv4-osoite\",ipv6:\"IPv6-osoite\",cidrv4:\"IPv4-alue\",cidrv6:\"IPv6-alue\",base64:\"base64-koodattu merkkijono\",base64url:\"base64url-koodattu merkkijono\",json_string:\"JSON-merkkijono\",e164:\"E.164-luku\",jwt:\"JWT\",template_literal:\"templaattimerkkijono\"};return e=>{switch(e.code){case\"invalid_type\":return`Virheellinen tyyppi: odotettiin ${e.expected}, oli ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Virheellinen syöte: täytyy olla ${C(e.values[0])}`:`Virheellinen valinta: täytyy olla yksi seuraavista: ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Liian suuri: ${r.subject} täytyy olla ${n}${e.maximum.toString()} ${r.unit}`.trim():`Liian suuri: arvon täytyy olla ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Liian pieni: ${r.subject} täytyy olla ${n}${e.minimum.toString()} ${r.unit}`.trim():`Liian pieni: arvon täytyy olla ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Virheellinen syöte: täytyy alkaa \"${t.prefix}\"`:\"ends_with\"===t.format?`Virheellinen syöte: täytyy loppua \"${t.suffix}\"`:\"includes\"===t.format?`Virheellinen syöte: täytyy sisältää \"${t.includes}\"`:\"regex\"===t.format?`Virheellinen syöte: täytyy vastata säännöllistä lauseketta ${t.pattern}`:`Virheellinen ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Virheellinen luku: täytyy olla luvun ${e.divisor} monikerta`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Tuntemattomat avaimet\":\"Tuntematon avain\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return\"Virheellinen avain tietueessa\";case\"invalid_union\":return\"Virheellinen unioni\";case\"invalid_element\":return\"Virheellinen arvo joukossa\";default:return\"Virheellinen syöte\"}}},Yr=()=>{const e={string:{unit:\"caractères\",verb:\"avoir\"},file:{unit:\"octets\",verb:\"avoir\"},array:{unit:\"éléments\",verb:\"avoir\"},set:{unit:\"éléments\",verb:\"avoir\"}};function t(t){return e[t]??null}const n={regex:\"entrée\",email:\"adresse e-mail\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"date et heure ISO\",date:\"date ISO\",time:\"heure ISO\",duration:\"durée ISO\",ipv4:\"adresse IPv4\",ipv6:\"adresse IPv6\",cidrv4:\"plage IPv4\",cidrv6:\"plage IPv6\",base64:\"chaîne encodée en base64\",base64url:\"chaîne encodée en base64url\",json_string:\"chaîne JSON\",e164:\"numéro E.164\",jwt:\"JWT\",template_literal:\"entrée\"};return e=>{switch(e.code){case\"invalid_type\":return`Entrée invalide : ${e.expected} attendu, ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"nombre\";case\"object\":if(Array.isArray(e))return\"tableau\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)} reçu`;case\"invalid_value\":return 1===e.values.length?`Entrée invalide : ${C(e.values[0])} attendu`:`Option invalide : une valeur parmi ${l(e.values,\"|\")} attendue`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Trop grand : ${e.origin??\"valeur\"} doit ${r.verb} ${n}${e.maximum.toString()} ${r.unit??\"élément(s)\"}`:`Trop grand : ${e.origin??\"valeur\"} doit être ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Trop petit : ${e.origin} doit ${r.verb} ${n}${e.minimum.toString()} ${r.unit}`:`Trop petit : ${e.origin} doit être ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Chaîne invalide : doit commencer par \"${t.prefix}\"`:\"ends_with\"===t.format?`Chaîne invalide : doit se terminer par \"${t.suffix}\"`:\"includes\"===t.format?`Chaîne invalide : doit inclure \"${t.includes}\"`:\"regex\"===t.format?`Chaîne invalide : doit correspondre au modèle ${t.pattern}`:`${n[t.format]??e.format} invalide`}case\"not_multiple_of\":return`Nombre invalide : doit être un multiple de ${e.divisor}`;case\"unrecognized_keys\":return`Clé${e.keys.length>1?\"s\":\"\"} non reconnue${e.keys.length>1?\"s\":\"\"} : ${l(e.keys,\", \")}`;case\"invalid_key\":return`Clé invalide dans ${e.origin}`;case\"invalid_union\":default:return\"Entrée invalide\";case\"invalid_element\":return`Valeur invalide dans ${e.origin}`}}},Kr=()=>{const e={string:{unit:\"caractères\",verb:\"avoir\"},file:{unit:\"octets\",verb:\"avoir\"},array:{unit:\"éléments\",verb:\"avoir\"},set:{unit:\"éléments\",verb:\"avoir\"}};function t(t){return e[t]??null}const n={regex:\"entrée\",email:\"adresse courriel\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"date-heure ISO\",date:\"date ISO\",time:\"heure ISO\",duration:\"durée ISO\",ipv4:\"adresse IPv4\",ipv6:\"adresse IPv6\",cidrv4:\"plage IPv4\",cidrv6:\"plage IPv6\",base64:\"chaîne encodée en base64\",base64url:\"chaîne encodée en base64url\",json_string:\"chaîne JSON\",e164:\"numéro E.164\",jwt:\"JWT\",template_literal:\"entrée\"};return e=>{switch(e.code){case\"invalid_type\":return`Entrée invalide : attendu ${e.expected}, reçu ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Entrée invalide : attendu ${C(e.values[0])}`:`Option invalide : attendu l'une des valeurs suivantes ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"≤\":\"<\",r=t(e.origin);return r?`Trop grand : attendu que ${e.origin??\"la valeur\"} ait ${n}${e.maximum.toString()} ${r.unit}`:`Trop grand : attendu que ${e.origin??\"la valeur\"} soit ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\"≥\":\">\",r=t(e.origin);return r?`Trop petit : attendu que ${e.origin} ait ${n}${e.minimum.toString()} ${r.unit}`:`Trop petit : attendu que ${e.origin} soit ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Chaîne invalide : doit commencer par \"${t.prefix}\"`:\"ends_with\"===t.format?`Chaîne invalide : doit se terminer par \"${t.suffix}\"`:\"includes\"===t.format?`Chaîne invalide : doit inclure \"${t.includes}\"`:\"regex\"===t.format?`Chaîne invalide : doit correspondre au motif ${t.pattern}`:`${n[t.format]??e.format} invalide`}case\"not_multiple_of\":return`Nombre invalide : doit être un multiple de ${e.divisor}`;case\"unrecognized_keys\":return`Clé${e.keys.length>1?\"s\":\"\"} non reconnue${e.keys.length>1?\"s\":\"\"} : ${l(e.keys,\", \")}`;case\"invalid_key\":return`Clé invalide dans ${e.origin}`;case\"invalid_union\":default:return\"Entrée invalide\";case\"invalid_element\":return`Valeur invalide dans ${e.origin}`}}},Jr=()=>{const e={string:{unit:\"אותיות\",verb:\"לכלול\"},file:{unit:\"בייטים\",verb:\"לכלול\"},array:{unit:\"פריטים\",verb:\"לכלול\"},set:{unit:\"פריטים\",verb:\"לכלול\"}};function t(t){return e[t]??null}const n={regex:\"קלט\",email:\"כתובת אימייל\",url:\"כתובת רשת\",emoji:\"אימוג'י\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"תאריך וזמן ISO\",date:\"תאריך ISO\",time:\"זמן ISO\",duration:\"משך זמן ISO\",ipv4:\"כתובת IPv4\",ipv6:\"כתובת IPv6\",cidrv4:\"טווח IPv4\",cidrv6:\"טווח IPv6\",base64:\"מחרוזת בבסיס 64\",base64url:\"מחרוזת בבסיס 64 לכתובות רשת\",json_string:\"מחרוזת JSON\",e164:\"מספר E.164\",jwt:\"JWT\",template_literal:\"קלט\"};return e=>{switch(e.code){case\"invalid_type\":return`קלט לא תקין: צריך ${e.expected}, התקבל ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`קלט לא תקין: צריך ${C(e.values[0])}`:`קלט לא תקין: צריך אחת מהאפשרויות ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`גדול מדי: ${e.origin??\"value\"} צריך להיות ${n}${e.maximum.toString()} ${r.unit??\"elements\"}`:`גדול מדי: ${e.origin??\"value\"} צריך להיות ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`קטן מדי: ${e.origin} צריך להיות ${n}${e.minimum.toString()} ${r.unit}`:`קטן מדי: ${e.origin} צריך להיות ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`מחרוזת לא תקינה: חייבת להתחיל ב\"${t.prefix}\"`:\"ends_with\"===t.format?`מחרוזת לא תקינה: חייבת להסתיים ב \"${t.suffix}\"`:\"includes\"===t.format?`מחרוזת לא תקינה: חייבת לכלול \"${t.includes}\"`:\"regex\"===t.format?`מחרוזת לא תקינה: חייבת להתאים לתבנית ${t.pattern}`:`${n[t.format]??e.format} לא תקין`}case\"not_multiple_of\":return`מספר לא תקין: חייב להיות מכפלה של ${e.divisor}`;case\"unrecognized_keys\":return`מפתח${e.keys.length>1?\"ות\":\"\"} לא מזוה${e.keys.length>1?\"ים\":\"ה\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`מפתח לא תקין ב${e.origin}`;case\"invalid_union\":default:return\"קלט לא תקין\";case\"invalid_element\":return`ערך לא תקין ב${e.origin}`}}},ea=()=>{const e={string:{unit:\"karakter\",verb:\"legyen\"},file:{unit:\"byte\",verb:\"legyen\"},array:{unit:\"elem\",verb:\"legyen\"},set:{unit:\"elem\",verb:\"legyen\"}};function t(t){return e[t]??null}const n={regex:\"bemenet\",email:\"email cím\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO időbélyeg\",date:\"ISO dátum\",time:\"ISO idő\",duration:\"ISO időintervallum\",ipv4:\"IPv4 cím\",ipv6:\"IPv6 cím\",cidrv4:\"IPv4 tartomány\",cidrv6:\"IPv6 tartomány\",base64:\"base64-kódolt string\",base64url:\"base64url-kódolt string\",json_string:\"JSON string\",e164:\"E.164 szám\",jwt:\"JWT\",template_literal:\"bemenet\"};return e=>{switch(e.code){case\"invalid_type\":return`Érvénytelen bemenet: a várt érték ${e.expected}, a kapott érték ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"szám\";case\"object\":if(Array.isArray(e))return\"tömb\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Érvénytelen bemenet: a várt érték ${C(e.values[0])}`:`Érvénytelen opció: valamelyik érték várt ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Túl nagy: ${e.origin??\"érték\"} mérete túl nagy ${n}${e.maximum.toString()} ${r.unit??\"elem\"}`:`Túl nagy: a bemeneti érték ${e.origin??\"érték\"} túl nagy: ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Túl kicsi: a bemeneti érték ${e.origin} mérete túl kicsi ${n}${e.minimum.toString()} ${r.unit}`:`Túl kicsi: a bemeneti érték ${e.origin} túl kicsi ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Érvénytelen string: \"${t.prefix}\" értékkel kell kezdődnie`:\"ends_with\"===t.format?`Érvénytelen string: \"${t.suffix}\" értékkel kell végződnie`:\"includes\"===t.format?`Érvénytelen string: \"${t.includes}\" értéket kell tartalmaznia`:\"regex\"===t.format?`Érvénytelen string: ${t.pattern} mintának kell megfelelnie`:`Érvénytelen ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Érvénytelen szám: ${e.divisor} többszörösének kell lennie`;case\"unrecognized_keys\":return`Ismeretlen kulcs${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Érvénytelen kulcs ${e.origin}`;case\"invalid_union\":default:return\"Érvénytelen bemenet\";case\"invalid_element\":return`Érvénytelen érték: ${e.origin}`}}},ta=()=>{const e={string:{unit:\"karakter\",verb:\"memiliki\"},file:{unit:\"byte\",verb:\"memiliki\"},array:{unit:\"item\",verb:\"memiliki\"},set:{unit:\"item\",verb:\"memiliki\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"alamat email\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"tanggal dan waktu format ISO\",date:\"tanggal format ISO\",time:\"jam format ISO\",duration:\"durasi format ISO\",ipv4:\"alamat IPv4\",ipv6:\"alamat IPv6\",cidrv4:\"rentang alamat IPv4\",cidrv6:\"rentang alamat IPv6\",base64:\"string dengan enkode base64\",base64url:\"string dengan enkode base64url\",json_string:\"string JSON\",e164:\"angka E.164\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Input tidak valid: diharapkan ${e.expected}, diterima ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Input tidak valid: diharapkan ${C(e.values[0])}`:`Pilihan tidak valid: diharapkan salah satu dari ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Terlalu besar: diharapkan ${e.origin??\"value\"} memiliki ${n}${e.maximum.toString()} ${r.unit??\"elemen\"}`:`Terlalu besar: diharapkan ${e.origin??\"value\"} menjadi ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Terlalu kecil: diharapkan ${e.origin} memiliki ${n}${e.minimum.toString()} ${r.unit}`:`Terlalu kecil: diharapkan ${e.origin} menjadi ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`String tidak valid: harus dimulai dengan \"${t.prefix}\"`:\"ends_with\"===t.format?`String tidak valid: harus berakhir dengan \"${t.suffix}\"`:\"includes\"===t.format?`String tidak valid: harus menyertakan \"${t.includes}\"`:\"regex\"===t.format?`String tidak valid: harus sesuai pola ${t.pattern}`:`${n[t.format]??e.format} tidak valid`}case\"not_multiple_of\":return`Angka tidak valid: harus kelipatan dari ${e.divisor}`;case\"unrecognized_keys\":return`Kunci tidak dikenali ${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Kunci tidak valid di ${e.origin}`;case\"invalid_union\":default:return\"Input tidak valid\";case\"invalid_element\":return`Nilai tidak valid di ${e.origin}`}}},na=()=>{const e={string:{unit:\"stafi\",verb:\"að hafa\"},file:{unit:\"bæti\",verb:\"að hafa\"},array:{unit:\"hluti\",verb:\"að hafa\"},set:{unit:\"hluti\",verb:\"að hafa\"}};function t(t){return e[t]??null}const n={regex:\"gildi\",email:\"netfang\",url:\"vefslóð\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO dagsetning og tími\",date:\"ISO dagsetning\",time:\"ISO tími\",duration:\"ISO tímalengd\",ipv4:\"IPv4 address\",ipv6:\"IPv6 address\",cidrv4:\"IPv4 range\",cidrv6:\"IPv6 range\",base64:\"base64-encoded strengur\",base64url:\"base64url-encoded strengur\",json_string:\"JSON strengur\",e164:\"E.164 tölugildi\",jwt:\"JWT\",template_literal:\"gildi\"};return e=>{switch(e.code){case\"invalid_type\":return`Rangt gildi: Þú slóst inn ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"númer\";case\"object\":if(Array.isArray(e))return\"fylki\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)} þar sem á að vera ${e.expected}`;case\"invalid_value\":return 1===e.values.length?`Rangt gildi: gert ráð fyrir ${C(e.values[0])}`:`Ógilt val: má vera eitt af eftirfarandi ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Of stórt: gert er ráð fyrir að ${e.origin??\"gildi\"} hafi ${n}${e.maximum.toString()} ${r.unit??\"hluti\"}`:`Of stórt: gert er ráð fyrir að ${e.origin??\"gildi\"} sé ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Of lítið: gert er ráð fyrir að ${e.origin} hafi ${n}${e.minimum.toString()} ${r.unit}`:`Of lítið: gert er ráð fyrir að ${e.origin} sé ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ógildur strengur: verður að byrja á \"${t.prefix}\"`:\"ends_with\"===t.format?`Ógildur strengur: verður að enda á \"${t.suffix}\"`:\"includes\"===t.format?`Ógildur strengur: verður að innihalda \"${t.includes}\"`:\"regex\"===t.format?`Ógildur strengur: verður að fylgja mynstri ${t.pattern}`:`Rangt ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Röng tala: verður að vera margfeldi af ${e.divisor}`;case\"unrecognized_keys\":return`Óþekkt ${e.keys.length>1?\"ir lyklar\":\"ur lykill\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Rangur lykill í ${e.origin}`;case\"invalid_union\":default:return\"Rangt gildi\";case\"invalid_element\":return`Rangt gildi í ${e.origin}`}}},ra=()=>{const e={string:{unit:\"caratteri\",verb:\"avere\"},file:{unit:\"byte\",verb:\"avere\"},array:{unit:\"elementi\",verb:\"avere\"},set:{unit:\"elementi\",verb:\"avere\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"indirizzo email\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"data e ora ISO\",date:\"data ISO\",time:\"ora ISO\",duration:\"durata ISO\",ipv4:\"indirizzo IPv4\",ipv6:\"indirizzo IPv6\",cidrv4:\"intervallo IPv4\",cidrv6:\"intervallo IPv6\",base64:\"stringa codificata in base64\",base64url:\"URL codificata in base64\",json_string:\"stringa JSON\",e164:\"numero E.164\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Input non valido: atteso ${e.expected}, ricevuto ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"numero\";case\"object\":if(Array.isArray(e))return\"vettore\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Input non valido: atteso ${C(e.values[0])}`:`Opzione non valida: atteso uno tra ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Troppo grande: ${e.origin??\"valore\"} deve avere ${n}${e.maximum.toString()} ${r.unit??\"elementi\"}`:`Troppo grande: ${e.origin??\"valore\"} deve essere ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Troppo piccolo: ${e.origin} deve avere ${n}${e.minimum.toString()} ${r.unit}`:`Troppo piccolo: ${e.origin} deve essere ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Stringa non valida: deve iniziare con \"${t.prefix}\"`:\"ends_with\"===t.format?`Stringa non valida: deve terminare con \"${t.suffix}\"`:\"includes\"===t.format?`Stringa non valida: deve includere \"${t.includes}\"`:\"regex\"===t.format?`Stringa non valida: deve corrispondere al pattern ${t.pattern}`:`Invalid ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Numero non valido: deve essere un multiplo di ${e.divisor}`;case\"unrecognized_keys\":return`Chiav${e.keys.length>1?\"i\":\"e\"} non riconosciut${e.keys.length>1?\"e\":\"a\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Chiave non valida in ${e.origin}`;case\"invalid_union\":default:return\"Input non valido\";case\"invalid_element\":return`Valore non valido in ${e.origin}`}}},aa=()=>{const e={string:{unit:\"文字\",verb:\"である\"},file:{unit:\"バイト\",verb:\"である\"},array:{unit:\"要素\",verb:\"である\"},set:{unit:\"要素\",verb:\"である\"}};function t(t){return e[t]??null}const n={regex:\"入力値\",email:\"メールアドレス\",url:\"URL\",emoji:\"絵文字\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO日時\",date:\"ISO日付\",time:\"ISO時刻\",duration:\"ISO期間\",ipv4:\"IPv4アドレス\",ipv6:\"IPv6アドレス\",cidrv4:\"IPv4範囲\",cidrv6:\"IPv6範囲\",base64:\"base64エンコード文字列\",base64url:\"base64urlエンコード文字列\",json_string:\"JSON文字列\",e164:\"E.164番号\",jwt:\"JWT\",template_literal:\"入力値\"};return e=>{switch(e.code){case\"invalid_type\":return`無効な入力: ${e.expected}が期待されましたが、${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"数値\";case\"object\":if(Array.isArray(e))return\"配列\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}が入力されました`;case\"invalid_value\":return 1===e.values.length?`無効な入力: ${C(e.values[0])}が期待されました`:`無効な選択: ${l(e.values,\"、\")}のいずれかである必要があります`;case\"too_big\":{const n=e.inclusive?\"以下である\":\"より小さい\",r=t(e.origin);return r?`大きすぎる値: ${e.origin??\"値\"}は${e.maximum.toString()}${r.unit??\"要素\"}${n}必要があります`:`大きすぎる値: ${e.origin??\"値\"}は${e.maximum.toString()}${n}必要があります`}case\"too_small\":{const n=e.inclusive?\"以上である\":\"より大きい\",r=t(e.origin);return r?`小さすぎる値: ${e.origin}は${e.minimum.toString()}${r.unit}${n}必要があります`:`小さすぎる値: ${e.origin}は${e.minimum.toString()}${n}必要があります`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`無効な文字列: \"${t.prefix}\"で始まる必要があります`:\"ends_with\"===t.format?`無効な文字列: \"${t.suffix}\"で終わる必要があります`:\"includes\"===t.format?`無効な文字列: \"${t.includes}\"を含む必要があります`:\"regex\"===t.format?`無効な文字列: パターン${t.pattern}に一致する必要があります`:`無効な${n[t.format]??e.format}`}case\"not_multiple_of\":return`無効な数値: ${e.divisor}の倍数である必要があります`;case\"unrecognized_keys\":return`認識されていないキー${e.keys.length>1?\"群\":\"\"}: ${l(e.keys,\"、\")}`;case\"invalid_key\":return`${e.origin}内の無効なキー`;case\"invalid_union\":default:return\"無効な入力\";case\"invalid_element\":return`${e.origin}内の無効な値`}}},oa=()=>{const e={string:{unit:\"სიმბოლო\",verb:\"უნდა შეიცავდეს\"},file:{unit:\"ბაიტი\",verb:\"უნდა შეიცავდეს\"},array:{unit:\"ელემენტი\",verb:\"უნდა შეიცავდეს\"},set:{unit:\"ელემენტი\",verb:\"უნდა შეიცავდეს\"}};function t(t){return e[t]??null}const n={regex:\"შეყვანა\",email:\"ელ-ფოსტის მისამართი\",url:\"URL\",emoji:\"ემოჯი\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"თარიღი-დრო\",date:\"თარიღი\",time:\"დრო\",duration:\"ხანგრძლივობა\",ipv4:\"IPv4 მისამართი\",ipv6:\"IPv6 მისამართი\",cidrv4:\"IPv4 დიაპაზონი\",cidrv6:\"IPv6 დიაპაზონი\",base64:\"base64-კოდირებული სტრინგი\",base64url:\"base64url-კოდირებული სტრინგი\",json_string:\"JSON სტრინგი\",e164:\"E.164 ნომერი\",jwt:\"JWT\",template_literal:\"შეყვანა\"};return e=>{switch(e.code){case\"invalid_type\":return`არასწორი შეყვანა: მოსალოდნელი ${e.expected}, მიღებული ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"რიცხვი\";case\"object\":if(Array.isArray(e))return\"მასივი\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return{string:\"სტრინგი\",boolean:\"ბულეანი\",undefined:\"undefined\",bigint:\"bigint\",symbol:\"symbol\",function:\"ფუნქცია\"}[t]??t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`არასწორი შეყვანა: მოსალოდნელი ${C(e.values[0])}`:`არასწორი ვარიანტი: მოსალოდნელია ერთ-ერთი ${l(e.values,\"|\")}-დან`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`ზედმეტად დიდი: მოსალოდნელი ${e.origin??\"მნიშვნელობა\"} ${r.verb} ${n}${e.maximum.toString()} ${r.unit}`:`ზედმეტად დიდი: მოსალოდნელი ${e.origin??\"მნიშვნელობა\"} იყოს ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`ზედმეტად პატარა: მოსალოდნელი ${e.origin} ${r.verb} ${n}${e.minimum.toString()} ${r.unit}`:`ზედმეტად პატარა: მოსალოდნელი ${e.origin} იყოს ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`არასწორი სტრინგი: უნდა იწყებოდეს \"${t.prefix}\"-ით`:\"ends_with\"===t.format?`არასწორი სტრინგი: უნდა მთავრდებოდეს \"${t.suffix}\"-ით`:\"includes\"===t.format?`არასწორი სტრინგი: უნდა შეიცავდეს \"${t.includes}\"-ს`:\"regex\"===t.format?`არასწორი სტრინგი: უნდა შეესაბამებოდეს შაბლონს ${t.pattern}`:`არასწორი ${n[t.format]??e.format}`}case\"not_multiple_of\":return`არასწორი რიცხვი: უნდა იყოს ${e.divisor}-ის ჯერადი`;case\"unrecognized_keys\":return`უცნობი გასაღებ${e.keys.length>1?\"ები\":\"ი\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`არასწორი გასაღები ${e.origin}-ში`;case\"invalid_union\":default:return\"არასწორი შეყვანა\";case\"invalid_element\":return`არასწორი მნიშვნელობა ${e.origin}-ში`}}},ia=()=>{const e={string:{unit:\"តួអក្សរ\",verb:\"គួរមាន\"},file:{unit:\"បៃ\",verb:\"គួរមាន\"},array:{unit:\"ធាតុ\",verb:\"គួរមាន\"},set:{unit:\"ធាតុ\",verb:\"គួរមាន\"}};function t(t){return e[t]??null}const n={regex:\"ទិន្នន័យបញ្ចូល\",email:\"អាសយដ្ឋានអ៊ីមែល\",url:\"URL\",emoji:\"សញ្ញាអារម្មណ៍\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"កាលបរិច្ឆេទ និងម៉ោង ISO\",date:\"កាលបរិច្ឆេទ ISO\",time:\"ម៉ោង ISO\",duration:\"រយៈពេល ISO\",ipv4:\"អាសយដ្ឋាន IPv4\",ipv6:\"អាសយដ្ឋាន IPv6\",cidrv4:\"ដែនអាសយដ្ឋាន IPv4\",cidrv6:\"ដែនអាសយដ្ឋាន IPv6\",base64:\"ខ្សែអក្សរអ៊ិកូដ base64\",base64url:\"ខ្សែអក្សរអ៊ិកូដ base64url\",json_string:\"ខ្សែអក្សរ JSON\",e164:\"លេខ E.164\",jwt:\"JWT\",template_literal:\"ទិន្នន័យបញ្ចូល\"};return e=>{switch(e.code){case\"invalid_type\":return`ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${e.expected} ប៉ុន្តែទទួលបាន ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"មិនមែនជាលេខ (NaN)\":\"លេខ\";case\"object\":if(Array.isArray(e))return\"អារេ (Array)\";if(null===e)return\"គ្មានតម្លៃ (null)\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${C(e.values[0])}`:`ជម្រើសមិនត្រឹមត្រូវ៖ ត្រូវជាមួយក្នុងចំណោម ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`ធំពេក៖ ត្រូវការ ${e.origin??\"តម្លៃ\"} ${n} ${e.maximum.toString()} ${r.unit??\"ធាតុ\"}`:`ធំពេក៖ ត្រូវការ ${e.origin??\"តម្លៃ\"} ${n} ${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`តូចពេក៖ ត្រូវការ ${e.origin} ${n} ${e.minimum.toString()} ${r.unit}`:`តូចពេក៖ ត្រូវការ ${e.origin} ${n} ${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវចាប់ផ្តើមដោយ \"${t.prefix}\"`:\"ends_with\"===t.format?`ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវបញ្ចប់ដោយ \"${t.suffix}\"`:\"includes\"===t.format?`ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវមាន \"${t.includes}\"`:\"regex\"===t.format?`ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវតែផ្គូផ្គងនឹងទម្រង់ដែលបានកំណត់ ${t.pattern}`:`មិនត្រឹមត្រូវ៖ ${n[t.format]??e.format}`}case\"not_multiple_of\":return`លេខមិនត្រឹមត្រូវ៖ ត្រូវតែជាពហុគុណនៃ ${e.divisor}`;case\"unrecognized_keys\":return`រកឃើញសោមិនស្គាល់៖ ${l(e.keys,\", \")}`;case\"invalid_key\":return`សោមិនត្រឹមត្រូវនៅក្នុង ${e.origin}`;case\"invalid_union\":default:return\"ទិន្នន័យមិនត្រឹមត្រូវ\";case\"invalid_element\":return`ទិន្នន័យមិនត្រឹមត្រូវនៅក្នុង ${e.origin}`}}};function sa(){return{localeError:ia()}}const la=()=>{const e={string:{unit:\"문자\",verb:\"to have\"},file:{unit:\"바이트\",verb:\"to have\"},array:{unit:\"개\",verb:\"to have\"},set:{unit:\"개\",verb:\"to have\"}};function t(t){return e[t]??null}const n={regex:\"입력\",email:\"이메일 주소\",url:\"URL\",emoji:\"이모지\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO 날짜시간\",date:\"ISO 날짜\",time:\"ISO 시간\",duration:\"ISO 기간\",ipv4:\"IPv4 주소\",ipv6:\"IPv6 주소\",cidrv4:\"IPv4 범위\",cidrv6:\"IPv6 범위\",base64:\"base64 인코딩 문자열\",base64url:\"base64url 인코딩 문자열\",json_string:\"JSON 문자열\",e164:\"E.164 번호\",jwt:\"JWT\",template_literal:\"입력\"};return e=>{switch(e.code){case\"invalid_type\":return`잘못된 입력: 예상 타입은 ${e.expected}, 받은 타입은 ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}입니다`;case\"invalid_value\":return 1===e.values.length?`잘못된 입력: 값은 ${C(e.values[0])} 이어야 합니다`:`잘못된 옵션: ${l(e.values,\"또는 \")} 중 하나여야 합니다`;case\"too_big\":{const n=e.inclusive?\"이하\":\"미만\",r=\"미만\"===n?\"이어야 합니다\":\"여야 합니다\",a=t(e.origin),o=a?.unit??\"요소\";return a?`${e.origin??\"값\"}이 너무 큽니다: ${e.maximum.toString()}${o} ${n}${r}`:`${e.origin??\"값\"}이 너무 큽니다: ${e.maximum.toString()} ${n}${r}`}case\"too_small\":{const n=e.inclusive?\"이상\":\"초과\",r=\"이상\"===n?\"이어야 합니다\":\"여야 합니다\",a=t(e.origin),o=a?.unit??\"요소\";return a?`${e.origin??\"값\"}이 너무 작습니다: ${e.minimum.toString()}${o} ${n}${r}`:`${e.origin??\"값\"}이 너무 작습니다: ${e.minimum.toString()} ${n}${r}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`잘못된 문자열: \"${t.prefix}\"(으)로 시작해야 합니다`:\"ends_with\"===t.format?`잘못된 문자열: \"${t.suffix}\"(으)로 끝나야 합니다`:\"includes\"===t.format?`잘못된 문자열: \"${t.includes}\"을(를) 포함해야 합니다`:\"regex\"===t.format?`잘못된 문자열: 정규식 ${t.pattern} 패턴과 일치해야 합니다`:`잘못된 ${n[t.format]??e.format}`}case\"not_multiple_of\":return`잘못된 숫자: ${e.divisor}의 배수여야 합니다`;case\"unrecognized_keys\":return`인식할 수 없는 키: ${l(e.keys,\", \")}`;case\"invalid_key\":return`잘못된 키: ${e.origin}`;case\"invalid_union\":default:return\"잘못된 입력\";case\"invalid_element\":return`잘못된 값: ${e.origin}`}}},ca=(e,t=void 0)=>{switch(e){case\"number\":return Number.isNaN(t)?\"NaN\":\"skaičius\";case\"bigint\":return\"sveikasis skaičius\";case\"string\":return\"eilutė\";case\"boolean\":return\"loginė reikšmė\";case\"undefined\":case\"void\":return\"neapibrėžta reikšmė\";case\"function\":return\"funkcija\";case\"symbol\":return\"simbolis\";case\"object\":return void 0===t?\"nežinomas objektas\":null===t?\"nulinė reikšmė\":Array.isArray(t)?\"masyvas\":Object.getPrototypeOf(t)!==Object.prototype&&t.constructor?t.constructor.name:\"objektas\";case\"null\":return\"nulinė reikšmė\"}return e},ua=e=>e.charAt(0).toUpperCase()+e.slice(1);function da(e){const t=Math.abs(e),n=t%10,r=t%100;return r>=11&&r<=19||0===n?\"many\":1===n?\"one\":\"few\"}const pa=()=>{const e={string:{unit:{one:\"simbolis\",few:\"simboliai\",many:\"simbolių\"},verb:{smaller:{inclusive:\"turi būti ne ilgesnė kaip\",notInclusive:\"turi būti trumpesnė kaip\"},bigger:{inclusive:\"turi būti ne trumpesnė kaip\",notInclusive:\"turi būti ilgesnė kaip\"}}},file:{unit:{one:\"baitas\",few:\"baitai\",many:\"baitų\"},verb:{smaller:{inclusive:\"turi būti ne didesnis kaip\",notInclusive:\"turi būti mažesnis kaip\"},bigger:{inclusive:\"turi būti ne mažesnis kaip\",notInclusive:\"turi būti didesnis kaip\"}}},array:{unit:{one:\"elementą\",few:\"elementus\",many:\"elementų\"},verb:{smaller:{inclusive:\"turi turėti ne daugiau kaip\",notInclusive:\"turi turėti mažiau kaip\"},bigger:{inclusive:\"turi turėti ne mažiau kaip\",notInclusive:\"turi turėti daugiau kaip\"}}},set:{unit:{one:\"elementą\",few:\"elementus\",many:\"elementų\"},verb:{smaller:{inclusive:\"turi turėti ne daugiau kaip\",notInclusive:\"turi turėti mažiau kaip\"},bigger:{inclusive:\"turi turėti ne mažiau kaip\",notInclusive:\"turi turėti daugiau kaip\"}}}};function t(t,n,r,a){const o=e[t]??null;return null===o?o:{unit:o.unit[n],verb:o.verb[a][r?\"inclusive\":\"notInclusive\"]}}const n={regex:\"įvestis\",email:\"el. pašto adresas\",url:\"URL\",emoji:\"jaustukas\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO data ir laikas\",date:\"ISO data\",time:\"ISO laikas\",duration:\"ISO trukmė\",ipv4:\"IPv4 adresas\",ipv6:\"IPv6 adresas\",cidrv4:\"IPv4 tinklo prefiksas (CIDR)\",cidrv6:\"IPv6 tinklo prefiksas (CIDR)\",base64:\"base64 užkoduota eilutė\",base64url:\"base64url užkoduota eilutė\",json_string:\"JSON eilutė\",e164:\"E.164 numeris\",jwt:\"JWT\",template_literal:\"įvestis\"};return e=>{switch(e.code){case\"invalid_type\":return`Gautas tipas ${r=e.input,ca(typeof r,r)}, o tikėtasi - ${ca(e.expected)}`;case\"invalid_value\":return 1===e.values.length?`Privalo būti ${C(e.values[0])}`:`Privalo būti vienas iš ${l(e.values,\"|\")} pasirinkimų`;case\"too_big\":{const n=ca(e.origin),r=t(e.origin,da(Number(e.maximum)),e.inclusive??!1,\"smaller\");if(r?.verb)return`${ua(n??e.origin??\"reikšmė\")} ${r.verb} ${e.maximum.toString()} ${r.unit??\"elementų\"}`;const a=e.inclusive?\"ne didesnis kaip\":\"mažesnis kaip\";return`${ua(n??e.origin??\"reikšmė\")} turi būti ${a} ${e.maximum.toString()} ${r?.unit}`}case\"too_small\":{const n=ca(e.origin),r=t(e.origin,da(Number(e.minimum)),e.inclusive??!1,\"bigger\");if(r?.verb)return`${ua(n??e.origin??\"reikšmė\")} ${r.verb} ${e.minimum.toString()} ${r.unit??\"elementų\"}`;const a=e.inclusive?\"ne mažesnis kaip\":\"didesnis kaip\";return`${ua(n??e.origin??\"reikšmė\")} turi būti ${a} ${e.minimum.toString()} ${r?.unit}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Eilutė privalo prasidėti \"${t.prefix}\"`:\"ends_with\"===t.format?`Eilutė privalo pasibaigti \"${t.suffix}\"`:\"includes\"===t.format?`Eilutė privalo įtraukti \"${t.includes}\"`:\"regex\"===t.format?`Eilutė privalo atitikti ${t.pattern}`:`Neteisingas ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Skaičius privalo būti ${e.divisor} kartotinis.`;case\"unrecognized_keys\":return`Neatpažint${e.keys.length>1?\"i\":\"as\"} rakt${e.keys.length>1?\"ai\":\"as\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return\"Rastas klaidingas raktas\";case\"invalid_union\":default:return\"Klaidinga įvestis\";case\"invalid_element\":{const t=ca(e.origin);return`${ua(t??e.origin??\"reikšmė\")} turi klaidingą įvestį`}}var r}},ha=()=>{const e={string:{unit:\"знаци\",verb:\"да имаат\"},file:{unit:\"бајти\",verb:\"да имаат\"},array:{unit:\"ставки\",verb:\"да имаат\"},set:{unit:\"ставки\",verb:\"да имаат\"}};function t(t){return e[t]??null}const n={regex:\"внес\",email:\"адреса на е-пошта\",url:\"URL\",emoji:\"емоџи\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO датум и време\",date:\"ISO датум\",time:\"ISO време\",duration:\"ISO времетраење\",ipv4:\"IPv4 адреса\",ipv6:\"IPv6 адреса\",cidrv4:\"IPv4 опсег\",cidrv6:\"IPv6 опсег\",base64:\"base64-енкодирана низа\",base64url:\"base64url-енкодирана низа\",json_string:\"JSON низа\",e164:\"E.164 број\",jwt:\"JWT\",template_literal:\"внес\"};return e=>{switch(e.code){case\"invalid_type\":return`Грешен внес: се очекува ${e.expected}, примено ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"број\";case\"object\":if(Array.isArray(e))return\"низа\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Invalid input: expected ${C(e.values[0])}`:`Грешана опција: се очекува една ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Премногу голем: се очекува ${e.origin??\"вредноста\"} да има ${n}${e.maximum.toString()} ${r.unit??\"елементи\"}`:`Премногу голем: се очекува ${e.origin??\"вредноста\"} да биде ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Премногу мал: се очекува ${e.origin} да има ${n}${e.minimum.toString()} ${r.unit}`:`Премногу мал: се очекува ${e.origin} да биде ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Неважечка низа: мора да започнува со \"${t.prefix}\"`:\"ends_with\"===t.format?`Неважечка низа: мора да завршува со \"${t.suffix}\"`:\"includes\"===t.format?`Неважечка низа: мора да вклучува \"${t.includes}\"`:\"regex\"===t.format?`Неважечка низа: мора да одгоара на патернот ${t.pattern}`:`Invalid ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Грешен број: мора да биде делив со ${e.divisor}`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Непрепознаени клучеви\":\"Непрепознаен клуч\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Грешен клуч во ${e.origin}`;case\"invalid_union\":default:return\"Грешен внес\";case\"invalid_element\":return`Грешна вредност во ${e.origin}`}}},fa=()=>{const e={string:{unit:\"aksara\",verb:\"mempunyai\"},file:{unit:\"bait\",verb:\"mempunyai\"},array:{unit:\"elemen\",verb:\"mempunyai\"},set:{unit:\"elemen\",verb:\"mempunyai\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"alamat e-mel\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"tarikh masa ISO\",date:\"tarikh ISO\",time:\"masa ISO\",duration:\"tempoh ISO\",ipv4:\"alamat IPv4\",ipv6:\"alamat IPv6\",cidrv4:\"julat IPv4\",cidrv6:\"julat IPv6\",base64:\"string dikodkan base64\",base64url:\"string dikodkan base64url\",json_string:\"string JSON\",e164:\"nombor E.164\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Input tidak sah: dijangka ${e.expected}, diterima ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"nombor\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Input tidak sah: dijangka ${C(e.values[0])}`:`Pilihan tidak sah: dijangka salah satu daripada ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Terlalu besar: dijangka ${e.origin??\"nilai\"} ${r.verb} ${n}${e.maximum.toString()} ${r.unit??\"elemen\"}`:`Terlalu besar: dijangka ${e.origin??\"nilai\"} adalah ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Terlalu kecil: dijangka ${e.origin} ${r.verb} ${n}${e.minimum.toString()} ${r.unit}`:`Terlalu kecil: dijangka ${e.origin} adalah ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`String tidak sah: mesti bermula dengan \"${t.prefix}\"`:\"ends_with\"===t.format?`String tidak sah: mesti berakhir dengan \"${t.suffix}\"`:\"includes\"===t.format?`String tidak sah: mesti mengandungi \"${t.includes}\"`:\"regex\"===t.format?`String tidak sah: mesti sepadan dengan corak ${t.pattern}`:`${n[t.format]??e.format} tidak sah`}case\"not_multiple_of\":return`Nombor tidak sah: perlu gandaan ${e.divisor}`;case\"unrecognized_keys\":return`Kunci tidak dikenali: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Kunci tidak sah dalam ${e.origin}`;case\"invalid_union\":default:return\"Input tidak sah\";case\"invalid_element\":return`Nilai tidak sah dalam ${e.origin}`}}},ma=()=>{const e={string:{unit:\"tekens\"},file:{unit:\"bytes\"},array:{unit:\"elementen\"},set:{unit:\"elementen\"}};function t(t){return e[t]??null}const n={regex:\"invoer\",email:\"emailadres\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO datum en tijd\",date:\"ISO datum\",time:\"ISO tijd\",duration:\"ISO duur\",ipv4:\"IPv4-adres\",ipv6:\"IPv6-adres\",cidrv4:\"IPv4-bereik\",cidrv6:\"IPv6-bereik\",base64:\"base64-gecodeerde tekst\",base64url:\"base64 URL-gecodeerde tekst\",json_string:\"JSON string\",e164:\"E.164-nummer\",jwt:\"JWT\",template_literal:\"invoer\"};return e=>{switch(e.code){case\"invalid_type\":return`Ongeldige invoer: verwacht ${e.expected}, ontving ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"getal\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Ongeldige invoer: verwacht ${C(e.values[0])}`:`Ongeldige optie: verwacht één van ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Te lang: verwacht dat ${e.origin??\"waarde\"} ${n}${e.maximum.toString()} ${r.unit??\"elementen\"} bevat`:`Te lang: verwacht dat ${e.origin??\"waarde\"} ${n}${e.maximum.toString()} is`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Te kort: verwacht dat ${e.origin} ${n}${e.minimum.toString()} ${r.unit} bevat`:`Te kort: verwacht dat ${e.origin} ${n}${e.minimum.toString()} is`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ongeldige tekst: moet met \"${t.prefix}\" beginnen`:\"ends_with\"===t.format?`Ongeldige tekst: moet op \"${t.suffix}\" eindigen`:\"includes\"===t.format?`Ongeldige tekst: moet \"${t.includes}\" bevatten`:\"regex\"===t.format?`Ongeldige tekst: moet overeenkomen met patroon ${t.pattern}`:`Ongeldig: ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Ongeldig getal: moet een veelvoud van ${e.divisor} zijn`;case\"unrecognized_keys\":return`Onbekende key${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Ongeldige key in ${e.origin}`;case\"invalid_union\":default:return\"Ongeldige invoer\";case\"invalid_element\":return`Ongeldige waarde in ${e.origin}`}}},ga=()=>{const e={string:{unit:\"tegn\",verb:\"å ha\"},file:{unit:\"bytes\",verb:\"å ha\"},array:{unit:\"elementer\",verb:\"å inneholde\"},set:{unit:\"elementer\",verb:\"å inneholde\"}};function t(t){return e[t]??null}const n={regex:\"input\",email:\"e-postadresse\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO dato- og klokkeslett\",date:\"ISO-dato\",time:\"ISO-klokkeslett\",duration:\"ISO-varighet\",ipv4:\"IPv4-område\",ipv6:\"IPv6-område\",cidrv4:\"IPv4-spekter\",cidrv6:\"IPv6-spekter\",base64:\"base64-enkodet streng\",base64url:\"base64url-enkodet streng\",json_string:\"JSON-streng\",e164:\"E.164-nummer\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`Ugyldig input: forventet ${e.expected}, fikk ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"tall\";case\"object\":if(Array.isArray(e))return\"liste\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Ugyldig verdi: forventet ${C(e.values[0])}`:`Ugyldig valg: forventet en av ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`For stor(t): forventet ${e.origin??\"value\"} til å ha ${n}${e.maximum.toString()} ${r.unit??\"elementer\"}`:`For stor(t): forventet ${e.origin??\"value\"} til å ha ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`For lite(n): forventet ${e.origin} til å ha ${n}${e.minimum.toString()} ${r.unit}`:`For lite(n): forventet ${e.origin} til å ha ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ugyldig streng: må starte med \"${t.prefix}\"`:\"ends_with\"===t.format?`Ugyldig streng: må ende med \"${t.suffix}\"`:\"includes\"===t.format?`Ugyldig streng: må inneholde \"${t.includes}\"`:\"regex\"===t.format?`Ugyldig streng: må matche mønsteret ${t.pattern}`:`Ugyldig ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Ugyldig tall: må være et multiplum av ${e.divisor}`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Ukjente nøkler\":\"Ukjent nøkkel\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Ugyldig nøkkel i ${e.origin}`;case\"invalid_union\":default:return\"Ugyldig input\";case\"invalid_element\":return`Ugyldig verdi i ${e.origin}`}}},va=()=>{const e={string:{unit:\"harf\",verb:\"olmalıdır\"},file:{unit:\"bayt\",verb:\"olmalıdır\"},array:{unit:\"unsur\",verb:\"olmalıdır\"},set:{unit:\"unsur\",verb:\"olmalıdır\"}};function t(t){return e[t]??null}const n={regex:\"giren\",email:\"epostagâh\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO hengâmı\",date:\"ISO tarihi\",time:\"ISO zamanı\",duration:\"ISO müddeti\",ipv4:\"IPv4 nişânı\",ipv6:\"IPv6 nişânı\",cidrv4:\"IPv4 menzili\",cidrv6:\"IPv6 menzili\",base64:\"base64-şifreli metin\",base64url:\"base64url-şifreli metin\",json_string:\"JSON metin\",e164:\"E.164 sayısı\",jwt:\"JWT\",template_literal:\"giren\"};return e=>{switch(e.code){case\"invalid_type\":return`Fâsit giren: umulan ${e.expected}, alınan ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"numara\";case\"object\":if(Array.isArray(e))return\"saf\";if(null===e)return\"gayb\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Fâsit giren: umulan ${C(e.values[0])}`:`Fâsit tercih: mûteberler ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Fazla büyük: ${e.origin??\"value\"}, ${n}${e.maximum.toString()} ${r.unit??\"elements\"} sahip olmalıydı.`:`Fazla büyük: ${e.origin??\"value\"}, ${n}${e.maximum.toString()} olmalıydı.`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Fazla küçük: ${e.origin}, ${n}${e.minimum.toString()} ${r.unit} sahip olmalıydı.`:`Fazla küçük: ${e.origin}, ${n}${e.minimum.toString()} olmalıydı.`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Fâsit metin: \"${t.prefix}\" ile başlamalı.`:\"ends_with\"===t.format?`Fâsit metin: \"${t.suffix}\" ile bitmeli.`:\"includes\"===t.format?`Fâsit metin: \"${t.includes}\" ihtivâ etmeli.`:\"regex\"===t.format?`Fâsit metin: ${t.pattern} nakşına uymalı.`:`Fâsit ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Fâsit sayı: ${e.divisor} katı olmalıydı.`;case\"unrecognized_keys\":return`Tanınmayan anahtar ${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`${e.origin} için tanınmayan anahtar var.`;case\"invalid_union\":return\"Giren tanınamadı.\";case\"invalid_element\":return`${e.origin} için tanınmayan kıymet var.`;default:return\"Kıymet tanınamadı.\"}}},ba=()=>{const e={string:{unit:\"توکي\",verb:\"ولري\"},file:{unit:\"بایټس\",verb:\"ولري\"},array:{unit:\"توکي\",verb:\"ولري\"},set:{unit:\"توکي\",verb:\"ولري\"}};function t(t){return e[t]??null}const n={regex:\"ورودي\",email:\"بریښنالیک\",url:\"یو آر ال\",emoji:\"ایموجي\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"نیټه او وخت\",date:\"نېټه\",time:\"وخت\",duration:\"موده\",ipv4:\"د IPv4 پته\",ipv6:\"د IPv6 پته\",cidrv4:\"د IPv4 ساحه\",cidrv6:\"د IPv6 ساحه\",base64:\"base64-encoded متن\",base64url:\"base64url-encoded متن\",json_string:\"JSON متن\",e164:\"د E.164 شمېره\",jwt:\"JWT\",template_literal:\"ورودي\"};return e=>{switch(e.code){case\"invalid_type\":return`ناسم ورودي: باید ${e.expected} وای, مګر ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"عدد\";case\"object\":if(Array.isArray(e))return\"ارې\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)} ترلاسه شو`;case\"invalid_value\":return 1===e.values.length?`ناسم ورودي: باید ${C(e.values[0])} وای`:`ناسم انتخاب: باید یو له ${l(e.values,\"|\")} څخه وای`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`ډیر لوی: ${e.origin??\"ارزښت\"} باید ${n}${e.maximum.toString()} ${r.unit??\"عنصرونه\"} ولري`:`ډیر لوی: ${e.origin??\"ارزښت\"} باید ${n}${e.maximum.toString()} وي`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`ډیر کوچنی: ${e.origin} باید ${n}${e.minimum.toString()} ${r.unit} ولري`:`ډیر کوچنی: ${e.origin} باید ${n}${e.minimum.toString()} وي`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`ناسم متن: باید د \"${t.prefix}\" سره پیل شي`:\"ends_with\"===t.format?`ناسم متن: باید د \"${t.suffix}\" سره پای ته ورسيږي`:\"includes\"===t.format?`ناسم متن: باید \"${t.includes}\" ولري`:\"regex\"===t.format?`ناسم متن: باید د ${t.pattern} سره مطابقت ولري`:`${n[t.format]??e.format} ناسم دی`}case\"not_multiple_of\":return`ناسم عدد: باید د ${e.divisor} مضرب وي`;case\"unrecognized_keys\":return`ناسم ${e.keys.length>1?\"کلیډونه\":\"کلیډ\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`ناسم کلیډ په ${e.origin} کې`;case\"invalid_union\":default:return\"ناسمه ورودي\";case\"invalid_element\":return`ناسم عنصر په ${e.origin} کې`}}},ya=()=>{const e={string:{unit:\"znaków\",verb:\"mieć\"},file:{unit:\"bajtów\",verb:\"mieć\"},array:{unit:\"elementów\",verb:\"mieć\"},set:{unit:\"elementów\",verb:\"mieć\"}};function t(t){return e[t]??null}const n={regex:\"wyrażenie\",email:\"adres email\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"data i godzina w formacie ISO\",date:\"data w formacie ISO\",time:\"godzina w formacie ISO\",duration:\"czas trwania ISO\",ipv4:\"adres IPv4\",ipv6:\"adres IPv6\",cidrv4:\"zakres IPv4\",cidrv6:\"zakres IPv6\",base64:\"ciąg znaków zakodowany w formacie base64\",base64url:\"ciąg znaków zakodowany w formacie base64url\",json_string:\"ciąg znaków w formacie JSON\",e164:\"liczba E.164\",jwt:\"JWT\",template_literal:\"wejście\"};return e=>{switch(e.code){case\"invalid_type\":return`Nieprawidłowe dane wejściowe: oczekiwano ${e.expected}, otrzymano ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"liczba\";case\"object\":if(Array.isArray(e))return\"tablica\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Nieprawidłowe dane wejściowe: oczekiwano ${C(e.values[0])}`:`Nieprawidłowa opcja: oczekiwano jednej z wartości ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Za duża wartość: oczekiwano, że ${e.origin??\"wartość\"} będzie mieć ${n}${e.maximum.toString()} ${r.unit??\"elementów\"}`:`Zbyt duż(y/a/e): oczekiwano, że ${e.origin??\"wartość\"} będzie wynosić ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Za mała wartość: oczekiwano, że ${e.origin??\"wartość\"} będzie mieć ${n}${e.minimum.toString()} ${r.unit??\"elementów\"}`:`Zbyt mał(y/a/e): oczekiwano, że ${e.origin??\"wartość\"} będzie wynosić ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Nieprawidłowy ciąg znaków: musi zaczynać się od \"${t.prefix}\"`:\"ends_with\"===t.format?`Nieprawidłowy ciąg znaków: musi kończyć się na \"${t.suffix}\"`:\"includes\"===t.format?`Nieprawidłowy ciąg znaków: musi zawierać \"${t.includes}\"`:\"regex\"===t.format?`Nieprawidłowy ciąg znaków: musi odpowiadać wzorcowi ${t.pattern}`:`Nieprawidłow(y/a/e) ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Nieprawidłowa liczba: musi być wielokrotnością ${e.divisor}`;case\"unrecognized_keys\":return`Nierozpoznane klucze${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Nieprawidłowy klucz w ${e.origin}`;case\"invalid_union\":default:return\"Nieprawidłowe dane wejściowe\";case\"invalid_element\":return`Nieprawidłowa wartość w ${e.origin}`}}},Oa=()=>{const e={string:{unit:\"caracteres\",verb:\"ter\"},file:{unit:\"bytes\",verb:\"ter\"},array:{unit:\"itens\",verb:\"ter\"},set:{unit:\"itens\",verb:\"ter\"}};function t(t){return e[t]??null}const n={regex:\"padrão\",email:\"endereço de e-mail\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"data e hora ISO\",date:\"data ISO\",time:\"hora ISO\",duration:\"duração ISO\",ipv4:\"endereço IPv4\",ipv6:\"endereço IPv6\",cidrv4:\"faixa de IPv4\",cidrv6:\"faixa de IPv6\",base64:\"texto codificado em base64\",base64url:\"URL codificada em base64\",json_string:\"texto JSON\",e164:\"número E.164\",jwt:\"JWT\",template_literal:\"entrada\"};return e=>{switch(e.code){case\"invalid_type\":return`Tipo inválido: esperado ${e.expected}, recebido ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"número\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"nulo\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Entrada inválida: esperado ${C(e.values[0])}`:`Opção inválida: esperada uma das ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Muito grande: esperado que ${e.origin??\"valor\"} tivesse ${n}${e.maximum.toString()} ${r.unit??\"elementos\"}`:`Muito grande: esperado que ${e.origin??\"valor\"} fosse ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Muito pequeno: esperado que ${e.origin} tivesse ${n}${e.minimum.toString()} ${r.unit}`:`Muito pequeno: esperado que ${e.origin} fosse ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Texto inválido: deve começar com \"${t.prefix}\"`:\"ends_with\"===t.format?`Texto inválido: deve terminar com \"${t.suffix}\"`:\"includes\"===t.format?`Texto inválido: deve incluir \"${t.includes}\"`:\"regex\"===t.format?`Texto inválido: deve corresponder ao padrão ${t.pattern}`:`${n[t.format]??e.format} inválido`}case\"not_multiple_of\":return`Número inválido: deve ser múltiplo de ${e.divisor}`;case\"unrecognized_keys\":return`Chave${e.keys.length>1?\"s\":\"\"} desconhecida${e.keys.length>1?\"s\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Chave inválida em ${e.origin}`;case\"invalid_union\":return\"Entrada inválida\";case\"invalid_element\":return`Valor inválido em ${e.origin}`;default:return\"Campo inválido\"}}};function wa(e,t,n,r){const a=Math.abs(e),o=a%10,i=a%100;return i>=11&&i<=19?r:1===o?t:o>=2&&o<=4?n:r}const xa=()=>{const e={string:{unit:{one:\"символ\",few:\"символа\",many:\"символов\"},verb:\"иметь\"},file:{unit:{one:\"байт\",few:\"байта\",many:\"байт\"},verb:\"иметь\"},array:{unit:{one:\"элемент\",few:\"элемента\",many:\"элементов\"},verb:\"иметь\"},set:{unit:{one:\"элемент\",few:\"элемента\",many:\"элементов\"},verb:\"иметь\"}};function t(t){return e[t]??null}const n={regex:\"ввод\",email:\"email адрес\",url:\"URL\",emoji:\"эмодзи\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO дата и время\",date:\"ISO дата\",time:\"ISO время\",duration:\"ISO длительность\",ipv4:\"IPv4 адрес\",ipv6:\"IPv6 адрес\",cidrv4:\"IPv4 диапазон\",cidrv6:\"IPv6 диапазон\",base64:\"строка в формате base64\",base64url:\"строка в формате base64url\",json_string:\"JSON строка\",e164:\"номер E.164\",jwt:\"JWT\",template_literal:\"ввод\"};return e=>{switch(e.code){case\"invalid_type\":return`Неверный ввод: ожидалось ${e.expected}, получено ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"число\";case\"object\":if(Array.isArray(e))return\"массив\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Неверный ввод: ожидалось ${C(e.values[0])}`:`Неверный вариант: ожидалось одно из ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);if(r){const t=wa(Number(e.maximum),r.unit.one,r.unit.few,r.unit.many);return`Слишком большое значение: ожидалось, что ${e.origin??\"значение\"} будет иметь ${n}${e.maximum.toString()} ${t}`}return`Слишком большое значение: ожидалось, что ${e.origin??\"значение\"} будет ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);if(r){const t=wa(Number(e.minimum),r.unit.one,r.unit.few,r.unit.many);return`Слишком маленькое значение: ожидалось, что ${e.origin} будет иметь ${n}${e.minimum.toString()} ${t}`}return`Слишком маленькое значение: ожидалось, что ${e.origin} будет ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Неверная строка: должна начинаться с \"${t.prefix}\"`:\"ends_with\"===t.format?`Неверная строка: должна заканчиваться на \"${t.suffix}\"`:\"includes\"===t.format?`Неверная строка: должна содержать \"${t.includes}\"`:\"regex\"===t.format?`Неверная строка: должна соответствовать шаблону ${t.pattern}`:`Неверный ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Неверное число: должно быть кратным ${e.divisor}`;case\"unrecognized_keys\":return`Нераспознанн${e.keys.length>1?\"ые\":\"ый\"} ключ${e.keys.length>1?\"и\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Неверный ключ в ${e.origin}`;case\"invalid_union\":default:return\"Неверные входные данные\";case\"invalid_element\":return`Неверное значение в ${e.origin}`}}},ka=()=>{const e={string:{unit:\"znakov\",verb:\"imeti\"},file:{unit:\"bajtov\",verb:\"imeti\"},array:{unit:\"elementov\",verb:\"imeti\"},set:{unit:\"elementov\",verb:\"imeti\"}};function t(t){return e[t]??null}const n={regex:\"vnos\",email:\"e-poštni naslov\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO datum in čas\",date:\"ISO datum\",time:\"ISO čas\",duration:\"ISO trajanje\",ipv4:\"IPv4 naslov\",ipv6:\"IPv6 naslov\",cidrv4:\"obseg IPv4\",cidrv6:\"obseg IPv6\",base64:\"base64 kodiran niz\",base64url:\"base64url kodiran niz\",json_string:\"JSON niz\",e164:\"E.164 številka\",jwt:\"JWT\",template_literal:\"vnos\"};return e=>{switch(e.code){case\"invalid_type\":return`Neveljaven vnos: pričakovano ${e.expected}, prejeto ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"število\";case\"object\":if(Array.isArray(e))return\"tabela\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Neveljaven vnos: pričakovano ${C(e.values[0])}`:`Neveljavna možnost: pričakovano eno izmed ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Preveliko: pričakovano, da bo ${e.origin??\"vrednost\"} imelo ${n}${e.maximum.toString()} ${r.unit??\"elementov\"}`:`Preveliko: pričakovano, da bo ${e.origin??\"vrednost\"} ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Premajhno: pričakovano, da bo ${e.origin} imelo ${n}${e.minimum.toString()} ${r.unit}`:`Premajhno: pričakovano, da bo ${e.origin} ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Neveljaven niz: mora se začeti z \"${t.prefix}\"`:\"ends_with\"===t.format?`Neveljaven niz: mora se končati z \"${t.suffix}\"`:\"includes\"===t.format?`Neveljaven niz: mora vsebovati \"${t.includes}\"`:\"regex\"===t.format?`Neveljaven niz: mora ustrezati vzorcu ${t.pattern}`:`Neveljaven ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Neveljavno število: mora biti večkratnik ${e.divisor}`;case\"unrecognized_keys\":return`Neprepoznan${e.keys.length>1?\"i ključi\":\" ključ\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Neveljaven ključ v ${e.origin}`;case\"invalid_union\":default:return\"Neveljaven vnos\";case\"invalid_element\":return`Neveljavna vrednost v ${e.origin}`}}},Sa=()=>{const e={string:{unit:\"tecken\",verb:\"att ha\"},file:{unit:\"bytes\",verb:\"att ha\"},array:{unit:\"objekt\",verb:\"att innehålla\"},set:{unit:\"objekt\",verb:\"att innehålla\"}};function t(t){return e[t]??null}const n={regex:\"reguljärt uttryck\",email:\"e-postadress\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO-datum och tid\",date:\"ISO-datum\",time:\"ISO-tid\",duration:\"ISO-varaktighet\",ipv4:\"IPv4-intervall\",ipv6:\"IPv6-intervall\",cidrv4:\"IPv4-spektrum\",cidrv6:\"IPv6-spektrum\",base64:\"base64-kodad sträng\",base64url:\"base64url-kodad sträng\",json_string:\"JSON-sträng\",e164:\"E.164-nummer\",jwt:\"JWT\",template_literal:\"mall-literal\"};return e=>{switch(e.code){case\"invalid_type\":return`Ogiltig inmatning: förväntat ${e.expected}, fick ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"antal\";case\"object\":if(Array.isArray(e))return\"lista\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Ogiltig inmatning: förväntat ${C(e.values[0])}`:`Ogiltigt val: förväntade en av ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`För stor(t): förväntade ${e.origin??\"värdet\"} att ha ${n}${e.maximum.toString()} ${r.unit??\"element\"}`:`För stor(t): förväntat ${e.origin??\"värdet\"} att ha ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`För lite(t): förväntade ${e.origin??\"värdet\"} att ha ${n}${e.minimum.toString()} ${r.unit}`:`För lite(t): förväntade ${e.origin??\"värdet\"} att ha ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ogiltig sträng: måste börja med \"${t.prefix}\"`:\"ends_with\"===t.format?`Ogiltig sträng: måste sluta med \"${t.suffix}\"`:\"includes\"===t.format?`Ogiltig sträng: måste innehålla \"${t.includes}\"`:\"regex\"===t.format?`Ogiltig sträng: måste matcha mönstret \"${t.pattern}\"`:`Ogiltig(t) ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Ogiltigt tal: måste vara en multipel av ${e.divisor}`;case\"unrecognized_keys\":return`${e.keys.length>1?\"Okända nycklar\":\"Okänd nyckel\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Ogiltig nyckel i ${e.origin??\"värdet\"}`;case\"invalid_union\":default:return\"Ogiltig input\";case\"invalid_element\":return`Ogiltigt värde i ${e.origin??\"värdet\"}`}}},_a=()=>{const e={string:{unit:\"எழுத்துக்கள்\",verb:\"கொண்டிருக்க வேண்டும்\"},file:{unit:\"பைட்டுகள்\",verb:\"கொண்டிருக்க வேண்டும்\"},array:{unit:\"உறுப்புகள்\",verb:\"கொண்டிருக்க வேண்டும்\"},set:{unit:\"உறுப்புகள்\",verb:\"கொண்டிருக்க வேண்டும்\"}};function t(t){return e[t]??null}const n={regex:\"உள்ளீடு\",email:\"மின்னஞ்சல் முகவரி\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO தேதி நேரம்\",date:\"ISO தேதி\",time:\"ISO நேரம்\",duration:\"ISO கால அளவு\",ipv4:\"IPv4 முகவரி\",ipv6:\"IPv6 முகவரி\",cidrv4:\"IPv4 வரம்பு\",cidrv6:\"IPv6 வரம்பு\",base64:\"base64-encoded சரம்\",base64url:\"base64url-encoded சரம்\",json_string:\"JSON சரம்\",e164:\"E.164 எண்\",jwt:\"JWT\",template_literal:\"input\"};return e=>{switch(e.code){case\"invalid_type\":return`தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${e.expected}, பெறப்பட்டது ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"எண் அல்லாதது\":\"எண்\";case\"object\":if(Array.isArray(e))return\"அணி\";if(null===e)return\"வெறுமை\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${C(e.values[0])}`:`தவறான விருப்பம்: எதிர்பார்க்கப்பட்டது ${l(e.values,\"|\")} இல் ஒன்று`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`மிக பெரியது: எதிர்பார்க்கப்பட்டது ${e.origin??\"மதிப்பு\"} ${n}${e.maximum.toString()} ${r.unit??\"உறுப்புகள்\"} ஆக இருக்க வேண்டும்`:`மிக பெரியது: எதிர்பார்க்கப்பட்டது ${e.origin??\"மதிப்பு\"} ${n}${e.maximum.toString()} ஆக இருக்க வேண்டும்`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${e.origin} ${n}${e.minimum.toString()} ${r.unit} ஆக இருக்க வேண்டும்`:`மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${e.origin} ${n}${e.minimum.toString()} ஆக இருக்க வேண்டும்`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`தவறான சரம்: \"${t.prefix}\" இல் தொடங்க வேண்டும்`:\"ends_with\"===t.format?`தவறான சரம்: \"${t.suffix}\" இல் முடிவடைய வேண்டும்`:\"includes\"===t.format?`தவறான சரம்: \"${t.includes}\" ஐ உள்ளடக்க வேண்டும்`:\"regex\"===t.format?`தவறான சரம்: ${t.pattern} முறைபாட்டுடன் பொருந்த வேண்டும்`:`தவறான ${n[t.format]??e.format}`}case\"not_multiple_of\":return`தவறான எண்: ${e.divisor} இன் பலமாக இருக்க வேண்டும்`;case\"unrecognized_keys\":return`அடையாளம் தெரியாத விசை${e.keys.length>1?\"கள்\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`${e.origin} இல் தவறான விசை`;case\"invalid_union\":default:return\"தவறான உள்ளீடு\";case\"invalid_element\":return`${e.origin} இல் தவறான மதிப்பு`}}},Ta=()=>{const e={string:{unit:\"ตัวอักษร\",verb:\"ควรมี\"},file:{unit:\"ไบต์\",verb:\"ควรมี\"},array:{unit:\"รายการ\",verb:\"ควรมี\"},set:{unit:\"รายการ\",verb:\"ควรมี\"}};function t(t){return e[t]??null}const n={regex:\"ข้อมูลที่ป้อน\",email:\"ที่อยู่อีเมล\",url:\"URL\",emoji:\"อิโมจิ\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"วันที่เวลาแบบ ISO\",date:\"วันที่แบบ ISO\",time:\"เวลาแบบ ISO\",duration:\"ช่วงเวลาแบบ ISO\",ipv4:\"ที่อยู่ IPv4\",ipv6:\"ที่อยู่ IPv6\",cidrv4:\"ช่วง IP แบบ IPv4\",cidrv6:\"ช่วง IP แบบ IPv6\",base64:\"ข้อความแบบ Base64\",base64url:\"ข้อความแบบ Base64 สำหรับ URL\",json_string:\"ข้อความแบบ JSON\",e164:\"เบอร์โทรศัพท์ระหว่างประเทศ (E.164)\",jwt:\"โทเคน JWT\",template_literal:\"ข้อมูลที่ป้อน\"};return e=>{switch(e.code){case\"invalid_type\":return`ประเภทข้อมูลไม่ถูกต้อง: ควรเป็น ${e.expected} แต่ได้รับ ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"ไม่ใช่ตัวเลข (NaN)\":\"ตัวเลข\";case\"object\":if(Array.isArray(e))return\"อาร์เรย์ (Array)\";if(null===e)return\"ไม่มีค่า (null)\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`ค่าไม่ถูกต้อง: ควรเป็น ${C(e.values[0])}`:`ตัวเลือกไม่ถูกต้อง: ควรเป็นหนึ่งใน ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"ไม่เกิน\":\"น้อยกว่า\",r=t(e.origin);return r?`เกินกำหนด: ${e.origin??\"ค่า\"} ควรมี${n} ${e.maximum.toString()} ${r.unit??\"รายการ\"}`:`เกินกำหนด: ${e.origin??\"ค่า\"} ควรมี${n} ${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\"อย่างน้อย\":\"มากกว่า\",r=t(e.origin);return r?`น้อยกว่ากำหนด: ${e.origin} ควรมี${n} ${e.minimum.toString()} ${r.unit}`:`น้อยกว่ากำหนด: ${e.origin} ควรมี${n} ${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`รูปแบบไม่ถูกต้อง: ข้อความต้องขึ้นต้นด้วย \"${t.prefix}\"`:\"ends_with\"===t.format?`รูปแบบไม่ถูกต้อง: ข้อความต้องลงท้ายด้วย \"${t.suffix}\"`:\"includes\"===t.format?`รูปแบบไม่ถูกต้อง: ข้อความต้องมี \"${t.includes}\" อยู่ในข้อความ`:\"regex\"===t.format?`รูปแบบไม่ถูกต้อง: ต้องตรงกับรูปแบบที่กำหนด ${t.pattern}`:`รูปแบบไม่ถูกต้อง: ${n[t.format]??e.format}`}case\"not_multiple_of\":return`ตัวเลขไม่ถูกต้อง: ต้องเป็นจำนวนที่หารด้วย ${e.divisor} ได้ลงตัว`;case\"unrecognized_keys\":return`พบคีย์ที่ไม่รู้จัก: ${l(e.keys,\", \")}`;case\"invalid_key\":return`คีย์ไม่ถูกต้องใน ${e.origin}`;case\"invalid_union\":return\"ข้อมูลไม่ถูกต้อง: ไม่ตรงกับรูปแบบยูเนียนที่กำหนดไว้\";case\"invalid_element\":return`ข้อมูลไม่ถูกต้องใน ${e.origin}`;default:return\"ข้อมูลไม่ถูกต้อง\"}}},Ea=()=>{const e={string:{unit:\"karakter\",verb:\"olmalı\"},file:{unit:\"bayt\",verb:\"olmalı\"},array:{unit:\"öğe\",verb:\"olmalı\"},set:{unit:\"öğe\",verb:\"olmalı\"}};function t(t){return e[t]??null}const n={regex:\"girdi\",email:\"e-posta adresi\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO tarih ve saat\",date:\"ISO tarih\",time:\"ISO saat\",duration:\"ISO süre\",ipv4:\"IPv4 adresi\",ipv6:\"IPv6 adresi\",cidrv4:\"IPv4 aralığı\",cidrv6:\"IPv6 aralığı\",base64:\"base64 ile şifrelenmiş metin\",base64url:\"base64url ile şifrelenmiş metin\",json_string:\"JSON dizesi\",e164:\"E.164 sayısı\",jwt:\"JWT\",template_literal:\"Şablon dizesi\"};return e=>{switch(e.code){case\"invalid_type\":return`Geçersiz değer: beklenen ${e.expected}, alınan ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Geçersiz değer: beklenen ${C(e.values[0])}`:`Geçersiz seçenek: aşağıdakilerden biri olmalı: ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Çok büyük: beklenen ${e.origin??\"değer\"} ${n}${e.maximum.toString()} ${r.unit??\"öğe\"}`:`Çok büyük: beklenen ${e.origin??\"değer\"} ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Çok küçük: beklenen ${e.origin} ${n}${e.minimum.toString()} ${r.unit}`:`Çok küçük: beklenen ${e.origin} ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Geçersiz metin: \"${t.prefix}\" ile başlamalı`:\"ends_with\"===t.format?`Geçersiz metin: \"${t.suffix}\" ile bitmeli`:\"includes\"===t.format?`Geçersiz metin: \"${t.includes}\" içermeli`:\"regex\"===t.format?`Geçersiz metin: ${t.pattern} desenine uymalı`:`Geçersiz ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Geçersiz sayı: ${e.divisor} ile tam bölünebilmeli`;case\"unrecognized_keys\":return`Tanınmayan anahtar${e.keys.length>1?\"lar\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`${e.origin} içinde geçersiz anahtar`;case\"invalid_union\":default:return\"Geçersiz değer\";case\"invalid_element\":return`${e.origin} içinde geçersiz değer`}}},Aa=()=>{const e={string:{unit:\"символів\",verb:\"матиме\"},file:{unit:\"байтів\",verb:\"матиме\"},array:{unit:\"елементів\",verb:\"матиме\"},set:{unit:\"елементів\",verb:\"матиме\"}};function t(t){return e[t]??null}const n={regex:\"вхідні дані\",email:\"адреса електронної пошти\",url:\"URL\",emoji:\"емодзі\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"дата та час ISO\",date:\"дата ISO\",time:\"час ISO\",duration:\"тривалість ISO\",ipv4:\"адреса IPv4\",ipv6:\"адреса IPv6\",cidrv4:\"діапазон IPv4\",cidrv6:\"діапазон IPv6\",base64:\"рядок у кодуванні base64\",base64url:\"рядок у кодуванні base64url\",json_string:\"рядок JSON\",e164:\"номер E.164\",jwt:\"JWT\",template_literal:\"вхідні дані\"};return e=>{switch(e.code){case\"invalid_type\":return`Неправильні вхідні дані: очікується ${e.expected}, отримано ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"число\";case\"object\":if(Array.isArray(e))return\"масив\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Неправильні вхідні дані: очікується ${C(e.values[0])}`:`Неправильна опція: очікується одне з ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Занадто велике: очікується, що ${e.origin??\"значення\"} ${r.verb} ${n}${e.maximum.toString()} ${r.unit??\"елементів\"}`:`Занадто велике: очікується, що ${e.origin??\"значення\"} буде ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Занадто мале: очікується, що ${e.origin} ${r.verb} ${n}${e.minimum.toString()} ${r.unit}`:`Занадто мале: очікується, що ${e.origin} буде ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Неправильний рядок: повинен починатися з \"${t.prefix}\"`:\"ends_with\"===t.format?`Неправильний рядок: повинен закінчуватися на \"${t.suffix}\"`:\"includes\"===t.format?`Неправильний рядок: повинен містити \"${t.includes}\"`:\"regex\"===t.format?`Неправильний рядок: повинен відповідати шаблону ${t.pattern}`:`Неправильний ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Неправильне число: повинно бути кратним ${e.divisor}`;case\"unrecognized_keys\":return`Нерозпізнаний ключ${e.keys.length>1?\"і\":\"\"}: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Неправильний ключ у ${e.origin}`;case\"invalid_union\":default:return\"Неправильні вхідні дані\";case\"invalid_element\":return`Неправильне значення у ${e.origin}`}}};function Ca(){return{localeError:Aa()}}const Pa=()=>{const e={string:{unit:\"حروف\",verb:\"ہونا\"},file:{unit:\"بائٹس\",verb:\"ہونا\"},array:{unit:\"آئٹمز\",verb:\"ہونا\"},set:{unit:\"آئٹمز\",verb:\"ہونا\"}};function t(t){return e[t]??null}const n={regex:\"ان پٹ\",email:\"ای میل ایڈریس\",url:\"یو آر ایل\",emoji:\"ایموجی\",uuid:\"یو یو آئی ڈی\",uuidv4:\"یو یو آئی ڈی وی 4\",uuidv6:\"یو یو آئی ڈی وی 6\",nanoid:\"نینو آئی ڈی\",guid:\"جی یو آئی ڈی\",cuid:\"سی یو آئی ڈی\",cuid2:\"سی یو آئی ڈی 2\",ulid:\"یو ایل آئی ڈی\",xid:\"ایکس آئی ڈی\",ksuid:\"کے ایس یو آئی ڈی\",datetime:\"آئی ایس او ڈیٹ ٹائم\",date:\"آئی ایس او تاریخ\",time:\"آئی ایس او وقت\",duration:\"آئی ایس او مدت\",ipv4:\"آئی پی وی 4 ایڈریس\",ipv6:\"آئی پی وی 6 ایڈریس\",cidrv4:\"آئی پی وی 4 رینج\",cidrv6:\"آئی پی وی 6 رینج\",base64:\"بیس 64 ان کوڈڈ سٹرنگ\",base64url:\"بیس 64 یو آر ایل ان کوڈڈ سٹرنگ\",json_string:\"جے ایس او این سٹرنگ\",e164:\"ای 164 نمبر\",jwt:\"جے ڈبلیو ٹی\",template_literal:\"ان پٹ\"};return e=>{switch(e.code){case\"invalid_type\":return`غلط ان پٹ: ${e.expected} متوقع تھا، ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"نمبر\";case\"object\":if(Array.isArray(e))return\"آرے\";if(null===e)return\"نل\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)} موصول ہوا`;case\"invalid_value\":return 1===e.values.length?`غلط ان پٹ: ${C(e.values[0])} متوقع تھا`:`غلط آپشن: ${l(e.values,\"|\")} میں سے ایک متوقع تھا`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`بہت بڑا: ${e.origin??\"ویلیو\"} کے ${n}${e.maximum.toString()} ${r.unit??\"عناصر\"} ہونے متوقع تھے`:`بہت بڑا: ${e.origin??\"ویلیو\"} کا ${n}${e.maximum.toString()} ہونا متوقع تھا`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`بہت چھوٹا: ${e.origin} کے ${n}${e.minimum.toString()} ${r.unit} ہونے متوقع تھے`:`بہت چھوٹا: ${e.origin} کا ${n}${e.minimum.toString()} ہونا متوقع تھا`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`غلط سٹرنگ: \"${t.prefix}\" سے شروع ہونا چاہیے`:\"ends_with\"===t.format?`غلط سٹرنگ: \"${t.suffix}\" پر ختم ہونا چاہیے`:\"includes\"===t.format?`غلط سٹرنگ: \"${t.includes}\" شامل ہونا چاہیے`:\"regex\"===t.format?`غلط سٹرنگ: پیٹرن ${t.pattern} سے میچ ہونا چاہیے`:`غلط ${n[t.format]??e.format}`}case\"not_multiple_of\":return`غلط نمبر: ${e.divisor} کا مضاعف ہونا چاہیے`;case\"unrecognized_keys\":return`غیر تسلیم شدہ کی${e.keys.length>1?\"ز\":\"\"}: ${l(e.keys,\"، \")}`;case\"invalid_key\":return`${e.origin} میں غلط کی`;case\"invalid_union\":default:return\"غلط ان پٹ\";case\"invalid_element\":return`${e.origin} میں غلط ویلیو`}}},$a=()=>{const e={string:{unit:\"ký tự\",verb:\"có\"},file:{unit:\"byte\",verb:\"có\"},array:{unit:\"phần tử\",verb:\"có\"},set:{unit:\"phần tử\",verb:\"có\"}};function t(t){return e[t]??null}const n={regex:\"đầu vào\",email:\"địa chỉ email\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ngày giờ ISO\",date:\"ngày ISO\",time:\"giờ ISO\",duration:\"khoảng thời gian ISO\",ipv4:\"địa chỉ IPv4\",ipv6:\"địa chỉ IPv6\",cidrv4:\"dải IPv4\",cidrv6:\"dải IPv6\",base64:\"chuỗi mã hóa base64\",base64url:\"chuỗi mã hóa base64url\",json_string:\"chuỗi JSON\",e164:\"số E.164\",jwt:\"JWT\",template_literal:\"đầu vào\"};return e=>{switch(e.code){case\"invalid_type\":return`Đầu vào không hợp lệ: mong đợi ${e.expected}, nhận được ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"số\";case\"object\":if(Array.isArray(e))return\"mảng\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Đầu vào không hợp lệ: mong đợi ${C(e.values[0])}`:`Tùy chọn không hợp lệ: mong đợi một trong các giá trị ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Quá lớn: mong đợi ${e.origin??\"giá trị\"} ${r.verb} ${n}${e.maximum.toString()} ${r.unit??\"phần tử\"}`:`Quá lớn: mong đợi ${e.origin??\"giá trị\"} ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Quá nhỏ: mong đợi ${e.origin} ${r.verb} ${n}${e.minimum.toString()} ${r.unit}`:`Quá nhỏ: mong đợi ${e.origin} ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Chuỗi không hợp lệ: phải bắt đầu bằng \"${t.prefix}\"`:\"ends_with\"===t.format?`Chuỗi không hợp lệ: phải kết thúc bằng \"${t.suffix}\"`:\"includes\"===t.format?`Chuỗi không hợp lệ: phải bao gồm \"${t.includes}\"`:\"regex\"===t.format?`Chuỗi không hợp lệ: phải khớp với mẫu ${t.pattern}`:`${n[t.format]??e.format} không hợp lệ`}case\"not_multiple_of\":return`Số không hợp lệ: phải là bội số của ${e.divisor}`;case\"unrecognized_keys\":return`Khóa không được nhận dạng: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Khóa không hợp lệ trong ${e.origin}`;case\"invalid_union\":default:return\"Đầu vào không hợp lệ\";case\"invalid_element\":return`Giá trị không hợp lệ trong ${e.origin}`}}},Da=()=>{const e={string:{unit:\"字符\",verb:\"包含\"},file:{unit:\"字节\",verb:\"包含\"},array:{unit:\"项\",verb:\"包含\"},set:{unit:\"项\",verb:\"包含\"}};function t(t){return e[t]??null}const n={regex:\"输入\",email:\"电子邮件\",url:\"URL\",emoji:\"表情符号\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO日期时间\",date:\"ISO日期\",time:\"ISO时间\",duration:\"ISO时长\",ipv4:\"IPv4地址\",ipv6:\"IPv6地址\",cidrv4:\"IPv4网段\",cidrv6:\"IPv6网段\",base64:\"base64编码字符串\",base64url:\"base64url编码字符串\",json_string:\"JSON字符串\",e164:\"E.164号码\",jwt:\"JWT\",template_literal:\"输入\"};return e=>{switch(e.code){case\"invalid_type\":return`无效输入:期望 ${e.expected},实际接收 ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"非数字(NaN)\":\"数字\";case\"object\":if(Array.isArray(e))return\"数组\";if(null===e)return\"空值(null)\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`无效输入:期望 ${C(e.values[0])}`:`无效选项:期望以下之一 ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`数值过大:期望 ${e.origin??\"值\"} ${n}${e.maximum.toString()} ${r.unit??\"个元素\"}`:`数值过大:期望 ${e.origin??\"值\"} ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`数值过小:期望 ${e.origin} ${n}${e.minimum.toString()} ${r.unit}`:`数值过小:期望 ${e.origin} ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`无效字符串:必须以 \"${t.prefix}\" 开头`:\"ends_with\"===t.format?`无效字符串:必须以 \"${t.suffix}\" 结尾`:\"includes\"===t.format?`无效字符串:必须包含 \"${t.includes}\"`:\"regex\"===t.format?`无效字符串:必须满足正则表达式 ${t.pattern}`:`无效${n[t.format]??e.format}`}case\"not_multiple_of\":return`无效数字:必须是 ${e.divisor} 的倍数`;case\"unrecognized_keys\":return`出现未知的键(key): ${l(e.keys,\", \")}`;case\"invalid_key\":return`${e.origin} 中的键(key)无效`;case\"invalid_union\":default:return\"无效输入\";case\"invalid_element\":return`${e.origin} 中包含无效值(value)`}}},Ia=()=>{const e={string:{unit:\"字元\",verb:\"擁有\"},file:{unit:\"位元組\",verb:\"擁有\"},array:{unit:\"項目\",verb:\"擁有\"},set:{unit:\"項目\",verb:\"擁有\"}};function t(t){return e[t]??null}const n={regex:\"輸入\",email:\"郵件地址\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"ISO 日期時間\",date:\"ISO 日期\",time:\"ISO 時間\",duration:\"ISO 期間\",ipv4:\"IPv4 位址\",ipv6:\"IPv6 位址\",cidrv4:\"IPv4 範圍\",cidrv6:\"IPv6 範圍\",base64:\"base64 編碼字串\",base64url:\"base64url 編碼字串\",json_string:\"JSON 字串\",e164:\"E.164 數值\",jwt:\"JWT\",template_literal:\"輸入\"};return e=>{switch(e.code){case\"invalid_type\":return`無效的輸入值:預期為 ${e.expected},但收到 ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"number\";case\"object\":if(Array.isArray(e))return\"array\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`無效的輸入值:預期為 ${C(e.values[0])}`:`無效的選項:預期為以下其中之一 ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`數值過大:預期 ${e.origin??\"值\"} 應為 ${n}${e.maximum.toString()} ${r.unit??\"個元素\"}`:`數值過大:預期 ${e.origin??\"值\"} 應為 ${n}${e.maximum.toString()}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`數值過小:預期 ${e.origin} 應為 ${n}${e.minimum.toString()} ${r.unit}`:`數值過小:預期 ${e.origin} 應為 ${n}${e.minimum.toString()}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`無效的字串:必須以 \"${t.prefix}\" 開頭`:\"ends_with\"===t.format?`無效的字串:必須以 \"${t.suffix}\" 結尾`:\"includes\"===t.format?`無效的字串:必須包含 \"${t.includes}\"`:\"regex\"===t.format?`無效的字串:必須符合格式 ${t.pattern}`:`無效的 ${n[t.format]??e.format}`}case\"not_multiple_of\":return`無效的數字:必須為 ${e.divisor} 的倍數`;case\"unrecognized_keys\":return`無法識別的鍵值${e.keys.length>1?\"們\":\"\"}:${l(e.keys,\"、\")}`;case\"invalid_key\":return`${e.origin} 中有無效的鍵值`;case\"invalid_union\":default:return\"無效的輸入值\";case\"invalid_element\":return`${e.origin} 中有無效的值`}}},Ma=()=>{const e={string:{unit:\"àmi\",verb:\"ní\"},file:{unit:\"bytes\",verb:\"ní\"},array:{unit:\"nkan\",verb:\"ní\"},set:{unit:\"nkan\",verb:\"ní\"}};function t(t){return e[t]??null}const n={regex:\"ẹ̀rọ ìbáwọlé\",email:\"àdírẹ́sì ìmẹ́lì\",url:\"URL\",emoji:\"emoji\",uuid:\"UUID\",uuidv4:\"UUIDv4\",uuidv6:\"UUIDv6\",nanoid:\"nanoid\",guid:\"GUID\",cuid:\"cuid\",cuid2:\"cuid2\",ulid:\"ULID\",xid:\"XID\",ksuid:\"KSUID\",datetime:\"àkókò ISO\",date:\"ọjọ́ ISO\",time:\"àkókò ISO\",duration:\"àkókò tó pé ISO\",ipv4:\"àdírẹ́sì IPv4\",ipv6:\"àdírẹ́sì IPv6\",cidrv4:\"àgbègbè IPv4\",cidrv6:\"àgbègbè IPv6\",base64:\"ọ̀rọ̀ tí a kọ́ ní base64\",base64url:\"ọ̀rọ̀ base64url\",json_string:\"ọ̀rọ̀ JSON\",e164:\"nọ́mbà E.164\",jwt:\"JWT\",template_literal:\"ẹ̀rọ ìbáwọlé\"};return e=>{switch(e.code){case\"invalid_type\":return`Ìbáwọlé aṣìṣe: a ní láti fi ${e.expected}, àmọ̀ a rí ${(e=>{const t=typeof e;switch(t){case\"number\":return Number.isNaN(e)?\"NaN\":\"nọ́mbà\";case\"object\":if(Array.isArray(e))return\"akopọ\";if(null===e)return\"null\";if(Object.getPrototypeOf(e)!==Object.prototype&&e.constructor)return e.constructor.name}return t})(e.input)}`;case\"invalid_value\":return 1===e.values.length?`Ìbáwọlé aṣìṣe: a ní láti fi ${C(e.values[0])}`:`Àṣàyàn aṣìṣe: yan ọ̀kan lára ${l(e.values,\"|\")}`;case\"too_big\":{const n=e.inclusive?\"<=\":\"<\",r=t(e.origin);return r?`Tó pọ̀ jù: a ní láti jẹ́ pé ${e.origin??\"iye\"} ${r.verb} ${n}${e.maximum} ${r.unit}`:`Tó pọ̀ jù: a ní láti jẹ́ ${n}${e.maximum}`}case\"too_small\":{const n=e.inclusive?\">=\":\">\",r=t(e.origin);return r?`Kéré ju: a ní láti jẹ́ pé ${e.origin} ${r.verb} ${n}${e.minimum} ${r.unit}`:`Kéré ju: a ní láti jẹ́ ${n}${e.minimum}`}case\"invalid_format\":{const t=e;return\"starts_with\"===t.format?`Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ bẹ̀rẹ̀ pẹ̀lú \"${t.prefix}\"`:\"ends_with\"===t.format?`Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ parí pẹ̀lú \"${t.suffix}\"`:\"includes\"===t.format?`Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ ní \"${t.includes}\"`:\"regex\"===t.format?`Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ bá àpẹẹrẹ mu ${t.pattern}`:`Aṣìṣe: ${n[t.format]??e.format}`}case\"not_multiple_of\":return`Nọ́mbà aṣìṣe: gbọ́dọ̀ jẹ́ èyà pípín ti ${e.divisor}`;case\"unrecognized_keys\":return`Bọtìnì àìmọ̀: ${l(e.keys,\", \")}`;case\"invalid_key\":return`Bọtìnì aṣìṣe nínú ${e.origin}`;case\"invalid_union\":default:return\"Ìbáwọlé aṣìṣe\";case\"invalid_element\":return`Iye aṣìṣe nínú ${e.origin}`}}},Na=Object.freeze(Object.defineProperty({__proto__:null,ar:function(){return{localeError:Lr()}},az:function(){return{localeError:Br()}},be:function(){return{localeError:Ur()}},ca:function(){return{localeError:zr()}},cs:function(){return{localeError:Fr()}},da:function(){return{localeError:Qr()}},de:function(){return{localeError:Zr()}},en:Vr,eo:function(){return{localeError:qr()}},es:function(){return{localeError:Wr()}},fa:function(){return{localeError:Xr()}},fi:function(){return{localeError:Gr()}},fr:function(){return{localeError:Yr()}},frCA:function(){return{localeError:Kr()}},he:function(){return{localeError:Jr()}},hu:function(){return{localeError:ea()}},id:function(){return{localeError:ta()}},is:function(){return{localeError:na()}},it:function(){return{localeError:ra()}},ja:function(){return{localeError:aa()}},ka:function(){return{localeError:oa()}},kh:function(){return sa()},km:sa,ko:function(){return{localeError:la()}},lt:function(){return{localeError:pa()}},mk:function(){return{localeError:ha()}},ms:function(){return{localeError:fa()}},nl:function(){return{localeError:ma()}},no:function(){return{localeError:ga()}},ota:function(){return{localeError:va()}},pl:function(){return{localeError:ya()}},ps:function(){return{localeError:ba()}},pt:function(){return{localeError:Oa()}},ru:function(){return{localeError:xa()}},sl:function(){return{localeError:ka()}},sv:function(){return{localeError:Sa()}},ta:function(){return{localeError:_a()}},th:function(){return{localeError:Ta()}},tr:function(){return{localeError:Ea()}},ua:function(){return Ca()},uk:Ca,ur:function(){return{localeError:Pa()}},vi:function(){return{localeError:$a()}},yo:function(){return{localeError:Ma()}},zhCN:function(){return{localeError:Da()}},zhTW:function(){return{localeError:Ia()}}},Symbol.toStringTag,{value:\"Module\"})),Ra=Symbol(\"ZodOutput\"),La=Symbol(\"ZodInput\");class Ba{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){const n=t[0];if(this._map.set(e,n),n&&\"object\"==typeof n&&\"id\"in n){if(this._idmap.has(n.id))throw new Error(`ID ${n.id} already exists in the registry`);this._idmap.set(n.id,e)}return this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){const t=this._map.get(e);return t&&\"object\"==typeof t&&\"id\"in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){const t=e._zod.parent;if(t){const n={...this.get(t)??{}};delete n.id;const r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}}function ja(){return new Ba}const Ua=ja();function za(e,t){return new e({type:\"string\",...A(t)})}function Fa(e,t){return new e({type:\"string\",coerce:!0,...A(t)})}function Qa(e,t){return new e({type:\"string\",format:\"email\",check:\"string_format\",abort:!1,...A(t)})}function Za(e,t){return new e({type:\"string\",format:\"guid\",check:\"string_format\",abort:!1,...A(t)})}function Ha(e,t){return new e({type:\"string\",format:\"uuid\",check:\"string_format\",abort:!1,...A(t)})}function Va(e,t){return new e({type:\"string\",format:\"uuid\",check:\"string_format\",abort:!1,version:\"v4\",...A(t)})}function qa(e,t){return new e({type:\"string\",format:\"uuid\",check:\"string_format\",abort:!1,version:\"v6\",...A(t)})}function Wa(e,t){return new e({type:\"string\",format:\"uuid\",check:\"string_format\",abort:!1,version:\"v7\",...A(t)})}function Xa(e,t){return new e({type:\"string\",format:\"url\",check:\"string_format\",abort:!1,...A(t)})}function Ga(e,t){return new e({type:\"string\",format:\"emoji\",check:\"string_format\",abort:!1,...A(t)})}function Ya(e,t){return new e({type:\"string\",format:\"nanoid\",check:\"string_format\",abort:!1,...A(t)})}function Ka(e,t){return new e({type:\"string\",format:\"cuid\",check:\"string_format\",abort:!1,...A(t)})}function Ja(e,t){return new e({type:\"string\",format:\"cuid2\",check:\"string_format\",abort:!1,...A(t)})}function eo(e,t){return new e({type:\"string\",format:\"ulid\",check:\"string_format\",abort:!1,...A(t)})}function to(e,t){return new e({type:\"string\",format:\"xid\",check:\"string_format\",abort:!1,...A(t)})}function no(e,t){return new e({type:\"string\",format:\"ksuid\",check:\"string_format\",abort:!1,...A(t)})}function ro(e,t){return new e({type:\"string\",format:\"ipv4\",check:\"string_format\",abort:!1,...A(t)})}function ao(e,t){return new e({type:\"string\",format:\"ipv6\",check:\"string_format\",abort:!1,...A(t)})}function oo(e,t){return new e({type:\"string\",format:\"cidrv4\",check:\"string_format\",abort:!1,...A(t)})}function io(e,t){return new e({type:\"string\",format:\"cidrv6\",check:\"string_format\",abort:!1,...A(t)})}function so(e,t){return new e({type:\"string\",format:\"base64\",check:\"string_format\",abort:!1,...A(t)})}function lo(e,t){return new e({type:\"string\",format:\"base64url\",check:\"string_format\",abort:!1,...A(t)})}function co(e,t){return new e({type:\"string\",format:\"e164\",check:\"string_format\",abort:!1,...A(t)})}function uo(e,t){return new e({type:\"string\",format:\"jwt\",check:\"string_format\",abort:!1,...A(t)})}const po={Any:null,Minute:-1,Second:0,Millisecond:3,Microsecond:6};function ho(e,t){return new e({type:\"string\",format:\"datetime\",check:\"string_format\",offset:!1,local:!1,precision:null,...A(t)})}function fo(e,t){return new e({type:\"string\",format:\"date\",check:\"string_format\",...A(t)})}function mo(e,t){return new e({type:\"string\",format:\"time\",check:\"string_format\",precision:null,...A(t)})}function go(e,t){return new e({type:\"string\",format:\"duration\",check:\"string_format\",...A(t)})}function vo(e,t){return new e({type:\"number\",checks:[],...A(t)})}function bo(e,t){return new e({type:\"number\",coerce:!0,checks:[],...A(t)})}function yo(e,t){return new e({type:\"number\",check:\"number_format\",abort:!1,format:\"safeint\",...A(t)})}function Oo(e,t){return new e({type:\"number\",check:\"number_format\",abort:!1,format:\"float32\",...A(t)})}function wo(e,t){return new e({type:\"number\",check:\"number_format\",abort:!1,format:\"float64\",...A(t)})}function xo(e,t){return new e({type:\"number\",check:\"number_format\",abort:!1,format:\"int32\",...A(t)})}function ko(e,t){return new e({type:\"number\",check:\"number_format\",abort:!1,format:\"uint32\",...A(t)})}function So(e,t){return new e({type:\"boolean\",...A(t)})}function _o(e,t){return new e({type:\"boolean\",coerce:!0,...A(t)})}function To(e,t){return new e({type:\"bigint\",...A(t)})}function Eo(e,t){return new e({type:\"bigint\",coerce:!0,...A(t)})}function Ao(e,t){return new e({type:\"bigint\",check:\"bigint_format\",abort:!1,format:\"int64\",...A(t)})}function Co(e,t){return new e({type:\"bigint\",check:\"bigint_format\",abort:!1,format:\"uint64\",...A(t)})}function Po(e,t){return new e({type:\"symbol\",...A(t)})}function $o(e,t){return new e({type:\"undefined\",...A(t)})}function Do(e,t){return new e({type:\"null\",...A(t)})}function Io(e){return new e({type:\"any\"})}function Mo(e){return new e({type:\"unknown\"})}function No(e,t){return new e({type:\"never\",...A(t)})}function Ro(e,t){return new e({type:\"void\",...A(t)})}function Lo(e,t){return new e({type:\"date\",...A(t)})}function Bo(e,t){return new e({type:\"date\",coerce:!0,...A(t)})}function jo(e,t){return new e({type:\"nan\",...A(t)})}function Uo(e,t){return new Ct({check:\"less_than\",...A(t),value:e,inclusive:!1})}function zo(e,t){return new Ct({check:\"less_than\",...A(t),value:e,inclusive:!0})}function Fo(e,t){return new Pt({check:\"greater_than\",...A(t),value:e,inclusive:!1})}function Qo(e,t){return new Pt({check:\"greater_than\",...A(t),value:e,inclusive:!0})}function Zo(e){return Fo(0,e)}function Ho(e){return Uo(0,e)}function Vo(e){return zo(0,e)}function qo(e){return Qo(0,e)}function Wo(e,t){return new $t({check:\"multiple_of\",...A(t),value:e})}function Xo(e,t){return new Mt({check:\"max_size\",...A(t),maximum:e})}function Go(e,t){return new Nt({check:\"min_size\",...A(t),minimum:e})}function Yo(e,t){return new Rt({check:\"size_equals\",...A(t),size:e})}function Ko(e,t){return new Lt({check:\"max_length\",...A(t),maximum:e})}function Jo(e,t){return new Bt({check:\"min_length\",...A(t),minimum:e})}function ei(e,t){return new jt({check:\"length_equals\",...A(t),length:e})}function ti(e,t){return new zt({check:\"string_format\",format:\"regex\",...A(t),pattern:e})}function ni(e){return new Ft({check:\"string_format\",format:\"lowercase\",...A(e)})}function ri(e){return new Qt({check:\"string_format\",format:\"uppercase\",...A(e)})}function ai(e,t){return new Zt({check:\"string_format\",format:\"includes\",...A(t),includes:e})}function oi(e,t){return new Ht({check:\"string_format\",format:\"starts_with\",...A(t),prefix:e})}function ii(e,t){return new Vt({check:\"string_format\",format:\"ends_with\",...A(t),suffix:e})}function si(e,t,n){return new Wt({check:\"property\",property:e,schema:t,...A(n)})}function li(e,t){return new Xt({check:\"mime_type\",mime:e,...A(t)})}function ci(e){return new Gt({check:\"overwrite\",tx:e})}function ui(e){return ci((t=>t.normalize(e)))}function di(){return ci((e=>e.trim()))}function pi(){return ci((e=>e.toLowerCase()))}function hi(){return ci((e=>e.toUpperCase()))}function fi(e,t,n){return new e({type:\"array\",element:t,...A(n)})}function mi(e,t){return new e({type:\"file\",...A(t)})}function gi(e,t,n){const r=A(n);return r.abort??(r.abort=!0),new e({type:\"custom\",check:\"custom\",fn:t,...r})}function vi(e,t,n){return new e({type:\"custom\",check:\"custom\",fn:t,...A(n)})}function bi(e){const t=yi((n=>(n.addIssue=e=>{if(\"string\"==typeof e)n.issues.push(V(e,n.value,t._zod.def));else{const r=e;r.fatal&&(r.continue=!1),r.code??(r.code=\"custom\"),r.input??(r.input=n.value),r.inst??(r.inst=t),r.continue??(r.continue=!t._zod.def.abort),n.issues.push(V(r))}},e(n.value,n))));return t}function yi(e,t){const n=new Et({check:\"custom\",...A(t)});return n._zod.check=e,n}function Oi(e,t){const n=A(t);let r=n.truthy??[\"true\",\"1\",\"yes\",\"on\",\"y\",\"enabled\"],a=n.falsy??[\"false\",\"0\",\"no\",\"off\",\"n\",\"disabled\"];\"sensitive\"!==n.case&&(r=r.map((e=>\"string\"==typeof e?e.toLowerCase():e)),a=a.map((e=>\"string\"==typeof e?e.toLowerCase():e)));const o=new Set(r),i=new Set(a),s=e.Codec??Tr,l=e.Boolean??Dn,c=new s({type:\"pipe\",in:new(e.String??en)({type:\"string\",error:n.error}),out:new l({type:\"boolean\",error:n.error}),transform:(e,t)=>{let r=e;return\"sensitive\"!==n.case&&(r=r.toLowerCase()),!!o.has(r)||!i.has(r)&&(t.issues.push({code:\"invalid_value\",expected:\"stringbool\",values:[...o,...i],input:t.value,inst:c,continue:!1}),{})},reverseTransform:(e,t)=>!0===e?r[0]||\"true\":a[0]||\"false\",error:n.error});return c}function wi(e,t,n,r={}){const a=A(r),o={...A(r),check:\"string_format\",type:\"string\",format:t,fn:\"function\"==typeof n?n:e=>n.test(e),...a};return n instanceof RegExp&&(o.pattern=n),new e(o)}class xi{constructor(e){this.counter=0,this.metadataRegistry=e?.metadata??Ua,this.target=e?.target??\"draft-2020-12\",this.unrepresentable=e?.unrepresentable??\"throw\",this.override=e?.override??(()=>{}),this.io=e?.io??\"output\",this.seen=new Map}process(e,t={path:[],schemaPath:[]}){var n;const r=e._zod.def,a={guid:\"uuid\",url:\"uri\",datetime:\"date-time\",json_string:\"json-string\",regex:\"\"},o=this.seen.get(e);if(o)return o.count++,t.schemaPath.includes(e)&&(o.cycle=t.path),o.schema;const i={schema:{},count:1,cycle:void 0,path:t.path};this.seen.set(e,i);const l=e._zod.toJSONSchema?.();if(l)i.schema=l;else{const n={...t,schemaPath:[...t.schemaPath,e],path:t.path},o=e._zod.parent;if(o)i.ref=o,this.process(o,n),this.seen.get(o).isParent=!0;else{const t=i.schema;switch(r.type){case\"string\":{const n=t;n.type=\"string\";const{minimum:r,maximum:o,format:s,patterns:l,contentEncoding:c}=e._zod.bag;if(\"number\"==typeof r&&(n.minLength=r),\"number\"==typeof o&&(n.maxLength=o),s&&(n.format=a[s]??s,\"\"===n.format&&delete n.format),c&&(n.contentEncoding=c),l&&l.size>0){const e=[...l];1===e.length?n.pattern=e[0].source:e.length>1&&(i.schema.allOf=[...e.map((e=>({...\"draft-7\"===this.target||\"draft-4\"===this.target||\"openapi-3.0\"===this.target?{type:\"string\"}:{},pattern:e.source})))])}break}case\"number\":{const n=t,{minimum:r,maximum:a,format:o,multipleOf:i,exclusiveMaximum:s,exclusiveMinimum:l}=e._zod.bag;\"string\"==typeof o&&o.includes(\"int\")?n.type=\"integer\":n.type=\"number\",\"number\"==typeof l&&(\"draft-4\"===this.target||\"openapi-3.0\"===this.target?(n.minimum=l,n.exclusiveMinimum=!0):n.exclusiveMinimum=l),\"number\"==typeof r&&(n.minimum=r,\"number\"==typeof l&&\"draft-4\"!==this.target&&(l>=r?delete n.minimum:delete n.exclusiveMinimum)),\"number\"==typeof s&&(\"draft-4\"===this.target||\"openapi-3.0\"===this.target?(n.maximum=s,n.exclusiveMaximum=!0):n.exclusiveMaximum=s),\"number\"==typeof a&&(n.maximum=a,\"number\"==typeof s&&\"draft-4\"!==this.target&&(s<=a?delete n.maximum:delete n.exclusiveMaximum)),\"number\"==typeof i&&(n.multipleOf=i);break}case\"boolean\":case\"success\":t.type=\"boolean\";break;case\"bigint\":if(\"throw\"===this.unrepresentable)throw new Error(\"BigInt cannot be represented in JSON Schema\");break;case\"symbol\":if(\"throw\"===this.unrepresentable)throw new Error(\"Symbols cannot be represented in JSON Schema\");break;case\"null\":\"openapi-3.0\"===this.target?(t.type=\"string\",t.nullable=!0,t.enum=[null]):t.type=\"null\";break;case\"any\":case\"unknown\":break;case\"undefined\":if(\"throw\"===this.unrepresentable)throw new Error(\"Undefined cannot be represented in JSON Schema\");break;case\"void\":if(\"throw\"===this.unrepresentable)throw new Error(\"Void cannot be represented in JSON Schema\");break;case\"never\":t.not={};break;case\"date\":if(\"throw\"===this.unrepresentable)throw new Error(\"Date cannot be represented in JSON Schema\");break;case\"array\":{const a=t,{minimum:o,maximum:i}=e._zod.bag;\"number\"==typeof o&&(a.minItems=o),\"number\"==typeof i&&(a.maxItems=i),a.type=\"array\",a.items=this.process(r.element,{...n,path:[...n.path,\"items\"]});break}case\"object\":{const e=t;e.type=\"object\",e.properties={};const a=r.shape;for(const t in a)e.properties[t]=this.process(a[t],{...n,path:[...n.path,\"properties\",t]});const o=new Set(Object.keys(a)),i=new Set([...o].filter((e=>{const t=r.shape[e]._zod;return\"input\"===this.io?void 0===t.optin:void 0===t.optout})));i.size>0&&(e.required=Array.from(i)),\"never\"===r.catchall?._zod.def.type?e.additionalProperties=!1:r.catchall?r.catchall&&(e.additionalProperties=this.process(r.catchall,{...n,path:[...n.path,\"additionalProperties\"]})):\"output\"===this.io&&(e.additionalProperties=!1);break}case\"union\":{const e=t,a=r.options.map(((e,t)=>this.process(e,{...n,path:[...n.path,\"anyOf\",t]})));e.anyOf=a;break}case\"intersection\":{const e=t,a=this.process(r.left,{...n,path:[...n.path,\"allOf\",0]}),o=this.process(r.right,{...n,path:[...n.path,\"allOf\",1]}),i=e=>\"allOf\"in e&&1===Object.keys(e).length,s=[...i(a)?a.allOf:[a],...i(o)?o.allOf:[o]];e.allOf=s;break}case\"tuple\":{const a=t;a.type=\"array\";const o=\"draft-2020-12\"===this.target?\"prefixItems\":\"items\",i=\"draft-2020-12\"===this.target||\"openapi-3.0\"===this.target?\"items\":\"additionalItems\",s=r.items.map(((e,t)=>this.process(e,{...n,path:[...n.path,o,t]}))),l=r.rest?this.process(r.rest,{...n,path:[...n.path,i,...\"openapi-3.0\"===this.target?[r.items.length]:[]]}):null;\"draft-2020-12\"===this.target?(a.prefixItems=s,l&&(a.items=l)):\"openapi-3.0\"===this.target?(a.items={anyOf:s},l&&a.items.anyOf.push(l),a.minItems=s.length,l||(a.maxItems=s.length)):(a.items=s,l&&(a.additionalItems=l));const{minimum:c,maximum:u}=e._zod.bag;\"number\"==typeof c&&(a.minItems=c),\"number\"==typeof u&&(a.maxItems=u);break}case\"record\":{const e=t;e.type=\"object\",\"draft-7\"!==this.target&&\"draft-2020-12\"!==this.target||(e.propertyNames=this.process(r.keyType,{...n,path:[...n.path,\"propertyNames\"]})),e.additionalProperties=this.process(r.valueType,{...n,path:[...n.path,\"additionalProperties\"]});break}case\"map\":if(\"throw\"===this.unrepresentable)throw new Error(\"Map cannot be represented in JSON Schema\");break;case\"set\":if(\"throw\"===this.unrepresentable)throw new Error(\"Set cannot be represented in JSON Schema\");break;case\"enum\":{const e=t,n=s(r.entries);n.every((e=>\"number\"==typeof e))&&(e.type=\"number\"),n.every((e=>\"string\"==typeof e))&&(e.type=\"string\"),e.enum=n;break}case\"literal\":{const e=t,n=[];for(const e of r.values)if(void 0===e){if(\"throw\"===this.unrepresentable)throw new Error(\"Literal `undefined` cannot be represented in JSON Schema\")}else if(\"bigint\"==typeof e){if(\"throw\"===this.unrepresentable)throw new Error(\"BigInt literals cannot be represented in JSON Schema\");n.push(Number(e))}else n.push(e);if(0===n.length);else if(1===n.length){const t=n[0];e.type=null===t?\"null\":typeof t,\"draft-4\"===this.target||\"openapi-3.0\"===this.target?e.enum=[t]:e.const=t}else n.every((e=>\"number\"==typeof e))&&(e.type=\"number\"),n.every((e=>\"string\"==typeof e))&&(e.type=\"string\"),n.every((e=>\"boolean\"==typeof e))&&(e.type=\"string\"),n.every((e=>null===e))&&(e.type=\"null\"),e.enum=n;break}case\"file\":{const n=t,r={type:\"string\",format:\"binary\",contentEncoding:\"binary\"},{minimum:a,maximum:o,mime:i}=e._zod.bag;void 0!==a&&(r.minLength=a),void 0!==o&&(r.maxLength=o),i?1===i.length?(r.contentMediaType=i[0],Object.assign(n,r)):n.anyOf=i.map((e=>({...r,contentMediaType:e}))):Object.assign(n,r);break}case\"transform\":if(\"throw\"===this.unrepresentable)throw new Error(\"Transforms cannot be represented in JSON Schema\");break;case\"nullable\":{const e=this.process(r.innerType,n);\"openapi-3.0\"===this.target?(i.ref=r.innerType,t.nullable=!0):t.anyOf=[e,{type:\"null\"}];break}case\"nonoptional\":case\"promise\":case\"optional\":this.process(r.innerType,n),i.ref=r.innerType;break;case\"default\":this.process(r.innerType,n),i.ref=r.innerType,t.default=JSON.parse(JSON.stringify(r.defaultValue));break;case\"prefault\":this.process(r.innerType,n),i.ref=r.innerType,\"input\"===this.io&&(t._prefault=JSON.parse(JSON.stringify(r.defaultValue)));break;case\"catch\":{let e;this.process(r.innerType,n),i.ref=r.innerType;try{e=r.catchValue(void 0)}catch{throw new Error(\"Dynamic catch values are not supported in JSON Schema\")}t.default=e;break}case\"nan\":if(\"throw\"===this.unrepresentable)throw new Error(\"NaN cannot be represented in JSON Schema\");break;case\"template_literal\":{const n=t,r=e._zod.pattern;if(!r)throw new Error(\"Pattern not found in template literal\");n.type=\"string\",n.pattern=r.source;break}case\"pipe\":{const e=\"input\"===this.io?\"transform\"===r.in._zod.def.type?r.out:r.in:r.out;this.process(e,n),i.ref=e;break}case\"readonly\":this.process(r.innerType,n),i.ref=r.innerType,t.readOnly=!0;break;case\"lazy\":{const t=e._zod.innerType;this.process(t,n),i.ref=t;break}case\"custom\":if(\"throw\"===this.unrepresentable)throw new Error(\"Custom types cannot be represented in JSON Schema\");break;case\"function\":if(\"throw\"===this.unrepresentable)throw new Error(\"Function types cannot be represented in JSON Schema\")}}}const c=this.metadataRegistry.get(e);return c&&Object.assign(i.schema,c),\"input\"===this.io&&Si(e)&&(delete i.schema.examples,delete i.schema.default),\"input\"===this.io&&i.schema._prefault&&((n=i.schema).default??(n.default=i.schema._prefault)),delete i.schema._prefault,this.seen.get(e).schema}emit(e,t){const n={cycles:t?.cycles??\"ref\",reused:t?.reused??\"inline\",external:t?.external??void 0},r=this.seen.get(e);if(!r)throw new Error(\"Unprocessed schema. This is a bug in Zod.\");const a=e=>{const t=\"draft-2020-12\"===this.target?\"$defs\":\"definitions\";if(n.external){const r=n.external.registry.get(e[0])?.id,a=n.external.uri??(e=>e);if(r)return{ref:a(r)};const o=e[1].defId??e[1].schema.id??\"schema\"+this.counter++;return e[1].defId=o,{defId:o,ref:`${a(\"__shared\")}#/${t}/${o}`}}if(e[1]===r)return{ref:\"#\"};const a=`#/${t}/`,o=e[1].schema.id??\"__schema\"+this.counter++;return{defId:o,ref:a+o}},o=e=>{if(e[1].schema.$ref)return;const t=e[1],{ref:n,defId:r}=a(e);t.def={...t.schema},r&&(t.defId=r);const o=t.schema;for(const e in o)delete o[e];o.$ref=n};if(\"throw\"===n.cycles)for(const e of this.seen.entries()){const t=e[1];if(t.cycle)throw new Error(`Cycle detected: #/${t.cycle?.join(\"/\")}/\\n\\nSet the \\`cycles\\` parameter to \\`\"ref\"\\` to resolve cyclical schemas with defs.`)}for(const t of this.seen.entries()){const r=t[1];if(e===t[0]){o(t);continue}if(n.external){const r=n.external.registry.get(t[0])?.id;if(e!==t[0]&&r){o(t);continue}}const a=this.metadataRegistry.get(t[0])?.id;(a||r.cycle||r.count>1&&\"ref\"===n.reused)&&o(t)}const i=(e,t)=>{const n=this.seen.get(e),r=n.def??n.schema,a={...r};if(null===n.ref)return;const o=n.ref;if(n.ref=null,o){i(o,t);const e=this.seen.get(o).schema;!e.$ref||\"draft-7\"!==t.target&&\"draft-4\"!==t.target&&\"openapi-3.0\"!==t.target?(Object.assign(r,e),Object.assign(r,a)):(r.allOf=r.allOf??[],r.allOf.push(e))}n.isParent||this.override({zodSchema:e,jsonSchema:r,path:n.path??[]})};for(const e of[...this.seen.entries()].reverse())i(e[0],{target:this.target});const s={};if(\"draft-2020-12\"===this.target?s.$schema=\"https://json-schema.org/draft/2020-12/schema\":\"draft-7\"===this.target?s.$schema=\"http://json-schema.org/draft-07/schema#\":\"draft-4\"===this.target?s.$schema=\"http://json-schema.org/draft-04/schema#\":\"openapi-3.0\"===this.target||console.warn(`Invalid target: ${this.target}`),n.external?.uri){const t=n.external.registry.get(e)?.id;if(!t)throw new Error(\"Schema is missing an `id` property\");s.$id=n.external.uri(t)}Object.assign(s,r.def);const l=n.external?.defs??{};for(const e of this.seen.entries()){const t=e[1];t.def&&t.defId&&(l[t.defId]=t.def)}n.external||Object.keys(l).length>0&&(\"draft-2020-12\"===this.target?s.$defs=l:s.definitions=l);try{return JSON.parse(JSON.stringify(s))}catch(e){throw new Error(\"Error converting schema to JSON.\")}}}function ki(e,t){if(e instanceof Ba){const n=new xi(t),r={};for(const t of e._idmap.entries()){const[e,r]=t;n.process(r)}const a={},o={registry:e,uri:t?.uri,defs:r};for(const r of e._idmap.entries()){const[e,i]=r;a[e]=n.emit(i,{...t,external:o})}if(Object.keys(r).length>0){const e=\"draft-2020-12\"===n.target?\"$defs\":\"definitions\";a.__shared={[e]:r}}return{schemas:a}}const n=new xi(t);return n.process(e),n.emit(e,t)}function Si(e,t){const n=t??{seen:new Set};if(n.seen.has(e))return!1;n.seen.add(e);const r=e._zod.def;switch(r.type){case\"string\":case\"number\":case\"bigint\":case\"boolean\":case\"date\":case\"symbol\":case\"undefined\":case\"null\":case\"any\":case\"unknown\":case\"never\":case\"void\":case\"literal\":case\"enum\":case\"nan\":case\"file\":case\"template_literal\":case\"custom\":case\"success\":case\"catch\":case\"function\":return!1;case\"array\":return Si(r.element,n);case\"object\":for(const e in r.shape)if(Si(r.shape[e],n))return!0;return!1;case\"union\":for(const e of r.options)if(Si(e,n))return!0;return!1;case\"intersection\":return Si(r.left,n)||Si(r.right,n);case\"tuple\":for(const e of r.items)if(Si(e,n))return!0;return!(!r.rest||!Si(r.rest,n));case\"record\":case\"map\":return Si(r.keyType,n)||Si(r.valueType,n);case\"set\":return Si(r.valueType,n);case\"promise\":case\"optional\":case\"nonoptional\":case\"nullable\":case\"readonly\":case\"default\":case\"prefault\":return Si(r.innerType,n);case\"lazy\":return Si(r.getter(),n);case\"transform\":return!0;case\"pipe\":return Si(r.in,n)||Si(r.out,n)}throw new Error(`Unknown schema type: ${r.type}`)}const _i=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:\"Module\"})),Ti=Object.freeze(Object.defineProperty({__proto__:null,$ZodAny:Bn,$ZodArray:Zn,$ZodAsyncError:r,$ZodBase64:kn,$ZodBase64URL:_n,$ZodBigInt:In,$ZodBigIntFormat:Mn,$ZodBoolean:Dn,$ZodCIDRv4:On,$ZodCIDRv6:wn,$ZodCUID:cn,$ZodCUID2:un,$ZodCatch:xr,$ZodCheck:Et,$ZodCheckBigIntFormat:It,$ZodCheckEndsWith:Vt,$ZodCheckGreaterThan:Pt,$ZodCheckIncludes:Zt,$ZodCheckLengthEquals:jt,$ZodCheckLessThan:Ct,$ZodCheckLowerCase:Ft,$ZodCheckMaxLength:Lt,$ZodCheckMaxSize:Mt,$ZodCheckMimeType:Xt,$ZodCheckMinLength:Bt,$ZodCheckMinSize:Nt,$ZodCheckMultipleOf:$t,$ZodCheckNumberFormat:Dt,$ZodCheckOverwrite:Gt,$ZodCheckProperty:Wt,$ZodCheckRegex:zt,$ZodCheckSizeEquals:Rt,$ZodCheckStartsWith:Ht,$ZodCheckStringFormat:Ut,$ZodCheckUpperCase:Qt,$ZodCodec:Tr,$ZodCustom:Nr,$ZodCustomStringFormat:Cn,$ZodDate:Fn,$ZodDefault:gr,$ZodDiscriminatedUnion:Kn,$ZodE164:Tn,$ZodEmail:an,$ZodEmoji:sn,$ZodEncodeError:a,$ZodEnum:cr,$ZodError:Y,$ZodFile:dr,$ZodFunction:Dr,$ZodGUID:nn,$ZodIPv4:bn,$ZodIPv6:yn,$ZodISODate:mn,$ZodISODateTime:fn,$ZodISODuration:vn,$ZodISOTime:gn,$ZodIntersection:Jn,$ZodJWT:An,$ZodKSUID:hn,$ZodLazy:Mr,$ZodLiteral:ur,$ZodMap:or,$ZodNaN:kr,$ZodNanoID:ln,$ZodNever:Un,$ZodNonOptional:yr,$ZodNull:Ln,$ZodNullable:mr,$ZodNumber:Pn,$ZodNumberFormat:$n,$ZodObject:Wn,$ZodObjectJIT:Xn,$ZodOptional:fr,$ZodPipe:Sr,$ZodPrefault:br,$ZodPromise:Ir,$ZodReadonly:Cr,$ZodRealError:K,$ZodRecord:ar,$ZodRegistry:Ba,$ZodSet:sr,$ZodString:en,$ZodStringFormat:tn,$ZodSuccess:wr,$ZodSymbol:Nn,$ZodTemplateLiteral:$r,$ZodTransform:pr,$ZodTuple:nr,$ZodType:Jt,$ZodULID:dn,$ZodURL:on,$ZodUUID:rn,$ZodUndefined:Rn,$ZodUnion:Yn,$ZodUnknown:jn,$ZodVoid:zn,$ZodXID:pn,$brand:n,$constructor:t,$input:La,$output:Ra,Doc:Yt,JSONSchema:_i,JSONSchemaGenerator:xi,NEVER:e,TimePrecision:po,_any:Io,_array:fi,_base64:so,_base64url:lo,_bigint:To,_boolean:So,_catch:function(e,t,n){return new e({type:\"catch\",innerType:t,catchValue:\"function\"==typeof n?n:()=>n})},_check:yi,_cidrv4:oo,_cidrv6:io,_coercedBigint:Eo,_coercedBoolean:_o,_coercedDate:Bo,_coercedNumber:bo,_coercedString:Fa,_cuid:Ka,_cuid2:Ja,_custom:gi,_date:Lo,_decode:fe,_decodeAsync:be,_default:function(e,t,n){return new e({type:\"default\",innerType:t,get defaultValue(){return\"function\"==typeof n?n():k(n)}})},_discriminatedUnion:function(e,t,n,r){return new e({type:\"union\",options:n,discriminator:t,...A(r)})},_e164:co,_email:Qa,_emoji:Ga,_encode:pe,_encodeAsync:ge,_endsWith:ii,_enum:function(e,t,n){return new e({type:\"enum\",entries:Array.isArray(t)?Object.fromEntries(t.map((e=>[e,e]))):t,...A(n)})},_file:mi,_float32:Oo,_float64:wo,_gt:Fo,_gte:Qo,_guid:Za,_includes:ai,_int:yo,_int32:xo,_int64:Ao,_intersection:function(e,t,n){return new e({type:\"intersection\",left:t,right:n})},_ipv4:ro,_ipv6:ao,_isoDate:fo,_isoDateTime:ho,_isoDuration:go,_isoTime:mo,_jwt:uo,_ksuid:no,_lazy:function(e,t){return new e({type:\"lazy\",getter:t})},_length:ei,_literal:function(e,t,n){return new e({type:\"literal\",values:Array.isArray(t)?t:[t],...A(n)})},_lowercase:ni,_lt:Uo,_lte:zo,_map:function(e,t,n,r){return new e({type:\"map\",keyType:t,valueType:n,...A(r)})},_max:zo,_maxLength:Ko,_maxSize:Xo,_mime:li,_min:Qo,_minLength:Jo,_minSize:Go,_multipleOf:Wo,_nan:jo,_nanoid:Ya,_nativeEnum:function(e,t,n){return new e({type:\"enum\",entries:t,...A(n)})},_negative:Ho,_never:No,_nonnegative:qo,_nonoptional:function(e,t,n){return new e({type:\"nonoptional\",innerType:t,...A(n)})},_nonpositive:Vo,_normalize:ui,_null:Do,_nullable:function(e,t){return new e({type:\"nullable\",innerType:t})},_number:vo,_optional:function(e,t){return new e({type:\"optional\",innerType:t})},_overwrite:ci,_parse:ae,_parseAsync:ie,_pipe:function(e,t,n){return new e({type:\"pipe\",in:t,out:n})},_positive:Zo,_promise:function(e,t){return new e({type:\"promise\",innerType:t})},_property:si,_readonly:function(e,t){return new e({type:\"readonly\",innerType:t})},_record:function(e,t,n,r){return new e({type:\"record\",keyType:t,valueType:n,...A(r)})},_refine:vi,_regex:ti,_safeDecode:xe,_safeDecodeAsync:Te,_safeEncode:Oe,_safeEncodeAsync:Se,_safeParse:le,_safeParseAsync:ue,_set:function(e,t,n){return new e({type:\"set\",valueType:t,...A(n)})},_size:Yo,_startsWith:oi,_string:za,_stringFormat:wi,_stringbool:Oi,_success:function(e,t){return new e({type:\"success\",innerType:t})},_superRefine:bi,_symbol:Po,_templateLiteral:function(e,t,n){return new e({type:\"template_literal\",parts:t,...A(n)})},_toLowerCase:pi,_toUpperCase:hi,_transform:function(e,t){return new e({type:\"transform\",transform:t})},_trim:di,_tuple:function(e,t,n,r){const a=n instanceof Jt;return new e({type:\"tuple\",items:t,rest:a?n:null,...A(a?r:n)})},_uint32:ko,_uint64:Co,_ulid:eo,_undefined:$o,_union:function(e,t,n){return new e({type:\"union\",options:t,...A(n)})},_unknown:Mo,_uppercase:ri,_url:Xa,_uuid:Ha,_uuidv4:Va,_uuidv6:qa,_uuidv7:Wa,_void:Ro,_xid:to,clone:E,config:i,decode:me,decodeAsync:ye,encode:he,encodeAsync:ve,flattenError:J,formatError:ee,globalConfig:o,globalRegistry:Ua,isValidBase64:xn,isValidBase64URL:Sn,isValidJWT:En,locales:Na,parse:oe,parseAsync:se,prettifyError:re,regexes:Tt,registry:ja,safeDecode:ke,safeDecodeAsync:Ee,safeEncode:we,safeEncodeAsync:_e,safeParse:ce,safeParseAsync:de,toDotPath:ne,toJSONSchema:ki,treeifyError:te,util:X,version:Kt},Symbol.toStringTag,{value:\"Module\"})),Ei=t(\"ZodISODateTime\",((e,t)=>{fn.init(e,t),ts.init(e,t)}));function Ai(e){return ho(Ei,e)}const Ci=t(\"ZodISODate\",((e,t)=>{mn.init(e,t),ts.init(e,t)}));function Pi(e){return fo(Ci,e)}const $i=t(\"ZodISOTime\",((e,t)=>{gn.init(e,t),ts.init(e,t)}));function Di(e){return mo($i,e)}const Ii=t(\"ZodISODuration\",((e,t)=>{vn.init(e,t),ts.init(e,t)}));function Mi(e){return go(Ii,e)}const Ni=Object.freeze(Object.defineProperty({__proto__:null,ZodISODate:Ci,ZodISODateTime:Ei,ZodISODuration:Ii,ZodISOTime:$i,date:Pi,datetime:Ai,duration:Mi,time:Di},Symbol.toStringTag,{value:\"Module\"})),Ri=(e,t)=>{Y.init(e,t),e.name=\"ZodError\",Object.defineProperties(e,{format:{value:t=>ee(e,t)},flatten:{value:t=>J(e,t)},addIssue:{value:t=>{e.issues.push(t),e.message=JSON.stringify(e.issues,c,2)}},addIssues:{value:t=>{e.issues.push(...t),e.message=JSON.stringify(e.issues,c,2)}},isEmpty:{get:()=>0===e.issues.length}})},Li=t(\"ZodError\",Ri),Bi=t(\"ZodError\",Ri,{Parent:Error}),ji=ae(Bi),Ui=ie(Bi),zi=le(Bi),Fi=ue(Bi),Qi=pe(Bi),Zi=fe(Bi),Hi=ge(Bi),Vi=be(Bi),qi=Oe(Bi),Wi=xe(Bi),Xi=Se(Bi),Gi=Te(Bi),Yi=t(\"ZodType\",((e,t)=>(Jt.init(e,t),e.def=t,e.type=t.type,Object.defineProperty(e,\"_def\",{value:t}),e.check=(...n)=>e.clone(v(t,{checks:[...t.checks??[],...n.map((e=>\"function\"==typeof e?{_zod:{check:e,def:{check:\"custom\"},onattach:[]}}:e))]})),e.clone=(t,n)=>E(e,t,n),e.brand=()=>e,e.register=(t,n)=>(t.add(e,n),e),e.parse=(t,n)=>ji(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>zi(e,t,n),e.parseAsync=async(t,n)=>Ui(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>Fi(e,t,n),e.spa=e.safeParseAsync,e.encode=(t,n)=>Qi(e,t,n),e.decode=(t,n)=>Zi(e,t,n),e.encodeAsync=async(t,n)=>Hi(e,t,n),e.decodeAsync=async(t,n)=>Vi(e,t,n),e.safeEncode=(t,n)=>qi(e,t,n),e.safeDecode=(t,n)=>Wi(e,t,n),e.safeEncodeAsync=async(t,n)=>Xi(e,t,n),e.safeDecodeAsync=async(t,n)=>Gi(e,t,n),e.refine=(t,n)=>e.check(Ul(t,n)),e.superRefine=t=>e.check(zl(t)),e.overwrite=t=>e.check(ci(t)),e.optional=()=>fl(e),e.nullable=()=>gl(e),e.nullish=()=>fl(gl(e)),e.nonoptional=t=>xl(e,t),e.array=()=>Zs(e),e.or=t=>Ws([e,t]),e.and=t=>Ks(e,t),e.transform=t=>Al(e,pl(t)),e.default=t=>bl(e,t),e.prefault=t=>Ol(e,t),e.catch=t=>_l(e,t),e.pipe=t=>Al(e,t),e.readonly=()=>$l(e),e.describe=t=>{const n=e.clone();return Ua.add(n,{description:t}),n},Object.defineProperty(e,\"description\",{get:()=>Ua.get(e)?.description,configurable:!0}),e.meta=(...t)=>{if(0===t.length)return Ua.get(e);const n=e.clone();return Ua.add(n,t[0]),n},e.isOptional=()=>e.safeParse(void 0).success,e.isNullable=()=>e.safeParse(null).success,e))),Ki=t(\"_ZodString\",((e,t)=>{en.init(e,t),Yi.init(e,t);const n=e._zod.bag;e.format=n.format??null,e.minLength=n.minimum??null,e.maxLength=n.maximum??null,e.regex=(...t)=>e.check(ti(...t)),e.includes=(...t)=>e.check(ai(...t)),e.startsWith=(...t)=>e.check(oi(...t)),e.endsWith=(...t)=>e.check(ii(...t)),e.min=(...t)=>e.check(Jo(...t)),e.max=(...t)=>e.check(Ko(...t)),e.length=(...t)=>e.check(ei(...t)),e.nonempty=(...t)=>e.check(Jo(1,...t)),e.lowercase=t=>e.check(ni(t)),e.uppercase=t=>e.check(ri(t)),e.trim=()=>e.check(di()),e.normalize=(...t)=>e.check(ui(...t)),e.toLowerCase=()=>e.check(pi()),e.toUpperCase=()=>e.check(hi())})),Ji=t(\"ZodString\",((e,t)=>{en.init(e,t),Ki.init(e,t),e.email=t=>e.check(Qa(ns,t)),e.url=t=>e.check(Xa(os,t)),e.jwt=t=>e.check(uo(Os,t)),e.emoji=t=>e.check(Ga(is,t)),e.guid=t=>e.check(Za(rs,t)),e.uuid=t=>e.check(Ha(as,t)),e.uuidv4=t=>e.check(Va(as,t)),e.uuidv6=t=>e.check(qa(as,t)),e.uuidv7=t=>e.check(Wa(as,t)),e.nanoid=t=>e.check(Ya(ss,t)),e.guid=t=>e.check(Za(rs,t)),e.cuid=t=>e.check(Ka(ls,t)),e.cuid2=t=>e.check(Ja(cs,t)),e.ulid=t=>e.check(eo(us,t)),e.base64=t=>e.check(so(vs,t)),e.base64url=t=>e.check(lo(bs,t)),e.xid=t=>e.check(to(ds,t)),e.ksuid=t=>e.check(no(ps,t)),e.ipv4=t=>e.check(ro(hs,t)),e.ipv6=t=>e.check(ao(fs,t)),e.cidrv4=t=>e.check(oo(ms,t)),e.cidrv6=t=>e.check(io(gs,t)),e.e164=t=>e.check(co(ys,t)),e.datetime=t=>e.check(Ai(t)),e.date=t=>e.check(Pi(t)),e.time=t=>e.check(Di(t)),e.duration=t=>e.check(Mi(t))}));function es(e){return za(Ji,e)}const ts=t(\"ZodStringFormat\",((e,t)=>{tn.init(e,t),Ki.init(e,t)})),ns=t(\"ZodEmail\",((e,t)=>{an.init(e,t),ts.init(e,t)})),rs=t(\"ZodGUID\",((e,t)=>{nn.init(e,t),ts.init(e,t)})),as=t(\"ZodUUID\",((e,t)=>{rn.init(e,t),ts.init(e,t)})),os=t(\"ZodURL\",((e,t)=>{on.init(e,t),ts.init(e,t)})),is=t(\"ZodEmoji\",((e,t)=>{sn.init(e,t),ts.init(e,t)})),ss=t(\"ZodNanoID\",((e,t)=>{ln.init(e,t),ts.init(e,t)})),ls=t(\"ZodCUID\",((e,t)=>{cn.init(e,t),ts.init(e,t)})),cs=t(\"ZodCUID2\",((e,t)=>{un.init(e,t),ts.init(e,t)})),us=t(\"ZodULID\",((e,t)=>{dn.init(e,t),ts.init(e,t)})),ds=t(\"ZodXID\",((e,t)=>{pn.init(e,t),ts.init(e,t)})),ps=t(\"ZodKSUID\",((e,t)=>{hn.init(e,t),ts.init(e,t)})),hs=t(\"ZodIPv4\",((e,t)=>{bn.init(e,t),ts.init(e,t)})),fs=t(\"ZodIPv6\",((e,t)=>{yn.init(e,t),ts.init(e,t)})),ms=t(\"ZodCIDRv4\",((e,t)=>{On.init(e,t),ts.init(e,t)})),gs=t(\"ZodCIDRv6\",((e,t)=>{wn.init(e,t),ts.init(e,t)})),vs=t(\"ZodBase64\",((e,t)=>{kn.init(e,t),ts.init(e,t)})),bs=t(\"ZodBase64URL\",((e,t)=>{_n.init(e,t),ts.init(e,t)})),ys=t(\"ZodE164\",((e,t)=>{Tn.init(e,t),ts.init(e,t)})),Os=t(\"ZodJWT\",((e,t)=>{An.init(e,t),ts.init(e,t)})),ws=t(\"ZodCustomStringFormat\",((e,t)=>{Cn.init(e,t),ts.init(e,t)})),xs=t(\"ZodNumber\",((e,t)=>{Pn.init(e,t),Yi.init(e,t),e.gt=(t,n)=>e.check(Fo(t,n)),e.gte=(t,n)=>e.check(Qo(t,n)),e.min=(t,n)=>e.check(Qo(t,n)),e.lt=(t,n)=>e.check(Uo(t,n)),e.lte=(t,n)=>e.check(zo(t,n)),e.max=(t,n)=>e.check(zo(t,n)),e.int=t=>e.check(_s(t)),e.safe=t=>e.check(_s(t)),e.positive=t=>e.check(Fo(0,t)),e.nonnegative=t=>e.check(Qo(0,t)),e.negative=t=>e.check(Uo(0,t)),e.nonpositive=t=>e.check(zo(0,t)),e.multipleOf=(t,n)=>e.check(Wo(t,n)),e.step=(t,n)=>e.check(Wo(t,n)),e.finite=()=>e;const n=e._zod.bag;e.minValue=Math.max(n.minimum??Number.NEGATIVE_INFINITY,n.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,e.maxValue=Math.min(n.maximum??Number.POSITIVE_INFINITY,n.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,e.isInt=(n.format??\"\").includes(\"int\")||Number.isSafeInteger(n.multipleOf??.5),e.isFinite=!0,e.format=n.format??null}));function ks(e){return vo(xs,e)}const Ss=t(\"ZodNumberFormat\",((e,t)=>{$n.init(e,t),xs.init(e,t)}));function _s(e){return yo(Ss,e)}const Ts=t(\"ZodBoolean\",((e,t)=>{Dn.init(e,t),Yi.init(e,t)}));function Es(e){return So(Ts,e)}const As=t(\"ZodBigInt\",((e,t)=>{In.init(e,t),Yi.init(e,t),e.gte=(t,n)=>e.check(Qo(t,n)),e.min=(t,n)=>e.check(Qo(t,n)),e.gt=(t,n)=>e.check(Fo(t,n)),e.gte=(t,n)=>e.check(Qo(t,n)),e.min=(t,n)=>e.check(Qo(t,n)),e.lt=(t,n)=>e.check(Uo(t,n)),e.lte=(t,n)=>e.check(zo(t,n)),e.max=(t,n)=>e.check(zo(t,n)),e.positive=t=>e.check(Fo(BigInt(0),t)),e.negative=t=>e.check(Uo(BigInt(0),t)),e.nonpositive=t=>e.check(zo(BigInt(0),t)),e.nonnegative=t=>e.check(Qo(BigInt(0),t)),e.multipleOf=(t,n)=>e.check(Wo(t,n));const n=e._zod.bag;e.minValue=n.minimum??null,e.maxValue=n.maximum??null,e.format=n.format??null})),Cs=t(\"ZodBigIntFormat\",((e,t)=>{Mn.init(e,t),As.init(e,t)})),Ps=t(\"ZodSymbol\",((e,t)=>{Nn.init(e,t),Yi.init(e,t)})),$s=t(\"ZodUndefined\",((e,t)=>{Rn.init(e,t),Yi.init(e,t)})),Ds=t(\"ZodNull\",((e,t)=>{Ln.init(e,t),Yi.init(e,t)}));function Is(e){return Do(Ds,e)}const Ms=t(\"ZodAny\",((e,t)=>{Bn.init(e,t),Yi.init(e,t)}));function Ns(){return Io(Ms)}const Rs=t(\"ZodUnknown\",((e,t)=>{jn.init(e,t),Yi.init(e,t)}));function Ls(){return Mo(Rs)}const Bs=t(\"ZodNever\",((e,t)=>{Un.init(e,t),Yi.init(e,t)}));function js(e){return No(Bs,e)}const Us=t(\"ZodVoid\",((e,t)=>{zn.init(e,t),Yi.init(e,t)}));function zs(e){return Ro(Us,e)}const Fs=t(\"ZodDate\",((e,t)=>{Fn.init(e,t),Yi.init(e,t),e.min=(t,n)=>e.check(Qo(t,n)),e.max=(t,n)=>e.check(zo(t,n));const n=e._zod.bag;e.minDate=n.minimum?new Date(n.minimum):null,e.maxDate=n.maximum?new Date(n.maximum):null})),Qs=t(\"ZodArray\",((e,t)=>{Zn.init(e,t),Yi.init(e,t),e.element=t.element,e.min=(t,n)=>e.check(Jo(t,n)),e.nonempty=t=>e.check(Jo(1,t)),e.max=(t,n)=>e.check(Ko(t,n)),e.length=(t,n)=>e.check(ei(t,n)),e.unwrap=()=>e.element}));function Zs(e,t){return fi(Qs,e,t)}const Hs=t(\"ZodObject\",((e,t)=>{Xn.init(e,t),Yi.init(e,t),m(e,\"shape\",(()=>t.shape)),e.keyof=()=>sl(Object.keys(e._zod.def.shape)),e.catchall=t=>e.clone({...e._zod.def,catchall:t}),e.passthrough=()=>e.clone({...e._zod.def,catchall:Ls()}),e.loose=()=>e.clone({...e._zod.def,catchall:Ls()}),e.strict=()=>e.clone({...e._zod.def,catchall:js()}),e.strip=()=>e.clone({...e._zod.def,catchall:void 0}),e.extend=t=>N(e,t),e.safeExtend=t=>R(e,t),e.merge=t=>L(e,t),e.pick=t=>I(e,t),e.omit=t=>M(e,t),e.partial=(...t)=>B(hl,e,t[0]),e.required=(...t)=>j(wl,e,t[0])}));function Vs(e,t){const n={type:\"object\",shape:e??{},...A(t)};return new Hs(n)}const qs=t(\"ZodUnion\",((e,t)=>{Yn.init(e,t),Yi.init(e,t),e.options=t.options}));function Ws(e,t){return new qs({type:\"union\",options:e,...A(t)})}const Xs=t(\"ZodDiscriminatedUnion\",((e,t)=>{qs.init(e,t),Kn.init(e,t)}));function Gs(e,t,n){return new Xs({type:\"union\",options:t,discriminator:e,...A(n)})}const Ys=t(\"ZodIntersection\",((e,t)=>{Jn.init(e,t),Yi.init(e,t)}));function Ks(e,t){return new Ys({type:\"intersection\",left:e,right:t})}const Js=t(\"ZodTuple\",((e,t)=>{nr.init(e,t),Yi.init(e,t),e.rest=t=>e.clone({...e._zod.def,rest:t})}));function el(e,t,n){const r=t instanceof Jt;return new Js({type:\"tuple\",items:e,rest:r?t:null,...A(r?n:t)})}const tl=t(\"ZodRecord\",((e,t)=>{ar.init(e,t),Yi.init(e,t),e.keyType=t.keyType,e.valueType=t.valueType}));function nl(e,t,n){return new tl({type:\"record\",keyType:e,valueType:t,...A(n)})}function rl(e,t,n){const r=E(e);return r._zod.values=void 0,new tl({type:\"record\",keyType:r,valueType:t,...A(n)})}const al=t(\"ZodMap\",((e,t)=>{or.init(e,t),Yi.init(e,t),e.keyType=t.keyType,e.valueType=t.valueType})),ol=t(\"ZodSet\",((e,t)=>{sr.init(e,t),Yi.init(e,t),e.min=(...t)=>e.check(Go(...t)),e.nonempty=t=>e.check(Go(1,t)),e.max=(...t)=>e.check(Xo(...t)),e.size=(...t)=>e.check(Yo(...t))})),il=t(\"ZodEnum\",((e,t)=>{cr.init(e,t),Yi.init(e,t),e.enum=t.entries,e.options=Object.values(t.entries);const n=new Set(Object.keys(t.entries));e.extract=(e,r)=>{const a={};for(const r of e){if(!n.has(r))throw new Error(`Key ${r} not found in enum`);a[r]=t.entries[r]}return new il({...t,checks:[],...A(r),entries:a})},e.exclude=(e,r)=>{const a={...t.entries};for(const t of e){if(!n.has(t))throw new Error(`Key ${t} not found in enum`);delete a[t]}return new il({...t,checks:[],...A(r),entries:a})}}));function sl(e,t){const n=Array.isArray(e)?Object.fromEntries(e.map((e=>[e,e]))):e;return new il({type:\"enum\",entries:n,...A(t)})}const ll=t(\"ZodLiteral\",((e,t)=>{ur.init(e,t),Yi.init(e,t),e.values=new Set(t.values),Object.defineProperty(e,\"value\",{get(){if(t.values.length>1)throw new Error(\"This schema contains multiple valid literal values. Use `.values` instead.\");return t.values[0]}})}));function cl(e,t){return new ll({type:\"literal\",values:Array.isArray(e)?e:[e],...A(t)})}const ul=t(\"ZodFile\",((e,t)=>{dr.init(e,t),Yi.init(e,t),e.min=(t,n)=>e.check(Go(t,n)),e.max=(t,n)=>e.check(Xo(t,n)),e.mime=(t,n)=>e.check(li(Array.isArray(t)?t:[t],n))})),dl=t(\"ZodTransform\",((e,t)=>{pr.init(e,t),Yi.init(e,t),e._zod.parse=(n,r)=>{if(\"backward\"===r.direction)throw new a(e.constructor.name);n.addIssue=r=>{if(\"string\"==typeof r)n.issues.push(V(r,n.value,t));else{const t=r;t.fatal&&(t.continue=!1),t.code??(t.code=\"custom\"),t.input??(t.input=n.value),t.inst??(t.inst=e),n.issues.push(V(t))}};const o=t.transform(n.value,n);return o instanceof Promise?o.then((e=>(n.value=e,n))):(n.value=o,n)}}));function pl(e){return new dl({type:\"transform\",transform:e})}const hl=t(\"ZodOptional\",((e,t)=>{fr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType}));function fl(e){return new hl({type:\"optional\",innerType:e})}const ml=t(\"ZodNullable\",((e,t)=>{mr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType}));function gl(e){return new ml({type:\"nullable\",innerType:e})}const vl=t(\"ZodDefault\",((e,t)=>{gr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType,e.removeDefault=e.unwrap}));function bl(e,t){return new vl({type:\"default\",innerType:e,get defaultValue(){return\"function\"==typeof t?t():k(t)}})}const yl=t(\"ZodPrefault\",((e,t)=>{br.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType}));function Ol(e,t){return new yl({type:\"prefault\",innerType:e,get defaultValue(){return\"function\"==typeof t?t():k(t)}})}const wl=t(\"ZodNonOptional\",((e,t)=>{yr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType}));function xl(e,t){return new wl({type:\"nonoptional\",innerType:e,...A(t)})}const kl=t(\"ZodSuccess\",((e,t)=>{wr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType})),Sl=t(\"ZodCatch\",((e,t)=>{xr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType,e.removeCatch=e.unwrap}));function _l(e,t){return new Sl({type:\"catch\",innerType:e,catchValue:\"function\"==typeof t?t:()=>t})}const Tl=t(\"ZodNaN\",((e,t)=>{kr.init(e,t),Yi.init(e,t)})),El=t(\"ZodPipe\",((e,t)=>{Sr.init(e,t),Yi.init(e,t),e.in=t.in,e.out=t.out}));function Al(e,t){return new El({type:\"pipe\",in:e,out:t})}const Cl=t(\"ZodCodec\",((e,t)=>{El.init(e,t),Tr.init(e,t)})),Pl=t(\"ZodReadonly\",((e,t)=>{Cr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType}));function $l(e){return new Pl({type:\"readonly\",innerType:e})}const Dl=t(\"ZodTemplateLiteral\",((e,t)=>{$r.init(e,t),Yi.init(e,t)})),Il=t(\"ZodLazy\",((e,t)=>{Mr.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.getter()}));function Ml(e){return new Il({type:\"lazy\",getter:e})}const Nl=t(\"ZodPromise\",((e,t)=>{Ir.init(e,t),Yi.init(e,t),e.unwrap=()=>e._zod.def.innerType})),Rl=t(\"ZodFunction\",((e,t)=>{Dr.init(e,t),Yi.init(e,t)}));function Ll(e){return new Rl({type:\"function\",input:Array.isArray(e?.input)?el(e?.input):e?.input??Zs(Ls()),output:e?.output??Ls()})}const Bl=t(\"ZodCustom\",((e,t)=>{Nr.init(e,t),Yi.init(e,t)}));function jl(e,t){return gi(Bl,e??(()=>!0),t)}function Ul(e,t={}){return vi(Bl,e,t)}function zl(e){return bi(e)}function Fl(e,t={error:`Input not instance of ${e.name}`}){const n=new Bl({type:\"custom\",check:\"custom\",fn:t=>t instanceof e,abort:!0,...A(t)});return n._zod.bag.Class=e,n}var Ql;function Zl(e){return Fa(Ji,e)}Ql||(Ql={});const Hl=Object.freeze(Object.defineProperty({__proto__:null,bigint:function(e){return Eo(As,e)},boolean:function(e){return _o(Ts,e)},date:function(e){return Bo(Fs,e)},number:function(e){return bo(xs,e)},string:Zl},Symbol.toStringTag,{value:\"Module\"}));i(Vr());const Vl=Object.freeze(Object.defineProperty({__proto__:null,$brand:n,$input:La,$output:Ra,NEVER:e,TimePrecision:po,ZodAny:Ms,ZodArray:Qs,ZodBase64:vs,ZodBase64URL:bs,ZodBigInt:As,ZodBigIntFormat:Cs,ZodBoolean:Ts,ZodCIDRv4:ms,ZodCIDRv6:gs,ZodCUID:ls,ZodCUID2:cs,ZodCatch:Sl,ZodCodec:Cl,ZodCustom:Bl,ZodCustomStringFormat:ws,ZodDate:Fs,ZodDefault:vl,ZodDiscriminatedUnion:Xs,ZodE164:ys,ZodEmail:ns,ZodEmoji:is,ZodEnum:il,ZodError:Li,ZodFile:ul,get ZodFirstPartyTypeKind(){return Ql},ZodFunction:Rl,ZodGUID:rs,ZodIPv4:hs,ZodIPv6:fs,ZodISODate:Ci,ZodISODateTime:Ei,ZodISODuration:Ii,ZodISOTime:$i,ZodIntersection:Ys,ZodIssueCode:{invalid_type:\"invalid_type\",too_big:\"too_big\",too_small:\"too_small\",invalid_format:\"invalid_format\",not_multiple_of:\"not_multiple_of\",unrecognized_keys:\"unrecognized_keys\",invalid_union:\"invalid_union\",invalid_key:\"invalid_key\",invalid_element:\"invalid_element\",invalid_value:\"invalid_value\",custom:\"custom\"},ZodJWT:Os,ZodKSUID:ps,ZodLazy:Il,ZodLiteral:ll,ZodMap:al,ZodNaN:Tl,ZodNanoID:ss,ZodNever:Bs,ZodNonOptional:wl,ZodNull:Ds,ZodNullable:ml,ZodNumber:xs,ZodNumberFormat:Ss,ZodObject:Hs,ZodOptional:hl,ZodPipe:El,ZodPrefault:yl,ZodPromise:Nl,ZodReadonly:Pl,ZodRealError:Bi,ZodRecord:tl,ZodSet:ol,ZodString:Ji,ZodStringFormat:ts,ZodSuccess:kl,ZodSymbol:Ps,ZodTemplateLiteral:Dl,ZodTransform:dl,ZodTuple:Js,ZodType:Yi,ZodULID:us,ZodURL:os,ZodUUID:as,ZodUndefined:$s,ZodUnion:qs,ZodUnknown:Rs,ZodVoid:Us,ZodXID:ds,_ZodString:Ki,_default:bl,_function:Ll,any:Ns,array:Zs,base64:function(e){return so(vs,e)},base64url:function(e){return lo(bs,e)},bigint:function(e){return To(As,e)},boolean:Es,catch:_l,check:function(e){const t=new Et({check:\"custom\"});return t._zod.check=e,t},cidrv4:function(e){return oo(ms,e)},cidrv6:function(e){return io(gs,e)},clone:E,codec:function(e,t,n){return new Cl({type:\"pipe\",in:e,out:t,transform:n.decode,reverseTransform:n.encode})},coerce:Hl,config:i,core:Ti,cuid:function(e){return Ka(ls,e)},cuid2:function(e){return Ja(cs,e)},custom:jl,date:function(e){return Lo(Fs,e)},decode:Zi,decodeAsync:Vi,discriminatedUnion:Gs,e164:function(e){return co(ys,e)},email:function(e){return Qa(ns,e)},emoji:function(e){return Ga(is,e)},encode:Qi,encodeAsync:Hi,endsWith:ii,enum:sl,file:function(e){return mi(ul,e)},flattenError:J,float32:function(e){return Oo(Ss,e)},float64:function(e){return wo(Ss,e)},formatError:ee,function:Ll,getErrorMap:function(){return i().customError},globalRegistry:Ua,gt:Fo,gte:Qo,guid:function(e){return Za(rs,e)},hash:function(e,t){const n=`${e}_${t?.enc??\"hex\"}`,r=Tt[n];if(!r)throw new Error(`Unrecognized hash format: ${n}`);return wi(ws,n,r,t)},hex:function(e){return wi(ws,\"hex\",ht,e)},hostname:function(e){return wi(ws,\"hostname\",Ge,e)},httpUrl:function(e){return Xa(os,{protocol:/^https?$/,hostname:Ye,...A(e)})},includes:ai,instanceof:Fl,int:_s,int32:function(e){return xo(Ss,e)},int64:function(e){return Ao(Cs,e)},intersection:Ks,ipv4:function(e){return ro(hs,e)},ipv6:function(e){return ao(fs,e)},iso:Ni,json:function(e){const t=Ml((()=>Ws([es(e),ks(),Es(),Is(),Zs(t),nl(es(),t)])));return t},jwt:function(e){return uo(Os,e)},keyof:function(e){const t=e._zod.def.shape;return sl(Object.keys(t))},ksuid:function(e){return no(ps,e)},lazy:Ml,length:ei,literal:cl,locales:Na,looseObject:function(e,t){return new Hs({type:\"object\",shape:e,catchall:Ls(),...A(t)})},lowercase:ni,lt:Uo,lte:zo,map:function(e,t,n){return new al({type:\"map\",keyType:e,valueType:t,...A(n)})},maxLength:Ko,maxSize:Xo,mime:li,minLength:Jo,minSize:Go,multipleOf:Wo,nan:function(e){return jo(Tl,e)},nanoid:function(e){return Ya(ss,e)},nativeEnum:function(e,t){return new il({type:\"enum\",entries:e,...A(t)})},negative:Ho,never:js,nonnegative:qo,nonoptional:xl,nonpositive:Vo,normalize:ui,null:Is,nullable:gl,nullish:function(e){return fl(gl(e))},number:ks,object:Vs,optional:fl,overwrite:ci,parse:ji,parseAsync:Ui,partialRecord:rl,pipe:Al,positive:Zo,prefault:Ol,preprocess:function(e,t){return Al(pl(e),t)},prettifyError:re,promise:function(e){return new Nl({type:\"promise\",innerType:e})},property:si,readonly:$l,record:nl,refine:Ul,regex:ti,regexes:Tt,registry:ja,safeDecode:Wi,safeDecodeAsync:Gi,safeEncode:qi,safeEncodeAsync:Xi,safeParse:zi,safeParseAsync:Fi,set:function(e,t){return new ol({type:\"set\",valueType:e,...A(t)})},setErrorMap:function(e){i({customError:e})},size:Yo,startsWith:oi,strictObject:function(e,t){return new Hs({type:\"object\",shape:e,catchall:js(),...A(t)})},string:es,stringFormat:function(e,t,n={}){return wi(ws,e,t,n)},stringbool:(...e)=>Oi({Codec:Cl,Boolean:Ts,String:Ji},...e),success:function(e){return new kl({type:\"success\",innerType:e})},superRefine:zl,symbol:function(e){return Po(Ps,e)},templateLiteral:function(e,t){return new Dl({type:\"template_literal\",parts:e,...A(t)})},toJSONSchema:ki,toLowerCase:pi,toUpperCase:hi,transform:pl,treeifyError:te,trim:di,tuple:el,uint32:function(e){return ko(Ss,e)},uint64:function(e){return Co(Cs,e)},ulid:function(e){return eo(us,e)},undefined:function(e){return $o($s,e)},union:Ws,unknown:Ls,uppercase:ri,url:function(e){return Xa(os,e)},util:X,uuid:function(e){return Ha(as,e)},uuidv4:function(e){return Va(as,e)},uuidv6:function(e){return qa(as,e)},uuidv7:function(e){return Wa(as,e)},void:zs,xid:function(e){return to(ds,e)}},Symbol.toStringTag,{value:\"Module\"})),ql=Vs({title:es().optional(),component:Ls(),props:nl(es(),Ns()).optional()}),Wl=Vs({\"request.section\":Zs(ql).optional(),\"response.section\":Zs(ql).optional()}),Xl=Vs({onBeforeRequest:Ll({input:[Vs({request:Fl(Request)})]}).optional(),onResponseReceived:Ll({input:[Vs({response:Fl(Response),operation:nl(es(),Ns())})]}).optional()}),Gl=Ll({input:[],output:Vs({name:es(),views:Wl.optional(),hooks:Xl.optional()})}),Yl=\"https://api.scalar.com/request-proxy\",Kl=\"https://proxy.scalar.com\",Jl=Vl.object({title:Vl.string().optional(),slug:Vl.string().optional(),authentication:Vl.any().optional(),baseServerURL:Vl.string().optional(),hideClientButton:Vl.boolean().optional().default(!1).catch(!1),proxyUrl:Vl.string().optional(),searchHotKey:Vl.enum([\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\",\"i\",\"j\",\"k\",\"l\",\"m\",\"n\",\"o\",\"p\",\"q\",\"r\",\"s\",\"t\",\"u\",\"v\",\"w\",\"x\",\"y\",\"z\"]).optional(),servers:Vl.array(Vl.any()).optional(),showSidebar:Vl.boolean().optional().default(!0).catch(!0),showDeveloperTools:Vl.enum([\"always\",\"localhost\",\"never\"]).optional().default(\"localhost\").catch(\"localhost\"),showToolbar:Vl.enum([\"always\",\"localhost\",\"never\"]).optional().default(\"localhost\").catch(\"localhost\"),operationTitleSource:Vl.enum([\"summary\",\"path\"]).optional().default(\"summary\").catch(\"summary\"),theme:Vl.enum([\"alternate\",\"default\",\"moon\",\"purple\",\"solarized\",\"bluePlanet\",\"deepSpace\",\"saturn\",\"kepler\",\"elysiajs\",\"fastify\",\"mars\",\"laserwave\",\"none\"]).optional().default(\"default\").catch(\"default\"),_integration:Vl.enum([\"adonisjs\",\"astro\",\"docusaurus\",\"dotnet\",\"elysiajs\",\"express\",\"fastapi\",\"fastify\",\"go\",\"hono\",\"html\",\"laravel\",\"litestar\",\"nestjs\",\"nextjs\",\"nitro\",\"nuxt\",\"platformatic\",\"react\",\"rust\",\"svelte\",\"vue\"]).nullable().optional(),onRequestSent:Vl.function({input:[Vl.string()],output:Vl.void()}).optional(),persistAuth:Vl.boolean().optional().default(!1).catch(!1),plugins:Vl.array(Gl).optional(),telemetry:Vl.boolean().optional().default(!0)}),ec=Vl.object({default:Vl.boolean().default(!1).optional().catch(!1),url:Vl.string().optional(),content:Vl.union([Vl.string(),Vl.null(),Vl.record(Vl.string(),Vl.any()),Vl.function({input:[],output:Vl.record(Vl.string(),Vl.any())})]).optional(),title:Vl.string().optional(),slug:Vl.string().optional(),spec:Vl.object({url:Vl.string().optional(),content:Vl.union([Vl.string(),Vl.null(),Vl.record(Vl.string(),Vl.any()),Vl.function({input:[],output:Vl.record(Vl.string(),Vl.any())})]).optional()}).optional()});Jl.extend(ec.shape);const tc=Vs({name:es().regex(/^x-/),component:Ls(),renderer:Ls().optional()}),nc=Vs({component:Ls(),renderer:Ls().optional(),props:nl(es(),Ns()).optional()}),rc=Vs({\"content.end\":Zs(nc).optional()}),ac=Ll({input:[],output:Vs({name:es(),extensions:Zs(tc),views:rc.optional()})}),oc=jl(),ic=Jl.extend({layout:sl([\"modern\",\"classic\"]).optional().default(\"modern\").catch(\"modern\"),proxy:es().optional(),fetch:oc.optional(),plugins:Zs(ac).optional(),isEditable:Es().optional().default(!1).catch(!1),isLoading:Es().optional().default(!1).catch(!1),hideModels:Es().optional().default(!1).catch(!1),documentDownloadType:sl([\"yaml\",\"json\",\"both\",\"direct\",\"none\"]).optional().default(\"both\").catch(\"both\"),hideDownloadButton:Es().optional(),hideTestRequestButton:Es().optional().default(!1).catch(!1),hideSearch:Es().optional().default(!1).catch(!1),showOperationId:Es().optional().default(!1).catch(!1),darkMode:Es().optional(),forceDarkModeState:sl([\"dark\",\"light\"]).optional(),hideDarkModeToggle:Es().optional().default(!1).catch(!1),metaData:Ns().optional(),favicon:es().optional(),hiddenClients:Ws([nl(es(),Ws([Es(),Zs(es())])),Zs(es()),cl(!0)]).optional(),defaultHttpClient:Vs({targetKey:jl(),clientKey:es()}).optional(),customCss:es().optional(),onSpecUpdate:Ll({input:[es()],output:zs()}).optional(),onServerChange:Ll({input:[es()],output:zs()}).optional(),onDocumentSelect:Ll({input:[]}).optional(),onLoaded:Ll().optional(),onBeforeRequest:Ll({input:[Vs({request:Fl(Request)})]}).optional(),onShowMore:Ll({input:[es()]}).optional(),onSidebarClick:Ll({input:[es()]}).optional(),pathRouting:Vs({basePath:es()}).optional(),generateHeadingSlug:Ll({input:[Vs({slug:es().default(\"headingSlug\")})],output:es()}).optional(),generateModelSlug:Ll({input:[Vs({name:es().default(\"modelName\")})],output:es()}).optional(),generateTagSlug:Ll({input:[Vs({name:es().default(\"tagName\")})],output:es()}).optional(),generateOperationSlug:Ll({input:[Vs({path:es(),operationId:es().optional(),method:es(),summary:es().optional()})],output:es()}).optional(),generateWebhookSlug:Ll({input:[Vs({name:es(),method:es().optional()})],output:es()}).optional(),redirect:Ll({input:[es()],output:es().nullable().optional()}).optional(),withDefaultFonts:Es().optional().default(!0).catch(!0),defaultOpenAllTags:Es().optional().default(!1).catch(!1),expandAllModelSections:Es().optional().default(!1).catch(!1),expandAllResponses:Es().optional().default(!1).catch(!1),tagsSorter:Ws([cl(\"alpha\"),Ll({input:[Ns(),Ns()],output:ks()})]).optional(),operationsSorter:Ws([cl(\"alpha\"),cl(\"method\"),Ll({input:[Ns(),Ns()],output:ks()})]).optional(),orderSchemaPropertiesBy:Ws([cl(\"alpha\"),cl(\"preserve\")]).optional().default(\"alpha\").catch(\"alpha\"),orderRequiredPropertiesFirst:Es().optional().default(!0).catch(!0)}),sc=ic.extend(ec.shape).transform((e=>(e.hideDownloadButton&&(console.warn(\"[DEPRECATED] You're using the deprecated 'hideDownloadButton' attribute. Use 'documentDownloadType: 'none'' instead.\"),e.documentDownloadType=\"none\"),e.spec?.url&&(console.warn(\"[DEPRECATED] You're using the deprecated 'spec.url' attribute. Remove the spec prefix and move the 'url' attribute to the top level.\"),e.url=e.spec.url,delete e.spec),e.spec?.content&&(console.warn(\"[DEPRECATED] You're using the deprecated 'spec.content' attribute. Remove the spec prefix and move the 'content' attribute to the top level.\"),e.content=e.spec.content,delete e.spec),e.proxy&&(console.warn(\"[DEPRECATED] You're using the deprecated 'proxy' attribute, rename it to 'proxyUrl' or update the package.\"),e.proxyUrl||(e.proxyUrl=e.proxy),delete e.proxy),e.proxyUrl===Yl&&(console.warn(`[DEPRECATED] Warning: configuration.proxyUrl points to our old proxy (${Yl}).`),console.warn(`[DEPRECATED] We are overwriting the value and use the new proxy URL (${Kl}) instead.`),console.warn(`[DEPRECATED] Action Required: You should manually update your configuration to use the new URL (${Kl}). Read more: https://github.com/scalar/scalar`),e.proxyUrl=Kl),e.showToolbar&&\"localhost\"!==e.showToolbar&&(console.warn(\"[DEPRECATED] You're using the deprecated 'showToolbar' attribute. Use 'showDeveloperTools' instead.\"),e.showDeveloperTools=e.showToolbar,delete e.showToolbar),e)));function lc(e,t){const n=[],r=t.resolveKeyData||(e=>e.key),a=t.resolveValueData||(e=>e.value);for(const[o,i]of Object.entries(e))n.push(...(Array.isArray(i)?i:[i]).map((e=>{const n={key:o,value:e},i=a(n);return\"object\"==typeof i?lc(i,t):Array.isArray(i)?i:{[\"function\"==typeof t.key?t.key(n):t.key]:r(n),[\"function\"==typeof t.value?t.value(n):t.value]:i}})).flat());return n}function cc(e,t){return Object.entries(e).map((([e,n])=>{if(\"object\"==typeof n&&(n=cc(n,t)),t.resolve){const r=t.resolve({key:e,value:n});if(void 0!==r)return r}return\"number\"==typeof n&&(n=n.toString()),\"string\"==typeof n&&t.wrapValue&&(n=n.replace(new RegExp(t.wrapValue,\"g\"),`\\\\${t.wrapValue}`),n=`${t.wrapValue}${n}${t.wrapValue}`),`${e}${t.keyValueSeparator||\"\"}${n}`})).join(t.entrySeparator||\"\")}Vs({cdn:es().optional().default(\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"),pageTitle:es().optional().default(\"Scalar API Reference\")});const uc=new Set([\"title\",\"titleTemplate\",\"script\",\"style\",\"noscript\"]),dc=new Set([\"base\",\"meta\",\"link\",\"style\",\"script\",\"noscript\"]),pc=new Set([\"title\",\"titleTemplate\",\"templateParams\",\"base\",\"htmlAttrs\",\"bodyAttrs\",\"meta\",\"link\",\"style\",\"script\",\"noscript\"]),hc=new Set([\"base\",\"title\",\"titleTemplate\",\"bodyAttrs\",\"htmlAttrs\",\"templateParams\"]),fc=new Set([\"tagPosition\",\"tagPriority\",\"tagDuplicateStrategy\",\"children\",\"innerHTML\",\"textContent\",\"processTemplateParams\"]),mc=\"undefined\"!=typeof window;function gc(e){let t=9;for(let n=0;n>>9)).toString(16).substring(1,8).toLowerCase()}function vc(e){if(e._h)return e._h;if(e._d)return gc(e._d);let t=`${e.tag}:${e.textContent||e.innerHTML||\"\"}:`;for(const n in e.props)t+=`${n}:${String(e.props[n])},`;return gc(t)}const bc=e=>({keyValue:e,metaKey:\"property\"}),yc=e=>({keyValue:e}),Oc={appleItunesApp:{unpack:{entrySeparator:\", \",resolve:({key:e,value:t})=>`${kc(e)}=${t}`}},articleExpirationTime:bc(\"article:expiration_time\"),articleModifiedTime:bc(\"article:modified_time\"),articlePublishedTime:bc(\"article:published_time\"),bookReleaseDate:bc(\"book:release_date\"),charset:{metaKey:\"charset\"},contentSecurityPolicy:{unpack:{entrySeparator:\"; \",resolve:({key:e,value:t})=>`${kc(e)} ${t}`},metaKey:\"http-equiv\"},contentType:{metaKey:\"http-equiv\"},defaultStyle:{metaKey:\"http-equiv\"},fbAppId:bc(\"fb:app_id\"),msapplicationConfig:yc(\"msapplication-Config\"),msapplicationTileColor:yc(\"msapplication-TileColor\"),msapplicationTileImage:yc(\"msapplication-TileImage\"),ogAudioSecureUrl:bc(\"og:audio:secure_url\"),ogAudioUrl:bc(\"og:audio\"),ogImageSecureUrl:bc(\"og:image:secure_url\"),ogImageUrl:bc(\"og:image\"),ogSiteName:bc(\"og:site_name\"),ogVideoSecureUrl:bc(\"og:video:secure_url\"),ogVideoUrl:bc(\"og:video\"),profileFirstName:bc(\"profile:first_name\"),profileLastName:bc(\"profile:last_name\"),profileUsername:bc(\"profile:username\"),refresh:{metaKey:\"http-equiv\",unpack:{entrySeparator:\";\",resolve({key:e,value:t}){if(\"seconds\"===e)return`${t}`}}},robots:{unpack:{entrySeparator:\", \",resolve:({key:e,value:t})=>\"boolean\"==typeof t?`${kc(e)}`:`${kc(e)}:${t}`}},xUaCompatible:{metaKey:\"http-equiv\"}},wc=new Set([\"og\",\"book\",\"article\",\"profile\"]);function xc(e){const t=kc(e),n=t.indexOf(\":\");return wc.has(t.substring(0,n))?\"property\":Oc[e]?.metaKey||\"name\"}function kc(e){const t=e.replace(/([A-Z])/g,\"-$1\").toLowerCase(),n=t.indexOf(\"-\"),r=t.substring(0,n);return\"twitter\"===r||wc.has(r)?e.replace(/([A-Z])/g,\":$1\").toLowerCase():t}function Sc(e){if(Array.isArray(e))return e.map((e=>Sc(e)));if(\"object\"!=typeof e||Array.isArray(e))return e;const t={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[kc(n)]=Sc(e[n]));return t}function _c(e,t){const n=Oc[t];return\"refresh\"===t?`${e.seconds};url=${e.url}`:cc(Sc(e),{keyValueSeparator:\"=\",entrySeparator:\", \",resolve:({value:e,key:t})=>null===e?\"\":\"boolean\"==typeof e?`${t}`:void 0,...n?.unpack})}const Tc=new Set([\"og:image\",\"og:video\",\"og:audio\",\"twitter:image\"]);function Ec(e){const t={};for(const n in e){if(!Object.prototype.hasOwnProperty.call(e,n))continue;const r=e[n];\"false\"!==String(r)&&n&&(t[n]=r)}return t}function Ac(e,t){const n=Ec(t),r=kc(e),a=xc(r);if(Tc.has(r)){const t={};for(const r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[`${e}${\"url\"===r?\"\":`${r[0].toUpperCase()}${r.slice(1)}`}`]=n[r]);return Cc(t).sort(((e,t)=>(e[a]?.length||0)-(t[a]?.length||0)))}return[{[a]:r,...n}]}function Cc(e){const t=[],n={};for(const r in e){if(!Object.prototype.hasOwnProperty.call(e,r))continue;const a=e[r];if(Array.isArray(a))for(const e of a)t.push(...\"string\"==typeof e?Cc({[r]:e}):Ac(r,e));else if(\"object\"==typeof a&&a){if(Tc.has(kc(r))){t.push(...Ac(r,a));continue}n[r]=Ec(a)}else n[r]=a}const r=lc(n,{key:({key:e})=>xc(e),value:({key:e})=>\"charset\"===e?\"charset\":\"content\",resolveKeyData:({key:e})=>function(e){return Oc[e]?.keyValue||kc(e)}(e),resolveValueData:({value:e,key:t})=>null===e?\"_null\":\"object\"==typeof e?_c(e,t):\"number\"==typeof e?e.toString():e});return[...t,...r].map((e=>(\"_null\"===e.content&&(e.content=null),e)))}function Pc(e,t,n,r){const a=r||Ic(\"object\"!=typeof t||\"function\"==typeof t||t instanceof Promise?{[\"script\"===e||\"noscript\"===e||\"style\"===e?\"innerHTML\":\"textContent\"]:t}:{...t},\"templateParams\"===e||\"titleTemplate\"===e);if(a instanceof Promise)return a.then((r=>Pc(e,t,n,r)));const o={tag:e,props:a};for(const e of fc){const t=void 0!==o.props[e]?o.props[e]:n[e];void 0!==t&&((\"innerHTML\"!==e&&\"textContent\"!==e&&\"children\"!==e||uc.has(o.tag))&&(o[\"children\"===e?\"innerHTML\":e]=t),delete o.props[e])}return o.props.body&&(o.tagPosition=\"bodyClose\",delete o.props.body),\"script\"===o.tag&&\"object\"==typeof o.innerHTML&&(o.innerHTML=JSON.stringify(o.innerHTML),o.props.type=o.props.type||\"application/json\"),Array.isArray(o.props.content)?o.props.content.map((e=>({...o,props:{...o.props,content:e}}))):o}function $c(e,t){const n=\"class\"===e?\" \":\";\";return t&&\"object\"==typeof t&&!Array.isArray(t)&&(t=Object.entries(t).filter((([,e])=>e)).map((([t,n])=>\"style\"===e?`${t}:${n}`:t))),String(Array.isArray(t)?t.join(n):t)?.split(n).filter((e=>Boolean(e.trim()))).join(n)}function Dc(e,t,n,r){for(let a=r;a(e[r]=o,Dc(e,t,n,a))));if(!t&&!fc.has(r)){const t=String(e[r]),n=r.startsWith(\"data-\");\"true\"===t||\"\"===t?e[r]=!n||\"true\":e[r]||(n&&\"false\"===t?e[r]=\"false\":delete e[r])}}else e[r]=$c(r,e[r])}}function Ic(e,t=!1){const n=Dc(e,t,Object.keys(e),0);return n instanceof Promise?n.then((()=>e)):e}function Mc(e,t,n){for(let r=n;r(t[r]=n,Mc(e,t,r))));Array.isArray(n)?e.push(...n):e.push(n)}}function Nc(e){const t=[],n=e.resolvedInput;for(const r in n){if(!Object.prototype.hasOwnProperty.call(n,r))continue;const a=n[r];if(void 0!==a&&pc.has(r))if(Array.isArray(a))for(const n of a)t.push(Pc(r,n,e));else t.push(Pc(r,a,e))}if(0===t.length)return[];const r=[];return o=()=>r.map(((t,n)=>(t._e=e._i,e.mode&&(t._m=e.mode),t._p=(e._i<<10)+n,t))),(a=Mc(r,t,0))instanceof Promise?a.then(o):o();var a,o}const Rc=new Set([\"onload\",\"onerror\",\"onabort\",\"onprogress\",\"onloadstart\"]),Lc={base:-10,title:10},Bc={critical:-80,high:-10,low:20};function jc(e){const t=e.tagPriority;if(\"number\"==typeof t)return t;let n=100;return\"meta\"===e.tag?\"content-security-policy\"===e.props[\"http-equiv\"]?n=-30:e.props.charset?n=-20:\"viewport\"===e.props.name&&(n=-15):\"link\"===e.tag&&\"preconnect\"===e.props.rel?n=20:e.tag in Lc&&(n=Lc[e.tag]),t&&t in Bc?n+Bc[t]:n}const Uc=[{prefix:\"before:\",offset:-1},{prefix:\"after:\",offset:1}],zc=[\"name\",\"property\",\"http-equiv\"];function Fc(e){const{props:t,tag:n}=e;if(hc.has(n))return n;if(\"link\"===n&&\"canonical\"===t.rel)return\"canonical\";if(t.charset)return\"charset\";if(t.id)return`${n}:id:${t.id}`;for(const e of zc)if(void 0!==t[e])return`${n}:${e}:${t[e]}`;return!1}const Qc=\"%separator\",Zc=new RegExp(`${Qc}(?:\\\\s*${Qc})*`,\"g\");function Hc(e,t,n,r=!1){if(\"string\"!=typeof e||!e.includes(\"%\"))return e;let a=e;try{a=decodeURI(e)}catch{}const o=a.match(/%\\w+(?:\\.\\w+)?/g);if(!o)return e;const i=e.includes(Qc);return e=e.replace(/%\\w+(?:\\.\\w+)?/g,(e=>{if(e===Qc||!o.includes(e))return e;const n=function(e,t,n=!1){let r;if(\"s\"===t||\"pageTitle\"===t)r=e.pageTitle;else if(t.includes(\".\")){const n=t.indexOf(\".\");r=e[t.substring(0,n)]?.[t.substring(n+1)]}else r=e[t];if(void 0!==r)return n?(r||\"\").replace(/\"/g,'\\\\\"'):r||\"\"}(t,e.slice(1),r);return void 0!==n?n:e})).trim(),i&&(e.endsWith(Qc)&&(e=e.slice(0,-10)),e.startsWith(Qc)&&(e=e.slice(10)),e=e.replace(Zc,n).trim()),e}function Vc(e,t){return null==e?t||null:\"function\"==typeof e?e(t):e}function qc(e){return t=>{const n=t.resolvedOptions.document?.head.querySelector('script[id=\"unhead:payload\"]')?.innerHTML||!1;return n&&t.push(JSON.parse(n)),{mode:\"client\",hooks:{\"entries:updated\":t=>{!function(e,t={}){const n=t.delayFn||(e=>setTimeout(e,10));e._domDebouncedUpdatePromise=e._domDebouncedUpdatePromise||new Promise((r=>n((()=>async function(e,t={}){const n=t.document||e.resolvedOptions.document;if(!n||!e.dirty)return;const r={shouldRender:!0,tags:[]};return await e.hooks.callHook(\"dom:beforeRender\",r),r.shouldRender?(e._domUpdatePromise||(e._domUpdatePromise=new Promise((async t=>{const r=(await e.resolveTags()).map((e=>({tag:e,id:dc.has(e.tag)?vc(e):e.tag,shouldRender:!0})));let a=e._dom;if(!a){a={elMap:{htmlAttrs:n.documentElement,bodyAttrs:n.body}};const e=new Set;for(const t of[\"body\",\"head\"]){const r=n[t]?.children;for(const t of r){const n=t.tagName.toLowerCase();if(!dc.has(n))continue;const r={tag:n,props:await Ic(t.getAttributeNames().reduce(((e,n)=>({...e,[n]:t.getAttribute(n)})),{})),innerHTML:t.innerHTML},o=Fc(r);let i=o,s=1;for(;i&&e.has(i);)i=`${o}:${s++}`;i&&(r._d=i,e.add(i)),a.elMap[t.getAttribute(\"data-hid\")||vc(r)]=t}}}function o(e,t,n){const r=`${e}:${t}`;a.sideEffects[r]=n,delete a.pendingSideEffects[r]}function i({id:e,$el:t,tag:r}){const i=r.tag.endsWith(\"Attrs\");if(a.elMap[e]=t,i||(r.textContent&&r.textContent!==t.textContent&&(t.textContent=r.textContent),r.innerHTML&&r.innerHTML!==t.innerHTML&&(t.innerHTML=r.innerHTML),o(e,\"el\",(()=>{a.elMap[e]?.remove(),delete a.elMap[e]}))),r._eventHandlers)for(const e in r._eventHandlers)Object.prototype.hasOwnProperty.call(r._eventHandlers,e)&&\"\"!==t.getAttribute(`data-${e}`)&&((\"bodyAttrs\"===r.tag?n.defaultView:t).addEventListener(e.substring(2),r._eventHandlers[e].bind(t)),t.setAttribute(`data-${e}`,\"\"));for(const n in r.props){if(!Object.prototype.hasOwnProperty.call(r.props,n))continue;const a=r.props[n],s=`attr:${n}`;if(\"class\"===n){if(!a)continue;for(const n of a.split(\" \"))i&&o(e,`${s}:${n}`,(()=>t.classList.remove(n))),!t.classList.contains(n)&&t.classList.add(n)}else if(\"style\"===n){if(!a)continue;for(const n of a.split(\";\")){const r=n.indexOf(\":\"),a=n.substring(0,r).trim(),i=n.substring(r+1).trim();o(e,`${s}:${a}`,(()=>{t.style.removeProperty(a)})),t.style.setProperty(a,i)}}else t.getAttribute(n)!==a&&t.setAttribute(n,!0===a?\"\":String(a)),i&&o(e,s,(()=>t.removeAttribute(n)))}}a.pendingSideEffects={...a.sideEffects},a.sideEffects={};const s=[],l={bodyClose:void 0,bodyOpen:void 0,head:void 0};for(const e of r){const{tag:t,shouldRender:r,id:o}=e;r&&(\"title\"!==t.tag?(e.$el=e.$el||a.elMap[o],e.$el?i(e):dc.has(t.tag)&&s.push(e)):n.title=t.textContent)}for(const e of s){const t=e.tag.tagPosition||\"head\";e.$el=n.createElement(e.tag.tag),i(e),l[t]=l[t]||n.createDocumentFragment(),l[t].appendChild(e.$el)}for(const t of r)await e.hooks.callHook(\"dom:renderTag\",t,n,o);l.head&&n.head.appendChild(l.head),l.bodyOpen&&n.body.insertBefore(l.bodyOpen,n.body.firstChild),l.bodyClose&&n.body.appendChild(l.bodyClose);for(const e in a.pendingSideEffects)a.pendingSideEffects[e]();e._dom=a,await e.hooks.callHook(\"dom:rendered\",{renders:r}),t()})).finally((()=>{e._domUpdatePromise=void 0,e.dirty=!1}))),e._domUpdatePromise):void 0}(e,t).then((()=>{delete e._domDebouncedUpdatePromise,r()}))))))}(t,e)}}}}}function Wc(e,t={},n){for(const r in e){const a=e[r],o=n?`${n}:${r}`:r;\"object\"==typeof a&&null!==a?Wc(a,t,o):\"function\"==typeof a&&(t[o]=a)}return t}const Xc={run:e=>e()},Gc=void 0!==console.createTask?console.createTask:()=>Xc;function Yc(e,t){const n=t.shift(),r=Gc(n);return e.reduce(((e,n)=>e.then((()=>r.run((()=>n(...t)))))),Promise.resolve())}function Kc(e,t){const n=t.shift(),r=Gc(n);return Promise.all(e.map((e=>r.run((()=>e(...t))))))}function Jc(e,t){for(const n of[...e])n(t)}class eu{constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(e,t,n={}){if(!e||\"function\"!=typeof t)return()=>{};const r=e;let a;for(;this._deprecatedHooks[e];)a=this._deprecatedHooks[e],e=a.to;if(a&&!n.allowDeprecated){let e=a.message;e||(e=`${r} hook has been deprecated`+(a.to?`, please use ${a.to}`:\"\")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(e)||(console.warn(e),this._deprecatedMessages.add(e))}if(!t.name)try{Object.defineProperty(t,\"name\",{get:()=>\"_\"+e.replace(/\\W+/g,\"_\")+\"_hook_cb\",configurable:!0})}catch{}return this._hooks[e]=this._hooks[e]||[],this._hooks[e].push(t),()=>{t&&(this.removeHook(e,t),t=void 0)}}hookOnce(e,t){let n,r=(...e)=>(\"function\"==typeof n&&n(),n=void 0,r=void 0,t(...e));return n=this.hook(e,r),n}removeHook(e,t){if(this._hooks[e]){const n=this._hooks[e].indexOf(t);-1!==n&&this._hooks[e].splice(n,1),0===this._hooks[e].length&&delete this._hooks[e]}}deprecateHook(e,t){this._deprecatedHooks[e]=\"string\"==typeof t?{to:t}:t;const n=this._hooks[e]||[];delete this._hooks[e];for(const t of n)this.hook(e,t)}deprecateHooks(e){Object.assign(this._deprecatedHooks,e);for(const t in e)this.deprecateHook(t,e[t])}addHooks(e){const t=Wc(e),n=Object.keys(t).map((e=>this.hook(e,t[e])));return()=>{for(const e of n.splice(0,n.length))e()}}removeHooks(e){const t=Wc(e);for(const e in t)this.removeHook(e,t[e])}removeAllHooks(){for(const e in this._hooks)delete this._hooks[e]}callHook(e,...t){return t.unshift(e),this.callHookWith(Yc,e,...t)}callHookParallel(e,...t){return t.unshift(e),this.callHookWith(Kc,e,...t)}callHookWith(e,t,...n){const r=this._before||this._after?{name:t,args:n,context:{}}:void 0;this._before&&Jc(this._before,r);const a=e(t in this._hooks?[...this._hooks[t]]:[],n);return a instanceof Promise?a.finally((()=>{this._after&&r&&Jc(this._after,r)})):(this._after&&r&&Jc(this._after,r),a)}beforeEach(e){return this._before=this._before||[],this._before.push(e),()=>{if(void 0!==this._before){const t=this._before.indexOf(e);-1!==t&&this._before.splice(t,1)}}}afterEach(e){return this._after=this._after||[],this._after.push(e),()=>{if(void 0!==this._after){const t=this._after.indexOf(e);-1!==t&&this._after.splice(t,1)}}}}const tu=new Set([\"templateParams\",\"htmlAttrs\",\"bodyAttrs\"]),nu={hooks:{\"tag:normalise\":({tag:e})=>{e.props.hid&&(e.key=e.props.hid,delete e.props.hid),e.props.vmid&&(e.key=e.props.vmid,delete e.props.vmid),e.props.key&&(e.key=e.props.key,delete e.props.key);const t=Fc(e);!t||t.startsWith(\"meta:og:\")||t.startsWith(\"meta:twitter:\")||delete e.key;const n=t||!!e.key&&`${e.tag}:${e.key}`;n&&(e._d=n)},\"tags:resolve\":e=>{const t=Object.create(null);for(const n of e.tags){const e=(n.key?`${n.tag}:${n.key}`:n._d)||vc(n),r=t[e];if(r){let a=n?.tagDuplicateStrategy;if(!a&&tu.has(n.tag)&&(a=\"merge\"),\"merge\"===a){const a=r.props;a.style&&n.props.style&&(\";\"!==a.style[a.style.length-1]&&(a.style+=\";\"),n.props.style=`${a.style} ${n.props.style}`),a.class&&n.props.class?n.props.class=`${a.class} ${n.props.class}`:a.class&&(n.props.class=a.class),t[e].props={...a,...n.props};continue}if(n._e===r._e){r._duped=r._duped||[],n._d=`${r._d}:${r._duped.length+1}`,r._duped.push(n);continue}if(jc(n)>jc(r))continue}n.innerHTML||n.textContent||0!==Object.keys(n.props).length||!dc.has(n.tag)?t[e]=n:delete t[e]}const n=[];for(const e in t){const r=t[e],a=r._duped;n.push(r),a&&(delete r._duped,n.push(...a))}e.tags=n,e.tags=e.tags.filter((e=>!(\"meta\"===e.tag&&(e.props.name||e.props.property)&&!e.props.content)))}}},ru=new Set([\"script\",\"link\",\"bodyAttrs\"]),au=e=>({hooks:{\"tags:resolve\":t=>{for(const n of t.tags){if(!ru.has(n.tag))continue;const t=n.props;for(const r in t){if(\"o\"!==r[0]||\"n\"!==r[1])continue;if(!Object.prototype.hasOwnProperty.call(t,r))continue;const a=t[r];\"function\"==typeof a&&(e.ssr&&Rc.has(r)?t[r]=`this.dataset.${r}fired = true`:delete t[r],n._eventHandlers=n._eventHandlers||{},n._eventHandlers[r]=a)}e.ssr&&n._eventHandlers&&(n.props.src||n.props.href)&&(n.key=n.key||gc(n.props.src||n.props.href))}},\"dom:renderTag\":({$el:e,tag:t})=>{const n=e?.dataset;if(n)for(const r in n){if(!r.endsWith(\"fired\"))continue;const n=r.slice(0,-5);Rc.has(n)&&t._eventHandlers?.[n]?.call(e,new Event(n.substring(2)))}}}}),ou=new Set([\"link\",\"style\",\"script\",\"noscript\"]),iu={hooks:{\"tag:normalise\":({tag:e})=>{e.key&&ou.has(e.tag)&&(e.props[\"data-hid\"]=e._h=gc(e.key))}}},su={mode:\"server\",hooks:{\"tags:beforeResolve\":e=>{const t={};let n=!1;for(const r of e.tags)\"server\"!==r._m||\"titleTemplate\"!==r.tag&&\"templateParams\"!==r.tag&&\"title\"!==r.tag||(t[r.tag]=\"title\"===r.tag||\"titleTemplate\"===r.tag?r.textContent:r.props,n=!0);n&&e.tags.push({tag:\"script\",innerHTML:JSON.stringify(t),props:{id:\"unhead:payload\",type:\"application/json\"}})}}},lu={hooks:{\"tags:resolve\":e=>{for(const t of e.tags)if(\"string\"==typeof t.tagPriority)for(const{prefix:n,offset:r}of Uc){if(!t.tagPriority.startsWith(n))continue;const a=t.tagPriority.substring(n.length),o=e.tags.find((e=>e._d===a))?._p;if(void 0!==o){t._p=o+r;break}}e.tags.sort(((e,t)=>{const n=jc(e),r=jc(t);return nr?1:e._p-t._p}))}}},cu={meta:\"content\",link:\"href\",htmlAttrs:\"lang\"},uu=[\"innerHTML\",\"textContent\"],du=e=>({hooks:{\"tags:resolve\":t=>{const{tags:n}=t;let r;for(let e=0;e\"title\"===e.tag))?.textContent||\"\",a,o);for(const e of n){if(!1===e.processTemplateParams)continue;const t=cu[e.tag];if(t&&\"string\"==typeof e.props[t])e.props[t]=Hc(e.props[t],a,o);else if(e.processTemplateParams||\"titleTemplate\"===e.tag||\"title\"===e.tag)for(const t of uu)\"string\"==typeof e[t]&&(e[t]=Hc(e[t],a,o,\"script\"===e.tag&&e.props.type.endsWith(\"json\")))}e._templateParams=a,e._separator=o},\"tags:afterResolve\":({tags:t})=>{let n;for(let e=0;e{const{tags:t}=e;let n,r;for(let e=0;e{for(const t of e.tags)\"string\"==typeof t.innerHTML&&(!t.innerHTML||\"application/ld+json\"!==t.props.type&&\"application/json\"!==t.props.type?t.innerHTML=t.innerHTML.replace(new RegExp(`e in t}const vu={},bu=[],yu=()=>{},Ou=()=>!1,wu=e=>111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),xu=e=>e.startsWith(\"onUpdate:\"),ku=Object.assign,Su=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},_u=Object.prototype.hasOwnProperty,Tu=(e,t)=>_u.call(e,t),Eu=Array.isArray,Au=e=>\"[object Map]\"===Lu(e),Cu=e=>\"[object Set]\"===Lu(e),Pu=e=>\"[object Date]\"===Lu(e),$u=e=>\"function\"==typeof e,Du=e=>\"string\"==typeof e,Iu=e=>\"symbol\"==typeof e,Mu=e=>null!==e&&\"object\"==typeof e,Nu=e=>(Mu(e)||$u(e))&&$u(e.then)&&$u(e.catch),Ru=Object.prototype.toString,Lu=e=>Ru.call(e),Bu=e=>\"[object Object]\"===Lu(e),ju=e=>Du(e)&&\"NaN\"!==e&&\"-\"!==e[0]&&\"\"+parseInt(e,10)===e,Uu=gu(\",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted\"),zu=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Fu=/-\\w/g,Qu=zu((e=>e.replace(Fu,(e=>e.slice(1).toUpperCase())))),Zu=/\\B([A-Z])/g,Hu=zu((e=>e.replace(Zu,\"-$1\").toLowerCase())),Vu=zu((e=>e.charAt(0).toUpperCase()+e.slice(1))),qu=zu((e=>e?`on${Vu(e)}`:\"\")),Wu=(e,t)=>!Object.is(e,t),Xu=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},Yu=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Ku;const Ju=()=>Ku||(Ku=\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:{});function ed(e){if(Eu(e)){const t={};for(let n=0;n{if(e){const n=e.split(nd);n.length>1&&(t[n[0].trim()]=n[1].trim())}})),t}function od(e){let t=\"\";if(Du(e))t=e;else if(Eu(e))for(let n=0;ncd(e,t)))}const dd=e=>!(!e||!0!==e.__v_isRef),pd=e=>Du(e)?e:null==e?\"\":Eu(e)||Mu(e)&&(e.toString===Ru||!$u(e.toString))?dd(e)?pd(e.value):JSON.stringify(e,hd,2):String(e),hd=(e,t)=>dd(t)?hd(e,t.value):Au(t)?{[`Map(${t.size})`]:[...t.entries()].reduce(((e,[t,n],r)=>(e[fd(t,r)+\" =>\"]=n,e)),{})}:Cu(t)?{[`Set(${t.size})`]:[...t.values()].map((e=>fd(e)))}:Iu(t)?fd(t):!Mu(t)||Eu(t)||Bu(t)?t:String(t),fd=(e,t=\"\")=>{var n;return Iu(e)?`Symbol(${null!=(n=e.description)?n:t})`:e};function md(e){return null==e?\"initial\":\"string\"==typeof e?\"\"===e?\" \":e:String(e)}let gd,vd;class bd{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=gd,!e&&gd&&(this.index=(gd.scopes||(gd.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){let e,t;if(this._isPaused=!0,this.scopes)for(e=0,t=this.scopes.length;e0&&0==--this._on&&(gd=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){let t,n;for(this._active=!1,t=0,n=this.effects.length;t0)return;if(_d){let e=_d;for(_d=void 0;e;){const t=e.next;e.next=void 0,e.flags&=-9,e=t}}let e;for(;Sd;){let t=Sd;for(Sd=void 0;t;){const n=t.next;if(t.next=void 0,t.flags&=-9,1&t.flags)try{t.trigger()}catch(t){e||(e=t)}t=n}}if(e)throw e}function Pd(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function $d(e){let t,n=e.depsTail,r=n;for(;r;){const e=r.prevDep;-1===r.version?(r===n&&(n=e),Md(r),Nd(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=e}e.deps=t,e.depsTail=n}function Dd(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Id(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Id(e){if(4&e.flags&&!(16&e.flags))return;if(e.flags&=-17,e.globalVersion===zd)return;if(e.globalVersion=zd,!e.isSSR&&128&e.flags&&(!e.deps&&!e._dirty||!Dd(e)))return;e.flags|=2;const t=e.dep,n=vd,r=Rd;vd=e,Rd=!0;try{Pd(e);const n=e.fn(e._value);(0===t.version||Wu(n,e._value))&&(e.flags|=128,e._value=n,t.version++)}catch(e){throw t.version++,e}finally{vd=n,Rd=r,$d(e),e.flags&=-3}}function Md(e,t=!1){const{dep:n,prevSub:r,nextSub:a}=e;if(r&&(r.nextSub=a,e.prevSub=void 0),a&&(a.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let e=n.computed.deps;e;e=e.nextDep)Md(e,!0)}t||--n.sc||!n.map||n.map.delete(n.key)}function Nd(e){const{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}let Rd=!0;const Ld=[];function Bd(){Ld.push(Rd),Rd=!1}function jd(){const e=Ld.pop();Rd=void 0===e||e}function Ud(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const e=vd;vd=void 0;try{t()}finally{vd=e}}}let zd=0;class Fd{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class Qd{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!vd||!Rd||vd===this.computed)return;let t=this.activeLink;if(void 0===t||t.sub!==vd)t=this.activeLink=new Fd(vd,this),vd.deps?(t.prevDep=vd.depsTail,vd.depsTail.nextDep=t,vd.depsTail=t):vd.deps=vd.depsTail=t,Zd(t);else if(-1===t.version&&(t.version=this.version,t.nextDep)){const e=t.nextDep;e.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=e),t.prevDep=vd.depsTail,t.nextDep=void 0,vd.depsTail.nextDep=t,vd.depsTail=t,vd.deps===t&&(vd.deps=e)}return t}trigger(e){this.version++,zd++,this.notify(e)}notify(e){Ad();try{for(let e=this.subs;e;e=e.prevSub)e.sub.notify()&&e.sub.dep.notify()}finally{Cd()}}}function Zd(e){if(e.dep.sc++,4&e.sub.flags){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let e=t.deps;e;e=e.nextDep)Zd(e)}const n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}const Hd=new WeakMap,Vd=Symbol(\"\"),qd=Symbol(\"\"),Wd=Symbol(\"\");function Xd(e,t,n){if(Rd&&vd){let t=Hd.get(e);t||Hd.set(e,t=new Map);let r=t.get(n);r||(t.set(n,r=new Qd),r.map=t,r.key=n),r.track()}}function Gd(e,t,n,r,a,o){const i=Hd.get(e);if(!i)return void zd++;const s=e=>{e&&e.trigger()};if(Ad(),\"clear\"===t)i.forEach(s);else{const a=Eu(e),o=a&&ju(n);if(a&&\"length\"===n){const e=Number(r);i.forEach(((t,n)=>{(\"length\"===n||n===Wd||!Iu(n)&&n>=e)&&s(t)}))}else switch((void 0!==n||i.has(void 0))&&s(i.get(n)),o&&s(i.get(Wd)),t){case\"add\":a?o&&s(i.get(\"length\")):(s(i.get(Vd)),Au(e)&&s(i.get(qd)));break;case\"delete\":a||(s(i.get(Vd)),Au(e)&&s(i.get(qd)));break;case\"set\":Au(e)&&s(i.get(Vd))}}Cd()}function Yd(e){const t=Rp(e);return t===e?t:(Xd(t,0,Wd),Mp(e)?t:t.map(Bp))}function Kd(e){return Xd(e=Rp(e),0,Wd),e}const Jd={__proto__:null,[Symbol.iterator](){return ep(this,Symbol.iterator,Bp)},concat(...e){return Yd(this).concat(...e.map((e=>Eu(e)?Yd(e):e)))},entries(){return ep(this,\"entries\",(e=>(e[1]=Bp(e[1]),e)))},every(e,t){return np(this,\"every\",e,t,void 0,arguments)},filter(e,t){return np(this,\"filter\",e,t,(e=>e.map(Bp)),arguments)},find(e,t){return np(this,\"find\",e,t,Bp,arguments)},findIndex(e,t){return np(this,\"findIndex\",e,t,void 0,arguments)},findLast(e,t){return np(this,\"findLast\",e,t,Bp,arguments)},findLastIndex(e,t){return np(this,\"findLastIndex\",e,t,void 0,arguments)},forEach(e,t){return np(this,\"forEach\",e,t,void 0,arguments)},includes(...e){return ap(this,\"includes\",e)},indexOf(...e){return ap(this,\"indexOf\",e)},join(e){return Yd(this).join(e)},lastIndexOf(...e){return ap(this,\"lastIndexOf\",e)},map(e,t){return np(this,\"map\",e,t,void 0,arguments)},pop(){return op(this,\"pop\")},push(...e){return op(this,\"push\",e)},reduce(e,...t){return rp(this,\"reduce\",e,t)},reduceRight(e,...t){return rp(this,\"reduceRight\",e,t)},shift(){return op(this,\"shift\")},some(e,t){return np(this,\"some\",e,t,void 0,arguments)},splice(...e){return op(this,\"splice\",e)},toReversed(){return Yd(this).toReversed()},toSorted(e){return Yd(this).toSorted(e)},toSpliced(...e){return Yd(this).toSpliced(...e)},unshift(...e){return op(this,\"unshift\",e)},values(){return ep(this,\"values\",Bp)}};function ep(e,t,n){const r=Kd(e),a=r[t]();return r===e||Mp(e)||(a._next=a.next,a.next=()=>{const e=a._next();return e.value&&(e.value=n(e.value)),e}),a}const tp=Array.prototype;function np(e,t,n,r,a,o){const i=Kd(e),s=i!==e&&!Mp(e),l=i[t];if(l!==tp[t]){const t=l.apply(e,o);return s?Bp(t):t}let c=n;i!==e&&(s?c=function(t,r){return n.call(this,Bp(t),r,e)}:n.length>2&&(c=function(t,r){return n.call(this,t,r,e)}));const u=l.call(i,c,r);return s&&a?a(u):u}function rp(e,t,n,r){const a=Kd(e);let o=n;return a!==e&&(Mp(e)?n.length>3&&(o=function(t,r,a){return n.call(this,t,r,a,e)}):o=function(t,r,a){return n.call(this,t,Bp(r),a,e)}),a[t](o,...r)}function ap(e,t,n){const r=Rp(e);Xd(r,0,Wd);const a=r[t](...n);return-1!==a&&!1!==a||!Np(n[0])?a:(n[0]=Rp(n[0]),r[t](...n))}function op(e,t,n=[]){Bd(),Ad();const r=Rp(e)[t].apply(e,n);return Cd(),jd(),r}const ip=gu(\"__proto__,__v_isRef,__isVue\"),sp=new Set(Object.getOwnPropertyNames(Symbol).filter((e=>\"arguments\"!==e&&\"caller\"!==e)).map((e=>Symbol[e])).filter(Iu));function lp(e){Iu(e)||(e=String(e));const t=Rp(this);return Xd(t,0,e),t.hasOwnProperty(e)}class cp{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,n){if(\"__v_skip\"===t)return e.__v_skip;const r=this._isReadonly,a=this._isShallow;if(\"__v_isReactive\"===t)return!r;if(\"__v_isReadonly\"===t)return r;if(\"__v_isShallow\"===t)return a;if(\"__v_raw\"===t)return n===(r?a?Ep:Tp:a?_p:Sp).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;const o=Eu(e);if(!r){let e;if(o&&(e=Jd[t]))return e;if(\"hasOwnProperty\"===t)return lp}const i=Reflect.get(e,t,Up(e)?e:n);return(Iu(t)?sp.has(t):ip(t))?i:(r||Xd(e,0,t),a?i:Up(i)?o&&ju(t)?i:i.value:Mu(i)?r?Cp(i):Ap(i):i)}}class up extends cp{constructor(e=!1){super(!1,e)}set(e,t,n,r){let a=e[t];if(!this._isShallow){const t=Ip(a);if(Mp(n)||Ip(n)||(a=Rp(a),n=Rp(n)),!Eu(e)&&Up(a)&&!Up(n))return t||(a.value=n),!0}const o=Eu(e)&&ju(t)?Number(t)e,vp=e=>Reflect.getPrototypeOf(e);function bp(e){return function(...t){return\"delete\"!==e&&(\"clear\"===e?void 0:this)}}function yp(e,t){const n=function(e,t){const n={get(n){const r=this.__v_raw,a=Rp(r),o=Rp(n);e||(Wu(n,o)&&Xd(a,0,n),Xd(a,0,o));const{has:i}=vp(a),s=t?gp:e?jp:Bp;return i.call(a,n)?s(r.get(n)):i.call(a,o)?s(r.get(o)):void(r!==a&&r.get(n))},get size(){const t=this.__v_raw;return!e&&Xd(Rp(t),0,Vd),t.size},has(t){const n=this.__v_raw,r=Rp(n),a=Rp(t);return e||(Wu(t,a)&&Xd(r,0,t),Xd(r,0,a)),t===a?n.has(t):n.has(t)||n.has(a)},forEach(n,r){const a=this,o=a.__v_raw,i=Rp(o),s=t?gp:e?jp:Bp;return!e&&Xd(i,0,Vd),o.forEach(((e,t)=>n.call(r,s(e),s(t),a)))}};return ku(n,e?{add:bp(\"add\"),set:bp(\"set\"),delete:bp(\"delete\"),clear:bp(\"clear\")}:{add(e){t||Mp(e)||Ip(e)||(e=Rp(e));const n=Rp(this);return vp(n).has.call(n,e)||(n.add(e),Gd(n,\"add\",e,e)),this},set(e,n){t||Mp(n)||Ip(n)||(n=Rp(n));const r=Rp(this),{has:a,get:o}=vp(r);let i=a.call(r,e);i||(e=Rp(e),i=a.call(r,e));const s=o.call(r,e);return r.set(e,n),i?Wu(n,s)&&Gd(r,\"set\",e,n):Gd(r,\"add\",e,n),this},delete(e){const t=Rp(this),{has:n,get:r}=vp(t);let a=n.call(t,e);a||(e=Rp(e),a=n.call(t,e)),r&&r.call(t,e);const o=t.delete(e);return a&&Gd(t,\"delete\",e,void 0),o},clear(){const e=Rp(this),t=0!==e.size,n=e.clear();return t&&Gd(e,\"clear\",void 0,void 0),n}}),[\"keys\",\"values\",\"entries\",Symbol.iterator].forEach((r=>{n[r]=function(e,t,n){return function(...r){const a=this.__v_raw,o=Rp(a),i=Au(o),s=\"entries\"===e||e===Symbol.iterator&&i,l=\"keys\"===e&&i,c=a[e](...r),u=n?gp:t?jp:Bp;return!t&&Xd(o,0,l?qd:Vd),{next(){const{value:e,done:t}=c.next();return t?{value:e,done:t}:{value:s?[u(e[0]),u(e[1])]:u(e),done:t}},[Symbol.iterator](){return this}}}}(r,e,t)})),n}(e,t);return(t,r,a)=>\"__v_isReactive\"===r?!e:\"__v_isReadonly\"===r?e:\"__v_raw\"===r?t:Reflect.get(Tu(n,r)&&r in t?n:t,r,a)}const Op={get:yp(!1,!1)},wp={get:yp(!1,!0)},xp={get:yp(!0,!1)},kp={get:yp(!0,!0)},Sp=new WeakMap,_p=new WeakMap,Tp=new WeakMap,Ep=new WeakMap;function Ap(e){return Ip(e)?e:$p(e,!1,pp,Op,Sp)}function Cp(e){return $p(e,!0,hp,xp,Tp)}function Pp(e){return $p(e,!0,mp,kp,Ep)}function $p(e,t,n,r,a){if(!Mu(e))return e;if(e.__v_raw&&(!t||!e.__v_isReactive))return e;const o=(i=e).__v_skip||!Object.isExtensible(i)?0:function(e){switch(e){case\"Object\":case\"Array\":return 1;case\"Map\":case\"Set\":case\"WeakMap\":case\"WeakSet\":return 2;default:return 0}}((e=>Lu(e).slice(8,-1))(i));var i;if(0===o)return e;const s=a.get(e);if(s)return s;const l=new Proxy(e,2===o?r:n);return a.set(e,l),l}function Dp(e){return Ip(e)?Dp(e.__v_raw):!(!e||!e.__v_isReactive)}function Ip(e){return!(!e||!e.__v_isReadonly)}function Mp(e){return!(!e||!e.__v_isShallow)}function Np(e){return!!e&&!!e.__v_raw}function Rp(e){const t=e&&e.__v_raw;return t?Rp(t):e}function Lp(e){return!Tu(e,\"__v_skip\")&&Object.isExtensible(e)&&Gu(e,\"__v_skip\",!0),e}const Bp=e=>Mu(e)?Ap(e):e,jp=e=>Mu(e)?Cp(e):e;function Up(e){return!!e&&!0===e.__v_isRef}function zp(e){return Qp(e,!1)}function Fp(e){return Qp(e,!0)}function Qp(e,t){return Up(e)?e:new Zp(e,t)}class Zp{constructor(e,t){this.dep=new Qd,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=t?e:Rp(e),this._value=t?e:Bp(e),this.__v_isShallow=t}get value(){return this.dep.track(),this._value}set value(e){const t=this._rawValue,n=this.__v_isShallow||Mp(e)||Ip(e);e=n?e:Rp(e),Wu(e,t)&&(this._rawValue=e,this._value=n?e:Bp(e),this.dep.trigger())}}function Hp(e){return Up(e)?e.value:e}function Vp(e){return $u(e)?e():Hp(e)}const qp={get:(e,t,n)=>\"__v_raw\"===t?e:Hp(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const a=e[t];return Up(a)&&!Up(n)?(a.value=n,!0):Reflect.set(e,t,n,r)}};function Wp(e){return Dp(e)?e:new Proxy(e,qp)}class Xp{constructor(e){this.__v_isRef=!0,this._value=void 0;const t=this.dep=new Qd,{get:n,set:r}=e(t.track.bind(t),t.trigger.bind(t));this._get=n,this._set=r}get value(){return this._value=this._get()}set value(e){this._set(e)}}function Gp(e){return new Xp(e)}function Yp(e){const t=Eu(e)?new Array(e.length):{};for(const n in e)t[n]=th(e,n);return t}class Kp{constructor(e,t,n){this._object=e,this._key=t,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0}get value(){const e=this._object[this._key];return this._value=void 0===e?this._defaultValue:e}set value(e){this._object[this._key]=e}get dep(){return function(e,t){const n=Hd.get(e);return n&&n.get(t)}(Rp(this._object),this._key)}}class Jp{constructor(e){this._getter=e,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function eh(e,t,n){return Up(e)?e:$u(e)?new Jp(e):Mu(e)&&arguments.length>1?th(e,t,n):zp(e)}function th(e,t,n){const r=e[t];return Up(r)?r:new Kp(e,t,n)}class nh{constructor(e,t,n){this.fn=e,this.setter=t,this._value=void 0,this.dep=new Qd(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=zd-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!t,this.isSSR=n}notify(){if(this.flags|=16,!(8&this.flags)&&vd!==this)return Ed(this,!0),!0}get value(){const e=this.dep.track();return Id(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}}const rh={},ah=new WeakMap;let oh;function ih(e,t=1/0,n){if(t<=0||!Mu(e)||e.__v_skip)return e;if(((n=n||new Map).get(e)||0)>=t)return e;if(n.set(e,t),t--,Up(e))ih(e.value,t,n);else if(Eu(e))for(let r=0;r{ih(e,t,n)}));else if(Bu(e)){for(const r in e)ih(e[r],t,n);for(const r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&ih(e[r],t,n)}return e}function sh(e,t,n,r){try{return r?e(...r):e()}catch(e){ch(e,t,n)}}function lh(e,t,n,r){if($u(e)){const a=sh(e,t,n,r);return a&&Nu(a)&&a.catch((e=>{ch(e,t,n)})),a}if(Eu(e)){const a=[];for(let o=0;o=kh(n)?uh.push(e):uh.splice(function(e){let t=dh+1,n=uh.length;for(;t>>1,a=uh[r],o=kh(a);okh(e)-kh(t)));if(ph.length=0,hh)return void hh.push(...e);for(hh=e,fh=0;fhnull==e.id?2&e.flags?-1:1/0:e.id;function Sh(e){try{for(dh=0;dh{r._d&&ig(-1);const a=Eh(t);let o;try{o=e(...n)}finally{Eh(a),r._d&&ig(1)}return o};return r._n=!0,r._c=!0,r._d=!0,r}function Ch(e,t){if(null===_h)return e;const n=Ug(_h),r=e.dirs||(e.dirs=[]);for(let e=0;ee.__isTeleport,Ih=e=>e&&(e.disabled||\"\"===e.disabled),Mh=e=>e&&(e.defer||\"\"===e.defer),Nh=e=>\"undefined\"!=typeof SVGElement&&e instanceof SVGElement,Rh=e=>\"function\"==typeof MathMLElement&&e instanceof MathMLElement,Lh=(e,t)=>{const n=e&&e.to;return Du(n)?t?t(n):null:n},Bh={name:\"Teleport\",__isTeleport:!0,process(e,t,n,r,a,o,i,s,l,c){const{mc:u,pc:d,pbc:p,o:{insert:h,querySelector:f,createText:m,createComment:g}}=c,v=Ih(t.props);let{shapeFlag:b,children:y,dynamicChildren:O}=t;if(null==e){const e=t.el=m(\"\"),c=t.anchor=m(\"\");h(e,n,r),h(c,n,r);const d=(e,t)=>{16&b&&(a&&a.isCE&&(a.ce._teleportTarget=e),u(y,e,t,a,o,i,s,l))},p=()=>{const e=t.target=Lh(t.props,f),n=Fh(e,t,m,h);e&&(\"svg\"!==i&&Nh(e)?i=\"svg\":\"mathml\"!==i&&Rh(e)&&(i=\"mathml\"),v||(d(e,n),zh(t,!1)))};v&&(d(n,c),zh(t,!0)),Mh(t.props)?(t.el.__isMounted=!1,Em((()=>{p(),delete t.el.__isMounted}),o)):p()}else{if(Mh(t.props)&&!1===e.el.__isMounted)return void Em((()=>{Bh.process(e,t,n,r,a,o,i,s,l,c)}),o);t.el=e.el,t.targetStart=e.targetStart;const u=t.anchor=e.anchor,h=t.target=e.target,m=t.targetAnchor=e.targetAnchor,g=Ih(e.props),b=g?n:h,y=g?u:m;if(\"svg\"===i||Nh(h)?i=\"svg\":(\"mathml\"===i||Rh(h))&&(i=\"mathml\"),O?(p(e.dynamicChildren,O,b,a,o,i,s),$m(e,t,!0)):l||d(e,t,b,y,a,o,i,s,!1),v)g?t.props&&e.props&&t.props.to!==e.props.to&&(t.props.to=e.props.to):jh(t,n,u,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const e=t.target=Lh(t.props,f);e&&jh(t,e,null,c,0)}else g&&jh(t,h,m,c,1);zh(t,v)}},remove(e,t,n,{um:r,o:{remove:a}},o){const{shapeFlag:i,children:s,anchor:l,targetStart:c,targetAnchor:u,target:d,props:p}=e;if(d&&(a(c),a(u)),o&&a(l),16&i){const e=o||!Ih(p);for(let a=0;a{const t=e.subTree;return t.component?qh(t.component):t};function Wh(e){let t=e[0];if(e.length>1)for(const n of e)if(n.type!==eg){t=n;break}return t}const Xh={name:\"BaseTransition\",props:Vh,setup(e,{slots:t}){const n=Cg(),r=function(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return yf((()=>{e.isMounted=!0})),xf((()=>{e.isUnmounting=!0})),e}();return()=>{const a=t.default&&tf(t.default(),!0);if(!a||!a.length)return;const o=Wh(a),i=Rp(e),{mode:s}=i;if(r.isLeaving)return Kh(o);const l=Jh(o);if(!l)return Kh(o);let c=Yh(l,i,r,n,(e=>c=e));l.type!==eg&&ef(l,c);let u=n.subTree&&Jh(n.subTree);if(u&&u.type!==eg&&!dg(u,l)&&qh(n).type!==eg){let e=Yh(u,i,r,n);if(ef(u,e),\"out-in\"===s&&l.type!==eg)return r.isLeaving=!0,e.afterLeave=()=>{r.isLeaving=!1,8&n.job.flags||n.update(),delete e.afterLeave,u=void 0},Kh(o);\"in-out\"===s&&l.type!==eg?e.delayLeave=(e,t,n)=>{Gh(r,u)[String(u.key)]=u,e[Qh]=()=>{t(),e[Qh]=void 0,delete c.delayedLeave,u=void 0},c.delayedLeave=()=>{n(),delete c.delayedLeave,u=void 0}}:u=void 0}else u&&(u=void 0);return o}}};function Gh(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function Yh(e,t,n,r,a){const{appear:o,mode:i,persisted:s=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:f,onLeaveCancelled:m,onBeforeAppear:g,onAppear:v,onAfterAppear:b,onAppearCancelled:y}=t,O=String(e.key),w=Gh(n,e),x=(e,t)=>{e&&lh(e,r,9,t)},k=(e,t)=>{const n=t[1];x(e,t),Eu(e)?e.every((e=>e.length<=1))&&n():e.length<=1&&n()},S={mode:i,persisted:s,beforeEnter(t){let r=l;if(!n.isMounted){if(!o)return;r=g||l}t[Qh]&&t[Qh](!0);const a=w[O];a&&dg(e,a)&&a.el[Qh]&&a.el[Qh](),x(r,[t])},enter(e){let t=c,r=u,a=d;if(!n.isMounted){if(!o)return;t=v||c,r=b||u,a=y||d}let i=!1;const s=e[Zh]=t=>{i||(i=!0,x(t?a:r,[e]),S.delayedLeave&&S.delayedLeave(),e[Zh]=void 0)};t?k(t,[e,s]):s()},leave(t,r){const a=String(e.key);if(t[Zh]&&t[Zh](!0),n.isUnmounting)return r();x(p,[t]);let o=!1;const i=t[Qh]=n=>{o||(o=!0,r(),x(n?m:f,[t]),t[Qh]=void 0,w[a]===e&&delete w[a])};w[a]=e,h?k(h,[t,i]):i()},clone(e){const o=Yh(e,t,n,r,a);return a&&a(o),o}};return S}function Kh(e){if(df(e))return(e=vg(e)).children=null,e}function Jh(e){if(!df(e))return Dh(e.type)&&e.children?Wh(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:n}=e;if(n){if(16&t)return n[0];if(32&t&&$u(n.default))return n.default()}}function ef(e,t){6&e.shapeFlag&&e.component?(e.transition=t,ef(e.component.subTree,t)):128&e.shapeFlag?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function tf(e,t=!1,n){let r=[],a=0;for(let o=0;o1)for(let e=0;en.value,set:e=>n.value=e})}return n}const sf=new WeakMap;function lf(e,t,n,r,a=!1){if(Eu(e))return void e.forEach(((e,o)=>lf(e,t&&(Eu(t)?t[o]:t),n,r,a)));if(uf(r)&&!a)return void(512&r.shapeFlag&&r.type.__asyncResolved&&r.component.subTree.component&&lf(e,t,n,r.component.subTree));const o=4&r.shapeFlag?Ug(r.component):r.el,i=a?null:o,{i:s,r:l}=e,c=t&&t.r,u=s.refs===vu?s.refs={}:s.refs,d=s.setupState,p=Rp(d),h=d===vu?Ou:e=>Tu(p,e);if(null!=c&&c!==l)if(cf(t),Du(c))u[c]=null,h(c)&&(d[c]=null);else if(Up(c)){c.value=null;const e=t;e.k&&(u[e.k]=null)}if($u(l))sh(l,s,12,[i,u]);else{const t=Du(l),r=Up(l);if(t||r){const s=()=>{if(e.f){const n=t?h(l)?d[l]:u[l]:l.value;if(a)Eu(n)&&Su(n,o);else if(Eu(n))n.includes(o)||n.push(o);else if(t)u[l]=[o],h(l)&&(d[l]=u[l]);else{const t=[o];l.value=t,e.k&&(u[e.k]=t)}}else t?(u[l]=i,h(l)&&(d[l]=i)):r&&(l.value=i,e.k&&(u[e.k]=i))};if(i){const t=()=>{s(),sf.delete(e)};t.id=-1,sf.set(e,t),Em(t,n)}else cf(e),s()}}}function cf(e){const t=sf.get(e);t&&(t.flags|=8,sf.delete(e))}Ju().requestIdleCallback,Ju().cancelIdleCallback;const uf=e=>!!e.type.__asyncLoader,df=e=>e.type.__isKeepAlive;function pf(e,t){ff(e,\"a\",t)}function hf(e,t){ff(e,\"da\",t)}function ff(e,t,n=Ag){const r=e.__wdc||(e.__wdc=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()});if(gf(t,r,n),n){let e=n.parent;for(;e&&e.parent;)df(e.parent.vnode)&&mf(r,t,n,e),e=e.parent}}function mf(e,t,n,r){const a=gf(t,e,r,!0);kf((()=>{Su(r[t],a)}),n)}function gf(e,t,n=Ag,r=!1){if(n){const a=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...r)=>{Bd();const a=Dg(n),o=lh(t,n,e,r);return a(),jd(),o});return r?a.unshift(o):a.push(o),o}}const vf=e=>(t,n=Ag)=>{Ng&&\"sp\"!==e||gf(e,((...e)=>t(...e)),n)},bf=vf(\"bm\"),yf=vf(\"m\"),Of=vf(\"bu\"),wf=vf(\"u\"),xf=vf(\"bum\"),kf=vf(\"um\"),Sf=vf(\"sp\"),_f=vf(\"rtg\"),Tf=vf(\"rtc\");function Ef(e,t=Ag){gf(\"ec\",e,t)}const Af=\"components\";function Cf(e,t){return Df(Af,e,!0,t)||e}const Pf=Symbol.for(\"v-ndc\");function $f(e){return Du(e)?Df(Af,e,!1)||e:e||Pf}function Df(e,t,n=!0,r=!1){const a=_h||Ag;if(a){const n=a.type;{const e=function(e,t=!0){return $u(e)?e.displayName||e.name:e.name||t&&e.__name}(n,!1);if(e&&(e===t||e===Qu(t)||e===Vu(Qu(t))))return n}const o=If(a[e]||n[e],t)||If(a.appContext[e],t);return!o&&r?n:o}}function If(e,t){return e&&(e[t]||e[Qu(t)]||e[Vu(Qu(t))])}function Mf(e,t,n,r){let a;const o=n,i=Eu(e);if(i||Du(e)){let n=!1,r=!1;i&&Dp(e)&&(n=!Mp(e),r=Ip(e),e=Kd(e)),a=new Array(e.length);for(let i=0,s=e.length;it(e,n,void 0,o)));else{const n=Object.keys(e);a=new Array(n.length);for(let r=0,i=n.length;r{const t=r.fn(...e);return t&&(t.key=r.key),t}:r.fn)}return e}function Rf(e,t,n={},r,a){if(_h.ce||_h.parent&&uf(_h.parent)&&_h.parent.ce)return\"default\"!==t&&(n.name=t),ag(),cg(Km,null,[mg(\"slot\",n,r&&r())],64);let o=e[t];o&&o._c&&(o._d=!1),ag();const i=o&&Lf(o(n)),s=n.key||i&&i.key,l=cg(Km,{key:(s&&!Iu(s)?s:`_${t}`)+(!i&&r?\"_fb\":\"\")},i||(r?r():[]),i&&1===e._?64:-2);return!a&&l.scopeId&&(l.slotScopeIds=[l.scopeId+\"-s\"]),o&&o._c&&(o._d=!0),l}function Lf(e){return e.some((e=>!ug(e)||e.type!==eg&&!(e.type===Km&&!Lf(e.children))))?e:null}function Bf(e,t){const n={};for(const t in e)n[qu(t)]=e[t];return n}const jf=e=>e?Mg(e)?Ug(e):jf(e.parent):null,Uf=ku(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>jf(e.parent),$root:e=>jf(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Kf(e),$forceUpdate:e=>e.f||(e.f=()=>{bh(e.update)}),$nextTick:e=>e.n||(e.n=vh.bind(e.proxy)),$watch:e=>jm.bind(e)}),zf=(e,t)=>e!==vu&&!e.__isScriptSetup&&Tu(e,t),Ff={get({_:e},t){if(\"__v_skip\"===t)return!0;const{ctx:n,setupState:r,data:a,props:o,accessCache:i,type:s,appContext:l}=e;let c;if(\"$\"!==t[0]){const s=i[t];if(void 0!==s)switch(s){case 1:return r[t];case 2:return a[t];case 4:return n[t];case 3:return o[t]}else{if(zf(r,t))return i[t]=1,r[t];if(a!==vu&&Tu(a,t))return i[t]=2,a[t];if((c=e.propsOptions[0])&&Tu(c,t))return i[t]=3,o[t];if(n!==vu&&Tu(n,t))return i[t]=4,n[t];Xf&&(i[t]=0)}}const u=Uf[t];let d,p;return u?(\"$attrs\"===t&&Xd(e.attrs,0,\"\"),u(e)):(d=s.__cssModules)&&(d=d[t])?d:n!==vu&&Tu(n,t)?(i[t]=4,n[t]):(p=l.config.globalProperties,Tu(p,t)?p[t]:void 0)},set({_:e},t,n){const{data:r,setupState:a,ctx:o}=e;return zf(a,t)?(a[t]=n,!0):r!==vu&&Tu(r,t)?(r[t]=n,!0):!(Tu(e.props,t)||\"$\"===t[0]&&t.slice(1)in e||(o[t]=n,0))},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:a,propsOptions:o,type:i}},s){let l,c;return!!(n[s]||e!==vu&&\"$\"!==s[0]&&Tu(e,s)||zf(t,s)||(l=o[0])&&Tu(l,s)||Tu(r,s)||Tu(Uf,s)||Tu(a.config.globalProperties,s)||(c=i.__cssModules)&&c[s])},defineProperty(e,t,n){return null!=n.get?e._.accessCache[t]=0:Tu(n,\"value\")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Qf(){return Hf().slots}function Zf(){return Hf().attrs}function Hf(e){const t=Cg();return t.setupContext||(t.setupContext=jg(t))}function Vf(e){return Eu(e)?e.reduce(((e,t)=>(e[t]=null,e)),{}):e}function qf(e,t){const n=Vf(e);for(const e in t){if(e.startsWith(\"__skip\"))continue;let r=n[e];r?Eu(r)||$u(r)?r=n[e]={type:r,default:t[e]}:r.default=t[e]:null===r&&(r=n[e]={default:t[e]}),r&&t[`__skip_${e}`]&&(r.skipFactory=!0)}return n}function Wf(e,t){return e&&t?Eu(e)&&Eu(t)?e.concat(t):ku({},Vf(e),Vf(t)):e||t}let Xf=!0;function Gf(e,t,n){lh(Eu(e)?e.map((e=>e.bind(t.proxy))):e.bind(t.proxy),t,n)}function Yf(e,t,n,r){let a=r.includes(\".\")?Um(n,r):()=>n[r];if(Du(e)){const n=t[e];$u(n)&&Lm(a,n)}else if($u(e))Lm(a,e.bind(n));else if(Mu(e))if(Eu(e))e.forEach((e=>Yf(e,t,n,r)));else{const r=$u(e.handler)?e.handler.bind(n):t[e.handler];$u(r)&&Lm(a,r,e)}}function Kf(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:a,optionsCache:o,config:{optionMergeStrategies:i}}=e.appContext,s=o.get(t);let l;return s?l=s:a.length||n||r?(l={},a.length&&a.forEach((e=>Jf(l,e,i,!0))),Jf(l,t,i)):l=t,Mu(t)&&o.set(t,l),l}function Jf(e,t,n,r=!1){const{mixins:a,extends:o}=t;o&&Jf(e,o,n,!0),a&&a.forEach((t=>Jf(e,t,n,!0)));for(const a in t)if(r&&\"expose\"===a);else{const r=em[a]||n&&n[a];e[a]=r?r(e[a],t[a]):t[a]}return e}const em={data:tm,props:om,emits:om,methods:am,computed:am,beforeCreate:rm,created:rm,beforeMount:rm,mounted:rm,beforeUpdate:rm,updated:rm,beforeDestroy:rm,beforeUnmount:rm,destroyed:rm,unmounted:rm,activated:rm,deactivated:rm,errorCaptured:rm,serverPrefetch:rm,components:am,directives:am,watch:function(e,t){if(!e)return t;if(!t)return e;const n=ku(Object.create(null),e);for(const r in t)n[r]=rm(e[r],t[r]);return n},provide:tm,inject:function(e,t){return am(nm(e),nm(t))}};function tm(e,t){return t?e?function(){return ku($u(e)?e.call(this,this):e,$u(t)?t.call(this,this):t)}:t:e}function nm(e){if(Eu(e)){const t={};for(let n=0;n(a.has(e)||(e&&$u(e.install)?(a.add(e),e.install(s,...t)):$u(e)&&(a.add(e),e(s,...t))),s),mixin:e=>(r.mixins.includes(e)||r.mixins.push(e),s),component:(e,t)=>t?(r.components[e]=t,s):r.components[e],directive:(e,t)=>t?(r.directives[e]=t,s):r.directives[e],mount(a,o,l){if(!i){const o=s._ceVNode||mg(t,n);return o.appContext=r,!0===l?l=\"svg\":!1===l&&(l=void 0),e(o,a,l),i=!0,s._container=a,a.__vue_app__=s,Ug(o.component)}},onUnmount(e){o.push(e)},unmount(){i&&(lh(o,s._instance,16),e(null,s._container),delete s._container.__vue_app__)},provide:(e,t)=>(r.provides[e]=t,s),runWithContext(e){const t=cm;cm=s;try{return e()}finally{cm=t}}};return s}}let cm=null;function um(e,t){if(Ag){let n=Ag.provides;const r=Ag.parent&&Ag.parent.provides;r===n&&(n=Ag.provides=Object.create(r)),n[e]=t}}function dm(e,t,n=!1){const r=Cg();if(r||cm){let a=cm?cm._context.provides:r?null==r.parent||r.ce?r.vnode.appContext&&r.vnode.appContext.provides:r.parent.provides:void 0;if(a&&e in a)return a[e];if(arguments.length>1)return n&&$u(t)?t.call(r&&r.proxy):t}}function pm(){return!(!Cg()&&!cm)}const hm={},fm=()=>Object.create(hm),mm=e=>Object.getPrototypeOf(e)===hm;function gm(e,t,n,r){const[a,o]=e.propsOptions;let i,s=!1;if(t)for(let l in t){if(Uu(l))continue;const c=t[l];let u;a&&Tu(a,u=Qu(l))?o&&o.includes(u)?(i||(i={}))[u]=c:n[u]=c:Vm(e.emitsOptions,l)||l in r&&c===r[l]||(r[l]=c,s=!0)}if(o){const t=Rp(n),r=i||vu;for(let i=0;i{l=!0;const[n,r]=ym(e,t,!0);ku(i,n),r&&s.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!o&&!l)return Mu(e)&&r.set(e,bu),bu;if(Eu(o))for(let e=0;e\"_\"===e||\"_ctx\"===e||\"$stable\"===e,xm=e=>Eu(e)?e.map(wg):[wg(e)],km=(e,t,n)=>{if(t._n)return t;const r=Ah(((...e)=>xm(t(...e))),n);return r._c=!1,r},Sm=(e,t,n)=>{const r=e._ctx;for(const n in e){if(wm(n))continue;const a=e[n];if($u(a))t[n]=km(0,a,r);else if(null!=a){const e=xm(a);t[n]=()=>e}}},_m=(e,t)=>{const n=xm(t);e.slots.default=()=>n},Tm=(e,t,n)=>{for(const r in t)!n&&wm(r)||(e[r]=t[r])},Em=function(e,t){t&&t.pendingBranch?Eu(e)?t.effects.push(...e):t.effects.push(e):Oh(e)};function Am(e){return function(e){Ju().__VUE__=!0;const{insert:t,remove:n,patchProp:r,createElement:a,createText:o,createComment:i,setText:s,setElementText:l,parentNode:c,nextSibling:u,setScopeId:d=yu,insertStaticContent:p}=e,h=(e,t,n,r=null,a=null,o=null,i=void 0,s=null,l=!!t.dynamicChildren)=>{if(e===t)return;e&&!dg(e,t)&&(r=U(e),N(e,a,o,!0),e=null),-2===t.patchFlag&&(l=!1,t.dynamicChildren=null);const{type:c,ref:u,shapeFlag:d}=t;switch(c){case Jm:f(e,t,n,r);break;case eg:m(e,t,n,r);break;case tg:null==e&&g(t,n,r,i);break;case Km:_(e,t,n,r,a,o,i,s,l);break;default:1&d?b(e,t,n,r,a,o,i,s,l):6&d?T(e,t,n,r,a,o,i,s,l):(64&d||128&d)&&c.process(e,t,n,r,a,o,i,s,l,Q)}null!=u&&a?lf(u,e&&e.ref,o,t||e,!t):null==u&&e&&null!=e.ref&&lf(e.ref,null,o,e,!0)},f=(e,n,r,a)=>{if(null==e)t(n.el=o(n.children),r,a);else{const t=n.el=e.el;n.children!==e.children&&s(t,n.children)}},m=(e,n,r,a)=>{null==e?t(n.el=i(n.children||\"\"),r,a):n.el=e.el},g=(e,t,n,r)=>{[e.el,e.anchor]=p(e.children,t,n,r,e.el,e.anchor)},v=({el:e,anchor:n},r,a)=>{let o;for(;e&&e!==n;)o=u(e),t(e,r,a),e=o;t(n,r,a)},b=(e,t,n,r,a,o,i,s,l)=>{\"svg\"===t.type?i=\"svg\":\"math\"===t.type&&(i=\"mathml\"),null==e?y(t,n,r,a,o,i,s,l):x(e,t,a,o,i,s,l)},y=(e,n,o,i,s,c,u,d)=>{let p,h;const{props:f,shapeFlag:m,transition:g,dirs:v}=e;if(p=e.el=a(e.type,c,f&&f.is,f),8&m?l(p,e.children):16&m&&w(e.children,p,null,i,s,Cm(e,c),u,d),v&&Ph(e,null,i,\"created\"),O(p,e,e.scopeId,u,i),f){for(const e in f)\"value\"===e||Uu(e)||r(p,e,null,f[e],c,i);\"value\"in f&&r(p,\"value\",null,f.value,c),(h=f.onVnodeBeforeMount)&&_g(h,i,e)}v&&Ph(e,null,i,\"beforeMount\");const b=function(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}(s,g);b&&g.beforeEnter(p),t(p,n,o),((h=f&&f.onVnodeMounted)||b||v)&&Em((()=>{h&&_g(h,i,e),b&&g.enter(p),v&&Ph(e,null,i,\"mounted\")}),s)},O=(e,t,n,r,a)=>{if(n&&d(e,n),r)for(let t=0;t{for(let c=l;c{const c=t.el=e.el;let{patchFlag:u,dynamicChildren:d,dirs:p}=t;u|=16&e.patchFlag;const h=e.props||vu,f=t.props||vu;let m;if(n&&Pm(n,!1),(m=f.onVnodeBeforeUpdate)&&_g(m,n,t,e),p&&Ph(t,e,n,\"beforeUpdate\"),n&&Pm(n,!0),(h.innerHTML&&null==f.innerHTML||h.textContent&&null==f.textContent)&&l(c,\"\"),d?k(e.dynamicChildren,d,c,n,a,Cm(t,o),i):s||$(e,t,c,null,n,a,Cm(t,o),i,!1),u>0){if(16&u)S(c,h,f,n,o);else if(2&u&&h.class!==f.class&&r(c,\"class\",null,f.class,o),4&u&&r(c,\"style\",h.style,f.style,o),8&u){const e=t.dynamicProps;for(let t=0;t{m&&_g(m,n,t,e),p&&Ph(t,e,n,\"updated\")}),a)},k=(e,t,n,r,a,o,i)=>{for(let s=0;s{if(t!==n){if(t!==vu)for(const i in t)Uu(i)||i in n||r(e,i,t[i],null,o,a);for(const i in n){if(Uu(i))continue;const s=n[i],l=t[i];s!==l&&\"value\"!==i&&r(e,i,l,s,o,a)}\"value\"in n&&r(e,\"value\",t.value,n.value,o)}},_=(e,n,r,a,i,s,l,c,u)=>{const d=n.el=e?e.el:o(\"\"),p=n.anchor=e?e.anchor:o(\"\");let{patchFlag:h,dynamicChildren:f,slotScopeIds:m}=n;m&&(c=c?c.concat(m):m),null==e?(t(d,r,a),t(p,r,a),w(n.children||[],r,p,i,s,l,c,u)):h>0&&64&h&&f&&e.dynamicChildren?(k(e.dynamicChildren,f,r,i,s,l,c),(null!=n.key||i&&n===i.subTree)&&$m(e,n,!0)):$(e,n,r,p,i,s,l,c,u)},T=(e,t,n,r,a,o,i,s,l)=>{t.slotScopeIds=s,null==e?512&t.shapeFlag?a.ctx.activate(t,n,r,i,l):E(t,n,r,a,o,i,l):A(e,t,l)},E=(e,t,n,r,a,o,i)=>{const s=e.component=function(e,t,n){const r=e.type,a=(t?t.appContext:e.appContext)||Tg,o={uid:Eg++,vnode:e,type:r,parent:t,appContext:a,root:null,next:null,subTree:null,effect:null,update:null,job:null,scope:new bd(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:t?t.provides:Object.create(a.provides),ids:t?t.ids:[\"\",0,0],accessCache:null,renderCache:[],components:null,directives:null,propsOptions:ym(r,a),emitsOptions:Hm(r,a),emit:null,emitted:null,propsDefaults:vu,inheritAttrs:r.inheritAttrs,ctx:vu,data:vu,props:vu,attrs:vu,slots:vu,refs:vu,setupState:vu,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return o.ctx={_:o},o.root=t?t.root:o,o.emit=Qm.bind(null,o),e.ce&&e.ce(o),o}(e,r,a);if(df(e)&&(s.ctx.renderer=Q),function(e,t=!1,n=!1){t&&$g(t);const{props:r,children:a}=e.vnode,o=Mg(e);(function(e,t,n,r=!1){const a={},o=fm();e.propsDefaults=Object.create(null),gm(e,t,a,o);for(const t in e.propsOptions[0])t in a||(a[t]=void 0);n?e.props=r?a:$p(a,!1,fp,wp,_p):e.type.props?e.props=a:e.props=o,e.attrs=o})(e,r,o,t),((e,t,n)=>{const r=e.slots=fm();if(32&e.vnode.shapeFlag){const e=t._;e?(Tm(r,t,n),n&&Gu(r,\"_\",e,!0)):Sm(t,r)}else t&&_m(e,t)})(e,a,n||t);o&&function(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Ff);const{setup:r}=n;if(r){Bd();const n=e.setupContext=r.length>1?jg(e):null,a=Dg(e),o=sh(r,e,0,[e.props,n]),i=Nu(o);if(jd(),a(),!i&&!e.sp||uf(e)||af(e),i){if(o.then(Ig,Ig),t)return o.then((t=>{Rg(e,t)})).catch((t=>{ch(t,e,0)}));e.asyncDep=o}else Rg(e,o)}else Lg(e)}(e,t);t&&$g(!1)}(s,!1,i),s.asyncDep){if(a&&a.registerDep(s,C,i),!e.el){const r=s.subTree=mg(eg);m(null,r,t,n),e.placeholder=r.el}}else C(s,e,t,n,a,o,i)},A=(e,t,n)=>{const r=t.component=e.component;if(function(e,t,n){const{props:r,children:a,component:o}=e,{props:i,children:s,patchFlag:l}=t,c=o.emitsOptions;if(t.dirs||t.transition)return!0;if(!(n&&l>=0))return!(!a&&!s||s&&s.$stable)||r!==i&&(r?!i||Gm(r,i,c):!!i);if(1024&l)return!0;if(16&l)return r?Gm(r,i,c):!!i;if(8&l){const e=t.dynamicProps;for(let t=0;t{const s=()=>{if(e.isMounted){let{next:t,bu:n,u:r,parent:l,vnode:u}=e;{const n=Dm(e);if(n)return t&&(t.el=u.el,P(e,t,i)),void n.asyncDep.then((()=>{e.isUnmounted||s()}))}let d,p=t;Pm(e,!1),t?(t.el=u.el,P(e,t,i)):t=u,n&&Xu(n),(d=t.props&&t.props.onVnodeBeforeUpdate)&&_g(d,l,t,u),Pm(e,!0);const f=qm(e),m=e.subTree;e.subTree=f,h(m,f,c(m.el),U(m),e,a,o),t.el=f.el,null===p&&function({vnode:e,parent:t},n){for(;t;){const r=t.subTree;if(r.suspense&&r.suspense.activeBranch===e&&(r.el=e.el),r!==e)break;(e=t.vnode).el=n,t=t.parent}}(e,f.el),r&&Em(r,a),(d=t.props&&t.props.onVnodeUpdated)&&Em((()=>_g(d,l,t,u)),a)}else{let i;const{el:s,props:l}=t,{bm:c,m:u,parent:d,root:p,type:f}=e,m=uf(t);Pm(e,!1),c&&Xu(c),!m&&(i=l&&l.onVnodeBeforeMount)&&_g(i,d,t),Pm(e,!0);{p.ce&&!1!==p.ce._def.shadowRoot&&p.ce._injectChildStyle(f);const i=e.subTree=qm(e);h(null,i,n,r,e,a,o),t.el=i.el}if(u&&Em(u,a),!m&&(i=l&&l.onVnodeMounted)){const e=t;Em((()=>_g(i,d,e)),a)}(256&t.shapeFlag||d&&uf(d.vnode)&&256&d.vnode.shapeFlag)&&e.a&&Em(e.a,a),e.isMounted=!0,t=n=r=null}};e.scope.on();const l=e.effect=new kd(s);e.scope.off();const u=e.update=l.run.bind(l),d=e.job=l.runIfDirty.bind(l);d.i=e,d.id=e.uid,l.scheduler=()=>bh(d),Pm(e,!0),u()},P=(e,t,n)=>{t.component=e;const r=e.vnode.props;e.vnode=t,e.next=null,function(e,t,n,r){const{props:a,attrs:o,vnode:{patchFlag:i}}=e,s=Rp(a),[l]=e.propsOptions;let c=!1;if(!(r||i>0)||16&i){let r;gm(e,t,a,o)&&(c=!0);for(const o in s)t&&(Tu(t,o)||(r=Hu(o))!==o&&Tu(t,r))||(l?!n||void 0===n[o]&&void 0===n[r]||(a[o]=vm(l,s,o,void 0,e,!0)):delete a[o]);if(o!==s)for(const e in o)t&&Tu(t,e)||(delete o[e],c=!0)}else if(8&i){const n=e.vnode.dynamicProps;for(let r=0;r{const{vnode:r,slots:a}=e;let o=!0,i=vu;if(32&r.shapeFlag){const e=t._;e?n&&1===e?o=!1:Tm(a,t,n):(o=!t.$stable,Sm(t,a)),i=t}else t&&(_m(e,t),i={default:1});if(o)for(const e in a)wm(e)||null!=i[e]||delete a[e]})(e,t.children,n),Bd(),wh(e),jd()},$=(e,t,n,r,a,o,i,s,c=!1)=>{const u=e&&e.children,d=e?e.shapeFlag:0,p=t.children,{patchFlag:h,shapeFlag:f}=t;if(h>0){if(128&h)return void I(u,p,n,r,a,o,i,s,c);if(256&h)return void D(u,p,n,r,a,o,i,s,c)}8&f?(16&d&&j(u,a,o),p!==u&&l(n,p)):16&d?16&f?I(u,p,n,r,a,o,i,s,c):j(u,a,o,!0):(8&d&&l(n,\"\"),16&f&&w(p,n,r,a,o,i,s,c))},D=(e,t,n,r,a,o,i,s,l)=>{t=t||bu;const c=(e=e||bu).length,u=t.length,d=Math.min(c,u);let p;for(p=0;pu?j(e,a,o,!0,!1,d):w(t,n,r,a,o,i,s,l,d)},I=(e,t,n,r,a,o,i,s,l)=>{let c=0;const u=t.length;let d=e.length-1,p=u-1;for(;c<=d&&c<=p;){const r=e[c],u=t[c]=l?xg(t[c]):wg(t[c]);if(!dg(r,u))break;h(r,u,n,null,a,o,i,s,l),c++}for(;c<=d&&c<=p;){const r=e[d],c=t[p]=l?xg(t[p]):wg(t[p]);if(!dg(r,c))break;h(r,c,n,null,a,o,i,s,l),d--,p--}if(c>d){if(c<=p){const e=p+1,d=ep)for(;c<=d;)N(e[c],a,o,!0),c++;else{const f=c,m=c,g=new Map;for(c=m;c<=p;c++){const e=t[c]=l?xg(t[c]):wg(t[c]);null!=e.key&&g.set(e.key,c)}let v,b=0;const y=p-m+1;let O=!1,w=0;const x=new Array(y);for(c=0;c=y){N(r,a,o,!0);continue}let u;if(null!=r.key)u=g.get(r.key);else for(v=m;v<=p;v++)if(0===x[v-m]&&dg(r,t[v])){u=v;break}void 0===u?N(r,a,o,!0):(x[u-m]=c+1,u>=w?w=u:O=!0,h(r,t[u],n,null,a,o,i,s,l),b++)}const k=O?function(e){const t=e.slice(),n=[0];let r,a,o,i,s;const l=e.length;for(r=0;r>1,e[n[s]]0&&(t[r]=n[o-1]),n[o]=r)}}for(o=n.length,i=n[o-1];o-- >0;)n[o]=i,i=t[i];return n}(x):bu;for(v=k.length-1,c=y-1;c>=0;c--){const e=m+c,d=t[e],p=t[e+1],f=e+1{const{el:s,type:l,transition:c,children:u,shapeFlag:d}=e;if(6&d)M(e.component.subTree,r,a,o);else if(128&d)e.suspense.move(r,a,o);else if(64&d)l.move(e,r,a,Q);else if(l!==Km)if(l!==tg)if(2!==o&&1&d&&c)if(0===o)c.beforeEnter(s),t(s,r,a),Em((()=>c.enter(s)),i);else{const{leave:o,delayLeave:i,afterLeave:l}=c,u=()=>{e.ctx.isUnmounted?n(s):t(s,r,a)},d=()=>{s._isLeaving&&s[Qh](!0),o(s,(()=>{u(),l&&l()}))};i?i(s,u,d):d()}else t(s,r,a);else v(e,r,a);else{t(s,r,a);for(let e=0;e{const{type:o,props:i,ref:s,children:l,dynamicChildren:c,shapeFlag:u,patchFlag:d,dirs:p,cacheIndex:h}=e;if(-2===d&&(a=!1),null!=s&&(Bd(),lf(s,null,n,e,!0),jd()),null!=h&&(t.renderCache[h]=void 0),256&u)return void t.ctx.deactivate(e);const f=1&u&&p,m=!uf(e);let g;if(m&&(g=i&&i.onVnodeBeforeUnmount)&&_g(g,t,e),6&u)B(e.component,n,r);else{if(128&u)return void e.suspense.unmount(n,r);f&&Ph(e,null,t,\"beforeUnmount\"),64&u?e.type.remove(e,t,n,Q,r):c&&!c.hasOnce&&(o!==Km||d>0&&64&d)?j(c,t,n,!1,!0):(o===Km&&384&d||!a&&16&u)&&j(l,t,n),r&&R(e)}(m&&(g=i&&i.onVnodeUnmounted)||f)&&Em((()=>{g&&_g(g,t,e),f&&Ph(e,null,t,\"unmounted\")}),n)},R=e=>{const{type:t,el:r,anchor:a,transition:o}=e;if(t===Km)return void L(r,a);if(t===tg)return void(({el:e,anchor:t})=>{let r;for(;e&&e!==t;)r=u(e),n(e),e=r;n(t)})(e);const i=()=>{n(r),o&&!o.persisted&&o.afterLeave&&o.afterLeave()};if(1&e.shapeFlag&&o&&!o.persisted){const{leave:t,delayLeave:n}=o,a=()=>t(r,i);n?n(e.el,i,a):a()}else i()},L=(e,t)=>{let r;for(;e!==t;)r=u(e),n(e),e=r;n(t)},B=(e,t,n)=>{const{bum:r,scope:a,job:o,subTree:i,um:s,m:l,a:c}=e;Im(l),Im(c),r&&Xu(r),a.stop(),o&&(o.flags|=8,N(i,e,t,n)),s&&Em(s,t),Em((()=>{e.isUnmounted=!0}),t)},j=(e,t,n,r=!1,a=!1,o=0)=>{for(let i=o;i{if(6&e.shapeFlag)return U(e.component.subTree);if(128&e.shapeFlag)return e.suspense.next();const t=u(e.anchor||e.el),n=t&&t[$h];return n?u(n):t};let z=!1;const F=(e,t,n)=>{null==e?t._vnode&&N(t._vnode,null,null,!0):h(t._vnode||null,e,t,null,null,null,n),t._vnode=e,z||(z=!0,wh(),xh(),z=!1)},Q={p:h,um:N,m:M,r:R,mt:E,mc:w,pc:$,pbc:k,n:U,o:e};return{render:F,hydrate:undefined,createApp:lm(F)}}(e)}function Cm({type:e,props:t},n){return\"svg\"===n&&\"foreignObject\"===e||\"mathml\"===n&&\"annotation-xml\"===e&&t&&t.encoding&&t.encoding.includes(\"html\")?void 0:n}function Pm({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function $m(e,t,n=!1){const r=e.children,a=t.children;if(Eu(r)&&Eu(a))for(let e=0;edm(Mm);function Rm(e,t){return Bm(e,null,t)}function Lm(e,t,n){return Bm(e,t,n)}function Bm(e,t,n=vu){const{immediate:r,deep:a,flush:o,once:i}=n,s=ku({},n),l=t&&r||!t&&\"post\"!==o;let c;if(Ng)if(\"sync\"===o){const e=Nm();c=e.__watcherHandles||(e.__watcherHandles=[])}else if(!l){const e=()=>{};return e.stop=yu,e.resume=yu,e.pause=yu,e}const u=Ag;s.call=(e,t,n)=>lh(e,u,t,n);let d=!1;\"post\"===o?s.scheduler=e=>{Em(e,u&&u.suspense)}:\"sync\"!==o&&(d=!0,s.scheduler=(e,t)=>{t?e():bh(e)}),s.augmentJob=e=>{t&&(e.flags|=4),d&&(e.flags|=2,u&&(e.id=u.uid,e.i=u))};const p=function(e,t,n=vu){const{immediate:r,deep:a,once:o,scheduler:i,augmentJob:s,call:l}=n,c=e=>a?e:Mp(e)||!1===a||0===a?ih(e,1):ih(e);let u,d,p,h,f=!1,m=!1;if(Up(e)?(d=()=>e.value,f=Mp(e)):Dp(e)?(d=()=>c(e),f=!0):Eu(e)?(m=!0,f=e.some((e=>Dp(e)||Mp(e))),d=()=>e.map((e=>Up(e)?e.value:Dp(e)?c(e):$u(e)?l?l(e,2):e():void 0))):d=$u(e)?t?l?()=>l(e,2):e:()=>{if(p){Bd();try{p()}finally{jd()}}const t=oh;oh=u;try{return l?l(e,3,[h]):e(h)}finally{oh=t}}:yu,t&&a){const e=d,t=!0===a?1/0:a;d=()=>ih(e(),t)}const g=Od(),v=()=>{u.stop(),g&&g.active&&Su(g.effects,u)};if(o&&t){const e=t;t=(...t)=>{e(...t),v()}}let b=m?new Array(e.length).fill(rh):rh;const y=e=>{if(1&u.flags&&(u.dirty||e))if(t){const e=u.run();if(a||f||(m?e.some(((e,t)=>Wu(e,b[t]))):Wu(e,b))){p&&p();const n=oh;oh=u;try{const n=[e,b===rh?void 0:m&&b[0]===rh?[]:b,h];b=e,l?l(t,3,n):t(...n)}finally{oh=n}}}else u.run()};return s&&s(y),u=new kd(d),u.scheduler=i?()=>i(y,!1):y,h=e=>function(e,t=!1,n=oh){if(n){let t=ah.get(n);t||ah.set(n,t=[]),t.push(e)}}(e,!1,u),p=u.onStop=()=>{const e=ah.get(u);if(e){if(l)l(e,4);else for(const t of e)t();ah.delete(u)}},t?r?y(!0):b=u.run():i?i(y.bind(null,!0),!0):u.run(),v.pause=u.pause.bind(u),v.resume=u.resume.bind(u),v.stop=v,v}(e,t,s);return Ng&&(c?c.push(p):l&&p()),p}function jm(e,t,n){const r=this.proxy,a=Du(e)?e.includes(\".\")?Um(r,e):()=>r[e]:e.bind(r,r);let o;$u(t)?o=t:(o=t.handler,n=t);const i=Dg(this),s=Bm(a,o.bind(r),n);return i(),s}function Um(e,t){const n=t.split(\".\");return()=>{let t=e;for(let e=0;e{let l,c,u=vu;return Bm((()=>{const t=e[a];Wu(l,t)&&(l=t,s())}),null,{flush:\"sync\"}),{get:()=>(i(),n.get?n.get(l):l),set(e){const i=n.set?n.set(e):e;if(!(Wu(i,l)||u!==vu&&Wu(e,u)))return;const d=r.vnode.props;d&&(t in d||a in d||o in d)&&(`onUpdate:${t}`in d||`onUpdate:${a}`in d||`onUpdate:${o}`in d)||(l=e,s()),r.emit(`update:${t}`,i),Wu(e,i)&&Wu(e,u)&&!Wu(i,c)&&s(),u=e,c=i}}}));return s[Symbol.iterator]=()=>{let e=0;return{next:()=>e<2?{value:e++?i||vu:s,done:!1}:{done:!0}}},s}const Fm=(e,t)=>\"modelValue\"===t||\"model-value\"===t?e.modelModifiers:e[`${t}Modifiers`]||e[`${Qu(t)}Modifiers`]||e[`${Hu(t)}Modifiers`];function Qm(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||vu;let a=n;const o=t.startsWith(\"update:\"),i=o&&Fm(r,t.slice(7));let s;i&&(i.trim&&(a=n.map((e=>Du(e)?e.trim():e))),i.number&&(a=n.map(Yu)));let l=r[s=qu(t)]||r[s=qu(Qu(t))];!l&&o&&(l=r[s=qu(Hu(t))]),l&&lh(l,e,6,a);const c=r[s+\"Once\"];if(c){if(e.emitted){if(e.emitted[s])return}else e.emitted={};e.emitted[s]=!0,lh(c,e,6,a)}}const Zm=new WeakMap;function Hm(e,t,n=!1){const r=n?Zm:t.emitsCache,a=r.get(e);if(void 0!==a)return a;const o=e.emits;let i={},s=!1;if(!$u(e)){const r=e=>{const n=Hm(e,t,!0);n&&(s=!0,ku(i,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return o||s?(Eu(o)?o.forEach((e=>i[e]=null)):ku(i,o),Mu(e)&&r.set(e,i),i):(Mu(e)&&r.set(e,null),null)}function Vm(e,t){return!(!e||!wu(t))&&(t=t.slice(2).replace(/Once$/,\"\"),Tu(e,t[0].toLowerCase()+t.slice(1))||Tu(e,Hu(t))||Tu(e,t))}function qm(e){const{type:t,vnode:n,proxy:r,withProxy:a,propsOptions:[o],slots:i,attrs:s,emit:l,render:c,renderCache:u,props:d,data:p,setupState:h,ctx:f,inheritAttrs:m}=e,g=Eh(e);let v,b;try{if(4&n.shapeFlag){const e=a||r,t=e;v=wg(c.call(t,e,u,d,h,p,f)),b=s}else{const e=t;v=wg(e.length>1?e(d,{attrs:s,slots:i,emit:l}):e(d,null)),b=t.props?s:Wm(s)}}catch(t){ng.length=0,ch(t,e,1),v=mg(eg)}let y=v;if(b&&!1!==m){const e=Object.keys(b),{shapeFlag:t}=y;e.length&&7&t&&(o&&e.some(xu)&&(b=Xm(b,o)),y=vg(y,b,!1,!0))}return n.dirs&&(y=vg(y,null,!1,!0),y.dirs=y.dirs?y.dirs.concat(n.dirs):n.dirs),n.transition&&ef(y,n.transition),v=y,Eh(g),v}const Wm=e=>{let t;for(const n in e)(\"class\"===n||\"style\"===n||wu(n))&&((t||(t={}))[n]=e[n]);return t},Xm=(e,t)=>{const n={};for(const r in e)xu(r)&&r.slice(9)in t||(n[r]=e[r]);return n};function Gm(e,t,n){const r=Object.keys(t);if(r.length!==Object.keys(e).length)return!0;for(let a=0;ae.__isSuspense,Km=Symbol.for(\"v-fgt\"),Jm=Symbol.for(\"v-txt\"),eg=Symbol.for(\"v-cmt\"),tg=Symbol.for(\"v-stc\"),ng=[];let rg=null;function ag(e=!1){ng.push(rg=e?null:[])}let og=1;function ig(e,t=!1){og+=e,e<0&&rg&&t&&(rg.hasOnce=!0)}function sg(e){return e.dynamicChildren=og>0?rg||bu:null,ng.pop(),rg=ng[ng.length-1]||null,og>0&&rg&&rg.push(e),e}function lg(e,t,n,r,a,o){return sg(fg(e,t,n,r,a,o,!0))}function cg(e,t,n,r,a){return sg(mg(e,t,n,r,a,!0))}function ug(e){return!!e&&!0===e.__v_isVNode}function dg(e,t){return e.type===t.type&&e.key===t.key}const pg=({key:e})=>null!=e?e:null,hg=({ref:e,ref_key:t,ref_for:n})=>(\"number\"==typeof e&&(e=\"\"+e),null!=e?Du(e)||Up(e)||$u(e)?{i:_h,r:e,k:t,f:!!n}:e:null);function fg(e,t=null,n=null,r=0,a=null,o=(e===Km?0:1),i=!1,s=!1){const l={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&pg(t),ref:t&&hg(t),scopeId:Th,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:r,dynamicProps:a,dynamicChildren:null,appContext:null,ctx:_h};return s?(kg(l,n),128&o&&e.normalize(l)):n&&(l.shapeFlag|=Du(n)?8:16),og>0&&!i&&rg&&(l.patchFlag>0||6&o)&&32!==l.patchFlag&&rg.push(l),l}const mg=function(e,t=null,n=null,r=0,a=null,o=!1){if(e&&e!==Pf||(e=eg),ug(e)){const r=vg(e,t,!0);return n&&kg(r,n),og>0&&!o&&rg&&(6&r.shapeFlag?rg[rg.indexOf(e)]=r:rg.push(r)),r.patchFlag=-2,r}var i;if($u(i=e)&&\"__vccOpts\"in i&&(e=e.__vccOpts),t){t=gg(t);let{class:e,style:n}=t;e&&!Du(e)&&(t.class=od(e)),Mu(n)&&(Np(n)&&!Eu(n)&&(n=ku({},n)),t.style=ed(n))}return fg(e,t,n,r,a,Du(e)?1:Ym(e)?128:Dh(e)?64:Mu(e)?4:$u(e)?2:0,o,!0)};function gg(e){return e?Np(e)||mm(e)?ku({},e):e:null}function vg(e,t,n=!1,r=!1){const{props:a,ref:o,patchFlag:i,children:s,transition:l}=e,c=t?Sg(a||{},t):a,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&pg(c),ref:t&&t.ref?n&&o?Eu(o)?o.concat(hg(t)):[o,hg(t)]:hg(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:s,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Km?-1===i?16:16|i:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:l,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&vg(e.ssContent),ssFallback:e.ssFallback&&vg(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return l&&r&&ef(u,l.clone(u)),u}function bg(e=\" \",t=0){return mg(Jm,null,e,t)}function yg(e,t){const n=mg(tg,null,e);return n.staticCount=t,n}function Og(e=\"\",t=!1){return t?(ag(),cg(eg,null,e)):mg(eg,null,e)}function wg(e){return null==e||\"boolean\"==typeof e?mg(eg):Eu(e)?mg(Km,null,e.slice()):ug(e)?xg(e):mg(Jm,null,String(e))}function xg(e){return null===e.el&&-1!==e.patchFlag||e.memo?e:vg(e)}function kg(e,t){let n=0;const{shapeFlag:r}=e;if(null==t)t=null;else if(Eu(t))n=16;else if(\"object\"==typeof t){if(65&r){const n=t.default;return void(n&&(n._c&&(n._d=!1),kg(e,n()),n._c&&(n._d=!0)))}{n=32;const r=t._;r||mm(t)?3===r&&_h&&(1===_h.slots._?t._=1:(t._=2,e.patchFlag|=1024)):t._ctx=_h}}else $u(t)?(t={default:t,_ctx:_h},n=32):(t=String(t),64&r?(n=16,t=[bg(t)]):n=8);e.children=t,e.shapeFlag|=n}function Sg(...e){const t={};for(let n=0;nAg||_h;let Pg,$g;{const e=Ju(),t=(t,n)=>{let r;return(r=e[t])||(r=e[t]=[]),r.push(n),e=>{r.length>1?r.forEach((t=>t(e))):r[0](e)}};Pg=t(\"__VUE_INSTANCE_SETTERS__\",(e=>Ag=e)),$g=t(\"__VUE_SSR_SETTERS__\",(e=>Ng=e))}const Dg=e=>{const t=Ag;return Pg(e),e.scope.on(),()=>{e.scope.off(),Pg(t)}},Ig=()=>{Ag&&Ag.scope.off(),Pg(null)};function Mg(e){return 4&e.vnode.shapeFlag}let Ng=!1;function Rg(e,t,n){$u(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Mu(t)&&(e.setupState=Wp(t)),Lg(e)}function Lg(e,t,n){const r=e.type;e.render||(e.render=r.render||yu);{const t=Dg(e);Bd();try{!function(e){const t=Kf(e),n=e.proxy,r=e.ctx;Xf=!1,t.beforeCreate&&Gf(t.beforeCreate,e,\"bc\");const{data:a,computed:o,methods:i,watch:s,provide:l,inject:c,created:u,beforeMount:d,mounted:p,beforeUpdate:h,updated:f,activated:m,deactivated:g,beforeDestroy:v,beforeUnmount:b,destroyed:y,unmounted:O,render:w,renderTracked:x,renderTriggered:k,errorCaptured:S,serverPrefetch:_,expose:T,inheritAttrs:E,components:A,directives:C,filters:P}=t;if(c&&function(e,t){Eu(e)&&(e=nm(e));for(const n in e){const r=e[n];let a;a=Mu(r)?\"default\"in r?dm(r.from||n,r.default,!0):dm(r.from||n):dm(r),Up(a)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>a.value,set:e=>a.value=e}):t[n]=a}}(c,r),i)for(const e in i){const t=i[e];$u(t)&&(r[e]=t.bind(n))}if(a){const t=a.call(n,n);Mu(t)&&(e.data=Ap(t))}if(Xf=!0,o)for(const e in o){const t=o[e],a=$u(t)?t.bind(n,n):$u(t.get)?t.get.bind(n,n):yu,i=!$u(t)&&$u(t.set)?t.set.bind(n):yu,s=zg({get:a,set:i});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>s.value,set:e=>s.value=e})}if(s)for(const e in s)Yf(s[e],r,n,e);if(l){const e=$u(l)?l.call(n):l;Reflect.ownKeys(e).forEach((t=>{um(t,e[t])}))}function $(e,t){Eu(t)?t.forEach((t=>e(t.bind(n)))):t&&e(t.bind(n))}if(u&&Gf(u,e,\"c\"),$(bf,d),$(yf,p),$(Of,h),$(wf,f),$(pf,m),$(hf,g),$(Ef,S),$(Tf,x),$(_f,k),$(xf,b),$(kf,O),$(Sf,_),Eu(T))if(T.length){const t=e.exposed||(e.exposed={});T.forEach((e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t,enumerable:!0})}))}else e.exposed||(e.exposed={});w&&e.render===yu&&(e.render=w),null!=E&&(e.inheritAttrs=E),A&&(e.components=A),C&&(e.directives=C),_&&af(e)}(e)}finally{jd(),t()}}}const Bg={get:(e,t)=>(Xd(e,0,\"\"),e[t])};function jg(e){return{attrs:new Proxy(e.attrs,Bg),slots:e.slots,emit:e.emit,expose:t=>{e.exposed=t||{}}}}function Ug(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Wp(Lp(e.exposed)),{get:(t,n)=>n in t?t[n]:n in Uf?Uf[n](e):void 0,has:(e,t)=>t in e||t in Uf})):e.proxy}const zg=(e,t)=>{const n=function(e,t,n=!1){let r,a;return $u(e)?r=e:(r=e.get,a=e.set),new nh(r,a,n)}(e,0,Ng);return n};function Fg(e,t,n){const r=(e,t,n)=>{ig(-1);try{return mg(e,t,n)}finally{ig(1)}},a=arguments.length;return 2===a?Mu(t)&&!Eu(t)?ug(t)?r(e,null,[t]):r(e,t):r(e,null,t):(a>3?n=Array.prototype.slice.call(arguments,2):3===a&&ug(n)&&(n=[n]),r(e,t,n))}const Qg=\"3.5.21\";let Zg;const Hg=\"undefined\"!=typeof window&&window.trustedTypes;if(Hg)try{Zg=Hg.createPolicy(\"vue\",{createHTML:e=>e})}catch(Gb){}const Vg=Zg?e=>Zg.createHTML(e):e=>e,qg=\"undefined\"!=typeof document?document:null,Wg=qg&&qg.createElement(\"template\"),Xg={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const a=\"svg\"===t?qg.createElementNS(\"http://www.w3.org/2000/svg\",e):\"mathml\"===t?qg.createElementNS(\"http://www.w3.org/1998/Math/MathML\",e):n?qg.createElement(e,{is:n}):qg.createElement(e);return\"select\"===e&&r&&null!=r.multiple&&a.setAttribute(\"multiple\",r.multiple),a},createText:e=>qg.createTextNode(e),createComment:e=>qg.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>qg.querySelector(e),setScopeId(e,t){e.setAttribute(t,\"\")},insertStaticContent(e,t,n,r,a,o){const i=n?n.previousSibling:t.lastChild;if(a&&(a===o||a.nextSibling))for(;t.insertBefore(a.cloneNode(!0),n),a!==o&&(a=a.nextSibling););else{Wg.innerHTML=Vg(\"svg\"===r?`${e}`:\"mathml\"===r?`${e}`:e);const a=Wg.content;if(\"svg\"===r||\"mathml\"===r){const e=a.firstChild;for(;e.firstChild;)a.appendChild(e.firstChild);a.removeChild(e)}t.insertBefore(a,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Gg=\"transition\",Yg=\"animation\",Kg=Symbol(\"_vtc\"),Jg={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},ev=ku({},Vh,Jg),tv=(e=>(e.displayName=\"Transition\",e.props=ev,e))(((e,{slots:t})=>Fg(Xh,function(e){const t={};for(const n in e)n in Jg||(t[n]=e[n]);if(!1===e.css)return t;const{name:n=\"v\",type:r,duration:a,enterFromClass:o=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:l=o,appearActiveClass:c=i,appearToClass:u=s,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:h=`${n}-leave-to`}=e,f=function(e){if(null==e)return null;if(Mu(e))return[av(e.enter),av(e.leave)];{const t=av(e);return[t,t]}}(a),m=f&&f[0],g=f&&f[1],{onBeforeEnter:v,onEnter:b,onEnterCancelled:y,onLeave:O,onLeaveCancelled:w,onBeforeAppear:x=v,onAppear:k=b,onAppearCancelled:S=y}=t,_=(e,t,n,r)=>{e._enterCancelled=r,iv(e,t?u:s),iv(e,t?c:i),n&&n()},T=(e,t)=>{e._isLeaving=!1,iv(e,d),iv(e,h),iv(e,p),t&&t()},E=e=>(t,n)=>{const a=e?k:b,i=()=>_(t,e,n);nv(a,[t,i]),sv((()=>{iv(t,e?l:o),ov(t,e?u:s),rv(a)||cv(t,r,m,i)}))};return ku(t,{onBeforeEnter(e){nv(v,[e]),ov(e,o),ov(e,i)},onBeforeAppear(e){nv(x,[e]),ov(e,l),ov(e,c)},onEnter:E(!1),onAppear:E(!0),onLeave(e,t){e._isLeaving=!0;const n=()=>T(e,t);ov(e,d),e._enterCancelled?(ov(e,p),pv()):(pv(),ov(e,p)),sv((()=>{e._isLeaving&&(iv(e,d),ov(e,h),rv(O)||cv(e,r,g,n))})),nv(O,[e,n])},onEnterCancelled(e){_(e,!1,void 0,!0),nv(y,[e])},onAppearCancelled(e){_(e,!0,void 0,!0),nv(S,[e])},onLeaveCancelled(e){T(e),nv(w,[e])}})}(e),t))),nv=(e,t=[])=>{Eu(e)?e.forEach((e=>e(...t))):e&&e(...t)},rv=e=>!!e&&(Eu(e)?e.some((e=>e.length>1)):e.length>1);function av(e){return(e=>{const t=Du(e)?Number(e):NaN;return isNaN(t)?e:t})(e)}function ov(e,t){t.split(/\\s+/).forEach((t=>t&&e.classList.add(t))),(e[Kg]||(e[Kg]=new Set)).add(t)}function iv(e,t){t.split(/\\s+/).forEach((t=>t&&e.classList.remove(t)));const n=e[Kg];n&&(n.delete(t),n.size||(e[Kg]=void 0))}function sv(e){requestAnimationFrame((()=>{requestAnimationFrame(e)}))}let lv=0;function cv(e,t,n,r){const a=e._endId=++lv,o=()=>{a===e._endId&&r()};if(null!=n)return setTimeout(o,n);const{type:i,timeout:s,propCount:l}=function(e,t){const n=window.getComputedStyle(e),r=e=>(n[e]||\"\").split(\", \"),a=r(`${Gg}Delay`),o=r(`${Gg}Duration`),i=uv(a,o),s=r(`${Yg}Delay`),l=r(`${Yg}Duration`),c=uv(s,l);let u=null,d=0,p=0;t===Gg?i>0&&(u=Gg,d=i,p=o.length):t===Yg?c>0&&(u=Yg,d=c,p=l.length):(d=Math.max(i,c),u=d>0?i>c?Gg:Yg:null,p=u?u===Gg?o.length:l.length:0);return{type:u,timeout:d,propCount:p,hasTransform:u===Gg&&/\\b(?:transform|all)(?:,|$)/.test(r(`${Gg}Property`).toString())}}(e,t);if(!i)return r();const c=i+\"end\";let u=0;const d=()=>{e.removeEventListener(c,p),o()},p=t=>{t.target===e&&++u>=l&&d()};setTimeout((()=>{udv(t)+dv(e[n]))))}function dv(e){return\"auto\"===e?0:1e3*Number(e.slice(0,-1).replace(\",\",\".\"))}function pv(){return document.body.offsetHeight}const hv=Symbol(\"_vod\"),fv=Symbol(\"_vsh\"),mv={name:\"show\",beforeMount(e,{value:t},{transition:n}){e[hv]=\"none\"===e.style.display?\"\":e.style.display,n&&t?n.beforeEnter(e):gv(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),gv(e,!0),r.enter(e)):r.leave(e,(()=>{gv(e,!1)})):gv(e,t))},beforeUnmount(e,{value:t}){gv(e,t)}};function gv(e,t){e.style.display=t?e[hv]:\"none\",e[fv]=!t}const vv=Symbol(\"\");function bv(e){const t=Cg();if(!t)return;const n=t.ut=(n=e(t.proxy))=>{Array.from(document.querySelectorAll(`[data-v-owner=\"${t.uid}\"]`)).forEach((e=>Ov(e,n)))},r=()=>{const r=e(t.proxy);t.ce?Ov(t.ce,r):yv(t.subTree,r),n(r)};Of((()=>{Oh(r)})),yf((()=>{Lm(r,yu,{flush:\"post\"});const e=new MutationObserver(r);e.observe(t.subTree.el.parentNode,{childList:!0}),kf((()=>e.disconnect()))}))}function yv(e,t){if(128&e.shapeFlag){const n=e.suspense;e=n.activeBranch,n.pendingBranch&&!n.isHydrating&&n.effects.push((()=>{yv(n.activeBranch,t)}))}for(;e.component;)e=e.component.subTree;if(1&e.shapeFlag&&e.el)Ov(e.el,t);else if(e.type===Km)e.children.forEach((e=>yv(e,t)));else if(e.type===tg){let{el:n,anchor:r}=e;for(;n&&(Ov(n,t),n!==r);)n=n.nextSibling}}function Ov(e,t){if(1===e.nodeType){const n=e.style;let r=\"\";for(const e in t){const a=md(t[e]);n.setProperty(`--${e}`,a),r+=`--${e}: ${a};`}n[vv]=r}}const wv=/(?:^|;)\\s*display\\s*:/,xv=/\\s*!important$/;function kv(e,t,n){if(Eu(n))n.forEach((n=>kv(e,t,n)));else if(null==n&&(n=\"\"),t.startsWith(\"--\"))e.setProperty(t,n);else{const r=function(e,t){const n=_v[t];if(n)return n;let r=Qu(t);if(\"filter\"!==r&&r in e)return _v[t]=r;r=Vu(r);for(let n=0;nDv||(Iv.then((()=>Dv=0)),Dv=Date.now()),Nv=e=>111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Rv=e=>{const t=e.props[\"onUpdate:modelValue\"]||!1;return Eu(t)?e=>Xu(t,e):t};function Lv(e){e.target.composing=!0}function Bv(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(\"input\")))}const jv=Symbol(\"_assign\"),Uv={created(e,{modifiers:{lazy:t,trim:n,number:r}},a){e[jv]=Rv(a);const o=r||a.props&&\"number\"===a.props.type;Cv(e,t?\"change\":\"input\",(t=>{if(t.target.composing)return;let r=e.value;n&&(r=r.trim()),o&&(r=Yu(r)),e[jv](r)})),n&&Cv(e,\"change\",(()=>{e.value=e.value.trim()})),t||(Cv(e,\"compositionstart\",Lv),Cv(e,\"compositionend\",Bv),Cv(e,\"change\",Bv))},mounted(e,{value:t}){e.value=null==t?\"\":t},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:a,number:o}},i){if(e[jv]=Rv(i),e.composing)return;const s=null==t?\"\":t;if((!o&&\"number\"!==e.type||/^0\\d/.test(e.value)?e.value:Yu(e.value))!==s){if(document.activeElement===e&&\"range\"!==e.type){if(r&&t===n)return;if(a&&e.value.trim()===s)return}e.value=s}}},zv={deep:!0,created(e,t,n){e[jv]=Rv(n),Cv(e,\"change\",(()=>{const t=e._modelValue,n=Vv(e),r=e.checked,a=e[jv];if(Eu(t)){const e=ud(t,n),o=-1!==e;if(r&&!o)a(t.concat(n));else if(!r&&o){const n=[...t];n.splice(e,1),a(n)}}else if(Cu(t)){const e=new Set(t);r?e.add(n):e.delete(n),a(e)}else a(qv(e,r))}))},mounted:Fv,beforeUpdate(e,t,n){e[jv]=Rv(n),Fv(e,t,n)}};function Fv(e,{value:t,oldValue:n},r){let a;if(e._modelValue=t,Eu(t))a=ud(t,r.props.value)>-1;else if(Cu(t))a=t.has(r.props.value);else{if(t===n)return;a=cd(t,qv(e,!0))}e.checked!==a&&(e.checked=a)}const Qv={created(e,{value:t},n){e.checked=cd(t,n.props.value),e[jv]=Rv(n),Cv(e,\"change\",(()=>{e[jv](Vv(e))}))},beforeUpdate(e,{value:t,oldValue:n},r){e[jv]=Rv(r),t!==n&&(e.checked=cd(t,r.props.value))}},Zv={deep:!0,created(e,{value:t,modifiers:{number:n}},r){const a=Cu(t);Cv(e,\"change\",(()=>{const t=Array.prototype.filter.call(e.options,(e=>e.selected)).map((e=>n?Yu(Vv(e)):Vv(e)));e[jv](e.multiple?a?new Set(t):t:t[0]),e._assigning=!0,vh((()=>{e._assigning=!1}))})),e[jv]=Rv(r)},mounted(e,{value:t}){Hv(e,t)},beforeUpdate(e,t,n){e[jv]=Rv(n)},updated(e,{value:t}){e._assigning||Hv(e,t)}};function Hv(e,t){const n=e.multiple,r=Eu(t);if(!n||r||Cu(t)){for(let a=0,o=e.options.length;aString(e)===String(i))):ud(t,i)>-1}else o.selected=t.has(i);else if(cd(Vv(o),t))return void(e.selectedIndex!==a&&(e.selectedIndex=a))}n||-1===e.selectedIndex||(e.selectedIndex=-1)}}function Vv(e){return\"_value\"in e?e._value:e.value}function qv(e,t){const n=t?\"_trueValue\":\"_falseValue\";return n in e?e[n]:t}const Wv={created(e,t,n){Xv(e,t,n,null,\"created\")},mounted(e,t,n){Xv(e,t,n,null,\"mounted\")},beforeUpdate(e,t,n,r){Xv(e,t,n,r,\"beforeUpdate\")},updated(e,t,n,r){Xv(e,t,n,r,\"updated\")}};function Xv(e,t,n,r,a){const o=function(e,t){switch(e){case\"SELECT\":return Zv;case\"TEXTAREA\":return Uv;default:switch(t){case\"checkbox\":return zv;case\"radio\":return Qv;default:return Uv}}}(e.tagName,n.props&&n.props.type)[a];o&&o(e,t,n,r)}const Gv=[\"ctrl\",\"shift\",\"alt\",\"meta\"],Yv={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>\"button\"in e&&0!==e.button,middle:e=>\"button\"in e&&1!==e.button,right:e=>\"button\"in e&&2!==e.button,exact:(e,t)=>Gv.some((n=>e[`${n}Key`]&&!t.includes(n)))},Kv=(e,t)=>{const n=e._withMods||(e._withMods={}),r=t.join(\".\");return n[r]||(n[r]=(n,...r)=>{for(let e=0;e{const n=e._withKeys||(e._withKeys={}),r=t.join(\".\");return n[r]||(n[r]=n=>{if(!(\"key\"in n))return;const r=Hu(n.key);return t.some((e=>e===r||Jv[e]===r))?e(n):void 0})},tb=ku({patchProp:(e,t,n,r,a,o)=>{const i=\"svg\"===a;\"class\"===t?function(e,t,n){const r=e[Kg];r&&(t=(t?[t,...r]:[...r]).join(\" \")),null==t?e.removeAttribute(\"class\"):n?e.setAttribute(\"class\",t):e.className=t}(e,r,i):\"style\"===t?function(e,t,n){const r=e.style,a=Du(n);let o=!1;if(n&&!a){if(t)if(Du(t))for(const e of t.split(\";\")){const t=e.slice(0,e.indexOf(\":\")).trim();null==n[t]&&kv(r,t,\"\")}else for(const e in t)null==n[e]&&kv(r,e,\"\");for(const e in n)\"display\"===e&&(o=!0),kv(r,e,n[e])}else if(a){if(t!==n){const e=r[vv];e&&(n+=\";\"+e),r.cssText=n,o=wv.test(n)}}else t&&e.removeAttribute(\"style\");hv in e&&(e[hv]=o?r.display:\"\",e[fv]&&(r.display=\"none\"))}(e,n,r):wu(t)?xu(t)||function(e,t,n,r,a=null){const o=e[Pv]||(e[Pv]={}),i=o[t];if(r&&i)i.value=r;else{const[n,s]=function(e){let t;if($v.test(e)){let n;for(t={};n=e.match($v);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[\":\"===e[2]?e.slice(3):Hu(e.slice(2)),t]}(t);if(r){const i=o[t]=function(e,t){const n=e=>{if(e._vts){if(e._vts<=n.attached)return}else e._vts=Date.now();lh(function(e,t){if(Eu(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map((e=>t=>!t._stopped&&e&&e(t)))}return t}(e,n.value),t,5,[e])};return n.value=e,n.attached=Mv(),n}(r,a);Cv(e,n,i,s)}else i&&(function(e,t,n,r){e.removeEventListener(t,n,r)}(e,n,i,s),o[t]=void 0)}}(e,t,0,r,o):(\".\"===t[0]?(t=t.slice(1),1):\"^\"===t[0]?(t=t.slice(1),0):function(e,t,n,r){if(r)return\"innerHTML\"===t||\"textContent\"===t||!!(t in e&&Nv(t)&&$u(n));if(\"spellcheck\"===t||\"draggable\"===t||\"translate\"===t||\"autocorrect\"===t)return!1;if(\"form\"===t)return!1;if(\"list\"===t&&\"INPUT\"===e.tagName)return!1;if(\"type\"===t&&\"TEXTAREA\"===e.tagName)return!1;if(\"width\"===t||\"height\"===t){const t=e.tagName;if(\"IMG\"===t||\"VIDEO\"===t||\"CANVAS\"===t||\"SOURCE\"===t)return!1}return(!Nv(t)||!Du(n))&&t in e}(e,t,r,i))?(Av(e,t,r),e.tagName.includes(\"-\")||\"value\"!==t&&\"checked\"!==t&&\"selected\"!==t||Ev(e,t,r,i,0,\"value\"!==t)):!e._isVueCE||!/[A-Z]/.test(t)&&Du(r)?(\"true-value\"===t?e._trueValue=r:\"false-value\"===t&&(e._falseValue=r),Ev(e,t,r,i)):Av(e,Qu(t),r,0,t)}},Xg);let nb;function rb(){return nb||(nb=Am(tb))}const ab=(...e)=>{rb().render(...e)},ob=(...e)=>{const t=rb().createApp(...e),{mount:n}=t;return t.mount=e=>{const r=function(e){return Du(e)?document.querySelector(e):e}(e);if(!r)return;const a=t._component;$u(a)||a.render||a.template||(a.template=r.innerHTML),1===r.nodeType&&(r.textContent=\"\");const o=n(r,!1,function(e){return e instanceof SVGElement?\"svg\":\"function\"==typeof MathMLElement&&e instanceof MathMLElement?\"mathml\":void 0}(r));return r instanceof Element&&(r.removeAttribute(\"v-cloak\"),r.setAttribute(\"data-v-app\",\"\")),o},t},ib=\"3\"===Qg[0];function sb(e){if(e instanceof Promise||e instanceof Date||e instanceof RegExp)return e;const t=\"function\"==typeof(n=e)?n():Hp(n);var n;if(!e||!t)return t;if(Array.isArray(t))return t.map((e=>sb(e)));if(\"object\"==typeof t){const e={};for(const n in t)Object.prototype.hasOwnProperty.call(t,n)&&(\"titleTemplate\"===n||\"o\"===n[0]&&\"n\"===n[1]?e[n]=Hp(t[n]):e[n]=sb(t[n]));return e}return t}const lb={hooks:{\"entries:resolve\":e=>{for(const t of e.entries)t.resolvedInput=sb(t.input)}}},cb=\"usehead\";function ub(e={}){e.domDelayFn=e.domDelayFn||(e=>vh((()=>setTimeout((()=>e()),0))));const t=function(e={}){const t=function(e={}){const t=new eu;t.addHooks(e.hooks||{}),e.document=e.document||(mc?document:void 0);const n=!e.document,r=()=>{s.dirty=!0,t.callHook(\"entries:updated\",s)};let a=0,o=[];const i=[],s={plugins:i,dirty:!1,resolvedOptions:e,hooks:t,headEntries:()=>o,use(e){const r=\"function\"==typeof e?e(s):e;r.key&&i.some((e=>e.key===r.key))||(i.push(r),mu(r.mode,n)&&t.addHooks(r.hooks||{}))},push(e,t){delete t?.head;const i={_i:a++,input:e,...t};return mu(i.mode,n)&&(o.push(i),r()),{dispose(){o=o.filter((e=>e._i!==i._i)),r()},patch(e){for(const t of o)t._i===i._i&&(t.input=i.input=e);r()}}},async resolveTags(){const e={tags:[],entries:[...o]};await t.callHook(\"entries:resolve\",e);for(const n of e.entries){const r=n.resolvedInput||n.input;if(n.resolvedInput=await(n.transform?n.transform(r):r),n.resolvedInput)for(const r of await Nc(n)){const a={tag:r,entry:n,resolvedOptions:s.resolvedOptions};await t.callHook(\"tag:normalise\",a),e.tags.push(a.tag)}}return await t.callHook(\"tags:beforeResolve\",e),await t.callHook(\"tags:resolve\",e),await t.callHook(\"tags:afterResolve\",e),e.tags},ssr:n};return[nu,su,au,iu,lu,du,pu,hu,...e?.plugins||[]].forEach((e=>s.use(e))),s.hooks.callHook(\"init\",s),s}(e);return t.use(qc()),fu=t}(e);return t.use(lb),t.install=function(e){return{install(t){ib&&(t.config.globalProperties.$unhead=e,t.config.globalProperties.$head=e,t.provide(cb,e))}}.install}(t),t}const db=\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:{},pb=\"__unhead_injection_handler__\";function hb(e,t={}){const n=t.head||(pb in db?db[pb]():dm(cb)||fu);if(n)return n.ssr?n.push(e,t):function(e,t,n={}){const r=zp(!1),a=zp({});Rm((()=>{a.value=r.value?{}:sb(t)}));const o=e.push(a.value,n);return Lm(a,(e=>{o.patch(e)})),Cg()&&(xf((()=>{o.dispose()})),hf((()=>{r.value=!0})),pf((()=>{r.value=!1}))),o}(n,e,t)}function fb(e){\"function\"==typeof queueMicrotask?queueMicrotask(e):Promise.resolve().then(e).catch((e=>setTimeout((()=>{throw e}))))}function mb(){let e=[],t={addEventListener:(e,n,r,a)=>(e.addEventListener(n,r,a),t.add((()=>e.removeEventListener(n,r,a)))),requestAnimationFrame(...e){let n=requestAnimationFrame(...e);t.add((()=>cancelAnimationFrame(n)))},nextFrame(...e){t.requestAnimationFrame((()=>{t.requestAnimationFrame(...e)}))},setTimeout(...e){let n=setTimeout(...e);t.add((()=>clearTimeout(n)))},microTask(...e){let n={current:!0};return fb((()=>{n.current&&e[0]()})),t.add((()=>{n.current=!1}))},style(e,t,n){let r=e.style.getPropertyValue(t);return Object.assign(e.style,{[t]:n}),this.add((()=>{Object.assign(e.style,{[t]:r})}))},group(e){let t=mb();return e(t),this.add((()=>t.dispose()))},add:t=>(e.push(t),()=>{let n=e.indexOf(t);if(n>=0)for(let t of e.splice(n,1))t()}),dispose(){for(let t of e.splice(0))t()}};return t}var gb;let vb=Symbol(\"headlessui.useid\"),bb=0;const yb=null!=(gb=rf)?gb:function(){return dm(vb,(()=>\"\"+ ++bb))()};function Ob(e){var t;if(null==e||null==e.value)return null;let n=null!=(t=e.value.$el)?t:e.value;return n instanceof Node?n:null}function wb(e,t,...n){if(e in t){let r=t[e];return\"function\"==typeof r?r(...n):r}let r=new Error(`Tried to handle \"${e}\" but there is no handler defined. Only defined handlers are: ${Object.keys(t).map((e=>`\"${e}\"`)).join(\", \")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,wb),r}var xb=Object.defineProperty,kb=(e,t,n)=>(((e,t,n)=>{t in e?xb(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n})(e,\"symbol\"!=typeof t?t+\"\":t,n),n);let Sb=new class{constructor(){kb(this,\"current\",this.detect()),kb(this,\"currentId\",0)}set(e){this.current!==e&&(this.currentId=0,this.current=e)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return\"server\"===this.current}get isClient(){return\"client\"===this.current}detect(){return\"undefined\"==typeof window||\"undefined\"==typeof document?\"server\":\"client\"}};function _b(e){if(Sb.isServer)return null;if(e instanceof Node)return e.ownerDocument;if(null!=e&&e.hasOwnProperty(\"value\")){let t=Ob(e);if(t)return t.ownerDocument}return document}let Tb=[\"[contentEditable=true]\",\"[tabindex]\",\"a[href]\",\"area[href]\",\"button:not([disabled])\",\"iframe\",\"input:not([disabled])\",\"select:not([disabled])\",\"textarea:not([disabled])\"].map((e=>`${e}:not([tabindex='-1'])`)).join(\",\");var Eb,Ab,Cb,Pb=((Cb=Pb||{})[Cb.First=1]=\"First\",Cb[Cb.Previous=2]=\"Previous\",Cb[Cb.Next=4]=\"Next\",Cb[Cb.Last=8]=\"Last\",Cb[Cb.WrapAround=16]=\"WrapAround\",Cb[Cb.NoScroll=32]=\"NoScroll\",Cb),$b=((Ab=$b||{})[Ab.Error=0]=\"Error\",Ab[Ab.Overflow=1]=\"Overflow\",Ab[Ab.Success=2]=\"Success\",Ab[Ab.Underflow=3]=\"Underflow\",Ab),Db=((Eb=Db||{})[Eb.Previous=-1]=\"Previous\",Eb[Eb.Next=1]=\"Next\",Eb);function Ib(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(Tb)).sort(((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER))))}var Mb=(e=>(e[e.Strict=0]=\"Strict\",e[e.Loose=1]=\"Loose\",e))(Mb||{});function Nb(e,t=0){var n;return e!==(null==(n=_b(e))?void 0:n.body)&&wb(t,{0:()=>e.matches(Tb),1(){let t=e;for(;null!==t;){if(t.matches(Tb))return!0;t=t.parentElement}return!1}})}function Rb(e){let t=_b(e);vh((()=>{t&&!Nb(t.activeElement,0)&&Bb(e)}))}var Lb=(e=>(e[e.Keyboard=0]=\"Keyboard\",e[e.Mouse=1]=\"Mouse\",e))(Lb||{});function Bb(e){null==e||e.focus({preventScroll:!0})}\"undefined\"!=typeof window&&\"undefined\"!=typeof document&&(document.addEventListener(\"keydown\",(e=>{e.metaKey||e.altKey||e.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible=\"\")}),!0),document.addEventListener(\"click\",(e=>{1===e.detail?delete document.documentElement.dataset.headlessuiFocusVisible:0===e.detail&&(document.documentElement.dataset.headlessuiFocusVisible=\"\")}),!0));let jb=[\"textarea\",\"input\"].join(\",\");function Ub(e,t=e=>e){return e.slice().sort(((e,n)=>{let r=t(e),a=t(n);if(null===r||null===a)return 0;let o=r.compareDocumentPosition(a);return o&Node.DOCUMENT_POSITION_FOLLOWING?-1:o&Node.DOCUMENT_POSITION_PRECEDING?1:0}))}function zb(e,t,{sorted:n=!0,relativeTo:r=null,skipElements:a=[]}={}){var o;let i=null!=(o=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:null==e?void 0:e.ownerDocument)?o:document,s=Array.isArray(e)?n?Ub(e):e:Ib(e);a.length>0&&s.length>1&&(s=s.filter((e=>!a.includes(e)))),r=null!=r?r:i.activeElement;let l,c=(()=>{if(5&t)return 1;if(10&t)return-1;throw new Error(\"Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last\")})(),u=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,s.indexOf(r))-1;if(4&t)return Math.max(0,s.indexOf(r))+1;if(8&t)return s.length-1;throw new Error(\"Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last\")})(),d=32&t?{preventScroll:!0}:{},p=0,h=s.length;do{if(p>=h||p+h<=0)return 0;let e=u+p;if(16&t)e=(e+h)%h;else{if(e<0)return 3;if(e>=h)return 1}l=s[e],null==l||l.focus(d),p+=c}while(l!==i.activeElement);return 6&t&&function(e){var t,n;return null!=(n=null==(t=null==e?void 0:e.matches)?void 0:t.call(e,jb))&&n}(l)&&l.select(),2}function Fb(){return/iPhone/gi.test(window.navigator.platform)||/Mac/gi.test(window.navigator.platform)&&window.navigator.maxTouchPoints>0}function Qb(e,t,n){Sb.isServer||Rm((r=>{document.addEventListener(e,t,n),r((()=>document.removeEventListener(e,t,n)))}))}function Zb(e,t,n){Sb.isServer||Rm((r=>{window.addEventListener(e,t,n),r((()=>window.removeEventListener(e,t,n)))}))}function Hb(e,t,n=zg((()=>!0))){function r(r,a){if(!n.value||r.defaultPrevented)return;let o=a(r);if(null===o||!o.getRootNode().contains(o))return;let i=function e(t){return\"function\"==typeof t?e(t()):Array.isArray(t)||t instanceof Set?t:[t]}(e);for(let e of i){if(null===e)continue;let t=e instanceof HTMLElement?e:Ob(e);if(null!=t&&t.contains(o)||r.composed&&r.composedPath().includes(t))return}return!Nb(o,Mb.Loose)&&-1!==o.tabIndex&&r.preventDefault(),t(r,o)}let a=zp(null);Qb(\"pointerdown\",(e=>{var t,r;n.value&&(a.value=(null==(r=null==(t=e.composedPath)?void 0:t.call(e))?void 0:r[0])||e.target)}),!0),Qb(\"mousedown\",(e=>{var t,r;n.value&&(a.value=(null==(r=null==(t=e.composedPath)?void 0:t.call(e))?void 0:r[0])||e.target)}),!0),Qb(\"click\",(e=>{Fb()||/Android/gi.test(window.navigator.userAgent)||a.value&&(r(e,(()=>a.value)),a.value=null)}),!0),Qb(\"touchend\",(e=>r(e,(()=>e.target instanceof HTMLElement?e.target:null))),!0),Zb(\"blur\",(e=>r(e,(()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null))),!0)}function Vb(e,t){if(e)return e;let n=null!=t?t:\"button\";return\"string\"==typeof n&&\"button\"===n.toLowerCase()?\"button\":void 0}function qb(e,t){let n=zp(Vb(e.value.type,e.value.as));return yf((()=>{n.value=Vb(e.value.type,e.value.as)})),Rm((()=>{var e;n.value||Ob(t)&&Ob(t)instanceof HTMLButtonElement&&(null==(e=Ob(t))||!e.hasAttribute(\"type\"))&&(n.value=\"button\")})),n}function Wb(e){return[e.screenX,e.screenY]}function Xb(){let e=zp([-1,-1]);return{wasMoved(t){let n=Wb(t);return(e.value[0]!==n[0]||e.value[1]!==n[1])&&(e.value=n,!0)},update(t){e.value=Wb(t)}}}var Gb,Yb=(e=>(e[e.None=0]=\"None\",e[e.RenderStrategy=1]=\"RenderStrategy\",e[e.Static=2]=\"Static\",e))(Yb||{}),Kb=((Gb=Kb||{})[Gb.Unmount=0]=\"Unmount\",Gb[Gb.Hidden=1]=\"Hidden\",Gb);function Jb({visible:e=!0,features:t=0,ourProps:n,theirProps:r,...a}){var o;let i=ny(r,n),s=Object.assign(a,{props:i});return e||2&t&&i.static?ey(s):1&t?wb(null==(o=i.unmount)||o?0:1,{0:()=>null,1:()=>ey({...a,props:{...i,hidden:!0,style:{display:\"none\"}}})}):ey(s)}function ey({props:e,attrs:t,slots:n,slot:r,name:a}){var o,i;let{as:s,...l}=ry(e,[\"unmount\",\"static\"]),c=null==(o=n.default)?void 0:o.call(n,r),u={};if(r){let e=!1,t=[];for(let[n,a]of Object.entries(r))\"boolean\"==typeof a&&(e=!0),!0===a&&t.push(n);e&&(u[\"data-headlessui-state\"]=t.join(\" \"))}if(\"template\"===s){if(c=ty(null!=c?c:[]),Object.keys(l).length>0||Object.keys(t).length>0){let[e,...n]=null!=c?c:[];if(!function(e){return null!=e&&(\"string\"==typeof e.type||\"object\"==typeof e.type||\"function\"==typeof e.type)}(e)||n.length>0)throw new Error(['Passing props on \"template\"!',\"\",`The current component <${a} /> is rendering a \"template\".`,\"However we need to passthrough the following props:\",Object.keys(l).concat(Object.keys(t)).map((e=>e.trim())).filter(((e,t,n)=>n.indexOf(e)===t)).sort(((e,t)=>e.localeCompare(t))).map((e=>` - ${e}`)).join(\"\\n\"),\"\",\"You can apply a few solutions:\",['Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\"Render a single element as the child so that we can forward the props onto that element.\"].map((e=>` - ${e}`)).join(\"\\n\")].join(\"\\n\"));let r=ny(null!=(i=e.props)?i:{},l,u),o=vg(e,r,!0);for(let e in r)e.startsWith(\"on\")&&(o.props||(o.props={}),o.props[e]=r[e]);return o}return Array.isArray(c)&&1===c.length?c[0]:c}return Fg(s,Object.assign({},l,u),{default:()=>c})}function ty(e){return e.flatMap((e=>e.type===Km?ty(e.children):[e]))}function ny(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},n={};for(let r of e)for(let e in r)e.startsWith(\"on\")&&\"function\"==typeof r[e]?(null!=n[e]||(n[e]=[]),n[e].push(r[e])):t[e]=r[e];if(t.disabled||t[\"aria-disabled\"])return Object.assign(t,Object.fromEntries(Object.keys(n).map((e=>[e,void 0]))));for(let e in n)Object.assign(t,{[e](t,...r){let a=n[e];for(let e of a){if(t instanceof Event&&t.defaultPrevented)return;e(t,...r)}}});return t}function ry(e,t=[]){let n=Object.assign({},e);for(let e of t)e in n&&delete n[e];return n}var ay=(e=>(e[e.None=1]=\"None\",e[e.Focusable=2]=\"Focusable\",e[e.Hidden=4]=\"Hidden\",e))(ay||{});let oy=nf({name:\"Hidden\",props:{as:{type:[Object,String],default:\"div\"},features:{type:Number,default:1}},setup:(e,{slots:t,attrs:n})=>()=>{var r;let{features:a,...o}=e;return Jb({ourProps:{\"aria-hidden\":!(2&~a)||(null!=(r=o[\"aria-hidden\"])?r:void 0),hidden:!(4&~a)||void 0,style:{position:\"fixed\",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:\"hidden\",clip:\"rect(0, 0, 0, 0)\",whiteSpace:\"nowrap\",borderWidth:\"0\",...!(4&~a)&&!!(2&~a)&&{display:\"none\"}}},theirProps:o,slot:{},attrs:n,slots:t,name:\"Hidden\"})}}),iy=Symbol(\"Context\");var sy=(e=>(e[e.Open=1]=\"Open\",e[e.Closed=2]=\"Closed\",e[e.Closing=4]=\"Closing\",e[e.Opening=8]=\"Opening\",e))(sy||{});function ly(){return dm(iy,null)}function cy(e){um(iy,e)}var uy,dy=((uy=dy||{}).Space=\" \",uy.Enter=\"Enter\",uy.Escape=\"Escape\",uy.Backspace=\"Backspace\",uy.Delete=\"Delete\",uy.ArrowLeft=\"ArrowLeft\",uy.ArrowUp=\"ArrowUp\",uy.ArrowRight=\"ArrowRight\",uy.ArrowDown=\"ArrowDown\",uy.Home=\"Home\",uy.End=\"End\",uy.PageUp=\"PageUp\",uy.PageDown=\"PageDown\",uy.Tab=\"Tab\",uy);let py=[];!function(){function e(){\"loading\"!==document.readyState&&((()=>{function e(e){e.target instanceof HTMLElement&&e.target!==document.body&&py[0]!==e.target&&(py.unshift(e.target),py=py.filter((e=>null!=e&&e.isConnected)),py.splice(10))}window.addEventListener(\"click\",e,{capture:!0}),window.addEventListener(\"mousedown\",e,{capture:!0}),window.addEventListener(\"focus\",e,{capture:!0}),document.body.addEventListener(\"click\",e,{capture:!0}),document.body.addEventListener(\"mousedown\",e,{capture:!0}),document.body.addEventListener(\"focus\",e,{capture:!0})})(),document.removeEventListener(\"DOMContentLoaded\",e))}\"undefined\"!=typeof window&&\"undefined\"!=typeof document&&(document.addEventListener(\"DOMContentLoaded\",e),e())}();var hy,fy=((hy=fy||{})[hy.First=0]=\"First\",hy[hy.Previous=1]=\"Previous\",hy[hy.Next=2]=\"Next\",hy[hy.Last=3]=\"Last\",hy[hy.Specific=4]=\"Specific\",hy[hy.Nothing=5]=\"Nothing\",hy);function my(e,t){let n=t.resolveItems();if(n.length<=0)return null;let r=t.resolveActiveIndex(),a=null!=r?r:-1;switch(e.focus){case 0:for(let e=0;e=0;--e)if(!t.resolveDisabled(n[e],e,n))return e;return r;case 2:for(let e=a+1;e=0;--e)if(!t.resolveDisabled(n[e],e,n))return e;return r;case 4:for(let r=0;r{(e=null!=e?e:window).addEventListener(t,n,r),a((()=>e.removeEventListener(t,n,r)))}))}var Oy=(e=>(e[e.Forwards=0]=\"Forwards\",e[e.Backwards=1]=\"Backwards\",e))(Oy||{});function wy(){let e=zp(0);return Zb(\"keydown\",(t=>{\"Tab\"===t.key&&(e.value=t.shiftKey?1:0)})),e}function xy(e){if(!e)return new Set;if(\"function\"==typeof e)return new Set(e());let t=new Set;for(let n of e.value){let e=Ob(n);e instanceof HTMLElement&&t.add(e)}return t}var ky=(e=>(e[e.None=1]=\"None\",e[e.InitialFocus=2]=\"InitialFocus\",e[e.TabLock=4]=\"TabLock\",e[e.FocusLock=8]=\"FocusLock\",e[e.RestoreFocus=16]=\"RestoreFocus\",e[e.All=30]=\"All\",e))(ky||{});let Sy=Object.assign(nf({name:\"FocusTrap\",props:{as:{type:[Object,String],default:\"div\"},initialFocus:{type:Object,default:null},features:{type:Number,default:30},containers:{type:[Object,Function],default:zp(new Set)}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:r}){let a=zp(null);r({el:a,$el:a});let o=zg((()=>_b(a))),i=zp(!1);yf((()=>i.value=!0)),kf((()=>i.value=!1)),function({ownerDocument:e},t){let n=function(e){let t=zp(py.slice());return Lm([e],(([e],[n])=>{!0===n&&!1===e?fb((()=>{t.value.splice(0)})):!1===n&&!0===e&&(t.value=py.slice())}),{flush:\"post\"}),()=>{var e;return null!=(e=t.value.find((e=>null!=e&&e.isConnected)))?e:null}}(t);yf((()=>{Rm((()=>{var r,a;t.value||(null==(r=e.value)?void 0:r.activeElement)===(null==(a=e.value)?void 0:a.body)&&Bb(n())}),{flush:\"post\"})})),kf((()=>{t.value&&Bb(n())}))}({ownerDocument:o},zg((()=>i.value&&Boolean(16&e.features))));let s=function({ownerDocument:e,container:t,initialFocus:n},r){let a=zp(null),o=zp(!1);return yf((()=>o.value=!0)),kf((()=>o.value=!1)),yf((()=>{Lm([t,n,r],((i,s)=>{if(i.every(((e,t)=>(null==s?void 0:s[t])===e))||!r.value)return;let l=Ob(t);l&&fb((()=>{var t,r;if(!o.value)return;let i=Ob(n),s=null==(t=e.value)?void 0:t.activeElement;if(i){if(i===s)return void(a.value=s)}else if(l.contains(s))return void(a.value=s);i?Bb(i):zb(l,Pb.First|Pb.NoScroll)===$b.Error&&console.warn(\"There are no focusable elements inside the \"),a.value=null==(r=e.value)?void 0:r.activeElement}))}),{immediate:!0,flush:\"post\"})})),a}({ownerDocument:o,container:a,initialFocus:zg((()=>e.initialFocus))},zg((()=>i.value&&Boolean(2&e.features))));!function({ownerDocument:e,container:t,containers:n,previousActiveElement:r},a){var o;yy(null==(o=e.value)?void 0:o.defaultView,\"focus\",(e=>{if(!a.value)return;let o=xy(n);Ob(t)instanceof HTMLElement&&o.add(Ob(t));let i=r.value;if(!i)return;let s=e.target;s&&s instanceof HTMLElement?_y(o,s)?(r.value=s,Bb(s)):(e.preventDefault(),e.stopPropagation(),Bb(i)):Bb(r.value)}),!0)}({ownerDocument:o,container:a,containers:e.containers,previousActiveElement:s},zg((()=>i.value&&Boolean(8&e.features))));let l=wy();function c(e){let t=Ob(a);t&&wb(l.value,{[Oy.Forwards]:()=>{zb(t,Pb.First,{skipElements:[e.relatedTarget]})},[Oy.Backwards]:()=>{zb(t,Pb.Last,{skipElements:[e.relatedTarget]})}})}let u=zp(!1);function d(e){\"Tab\"===e.key&&(u.value=!0,requestAnimationFrame((()=>{u.value=!1})))}function p(t){if(!i.value)return;let n=xy(e.containers);Ob(a)instanceof HTMLElement&&n.add(Ob(a));let r=t.relatedTarget;r instanceof HTMLElement&&\"true\"!==r.dataset.headlessuiFocusGuard&&(_y(n,r)||(u.value?zb(Ob(a),wb(l.value,{[Oy.Forwards]:()=>Pb.Next,[Oy.Backwards]:()=>Pb.Previous})|Pb.WrapAround,{relativeTo:t.target}):t.target instanceof HTMLElement&&Bb(t.target)))}return()=>{let r={ref:a,onKeydown:d,onFocusout:p},{features:o,initialFocus:i,containers:s,...l}=e;return Fg(Km,[Boolean(4&o)&&Fg(oy,{as:\"button\",type:\"button\",\"data-headlessui-focus-guard\":!0,onFocus:c,features:ay.Focusable}),Jb({ourProps:r,theirProps:{...t,...l},slot:{},attrs:t,slots:n,name:\"FocusTrap\"}),Boolean(4&o)&&Fg(oy,{as:\"button\",type:\"button\",\"data-headlessui-focus-guard\":!0,onFocus:c,features:ay.Focusable})])}}}),{features:ky});function _y(e,t){for(let n of e)if(n.contains(t))return!0;return!1}function Ty(){let e;return{before({doc:t}){var n;let r=t.documentElement;e=(null!=(n=t.defaultView)?n:window).innerWidth-r.clientWidth},after({doc:t,d:n}){let r=t.documentElement,a=r.clientWidth-r.offsetWidth,o=e-a;n.style(r,\"paddingRight\",`${o}px`)}}}function Ey(e){let t={};for(let n of e)Object.assign(t,n(t));return t}let Ay=function(e,t){let n=new Map,r=new Set;return{getSnapshot:()=>n,subscribe:e=>(r.add(e),()=>r.delete(e)),dispatch(e,...a){let o=t[e].call(n,...a);o&&(n=o,r.forEach((e=>e())))}}}(0,{PUSH(e,t){var n;let r=null!=(n=this.get(e))?n:{doc:e,count:0,d:mb(),meta:new Set};return r.count++,r.meta.add(t),this.set(e,r),this},POP(e,t){let n=this.get(e);return n&&(n.count--,n.meta.delete(t)),this},SCROLL_PREVENT({doc:e,d:t,meta:n}){let r={doc:e,d:t,meta:Ey(n)},a=[Fb()?{before({doc:e,d:t,meta:n}){function r(e){return n.containers.flatMap((e=>e())).some((t=>t.contains(e)))}t.microTask((()=>{var n;if(\"auto\"!==window.getComputedStyle(e.documentElement).scrollBehavior){let n=mb();n.style(e.documentElement,\"scrollBehavior\",\"auto\"),t.add((()=>t.microTask((()=>n.dispose()))))}let a=null!=(n=window.scrollY)?n:window.pageYOffset,o=null;t.addEventListener(e,\"click\",(t=>{if(t.target instanceof HTMLElement)try{let n=t.target.closest(\"a\");if(!n)return;let{hash:a}=new URL(n.href),i=e.querySelector(a);i&&!r(i)&&(o=i)}catch{}}),!0),t.addEventListener(e,\"touchstart\",(e=>{if(e.target instanceof HTMLElement)if(r(e.target)){let n=e.target;for(;n.parentElement&&r(n.parentElement);)n=n.parentElement;t.style(n,\"overscrollBehavior\",\"contain\")}else t.style(e.target,\"touchAction\",\"none\")})),t.addEventListener(e,\"touchmove\",(e=>{if(e.target instanceof HTMLElement){if(\"INPUT\"===e.target.tagName)return;if(r(e.target)){let t=e.target;for(;t.parentElement&&\"\"!==t.dataset.headlessuiPortal&&!(t.scrollHeight>t.clientHeight||t.scrollWidth>t.clientWidth);)t=t.parentElement;\"\"===t.dataset.headlessuiPortal&&e.preventDefault()}else e.preventDefault()}}),{passive:!1}),t.add((()=>{var e;let t=null!=(e=window.scrollY)?e:window.pageYOffset;a!==t&&window.scrollTo(0,a),o&&o.isConnected&&(o.scrollIntoView({block:\"nearest\"}),o=null)}))}))}}:{},Ty(),{before({doc:e,d:t}){t.style(e.documentElement,\"overflow\",\"hidden\")}}];a.forEach((({before:e})=>null==e?void 0:e(r))),a.forEach((({after:e})=>null==e?void 0:e(r)))},SCROLL_ALLOW({d:e}){e.dispose()},TEARDOWN({doc:e}){this.delete(e)}});Ay.subscribe((()=>{let e=Ay.getSnapshot(),t=new Map;for(let[n]of e)t.set(n,n.documentElement.style.overflow);for(let n of e.values()){let e=\"hidden\"===t.get(n.doc),r=0!==n.count;(r&&!e||!r&&e)&&Ay.dispatch(n.count>0?\"SCROLL_PREVENT\":\"SCROLL_ALLOW\",n),0===n.count&&Ay.dispatch(\"TEARDOWN\",n)}}));let Cy=new Map,Py=new Map;function $y(e,t=zp(!0)){Rm((n=>{var r;if(!t.value)return;let a=Ob(e);if(!a)return;n((function(){var e;if(!a)return;let t=null!=(e=Py.get(a))?e:1;if(1===t?Py.delete(a):Py.set(a,t-1),1!==t)return;let n=Cy.get(a);n&&(null===n[\"aria-hidden\"]?a.removeAttribute(\"aria-hidden\"):a.setAttribute(\"aria-hidden\",n[\"aria-hidden\"]),a.inert=n.inert,Cy.delete(a))}));let o=null!=(r=Py.get(a))?r:0;Py.set(a,o+1),0===o&&(Cy.set(a,{\"aria-hidden\":a.getAttribute(\"aria-hidden\"),inert:a.inert}),a.setAttribute(\"aria-hidden\",\"true\"),a.inert=!0)}))}function Dy({defaultContainers:e=[],portals:t,mainTreeNodeRef:n}={}){let r=zp(null),a=_b(r);function o(){var n,o,i;let s=[];for(let t of e)null!==t&&(t instanceof HTMLElement?s.push(t):\"value\"in t&&t.value instanceof HTMLElement&&s.push(t.value));if(null!=t&&t.value)for(let e of t.value)s.push(e);for(let e of null!=(n=null==a?void 0:a.querySelectorAll(\"html > *, body > *\"))?n:[])e!==document.body&&e!==document.head&&e instanceof HTMLElement&&\"headlessui-portal-root\"!==e.id&&(e.contains(Ob(r))||e.contains(null==(i=null==(o=Ob(r))?void 0:o.getRootNode())?void 0:i.host)||s.some((t=>e.contains(t)))||s.push(e));return s}return{resolveContainers:o,contains:e=>o().some((t=>t.contains(e))),mainTreeNodeRef:r,MainTreeNode:()=>null!=n?null:Fg(oy,{features:ay.Hidden,ref:r})}}let Iy=Symbol(\"ForcePortalRootContext\"),My=nf({name:\"ForcePortalRoot\",props:{as:{type:[Object,String],default:\"template\"},force:{type:Boolean,default:!1}},setup:(e,{slots:t,attrs:n})=>(um(Iy,e.force),()=>{let{force:r,...a}=e;return Jb({theirProps:a,ourProps:{},slot:{},slots:t,attrs:n,name:\"ForcePortalRoot\"})})}),Ny=Symbol(\"StackContext\");var Ry=(e=>(e[e.Add=0]=\"Add\",e[e.Remove=1]=\"Remove\",e))(Ry||{});let Ly=Symbol(\"DescriptionContext\");const By=new WeakMap;function jy(e,t){let n=t(function(e){var t;return null!=(t=By.get(e))?t:0}(e));return n<=0?By.delete(e):By.set(e,n),n}let Uy=nf({name:\"Portal\",props:{as:{type:[Object,String],default:\"div\"}},setup(e,{slots:t,attrs:n}){let r=zp(null),a=zg((()=>_b(r))),o=dm(Iy,!1),i=dm(Qy,null),s=zp(!0===o||null==i?function(e){let t=_b(e);if(!t){if(null===e)return null;throw new Error(`[Headless UI]: Cannot find ownerDocument for contextElement: ${e}`)}let n=t.getElementById(\"headlessui-portal-root\");if(n)return n;let r=t.createElement(\"div\");return r.setAttribute(\"id\",\"headlessui-portal-root\"),t.body.appendChild(r)}(r.value):i.resolveTarget());s.value&&jy(s.value,(e=>e+1));let l=zp(!1);yf((()=>{l.value=!0})),Rm((()=>{o||null!=i&&(s.value=i.resolveTarget())}));let c=dm(zy,null),u=!1,d=Cg();return Lm(r,(()=>{if(u||!c)return;let e=Ob(r);e&&(kf(c.register(e),d),u=!0)})),kf((()=>{var e,t;let n=null==(e=a.value)?void 0:e.getElementById(\"headlessui-portal-root\");!n||s.value!==n||jy(s.value,(e=>e-1))||s.value.children.length>0||null==(t=s.value.parentElement)||t.removeChild(s.value)})),()=>{if(!l.value||null===s.value)return null;let a={ref:r,\"data-headlessui-portal\":\"\"};return Fg(Uh,{to:s.value},Jb({ourProps:a,theirProps:e,slot:{},attrs:n,slots:t,name:\"Portal\"}))}}}),zy=Symbol(\"PortalParentContext\");function Fy(){let e=dm(zy,null),t=zp([]);function n(n){let r=t.value.indexOf(n);-1!==r&&t.value.splice(r,1),e&&e.unregister(n)}let r={register:function(r){return t.value.push(r),e&&e.register(r),()=>n(r)},unregister:n,portals:t};return[t,nf({name:\"PortalWrapper\",setup:(e,{slots:t})=>(um(zy,r),()=>{var e;return null==(e=t.default)?void 0:e.call(t)})})]}let Qy=Symbol(\"PortalGroupContext\"),Zy=nf({name:\"PortalGroup\",props:{as:{type:[Object,String],default:\"template\"},target:{type:Object,default:null}},setup(e,{attrs:t,slots:n}){let r=Ap({resolveTarget:()=>e.target});return um(Qy,r),()=>{let{target:r,...a}=e;return Jb({theirProps:a,ourProps:{},slot:{},attrs:t,slots:n,name:\"PortalGroup\"})}}});var Hy,Vy=((Hy=Vy||{})[Hy.Open=0]=\"Open\",Hy[Hy.Closed=1]=\"Closed\",Hy);let qy=Symbol(\"DialogContext\");function Wy(e){let t=dm(qy,null);if(null===t){let t=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,Wy),t}return t}let Xy=\"DC8F892D-2EBD-447C-A4C8-A03058436FF4\",Gy=nf({name:\"Dialog\",inheritAttrs:!1,props:{as:{type:[Object,String],default:\"div\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},open:{type:[Boolean,String],default:Xy},initialFocus:{type:Object,default:null},id:{type:String,default:null},role:{type:String,default:\"dialog\"}},emits:{close:e=>!0},setup(e,{emit:t,attrs:n,slots:r,expose:a}){var o,i;let s=null!=(o=e.id)?o:`headlessui-dialog-${yb()}`,l=zp(!1);yf((()=>{l.value=!0}));let c=!1,u=zg((()=>\"dialog\"===e.role||\"alertdialog\"===e.role?e.role:(c||(c=!0,console.warn(`Invalid role [${u}] passed to . Only \\`dialog\\` and and \\`alertdialog\\` are supported. Using \\`dialog\\` instead.`)),\"dialog\"))),d=zp(0),p=ly(),h=zg((()=>e.open===Xy&&null!==p?(p.value&sy.Open)===sy.Open:e.open)),f=zp(null),m=zg((()=>_b(f)));if(a({el:f,$el:f}),e.open===Xy&&null===p)throw new Error(\"You forgot to provide an `open` prop to the `Dialog`.\");if(\"boolean\"!=typeof h.value)throw new Error(`You provided an \\`open\\` prop to the \\`Dialog\\`, but the value is not a boolean. Received: ${h.value===Xy?void 0:e.open}`);let g=zg((()=>l.value&&h.value?0:1)),v=zg((()=>0===g.value)),b=zg((()=>d.value>1)),y=null!==dm(qy,null),[O,w]=Fy(),{resolveContainers:x,mainTreeNodeRef:k,MainTreeNode:S}=Dy({portals:O,defaultContainers:[zg((()=>{var e;return null!=(e=I.panelRef.value)?e:f.value}))]}),_=zg((()=>b.value?\"parent\":\"leaf\")),T=zg((()=>null!==p&&(p.value&sy.Closing)===sy.Closing)),E=zg((()=>!y&&!T.value&&v.value)),A=zg((()=>{var e,t,n;return null!=(n=Array.from(null!=(t=null==(e=m.value)?void 0:e.querySelectorAll(\"body > *\"))?t:[]).find((e=>\"headlessui-portal-root\"!==e.id&&e.contains(Ob(k))&&e instanceof HTMLElement)))?n:null}));$y(A,E);let C=zg((()=>!!b.value||v.value)),P=zg((()=>{var e,t,n;return null!=(n=Array.from(null!=(t=null==(e=m.value)?void 0:e.querySelectorAll(\"[data-headlessui-portal]\"))?t:[]).find((e=>e.contains(Ob(k))&&e instanceof HTMLElement)))?n:null}));$y(P,C),function({type:e,enabled:t,element:n,onUpdate:r}){let a=dm(Ny,(()=>{}));function o(...e){null==r||r(...e),a(...e)}yf((()=>{Lm(t,((t,r)=>{t?o(0,e,n):!0===r&&o(1,e,n)}),{immediate:!0,flush:\"sync\"})})),kf((()=>{t.value&&o(1,e,n)})),um(Ny,o)}({type:\"Dialog\",enabled:zg((()=>0===g.value)),element:f,onUpdate:(e,t)=>{if(\"Dialog\"===t)return wb(e,{[Ry.Add]:()=>d.value+=1,[Ry.Remove]:()=>d.value-=1})}});let $=function({slot:e=zp({}),name:t=\"Description\",props:n={}}={}){let r=zp([]);return um(Ly,{register:function(e){return r.value.push(e),()=>{let t=r.value.indexOf(e);-1!==t&&r.value.splice(t,1)}},slot:e,name:t,props:n}),zg((()=>r.value.length>0?r.value.join(\" \"):void 0))}({name:\"DialogDescription\",slot:zg((()=>({open:h.value})))}),D=zp(null),I={titleId:D,panelRef:zp(null),dialogState:g,setTitleId(e){D.value!==e&&(D.value=e)},close(){t(\"close\",!1)}};um(qy,I);let M=zg((()=>!(!v.value||b.value)));Hb(x,((e,t)=>{e.preventDefault(),I.close(),vh((()=>null==t?void 0:t.focus()))}),M);let N=zg((()=>!(b.value||0!==g.value)));yy(null==(i=m.value)?void 0:i.defaultView,\"keydown\",(e=>{N.value&&(e.defaultPrevented||e.key===dy.Escape&&(e.preventDefault(),e.stopPropagation(),I.close()))}));let R=zg((()=>!(T.value||0!==g.value||y)));return function(e,t,n){let r=function(e){let t=Fp(e.getSnapshot());return kf(e.subscribe((()=>{t.value=e.getSnapshot()}))),t}(Ay),a=zg((()=>{let t=e.value?r.value.get(e.value):void 0;return!!t&&t.count>0}));Lm([e,t],(([e,t],[r],a)=>{if(!e||!t)return;Ay.dispatch(\"PUSH\",e,n);let o=!1;a((()=>{o||(Ay.dispatch(\"POP\",null!=r?r:e,n),o=!0)}))}),{immediate:!0})}(m,R,(e=>{var t;return{containers:[...null!=(t=e.containers)?t:[],x]}})),Rm((e=>{if(0!==g.value)return;let t=Ob(f);if(!t)return;let n=new ResizeObserver((e=>{for(let t of e){let e=t.target.getBoundingClientRect();0===e.x&&0===e.y&&0===e.width&&0===e.height&&I.close()}}));n.observe(t),e((()=>n.disconnect()))})),()=>{let{open:t,initialFocus:a,...o}=e,i={...n,ref:f,id:s,role:u.value,\"aria-modal\":0===g.value||void 0,\"aria-labelledby\":D.value,\"aria-describedby\":$.value},l={open:0===g.value};return Fg(My,{force:!0},(()=>[Fg(Uy,(()=>Fg(Zy,{target:f.value},(()=>Fg(My,{force:!1},(()=>Fg(Sy,{initialFocus:a,containers:x,features:v.value?wb(_.value,{parent:Sy.features.RestoreFocus,leaf:Sy.features.All&~Sy.features.FocusLock}):Sy.features.None},(()=>Fg(w,{},(()=>Jb({ourProps:i,theirProps:{...o,...n},slot:l,attrs:n,slots:r,visible:0===g.value,features:Yb.RenderStrategy|Yb.Static,name:\"Dialog\"}))))))))))),Fg(S)]))}}}),Yy=nf({name:\"DialogPanel\",props:{as:{type:[Object,String],default:\"div\"},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-dialog-panel-${yb()}`,i=Wy(\"DialogPanel\");function s(e){e.stopPropagation()}return r({el:i.panelRef,$el:i.panelRef}),()=>{let{...r}=e;return Jb({ourProps:{id:o,ref:i.panelRef,onClick:s},theirProps:r,slot:{open:0===i.dialogState.value},attrs:t,slots:n,name:\"DialogPanel\"})}}}),Ky=nf({name:\"DialogTitle\",props:{as:{type:[Object,String],default:\"h2\"},id:{type:String,default:null}},setup(e,{attrs:t,slots:n}){var r;let a=null!=(r=e.id)?r:`headlessui-dialog-title-${yb()}`,o=Wy(\"DialogTitle\");return yf((()=>{o.setTitleId(a),kf((()=>o.setTitleId(null)))})),()=>{let{...r}=e;return Jb({ourProps:{id:a},theirProps:r,slot:{open:0===o.dialogState.value},attrs:t,slots:n,name:\"DialogTitle\"})}}});var Jy=(e=>(e[e.Open=0]=\"Open\",e[e.Closed=1]=\"Closed\",e))(Jy||{});let eO=Symbol(\"DisclosureContext\");function tO(e){let t=dm(eO,null);if(null===t){let t=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,tO),t}return t}let nO=Symbol(\"DisclosurePanelContext\"),rO=nf({name:\"Disclosure\",props:{as:{type:[Object,String],default:\"template\"},defaultOpen:{type:[Boolean],default:!1}},setup(e,{slots:t,attrs:n}){let r=zp(e.defaultOpen?0:1),a=zp(null),o=zp(null),i={buttonId:zp(`headlessui-disclosure-button-${yb()}`),panelId:zp(`headlessui-disclosure-panel-${yb()}`),disclosureState:r,panel:a,button:o,toggleDisclosure(){r.value=wb(r.value,{0:1,1:0})},closeDisclosure(){1!==r.value&&(r.value=1)},close(e){i.closeDisclosure();let t=e?e instanceof HTMLElement?e:e.value instanceof HTMLElement?Ob(e):Ob(i.button):Ob(i.button);null==t||t.focus()}};return um(eO,i),cy(zg((()=>wb(r.value,{0:sy.Open,1:sy.Closed})))),()=>{let{defaultOpen:a,...o}=e;return Jb({theirProps:o,ourProps:{},slot:{open:0===r.value,close:i.close},slots:t,attrs:n,name:\"Disclosure\"})}}}),aO=nf({name:\"DisclosureButton\",props:{as:{type:[Object,String],default:\"button\"},disabled:{type:[Boolean],default:!1},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){let a=tO(\"DisclosureButton\"),o=dm(nO,null),i=zg((()=>null!==o&&o.value===a.panelId.value));yf((()=>{i.value||null!==e.id&&(a.buttonId.value=e.id)})),kf((()=>{i.value||(a.buttonId.value=null)}));let s=zp(null);r({el:s,$el:s}),i.value||Rm((()=>{a.button.value=s.value}));let l=qb(zg((()=>({as:e.as,type:t.type}))),s);function c(){var t;e.disabled||(i.value?(a.toggleDisclosure(),null==(t=Ob(a.button))||t.focus()):a.toggleDisclosure())}function u(t){var n;if(!e.disabled)if(i.value)switch(t.key){case dy.Space:case dy.Enter:t.preventDefault(),t.stopPropagation(),a.toggleDisclosure(),null==(n=Ob(a.button))||n.focus()}else switch(t.key){case dy.Space:case dy.Enter:t.preventDefault(),t.stopPropagation(),a.toggleDisclosure()}}function d(e){e.key===dy.Space&&e.preventDefault()}return()=>{var r;let o={open:0===a.disclosureState.value},{id:p,...h}=e;return Jb({ourProps:i.value?{ref:s,type:l.value,onClick:c,onKeydown:u}:{id:null!=(r=a.buttonId.value)?r:p,ref:s,type:l.value,\"aria-expanded\":0===a.disclosureState.value,\"aria-controls\":0===a.disclosureState.value||Ob(a.panel)?a.panelId.value:void 0,disabled:!!e.disabled||void 0,onClick:c,onKeydown:u,onKeyup:d},theirProps:h,slot:o,attrs:t,slots:n,name:\"DisclosureButton\"})}}}),oO=nf({name:\"DisclosurePanel\",props:{as:{type:[Object,String],default:\"div\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){let a=tO(\"DisclosurePanel\");yf((()=>{null!==e.id&&(a.panelId.value=e.id)})),kf((()=>{a.panelId.value=null})),r({el:a.panel,$el:a.panel}),um(nO,a.panelId);let o=ly(),i=zg((()=>null!==o?(o.value&sy.Open)===sy.Open:0===a.disclosureState.value));return()=>{var r;let o={open:0===a.disclosureState.value,close:a.close},{id:s,...l}=e;return Jb({ourProps:{id:null!=(r=a.panelId.value)?r:s,ref:a.panel},theirProps:l,slot:o,attrs:t,slots:n,features:Yb.RenderStrategy|Yb.Static,visible:i.value,name:\"DisclosurePanel\"})}}}),iO=/([\\u2700-\\u27BF]|[\\uE000-\\uF8FF]|\\uD83C[\\uDC00-\\uDFFF]|\\uD83D[\\uDC00-\\uDFFF]|[\\u2011-\\u26FF]|\\uD83E[\\uDD10-\\uDDFF])/g;function sO(e){var t,n;let r=null!=(t=e.innerText)?t:\"\",a=e.cloneNode(!0);if(!(a instanceof HTMLElement))return r;let o=!1;for(let e of a.querySelectorAll('[hidden],[aria-hidden],[role=\"img\"]'))e.remove(),o=!0;let i=o?null!=(n=a.innerText)?n:\"\":r;return iO.test(i)&&(i=i.replace(iO,\"\")),i}function lO(e){let t=zp(\"\"),n=zp(\"\");return()=>{let r=Ob(e);if(!r)return\"\";let a=r.innerText;if(t.value===a)return n.value;let o=function(e){let t=e.getAttribute(\"aria-label\");if(\"string\"==typeof t)return t.trim();let n=e.getAttribute(\"aria-labelledby\");if(n){let e=n.split(\" \").map((e=>{let t=document.getElementById(e);if(t){let e=t.getAttribute(\"aria-label\");return\"string\"==typeof e?e.trim():sO(t).trim()}return null})).filter(Boolean);if(e.length>0)return e.join(\", \")}return sO(e).trim()}(r).trim().toLowerCase();return t.value=a,n.value=o,o}}function cO(e,t){return e===t}var uO=(e=>(e[e.Open=0]=\"Open\",e[e.Closed=1]=\"Closed\",e))(uO||{}),dO=(e=>(e[e.Single=0]=\"Single\",e[e.Multi=1]=\"Multi\",e))(dO||{}),pO=(e=>(e[e.Pointer=0]=\"Pointer\",e[e.Other=1]=\"Other\",e))(pO||{});let hO=Symbol(\"ListboxContext\");function fO(e){let t=dm(hO,null);if(null===t){let t=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,fO),t}return t}let mO=nf({name:\"Listbox\",emits:{\"update:modelValue\":e=>!0},props:{as:{type:[Object,String],default:\"template\"},disabled:{type:[Boolean],default:!1},by:{type:[String,Function],default:()=>cO},horizontal:{type:[Boolean],default:!1},modelValue:{type:[Object,String,Number,Boolean],default:void 0},defaultValue:{type:[Object,String,Number,Boolean],default:void 0},form:{type:String,optional:!0},name:{type:String,optional:!0},multiple:{type:[Boolean],default:!1}},inheritAttrs:!1,setup(e,{slots:t,attrs:n,emit:r}){let a=zp(1),o=zp(null),i=zp(null),s=zp(null),l=zp([]),c=zp(\"\"),u=zp(null),d=zp(1);function p(e=e=>e){let t=null!==u.value?l.value[u.value]:null,n=Ub(e(l.value.slice()),(e=>Ob(e.dataRef.domRef))),r=t?n.indexOf(t):null;return-1===r&&(r=null),{options:n,activeOptionIndex:r}}let h=zg((()=>e.multiple?1:0)),[f,m]=function(e,t,n){let r=zp(null==n?void 0:n.value),a=zg((()=>void 0!==e.value));return[zg((()=>a.value?e.value:r.value)),function(e){return a.value||(r.value=e),null==t?void 0:t(e)}]}(zg((()=>e.modelValue)),(e=>r(\"update:modelValue\",e)),zg((()=>e.defaultValue))),g=zg((()=>void 0===f.value?wb(h.value,{1:[],0:void 0}):f.value)),v={listboxState:a,value:g,mode:h,compare(t,n){if(\"string\"==typeof e.by){let r=e.by;return(null==t?void 0:t[r])===(null==n?void 0:n[r])}return e.by(t,n)},orientation:zg((()=>e.horizontal?\"horizontal\":\"vertical\")),labelRef:o,buttonRef:i,optionsRef:s,disabled:zg((()=>e.disabled)),options:l,searchQuery:c,activeOptionIndex:u,activationTrigger:d,closeListbox(){e.disabled||1!==a.value&&(a.value=1,u.value=null)},openListbox(){e.disabled||0!==a.value&&(a.value=0)},goToOption(t,n,r){if(e.disabled||1===a.value)return;let o=p(),i=my(t===fy.Specific?{focus:fy.Specific,id:n}:{focus:t},{resolveItems:()=>o.options,resolveActiveIndex:()=>o.activeOptionIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.disabled});c.value=\"\",u.value=i,d.value=null!=r?r:1,l.value=o.options},search(t){if(e.disabled||1===a.value)return;let n=\"\"!==c.value?0:1;c.value+=t.toLowerCase();let r=(null!==u.value?l.value.slice(u.value+n).concat(l.value.slice(0,u.value+n)):l.value).find((e=>e.dataRef.textValue.startsWith(c.value)&&!e.dataRef.disabled)),o=r?l.value.indexOf(r):-1;-1===o||o===u.value||(u.value=o,d.value=1)},clearSearch(){e.disabled||1!==a.value&&\"\"!==c.value&&(c.value=\"\")},registerOption(e,t){let n=p((n=>[...n,{id:e,dataRef:t}]));l.value=n.options,u.value=n.activeOptionIndex},unregisterOption(e){let t=p((t=>{let n=t.findIndex((t=>t.id===e));return-1!==n&&t.splice(n,1),t}));l.value=t.options,u.value=t.activeOptionIndex,d.value=1},theirOnChange(t){e.disabled||m(t)},select(t){e.disabled||m(wb(h.value,{0:()=>t,1:()=>{let e=Rp(v.value.value).slice(),n=Rp(t),r=e.findIndex((e=>v.compare(n,Rp(e))));return-1===r?e.push(n):e.splice(r,1),e}}))}};Hb([i,s],((e,t)=>{var n;v.closeListbox(),Nb(t,Mb.Loose)||(e.preventDefault(),null==(n=Ob(i))||n.focus())}),zg((()=>0===a.value))),um(hO,v),cy(zg((()=>wb(a.value,{0:sy.Open,1:sy.Closed}))));let b=zg((()=>{var e;return null==(e=Ob(i))?void 0:e.closest(\"form\")}));return yf((()=>{Lm([b],(()=>{if(b.value&&void 0!==e.defaultValue)return b.value.addEventListener(\"reset\",t),()=>{var e;null==(e=b.value)||e.removeEventListener(\"reset\",t)};function t(){v.theirOnChange(e.defaultValue)}}),{immediate:!0})})),()=>{let{name:r,modelValue:o,disabled:i,form:s,...l}=e,c={open:0===a.value,disabled:i,value:g.value};return Fg(Km,[...null!=r&&null!=g.value?gy({[r]:g.value}).map((([e,t])=>Fg(oy,function(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}({features:ay.Hidden,key:e,as:\"input\",type:\"hidden\",hidden:!0,readOnly:!0,form:s,disabled:i,name:e,value:t})))):[],Jb({ourProps:{},theirProps:{...n,...ry(l,[\"defaultValue\",\"onUpdate:modelValue\",\"horizontal\",\"multiple\",\"by\"])},slot:c,slots:t,attrs:n,name:\"Listbox\"})])}}}),gO=nf({name:\"ListboxLabel\",props:{as:{type:[Object,String],default:\"label\"},id:{type:String,default:null}},setup(e,{attrs:t,slots:n}){var r;let a=null!=(r=e.id)?r:`headlessui-listbox-label-${yb()}`,o=fO(\"ListboxLabel\");function i(){var e;null==(e=Ob(o.buttonRef))||e.focus({preventScroll:!0})}return()=>{let r={open:0===o.listboxState.value,disabled:o.disabled.value},{...s}=e;return Jb({ourProps:{id:a,ref:o.labelRef,onClick:i},theirProps:s,slot:r,attrs:t,slots:n,name:\"ListboxLabel\"})}}}),vO=nf({name:\"ListboxButton\",props:{as:{type:[Object,String],default:\"button\"},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-listbox-button-${yb()}`,i=fO(\"ListboxButton\");function s(e){switch(e.key){case dy.Space:case dy.Enter:case dy.ArrowDown:e.preventDefault(),i.openListbox(),vh((()=>{var e;null==(e=Ob(i.optionsRef))||e.focus({preventScroll:!0}),i.value.value||i.goToOption(fy.First)}));break;case dy.ArrowUp:e.preventDefault(),i.openListbox(),vh((()=>{var e;null==(e=Ob(i.optionsRef))||e.focus({preventScroll:!0}),i.value.value||i.goToOption(fy.Last)}))}}function l(e){e.key===dy.Space&&e.preventDefault()}function c(e){i.disabled.value||(0===i.listboxState.value?(i.closeListbox(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(e.preventDefault(),i.openListbox(),function(e){requestAnimationFrame((()=>requestAnimationFrame(e)))}((()=>{var e;return null==(e=Ob(i.optionsRef))?void 0:e.focus({preventScroll:!0})}))))}r({el:i.buttonRef,$el:i.buttonRef});let u=qb(zg((()=>({as:e.as,type:t.type}))),i.buttonRef);return()=>{var r,a;let d={open:0===i.listboxState.value,disabled:i.disabled.value,value:i.value.value},{...p}=e;return Jb({ourProps:{ref:i.buttonRef,id:o,type:u.value,\"aria-haspopup\":\"listbox\",\"aria-controls\":null==(r=Ob(i.optionsRef))?void 0:r.id,\"aria-expanded\":0===i.listboxState.value,\"aria-labelledby\":i.labelRef.value?[null==(a=Ob(i.labelRef))?void 0:a.id,o].join(\" \"):void 0,disabled:!0===i.disabled.value||void 0,onKeydown:s,onKeyup:l,onClick:c},theirProps:p,slot:d,attrs:t,slots:n,name:\"ListboxButton\"})}}}),bO=nf({name:\"ListboxOptions\",props:{as:{type:[Object,String],default:\"ul\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-listbox-options-${yb()}`,i=fO(\"ListboxOptions\"),s=zp(null);function l(e){switch(s.value&&clearTimeout(s.value),e.key){case dy.Space:if(\"\"!==i.searchQuery.value)return e.preventDefault(),e.stopPropagation(),i.search(e.key);case dy.Enter:if(e.preventDefault(),e.stopPropagation(),null!==i.activeOptionIndex.value){let e=i.options.value[i.activeOptionIndex.value];i.select(e.dataRef.value)}0===i.mode.value&&(i.closeListbox(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})})));break;case wb(i.orientation.value,{vertical:dy.ArrowDown,horizontal:dy.ArrowRight}):return e.preventDefault(),e.stopPropagation(),i.goToOption(fy.Next);case wb(i.orientation.value,{vertical:dy.ArrowUp,horizontal:dy.ArrowLeft}):return e.preventDefault(),e.stopPropagation(),i.goToOption(fy.Previous);case dy.Home:case dy.PageUp:return e.preventDefault(),e.stopPropagation(),i.goToOption(fy.First);case dy.End:case dy.PageDown:return e.preventDefault(),e.stopPropagation(),i.goToOption(fy.Last);case dy.Escape:e.preventDefault(),e.stopPropagation(),i.closeListbox(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})}));break;case dy.Tab:e.preventDefault(),e.stopPropagation();break;default:1===e.key.length&&(i.search(e.key),s.value=setTimeout((()=>i.clearSearch()),350))}}r({el:i.optionsRef,$el:i.optionsRef});let c=ly(),u=zg((()=>null!==c?(c.value&sy.Open)===sy.Open:0===i.listboxState.value));return()=>{var r,a;let s={open:0===i.listboxState.value},{...c}=e;return Jb({ourProps:{\"aria-activedescendant\":null===i.activeOptionIndex.value||null==(r=i.options.value[i.activeOptionIndex.value])?void 0:r.id,\"aria-multiselectable\":1===i.mode.value||void 0,\"aria-labelledby\":null==(a=Ob(i.buttonRef))?void 0:a.id,\"aria-orientation\":i.orientation.value,id:o,onKeydown:l,role:\"listbox\",tabIndex:0,ref:i.optionsRef},theirProps:c,slot:s,attrs:t,slots:n,features:Yb.RenderStrategy|Yb.Static,visible:u.value,name:\"ListboxOptions\"})}}}),yO=nf({name:\"ListboxOption\",props:{as:{type:[Object,String],default:\"li\"},value:{type:[Object,String,Number,Boolean]},disabled:{type:Boolean,default:!1},id:{type:String,default:null}},setup(e,{slots:t,attrs:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-listbox-option-${yb()}`,i=fO(\"ListboxOption\"),s=zp(null);r({el:s,$el:s});let l=zg((()=>null!==i.activeOptionIndex.value&&i.options.value[i.activeOptionIndex.value].id===o)),c=zg((()=>wb(i.mode.value,{0:()=>i.compare(Rp(i.value.value),Rp(e.value)),1:()=>Rp(i.value.value).some((t=>i.compare(Rp(t),Rp(e.value))))}))),u=zg((()=>wb(i.mode.value,{1:()=>{var e;let t=Rp(i.value.value);return(null==(e=i.options.value.find((e=>t.some((t=>i.compare(Rp(t),Rp(e.dataRef.value)))))))?void 0:e.id)===o},0:()=>c.value}))),d=lO(s),p=zg((()=>({disabled:e.disabled,value:e.value,get textValue(){return d()},domRef:s})));function h(t){if(e.disabled)return t.preventDefault();i.select(e.value),0===i.mode.value&&(i.closeListbox(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})})))}function f(){if(e.disabled)return i.goToOption(fy.Nothing);i.goToOption(fy.Specific,o)}yf((()=>i.registerOption(o,p))),kf((()=>i.unregisterOption(o))),yf((()=>{Lm([i.listboxState,c],(()=>{0===i.listboxState.value&&c.value&&wb(i.mode.value,{1:()=>{u.value&&i.goToOption(fy.Specific,o)},0:()=>{i.goToOption(fy.Specific,o)}})}),{immediate:!0})})),Rm((()=>{0===i.listboxState.value&&l.value&&0!==i.activationTrigger.value&&vh((()=>{var e,t;return null==(t=null==(e=Ob(s))?void 0:e.scrollIntoView)?void 0:t.call(e,{block:\"nearest\"})}))}));let m=Xb();function g(e){m.update(e)}function v(t){m.wasMoved(t)&&(e.disabled||l.value||i.goToOption(fy.Specific,o,0))}function b(t){m.wasMoved(t)&&(e.disabled||l.value&&i.goToOption(fy.Nothing))}return()=>{let{disabled:r}=e,a={active:l.value,selected:c.value,disabled:r},{value:i,disabled:u,...d}=e;return Jb({ourProps:{id:o,ref:s,role:\"option\",tabIndex:!0===r?void 0:-1,\"aria-disabled\":!0===r||void 0,\"aria-selected\":c.value,disabled:void 0,onClick:h,onFocus:f,onPointerenter:g,onMouseenter:g,onPointermove:v,onMousemove:v,onPointerleave:b,onMouseleave:b},theirProps:d,slot:a,attrs:n,slots:t,name:\"ListboxOption\"})}}});var OO=(e=>(e[e.Open=0]=\"Open\",e[e.Closed=1]=\"Closed\",e))(OO||{}),wO=(e=>(e[e.Pointer=0]=\"Pointer\",e[e.Other=1]=\"Other\",e))(wO||{});let xO=Symbol(\"MenuContext\");function kO(e){let t=dm(xO,null);if(null===t){let t=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,kO),t}return t}let SO=nf({name:\"Menu\",props:{as:{type:[Object,String],default:\"template\"}},setup(e,{slots:t,attrs:n}){let r=zp(1),a=zp(null),o=zp(null),i=zp([]),s=zp(\"\"),l=zp(null),c=zp(1);function u(e=e=>e){let t=null!==l.value?i.value[l.value]:null,n=Ub(e(i.value.slice()),(e=>Ob(e.dataRef.domRef))),r=t?n.indexOf(t):null;return-1===r&&(r=null),{items:n,activeItemIndex:r}}let d={menuState:r,buttonRef:a,itemsRef:o,items:i,searchQuery:s,activeItemIndex:l,activationTrigger:c,closeMenu:()=>{r.value=1,l.value=null},openMenu:()=>r.value=0,goToItem(e,t,n){let r=u(),a=my(e===fy.Specific?{focus:fy.Specific,id:t}:{focus:e},{resolveItems:()=>r.items,resolveActiveIndex:()=>r.activeItemIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.disabled});s.value=\"\",l.value=a,c.value=null!=n?n:1,i.value=r.items},search(e){let t=\"\"!==s.value?0:1;s.value+=e.toLowerCase();let n=(null!==l.value?i.value.slice(l.value+t).concat(i.value.slice(0,l.value+t)):i.value).find((e=>e.dataRef.textValue.startsWith(s.value)&&!e.dataRef.disabled)),r=n?i.value.indexOf(n):-1;-1===r||r===l.value||(l.value=r,c.value=1)},clearSearch(){s.value=\"\"},registerItem(e,t){let n=u((n=>[...n,{id:e,dataRef:t}]));i.value=n.items,l.value=n.activeItemIndex,c.value=1},unregisterItem(e){let t=u((t=>{let n=t.findIndex((t=>t.id===e));return-1!==n&&t.splice(n,1),t}));i.value=t.items,l.value=t.activeItemIndex,c.value=1}};return Hb([a,o],((e,t)=>{var n;d.closeMenu(),Nb(t,Mb.Loose)||(e.preventDefault(),null==(n=Ob(a))||n.focus())}),zg((()=>0===r.value))),um(xO,d),cy(zg((()=>wb(r.value,{0:sy.Open,1:sy.Closed})))),()=>{let a={open:0===r.value,close:d.closeMenu};return Jb({ourProps:{},theirProps:e,slot:a,slots:t,attrs:n,name:\"Menu\"})}}}),_O=nf({name:\"MenuButton\",props:{disabled:{type:Boolean,default:!1},as:{type:[Object,String],default:\"button\"},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-menu-button-${yb()}`,i=kO(\"MenuButton\");function s(e){switch(e.key){case dy.Space:case dy.Enter:case dy.ArrowDown:e.preventDefault(),e.stopPropagation(),i.openMenu(),vh((()=>{var e;null==(e=Ob(i.itemsRef))||e.focus({preventScroll:!0}),i.goToItem(fy.First)}));break;case dy.ArrowUp:e.preventDefault(),e.stopPropagation(),i.openMenu(),vh((()=>{var e;null==(e=Ob(i.itemsRef))||e.focus({preventScroll:!0}),i.goToItem(fy.Last)}))}}function l(e){e.key===dy.Space&&e.preventDefault()}function c(t){e.disabled||(0===i.menuState.value?(i.closeMenu(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(t.preventDefault(),i.openMenu(),function(e){requestAnimationFrame((()=>requestAnimationFrame(e)))}((()=>{var e;return null==(e=Ob(i.itemsRef))?void 0:e.focus({preventScroll:!0})}))))}r({el:i.buttonRef,$el:i.buttonRef});let u=qb(zg((()=>({as:e.as,type:t.type}))),i.buttonRef);return()=>{var r;let a={open:0===i.menuState.value},{...d}=e;return Jb({ourProps:{ref:i.buttonRef,id:o,type:u.value,\"aria-haspopup\":\"menu\",\"aria-controls\":null==(r=Ob(i.itemsRef))?void 0:r.id,\"aria-expanded\":0===i.menuState.value,onKeydown:s,onKeyup:l,onClick:c},theirProps:d,slot:a,attrs:t,slots:n,name:\"MenuButton\"})}}}),TO=nf({name:\"MenuItems\",props:{as:{type:[Object,String],default:\"div\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-menu-items-${yb()}`,i=kO(\"MenuItems\"),s=zp(null);function l(e){var t;switch(s.value&&clearTimeout(s.value),e.key){case dy.Space:if(\"\"!==i.searchQuery.value)return e.preventDefault(),e.stopPropagation(),i.search(e.key);case dy.Enter:e.preventDefault(),e.stopPropagation(),null!==i.activeItemIndex.value&&(null==(t=Ob(i.items.value[i.activeItemIndex.value].dataRef.domRef))||t.click()),i.closeMenu(),Rb(Ob(i.buttonRef));break;case dy.ArrowDown:return e.preventDefault(),e.stopPropagation(),i.goToItem(fy.Next);case dy.ArrowUp:return e.preventDefault(),e.stopPropagation(),i.goToItem(fy.Previous);case dy.Home:case dy.PageUp:return e.preventDefault(),e.stopPropagation(),i.goToItem(fy.First);case dy.End:case dy.PageDown:return e.preventDefault(),e.stopPropagation(),i.goToItem(fy.Last);case dy.Escape:e.preventDefault(),e.stopPropagation(),i.closeMenu(),vh((()=>{var e;return null==(e=Ob(i.buttonRef))?void 0:e.focus({preventScroll:!0})}));break;case dy.Tab:e.preventDefault(),e.stopPropagation(),i.closeMenu(),vh((()=>function(e,t){return zb(Ib(),t,{relativeTo:e})}(Ob(i.buttonRef),e.shiftKey?Pb.Previous:Pb.Next)));break;default:1===e.key.length&&(i.search(e.key),s.value=setTimeout((()=>i.clearSearch()),350))}}function c(e){e.key===dy.Space&&e.preventDefault()}r({el:i.itemsRef,$el:i.itemsRef}),function({container:e,accept:t,walk:n,enabled:r}){Rm((()=>{let a=e.value;if(!a||void 0!==r&&!r.value)return;let o=_b(e);if(!o)return;let i=Object.assign((e=>t(e)),{acceptNode:t}),s=o.createTreeWalker(a,NodeFilter.SHOW_ELEMENT,i,!1);for(;s.nextNode();)n(s.currentNode)}))}({container:zg((()=>Ob(i.itemsRef))),enabled:zg((()=>0===i.menuState.value)),accept:e=>\"menuitem\"===e.getAttribute(\"role\")?NodeFilter.FILTER_REJECT:e.hasAttribute(\"role\")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT,walk(e){e.setAttribute(\"role\",\"none\")}});let u=ly(),d=zg((()=>null!==u?(u.value&sy.Open)===sy.Open:0===i.menuState.value));return()=>{var r,a;let s={open:0===i.menuState.value},{...u}=e;return Jb({ourProps:{\"aria-activedescendant\":null===i.activeItemIndex.value||null==(r=i.items.value[i.activeItemIndex.value])?void 0:r.id,\"aria-labelledby\":null==(a=Ob(i.buttonRef))?void 0:a.id,id:o,onKeydown:l,onKeyup:c,role:\"menu\",tabIndex:0,ref:i.itemsRef},theirProps:u,slot:s,attrs:t,slots:n,features:Yb.RenderStrategy|Yb.Static,visible:d.value,name:\"MenuItems\"})}}}),EO=nf({name:\"MenuItem\",inheritAttrs:!1,props:{as:{type:[Object,String],default:\"template\"},disabled:{type:Boolean,default:!1},id:{type:String,default:null}},setup(e,{slots:t,attrs:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-menu-item-${yb()}`,i=kO(\"MenuItem\"),s=zp(null);r({el:s,$el:s});let l=zg((()=>null!==i.activeItemIndex.value&&i.items.value[i.activeItemIndex.value].id===o)),c=lO(s),u=zg((()=>({disabled:e.disabled,get textValue(){return c()},domRef:s})));function d(t){if(e.disabled)return t.preventDefault();i.closeMenu(),Rb(Ob(i.buttonRef))}function p(){if(e.disabled)return i.goToItem(fy.Nothing);i.goToItem(fy.Specific,o)}yf((()=>i.registerItem(o,u))),kf((()=>i.unregisterItem(o))),Rm((()=>{0===i.menuState.value&&l.value&&0!==i.activationTrigger.value&&vh((()=>{var e,t;return null==(t=null==(e=Ob(s))?void 0:e.scrollIntoView)?void 0:t.call(e,{block:\"nearest\"})}))}));let h=Xb();function f(e){h.update(e)}function m(t){h.wasMoved(t)&&(e.disabled||l.value||i.goToItem(fy.Specific,o,0))}function g(t){h.wasMoved(t)&&(e.disabled||l.value&&i.goToItem(fy.Nothing))}return()=>{let{disabled:r,...a}=e,c={active:l.value,disabled:r,close:i.closeMenu};return Jb({ourProps:{id:o,ref:s,role:\"menuitem\",tabIndex:!0===r?void 0:-1,\"aria-disabled\":!0===r||void 0,onClick:d,onFocus:p,onPointerenter:f,onMouseenter:f,onPointermove:m,onMousemove:m,onPointerleave:g,onMouseleave:g},theirProps:{...n,...a},slot:c,attrs:n,slots:t,name:\"MenuItem\"})}}});var AO,CO=((AO=CO||{})[AO.Open=0]=\"Open\",AO[AO.Closed=1]=\"Closed\",AO);let PO=Symbol(\"PopoverContext\");function $O(e){let t=dm(PO,null);if(null===t){let t=new Error(`<${e} /> is missing a parent <${NO.name} /> component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,$O),t}return t}let DO=Symbol(\"PopoverGroupContext\");function IO(){return dm(DO,null)}let MO=Symbol(\"PopoverPanelContext\"),NO=nf({name:\"Popover\",inheritAttrs:!1,props:{as:{type:[Object,String],default:\"div\"}},setup(e,{slots:t,attrs:n,expose:r}){var a;let o=zp(null);r({el:o,$el:o});let i=zp(1),s=zp(null),l=zp(null),c=zp(null),u=zp(null),d=zg((()=>_b(o))),p=zg((()=>{var e,t;if(!Ob(s)||!Ob(u))return!1;for(let e of document.querySelectorAll(\"body > *\"))if(Number(null==e?void 0:e.contains(Ob(s)))^Number(null==e?void 0:e.contains(Ob(u))))return!0;let n=Ib(),r=n.indexOf(Ob(s)),a=(r+n.length-1)%n.length,o=(r+1)%n.length,i=n[a],l=n[o];return!(null!=(e=Ob(u))&&e.contains(i)||null!=(t=Ob(u))&&t.contains(l))})),h={popoverState:i,buttonId:zp(null),panelId:zp(null),panel:u,button:s,isPortalled:p,beforePanelSentinel:l,afterPanelSentinel:c,togglePopover(){i.value=wb(i.value,{0:1,1:0})},closePopover(){1!==i.value&&(i.value=1)},close(e){h.closePopover();let t=e?e instanceof HTMLElement?e:e.value instanceof HTMLElement?Ob(e):Ob(h.button):Ob(h.button);null==t||t.focus()}};um(PO,h),cy(zg((()=>wb(i.value,{0:sy.Open,1:sy.Closed}))));let f={buttonId:h.buttonId,panelId:h.panelId,close(){h.closePopover()}},m=IO(),g=null==m?void 0:m.registerPopover,[v,b]=Fy(),y=Dy({mainTreeNodeRef:null==m?void 0:m.mainTreeNodeRef,portals:v,defaultContainers:[s,u]});return Rm((()=>null==g?void 0:g(f))),yy(null==(a=d.value)?void 0:a.defaultView,\"focus\",(e=>{var t,n;e.target!==window&&e.target instanceof HTMLElement&&0===i.value&&(function(){var e,t,n,r;return null!=(r=null==m?void 0:m.isFocusWithinPopoverGroup())?r:(null==(e=d.value)?void 0:e.activeElement)&&((null==(t=Ob(s))?void 0:t.contains(d.value.activeElement))||(null==(n=Ob(u))?void 0:n.contains(d.value.activeElement)))}()||s&&u&&(y.contains(e.target)||null!=(t=Ob(h.beforePanelSentinel))&&t.contains(e.target)||null!=(n=Ob(h.afterPanelSentinel))&&n.contains(e.target)||h.closePopover()))}),!0),Hb(y.resolveContainers,((e,t)=>{var n;h.closePopover(),Nb(t,Mb.Loose)||(e.preventDefault(),null==(n=Ob(s))||n.focus())}),zg((()=>0===i.value))),()=>{let r={open:0===i.value,close:h.close};return Fg(Km,[Fg(b,{},(()=>Jb({theirProps:{...e,...n},ourProps:{ref:o},slot:r,slots:t,attrs:n,name:\"Popover\"}))),Fg(y.MainTreeNode)])}}}),RO=nf({name:\"PopoverButton\",props:{as:{type:[Object,String],default:\"button\"},disabled:{type:[Boolean],default:!1},id:{type:String,default:null}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-popover-button-${yb()}`,i=$O(\"PopoverButton\"),s=zg((()=>_b(i.button)));r({el:i.button,$el:i.button}),yf((()=>{i.buttonId.value=o})),kf((()=>{i.buttonId.value=null}));let l=IO(),c=null==l?void 0:l.closeOthers,u=dm(MO,null),d=zg((()=>null!==u&&u.value===i.panelId.value)),p=zp(null),h=`headlessui-focus-sentinel-${yb()}`;d.value||Rm((()=>{i.button.value=Ob(p)}));let f=qb(zg((()=>({as:e.as,type:t.type}))),p);function m(e){var t,n,r,a,o;if(d.value){if(1===i.popoverState.value)return;switch(e.key){case dy.Space:case dy.Enter:e.preventDefault(),null==(n=(t=e.target).click)||n.call(t),i.closePopover(),null==(r=Ob(i.button))||r.focus()}}else switch(e.key){case dy.Space:case dy.Enter:e.preventDefault(),e.stopPropagation(),1===i.popoverState.value&&(null==c||c(i.buttonId.value)),i.togglePopover();break;case dy.Escape:if(0!==i.popoverState.value)return null==c?void 0:c(i.buttonId.value);if(!Ob(i.button)||null!=(a=s.value)&&a.activeElement&&(null==(o=Ob(i.button))||!o.contains(s.value.activeElement)))return;e.preventDefault(),e.stopPropagation(),i.closePopover()}}function g(e){d.value||e.key===dy.Space&&e.preventDefault()}function v(t){var n,r;e.disabled||(d.value?(i.closePopover(),null==(n=Ob(i.button))||n.focus()):(t.preventDefault(),t.stopPropagation(),1===i.popoverState.value&&(null==c||c(i.buttonId.value)),i.togglePopover(),null==(r=Ob(i.button))||r.focus()))}function b(e){e.preventDefault(),e.stopPropagation()}let y=wy();function O(){let e=Ob(i.panel);e&&wb(y.value,{[Oy.Forwards]:()=>zb(e,Pb.First),[Oy.Backwards]:()=>zb(e,Pb.Last)})===$b.Error&&zb(Ib().filter((e=>\"true\"!==e.dataset.headlessuiFocusGuard)),wb(y.value,{[Oy.Forwards]:Pb.Next,[Oy.Backwards]:Pb.Previous}),{relativeTo:Ob(i.button)})}return()=>{let r=0===i.popoverState.value,a={open:r},{...s}=e,l=d.value?{ref:p,type:f.value,onKeydown:m,onClick:v}:{ref:p,id:o,type:f.value,\"aria-expanded\":0===i.popoverState.value,\"aria-controls\":Ob(i.panel)?i.panelId.value:void 0,disabled:!!e.disabled||void 0,onKeydown:m,onKeyup:g,onClick:v,onMousedown:b};return Fg(Km,[Jb({ourProps:l,theirProps:{...t,...s},slot:a,attrs:t,slots:n,name:\"PopoverButton\"}),r&&!d.value&&i.isPortalled.value&&Fg(oy,{id:h,features:ay.Focusable,\"data-headlessui-focus-guard\":!0,as:\"button\",type:\"button\",onFocus:O})])}}}),LO=nf({name:\"PopoverPanel\",props:{as:{type:[Object,String],default:\"div\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},focus:{type:Boolean,default:!1},id:{type:String,default:null}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-popover-panel-${yb()}`,{focus:i}=e,s=$O(\"PopoverPanel\"),l=zg((()=>_b(s.panel))),c=`headlessui-focus-sentinel-before-${yb()}`,u=`headlessui-focus-sentinel-after-${yb()}`;r({el:s.panel,$el:s.panel}),yf((()=>{s.panelId.value=o})),kf((()=>{s.panelId.value=null})),um(MO,s.panelId),Rm((()=>{var e,t;if(!i||0!==s.popoverState.value||!s.panel)return;let n=null==(e=l.value)?void 0:e.activeElement;null!=(t=Ob(s.panel))&&t.contains(n)||zb(Ob(s.panel),Pb.First)}));let d=ly(),p=zg((()=>null!==d?(d.value&sy.Open)===sy.Open:0===s.popoverState.value));function h(e){var t,n;if(e.key===dy.Escape){if(0!==s.popoverState.value||!Ob(s.panel)||l.value&&(null==(t=Ob(s.panel))||!t.contains(l.value.activeElement)))return;e.preventDefault(),e.stopPropagation(),s.closePopover(),null==(n=Ob(s.button))||n.focus()}}function f(e){var t,n,r,a,o;let i=e.relatedTarget;i&&Ob(s.panel)&&(null!=(t=Ob(s.panel))&&t.contains(i)||(s.closePopover(),(null!=(r=null==(n=Ob(s.beforePanelSentinel))?void 0:n.contains)&&r.call(n,i)||null!=(o=null==(a=Ob(s.afterPanelSentinel))?void 0:a.contains)&&o.call(a,i))&&i.focus({preventScroll:!0})))}let m=wy();function g(){let e=Ob(s.panel);e&&wb(m.value,{[Oy.Forwards]:()=>{var t;zb(e,Pb.First)===$b.Error&&(null==(t=Ob(s.afterPanelSentinel))||t.focus())},[Oy.Backwards]:()=>{var e;null==(e=Ob(s.button))||e.focus({preventScroll:!0})}})}function v(){let e=Ob(s.panel);e&&wb(m.value,{[Oy.Forwards]:()=>{let e=Ob(s.button),t=Ob(s.panel);if(!e)return;let n=Ib(),r=n.indexOf(e),a=n.slice(0,r+1),o=[...n.slice(r+1),...a];for(let e of o.slice())if(\"true\"===e.dataset.headlessuiFocusGuard||null!=t&&t.contains(e)){let t=o.indexOf(e);-1!==t&&o.splice(t,1)}zb(o,Pb.First,{sorted:!1})},[Oy.Backwards]:()=>{var t;zb(e,Pb.Previous)===$b.Error&&(null==(t=Ob(s.button))||t.focus())}})}return()=>{let r={open:0===s.popoverState.value,close:s.close},{focus:a,...l}=e;return Jb({ourProps:{ref:s.panel,id:o,onKeydown:h,onFocusout:i&&0===s.popoverState.value?f:void 0,tabIndex:-1},theirProps:{...t,...l},attrs:t,slot:r,slots:{...n,default:(...e)=>{var t;return[Fg(Km,[p.value&&s.isPortalled.value&&Fg(oy,{id:c,ref:s.beforePanelSentinel,features:ay.Focusable,\"data-headlessui-focus-guard\":!0,as:\"button\",type:\"button\",onFocus:g}),null==(t=n.default)?void 0:t.call(n,...e),p.value&&s.isPortalled.value&&Fg(oy,{id:u,ref:s.afterPanelSentinel,features:ay.Focusable,\"data-headlessui-focus-guard\":!0,as:\"button\",type:\"button\",onFocus:v})])]}},features:Yb.RenderStrategy|Yb.Static,visible:p.value,name:\"PopoverPanel\"})}}}),BO=nf({props:{onFocus:{type:Function,required:!0}},setup(e){let t=zp(!0);return()=>t.value?Fg(oy,{as:\"button\",type:\"button\",features:ay.Focusable,onFocus(n){n.preventDefault();let r,a=50;r=requestAnimationFrame((function n(){var o;if(!(a--<=0))return null!=(o=e.onFocus)&&o.call(e)?(t.value=!1,void cancelAnimationFrame(r)):void(r=requestAnimationFrame(n));r&&cancelAnimationFrame(r)}))}}):null}});var jO,UO=(e=>(e[e.Forwards=0]=\"Forwards\",e[e.Backwards=1]=\"Backwards\",e))(UO||{}),zO=((jO=zO||{})[jO.Less=-1]=\"Less\",jO[jO.Equal=0]=\"Equal\",jO[jO.Greater=1]=\"Greater\",jO);let FO=Symbol(\"TabsContext\");function QO(e){let t=dm(FO,null);if(null===t){let t=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,QO),t}return t}let ZO=Symbol(\"TabsSSRContext\"),HO=nf({name:\"TabGroup\",emits:{change:e=>!0},props:{as:{type:[Object,String],default:\"template\"},selectedIndex:{type:[Number],default:null},defaultIndex:{type:[Number],default:0},vertical:{type:[Boolean],default:!1},manual:{type:[Boolean],default:!1}},inheritAttrs:!1,setup(e,{slots:t,attrs:n,emit:r}){var a;let o=zp(null!=(a=e.selectedIndex)?a:e.defaultIndex),i=zp([]),s=zp([]),l=zg((()=>null!==e.selectedIndex)),c=zg((()=>l.value?e.selectedIndex:o.value));function u(e){var t;let n=Ub(d.tabs.value,Ob),r=Ub(d.panels.value,Ob),a=n.filter((e=>{var t;return!(null!=(t=Ob(e))&&t.hasAttribute(\"disabled\"))}));if(e<0||e>n.length-1){let t=wb(null===o.value?0:Math.sign(e-o.value),{[-1]:()=>1,0:()=>wb(Math.sign(e),{[-1]:()=>0,0:()=>0,1:()=>1}),1:()=>0}),i=wb(t,{0:()=>n.indexOf(a[0]),1:()=>n.indexOf(a[a.length-1])});-1!==i&&(o.value=i),d.tabs.value=n,d.panels.value=r}else{let i=n.slice(0,e),s=[...n.slice(e),...i].find((e=>a.includes(e)));if(!s)return;let l=null!=(t=n.indexOf(s))?t:d.selectedIndex.value;-1===l&&(l=d.selectedIndex.value),o.value=l,d.tabs.value=n,d.panels.value=r}}let d={selectedIndex:zg((()=>{var t,n;return null!=(n=null!=(t=o.value)?t:e.defaultIndex)?n:null})),orientation:zg((()=>e.vertical?\"vertical\":\"horizontal\")),activation:zg((()=>e.manual?\"manual\":\"auto\")),tabs:i,panels:s,setSelectedIndex(e){c.value!==e&&r(\"change\",e),l.value||u(e)},registerTab(e){var t;if(i.value.includes(e))return;let n=i.value[o.value];if(i.value.push(e),i.value=Ub(i.value,Ob),!l.value){let e=null!=(t=i.value.indexOf(n))?t:o.value;-1!==e&&(o.value=e)}},unregisterTab(e){let t=i.value.indexOf(e);-1!==t&&i.value.splice(t,1)},registerPanel(e){s.value.includes(e)||(s.value.push(e),s.value=Ub(s.value,Ob))},unregisterPanel(e){let t=s.value.indexOf(e);-1!==t&&s.value.splice(t,1)}};um(FO,d);let p=zp({tabs:[],panels:[]}),h=zp(!1);yf((()=>{h.value=!0})),um(ZO,zg((()=>h.value?null:p.value)));let f=zg((()=>e.selectedIndex));return yf((()=>{Lm([f],(()=>{var t;return u(null!=(t=e.selectedIndex)?t:e.defaultIndex)}),{immediate:!0})})),Rm((()=>{if(!l.value||null==c.value||d.tabs.value.length<=0)return;let e=Ub(d.tabs.value,Ob);e.some(((e,t)=>Ob(d.tabs.value[t])!==Ob(e)))&&d.setSelectedIndex(e.findIndex((e=>Ob(e)===Ob(d.tabs.value[c.value]))))})),()=>{let r={selectedIndex:o.value};return Fg(Km,[i.value.length<=0&&Fg(BO,{onFocus:()=>{for(let e of i.value){let t=Ob(e);if(0===(null==t?void 0:t.tabIndex))return t.focus(),!0}return!1}}),Jb({theirProps:{...n,...ry(e,[\"selectedIndex\",\"defaultIndex\",\"manual\",\"vertical\",\"onChange\"])},ourProps:{},slot:r,slots:t,attrs:n,name:\"TabGroup\"})])}}}),VO=nf({name:\"TabList\",props:{as:{type:[Object,String],default:\"div\"}},setup(e,{attrs:t,slots:n}){let r=QO(\"TabList\");return()=>{let a={selectedIndex:r.selectedIndex.value};return Jb({ourProps:{role:\"tablist\",\"aria-orientation\":r.orientation.value},theirProps:e,slot:a,attrs:t,slots:n,name:\"TabList\"})}}}),qO=nf({name:\"Tab\",props:{as:{type:[Object,String],default:\"button\"},disabled:{type:[Boolean],default:!1},id:{type:String,default:null}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-tabs-tab-${yb()}`,i=QO(\"Tab\"),s=zp(null);r({el:s,$el:s}),yf((()=>i.registerTab(s))),kf((()=>i.unregisterTab(s)));let l=dm(ZO),c=zg((()=>{if(l.value){let e=l.value.tabs.indexOf(o);return-1===e?l.value.tabs.push(o)-1:e}return-1})),u=zg((()=>{let e=i.tabs.value.indexOf(s);return-1===e?c.value:e})),d=zg((()=>u.value===i.selectedIndex.value));function p(e){var t;let n=e();if(n===$b.Success&&\"auto\"===i.activation.value){let e=null==(t=_b(s))?void 0:t.activeElement,n=i.tabs.value.findIndex((t=>Ob(t)===e));-1!==n&&i.setSelectedIndex(n)}return n}function h(e){let t=i.tabs.value.map((e=>Ob(e))).filter(Boolean);if(e.key===dy.Space||e.key===dy.Enter)return e.preventDefault(),e.stopPropagation(),void i.setSelectedIndex(u.value);switch(e.key){case dy.Home:case dy.PageUp:return e.preventDefault(),e.stopPropagation(),p((()=>zb(t,Pb.First)));case dy.End:case dy.PageDown:return e.preventDefault(),e.stopPropagation(),p((()=>zb(t,Pb.Last)))}return p((()=>wb(i.orientation.value,{vertical:()=>e.key===dy.ArrowUp?zb(t,Pb.Previous|Pb.WrapAround):e.key===dy.ArrowDown?zb(t,Pb.Next|Pb.WrapAround):$b.Error,horizontal:()=>e.key===dy.ArrowLeft?zb(t,Pb.Previous|Pb.WrapAround):e.key===dy.ArrowRight?zb(t,Pb.Next|Pb.WrapAround):$b.Error})))===$b.Success?e.preventDefault():void 0}let f=zp(!1);function m(){var t;f.value||(f.value=!0,!e.disabled&&(null==(t=Ob(s))||t.focus({preventScroll:!0}),i.setSelectedIndex(u.value),fb((()=>{f.value=!1}))))}function g(e){e.preventDefault()}let v=qb(zg((()=>({as:e.as,type:t.type}))),s);return()=>{var r,a;let l={selected:d.value,disabled:null!=(r=e.disabled)&&r},{...c}=e;return Jb({ourProps:{ref:s,onKeydown:h,onMousedown:g,onClick:m,id:o,role:\"tab\",type:v.value,\"aria-controls\":null==(a=Ob(i.panels.value[u.value]))?void 0:a.id,\"aria-selected\":d.value,tabIndex:d.value?0:-1,disabled:!!e.disabled||void 0},theirProps:c,slot:l,attrs:t,slots:n,name:\"Tab\"})}}}),WO=nf({name:\"TabPanels\",props:{as:{type:[Object,String],default:\"div\"}},setup(e,{slots:t,attrs:n}){let r=QO(\"TabPanels\");return()=>{let a={selectedIndex:r.selectedIndex.value};return Jb({theirProps:e,ourProps:{},slot:a,attrs:n,slots:t,name:\"TabPanels\"})}}}),XO=nf({name:\"TabPanel\",props:{as:{type:[Object,String],default:\"div\"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},id:{type:String,default:null},tabIndex:{type:Number,default:0}},setup(e,{attrs:t,slots:n,expose:r}){var a;let o=null!=(a=e.id)?a:`headlessui-tabs-panel-${yb()}`,i=QO(\"TabPanel\"),s=zp(null);r({el:s,$el:s}),yf((()=>i.registerPanel(s))),kf((()=>i.unregisterPanel(s)));let l=dm(ZO),c=zg((()=>{if(l.value){let e=l.value.panels.indexOf(o);return-1===e?l.value.panels.push(o)-1:e}return-1})),u=zg((()=>{let e=i.panels.value.indexOf(s);return-1===e?c.value:e})),d=zg((()=>u.value===i.selectedIndex.value));return()=>{var r;let a={selected:d.value},{tabIndex:l,...c}=e,p={ref:s,id:o,role:\"tabpanel\",\"aria-labelledby\":null==(r=Ob(i.tabs.value[u.value]))?void 0:r.id,tabIndex:d.value?l:-1};return d.value||!e.unmount||e.static?Jb({ourProps:p,theirProps:c,slot:a,attrs:t,slots:n,features:Yb.Static|Yb.RenderStrategy,visible:d.value,name:\"TabPanel\"}):Fg(oy,{as:\"span\",\"aria-hidden\":!0,...p})}}});function GO(e){var t,n,r=\"\";if(\"string\"==typeof e||\"number\"==typeof e)r+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var a=e.length;for(t=0;t\"boolean\"==typeof e?`${e}`:0===e?\"0\":e,JO=e=>{const t=rw(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:e=>{const n=e.split(\"-\");return\"\"===n[0]&&1!==n.length&&n.shift(),ew(n,t)||nw(e)},getConflictingClassGroupIds:(e,t)=>{const a=n[e]||[];return t&&r[e]?[...a,...r[e]]:a}}},ew=(e,t)=>{if(0===e.length)return t.classGroupId;const n=e[0],r=t.nextPart.get(n),a=r?ew(e.slice(1),r):void 0;if(a)return a;if(0===t.validators.length)return;const o=e.join(\"-\");return t.validators.find((({validator:e})=>e(o)))?.classGroupId},tw=/^\\[(.+)\\]$/,nw=e=>{if(tw.test(e)){const t=tw.exec(e)[1],n=t?.substring(0,t.indexOf(\":\"));if(n)return\"arbitrary..\"+n}},rw=e=>{const{theme:t,prefix:n}=e,r={nextPart:new Map,validators:[]};return sw(Object.entries(e.classGroups),n).forEach((([e,n])=>{aw(n,r,e,t)})),r},aw=(e,t,n,r)=>{e.forEach((e=>{if(\"string\"!=typeof e){if(\"function\"==typeof e)return iw(e)?void aw(e(r),t,n,r):void t.validators.push({validator:e,classGroupId:n});Object.entries(e).forEach((([e,a])=>{aw(a,ow(t,e),n,r)}))}else(\"\"===e?t:ow(t,e)).classGroupId=n}))},ow=(e,t)=>{let n=e;return t.split(\"-\").forEach((e=>{n.nextPart.has(e)||n.nextPart.set(e,{nextPart:new Map,validators:[]}),n=n.nextPart.get(e)})),n},iw=e=>e.isThemeGetter,sw=(e,t)=>t?e.map((([e,n])=>[e,n.map((e=>\"string\"==typeof e?t+e:\"object\"==typeof e?Object.fromEntries(Object.entries(e).map((([e,n])=>[t+e,n]))):e))])):e,lw=e=>{if(e<1)return{get:()=>{},set:()=>{}};let t=0,n=new Map,r=new Map;const a=(a,o)=>{n.set(a,o),t++,t>e&&(t=0,r=n,n=new Map)};return{get(e){let t=n.get(e);return void 0!==t?t:void 0!==(t=r.get(e))?(a(e,t),t):void 0},set(e,t){n.has(e)?n.set(e,t):a(e,t)}}},cw=e=>{const{separator:t,experimentalParseClassName:n}=e,r=1===t.length,a=t[0],o=t.length,i=e=>{const n=[];let i,s=0,l=0;for(let c=0;cl?i-l:void 0}};return n?e=>n({className:e,parseClassName:i}):i},uw=e=>{if(e.length<=1)return e;const t=[];let n=[];return e.forEach((e=>{\"[\"===e[0]?(t.push(...n.sort(),e),n=[]):n.push(e)})),t.push(...n.sort()),t},dw=/\\s+/;function pw(){let e,t,n=0,r=\"\";for(;n{if(\"string\"==typeof e)return e;let t,n=\"\";for(let r=0;rt(e)),e());return n=(e=>({cache:lw(e.cacheSize),parseClassName:cw(e),...JO(e)}))(l),r=n.cache.get,a=n.cache.set,o=i,i(s)};function i(e){const t=r(e);if(t)return t;const o=((e,t)=>{const{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:a}=t,o=[],i=e.trim().split(dw);let s=\"\";for(let e=i.length-1;e>=0;e-=1){const t=i[e],{modifiers:l,hasImportantModifier:c,baseClassName:u,maybePostfixModifierPosition:d}=n(t);let p=Boolean(d),h=r(p?u.substring(0,d):u);if(!h){if(!p){s=t+(s.length>0?\" \"+s:s);continue}if(h=r(u),!h){s=t+(s.length>0?\" \"+s:s);continue}p=!1}const f=uw(l).join(\":\"),m=c?f+\"!\":f,g=m+h;if(o.includes(g))continue;o.push(g);const v=a(h,p);for(let e=0;e0?\" \"+s:s)}return s})(e,n);return a(e,o),o}return function(){return o(pw.apply(null,arguments))}}const mw=e=>{const t=t=>t[e]||[];return t.isThemeGetter=!0,t},gw=/^\\[(?:([a-z-]+):)?(.+)\\]$/i,vw=/^\\d+\\/\\d+$/,bw=new Set([\"px\",\"full\",\"screen\"]),yw=/^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/,Ow=/\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/,ww=/^(rgba?|hsla?|hwb|(ok)?(lab|lch))\\(.+\\)$/,xw=/^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/,kw=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/,Sw=e=>Tw(e)||bw.has(e)||vw.test(e),_w=e=>jw(e,\"length\",Uw),Tw=e=>Boolean(e)&&!Number.isNaN(Number(e)),Ew=e=>jw(e,\"number\",Tw),Aw=e=>Boolean(e)&&Number.isInteger(Number(e)),Cw=e=>e.endsWith(\"%\")&&Tw(e.slice(0,-1)),Pw=e=>gw.test(e),$w=e=>yw.test(e),Dw=new Set([\"length\",\"size\",\"percentage\"]),Iw=e=>jw(e,Dw,zw),Mw=e=>jw(e,\"position\",zw),Nw=new Set([\"image\",\"url\"]),Rw=e=>jw(e,Nw,Qw),Lw=e=>jw(e,\"\",Fw),Bw=()=>!0,jw=(e,t,n)=>{const r=gw.exec(e);return!!r&&(r[1]?\"string\"==typeof t?r[1]===t:t.has(r[1]):n(r[2]))},Uw=e=>Ow.test(e)&&!ww.test(e),zw=()=>!1,Fw=e=>xw.test(e),Qw=e=>kw.test(e),Zw=()=>{const e=mw(\"colors\"),t=mw(\"spacing\"),n=mw(\"blur\"),r=mw(\"brightness\"),a=mw(\"borderColor\"),o=mw(\"borderRadius\"),i=mw(\"borderSpacing\"),s=mw(\"borderWidth\"),l=mw(\"contrast\"),c=mw(\"grayscale\"),u=mw(\"hueRotate\"),d=mw(\"invert\"),p=mw(\"gap\"),h=mw(\"gradientColorStops\"),f=mw(\"gradientColorStopPositions\"),m=mw(\"inset\"),g=mw(\"margin\"),v=mw(\"opacity\"),b=mw(\"padding\"),y=mw(\"saturate\"),O=mw(\"scale\"),w=mw(\"sepia\"),x=mw(\"skew\"),k=mw(\"space\"),S=mw(\"translate\"),_=()=>[\"auto\",Pw,t],T=()=>[Pw,t],E=()=>[\"\",Sw,_w],A=()=>[\"auto\",Tw,Pw],C=()=>[\"\",\"0\",Pw],P=()=>[Tw,Pw];return{cacheSize:500,separator:\":\",theme:{colors:[Bw],spacing:[Sw,_w],blur:[\"none\",\"\",$w,Pw],brightness:P(),borderColor:[e],borderRadius:[\"none\",\"\",\"full\",$w,Pw],borderSpacing:T(),borderWidth:E(),contrast:P(),grayscale:C(),hueRotate:P(),invert:C(),gap:T(),gradientColorStops:[e],gradientColorStopPositions:[Cw,_w],inset:_(),margin:_(),opacity:P(),padding:T(),saturate:P(),scale:P(),sepia:C(),skew:P(),space:T(),translate:T()},classGroups:{aspect:[{aspect:[\"auto\",\"square\",\"video\",Pw]}],container:[\"container\"],columns:[{columns:[$w]}],\"break-after\":[{\"break-after\":[\"auto\",\"avoid\",\"all\",\"avoid-page\",\"page\",\"left\",\"right\",\"column\"]}],\"break-before\":[{\"break-before\":[\"auto\",\"avoid\",\"all\",\"avoid-page\",\"page\",\"left\",\"right\",\"column\"]}],\"break-inside\":[{\"break-inside\":[\"auto\",\"avoid\",\"avoid-page\",\"avoid-column\"]}],\"box-decoration\":[{\"box-decoration\":[\"slice\",\"clone\"]}],box:[{box:[\"border\",\"content\"]}],display:[\"block\",\"inline-block\",\"inline\",\"flex\",\"inline-flex\",\"table\",\"inline-table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row-group\",\"table-row\",\"flow-root\",\"grid\",\"inline-grid\",\"contents\",\"list-item\",\"hidden\"],float:[{float:[\"right\",\"left\",\"none\",\"start\",\"end\"]}],clear:[{clear:[\"left\",\"right\",\"both\",\"none\",\"start\",\"end\"]}],isolation:[\"isolate\",\"isolation-auto\"],\"object-fit\":[{object:[\"contain\",\"cover\",\"fill\",\"none\",\"scale-down\"]}],\"object-position\":[{object:[\"bottom\",\"center\",\"left\",\"left-bottom\",\"left-top\",\"right\",\"right-bottom\",\"right-top\",\"top\",Pw]}],overflow:[{overflow:[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],\"overflow-x\":[{\"overflow-x\":[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],\"overflow-y\":[{\"overflow-y\":[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],overscroll:[{overscroll:[\"auto\",\"contain\",\"none\"]}],\"overscroll-x\":[{\"overscroll-x\":[\"auto\",\"contain\",\"none\"]}],\"overscroll-y\":[{\"overscroll-y\":[\"auto\",\"contain\",\"none\"]}],position:[\"static\",\"fixed\",\"absolute\",\"relative\",\"sticky\"],inset:[{inset:[m]}],\"inset-x\":[{\"inset-x\":[m]}],\"inset-y\":[{\"inset-y\":[m]}],start:[{start:[m]}],end:[{end:[m]}],top:[{top:[m]}],right:[{right:[m]}],bottom:[{bottom:[m]}],left:[{left:[m]}],visibility:[\"visible\",\"invisible\",\"collapse\"],z:[{z:[\"auto\",Aw,Pw]}],basis:[{basis:_()}],\"flex-direction\":[{flex:[\"row\",\"row-reverse\",\"col\",\"col-reverse\"]}],\"flex-wrap\":[{flex:[\"wrap\",\"wrap-reverse\",\"nowrap\"]}],flex:[{flex:[\"1\",\"auto\",\"initial\",\"none\",Pw]}],grow:[{grow:C()}],shrink:[{shrink:C()}],order:[{order:[\"first\",\"last\",\"none\",Aw,Pw]}],\"grid-cols\":[{\"grid-cols\":[Bw]}],\"col-start-end\":[{col:[\"auto\",{span:[\"full\",Aw,Pw]},Pw]}],\"col-start\":[{\"col-start\":A()}],\"col-end\":[{\"col-end\":A()}],\"grid-rows\":[{\"grid-rows\":[Bw]}],\"row-start-end\":[{row:[\"auto\",{span:[Aw,Pw]},Pw]}],\"row-start\":[{\"row-start\":A()}],\"row-end\":[{\"row-end\":A()}],\"grid-flow\":[{\"grid-flow\":[\"row\",\"col\",\"dense\",\"row-dense\",\"col-dense\"]}],\"auto-cols\":[{\"auto-cols\":[\"auto\",\"min\",\"max\",\"fr\",Pw]}],\"auto-rows\":[{\"auto-rows\":[\"auto\",\"min\",\"max\",\"fr\",Pw]}],gap:[{gap:[p]}],\"gap-x\":[{\"gap-x\":[p]}],\"gap-y\":[{\"gap-y\":[p]}],\"justify-content\":[{justify:[\"normal\",\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\"]}],\"justify-items\":[{\"justify-items\":[\"start\",\"end\",\"center\",\"stretch\"]}],\"justify-self\":[{\"justify-self\":[\"auto\",\"start\",\"end\",\"center\",\"stretch\"]}],\"align-content\":[{content:[\"normal\",\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\",\"baseline\"]}],\"align-items\":[{items:[\"start\",\"end\",\"center\",\"baseline\",\"stretch\"]}],\"align-self\":[{self:[\"auto\",\"start\",\"end\",\"center\",\"stretch\",\"baseline\"]}],\"place-content\":[{\"place-content\":[\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\",\"baseline\"]}],\"place-items\":[{\"place-items\":[\"start\",\"end\",\"center\",\"baseline\",\"stretch\"]}],\"place-self\":[{\"place-self\":[\"auto\",\"start\",\"end\",\"center\",\"stretch\"]}],p:[{p:[b]}],px:[{px:[b]}],py:[{py:[b]}],ps:[{ps:[b]}],pe:[{pe:[b]}],pt:[{pt:[b]}],pr:[{pr:[b]}],pb:[{pb:[b]}],pl:[{pl:[b]}],m:[{m:[g]}],mx:[{mx:[g]}],my:[{my:[g]}],ms:[{ms:[g]}],me:[{me:[g]}],mt:[{mt:[g]}],mr:[{mr:[g]}],mb:[{mb:[g]}],ml:[{ml:[g]}],\"space-x\":[{\"space-x\":[k]}],\"space-x-reverse\":[\"space-x-reverse\"],\"space-y\":[{\"space-y\":[k]}],\"space-y-reverse\":[\"space-y-reverse\"],w:[{w:[\"auto\",\"min\",\"max\",\"fit\",\"svw\",\"lvw\",\"dvw\",Pw,t]}],\"min-w\":[{\"min-w\":[Pw,t,\"min\",\"max\",\"fit\"]}],\"max-w\":[{\"max-w\":[Pw,t,\"none\",\"full\",\"min\",\"max\",\"fit\",\"prose\",{screen:[$w]},$w]}],h:[{h:[Pw,t,\"auto\",\"min\",\"max\",\"fit\",\"svh\",\"lvh\",\"dvh\"]}],\"min-h\":[{\"min-h\":[Pw,t,\"min\",\"max\",\"fit\",\"svh\",\"lvh\",\"dvh\"]}],\"max-h\":[{\"max-h\":[Pw,t,\"min\",\"max\",\"fit\",\"svh\",\"lvh\",\"dvh\"]}],size:[{size:[Pw,t,\"auto\",\"min\",\"max\",\"fit\"]}],\"font-size\":[{text:[\"base\",$w,_w]}],\"font-smoothing\":[\"antialiased\",\"subpixel-antialiased\"],\"font-style\":[\"italic\",\"not-italic\"],\"font-weight\":[{font:[\"thin\",\"extralight\",\"light\",\"normal\",\"medium\",\"semibold\",\"bold\",\"extrabold\",\"black\",Ew]}],\"font-family\":[{font:[Bw]}],\"fvn-normal\":[\"normal-nums\"],\"fvn-ordinal\":[\"ordinal\"],\"fvn-slashed-zero\":[\"slashed-zero\"],\"fvn-figure\":[\"lining-nums\",\"oldstyle-nums\"],\"fvn-spacing\":[\"proportional-nums\",\"tabular-nums\"],\"fvn-fraction\":[\"diagonal-fractions\",\"stacked-fractions\"],tracking:[{tracking:[\"tighter\",\"tight\",\"normal\",\"wide\",\"wider\",\"widest\",Pw]}],\"line-clamp\":[{\"line-clamp\":[\"none\",Tw,Ew]}],leading:[{leading:[\"none\",\"tight\",\"snug\",\"normal\",\"relaxed\",\"loose\",Sw,Pw]}],\"list-image\":[{\"list-image\":[\"none\",Pw]}],\"list-style-type\":[{list:[\"none\",\"disc\",\"decimal\",Pw]}],\"list-style-position\":[{list:[\"inside\",\"outside\"]}],\"placeholder-color\":[{placeholder:[e]}],\"placeholder-opacity\":[{\"placeholder-opacity\":[v]}],\"text-alignment\":[{text:[\"left\",\"center\",\"right\",\"justify\",\"start\",\"end\"]}],\"text-color\":[{text:[e]}],\"text-opacity\":[{\"text-opacity\":[v]}],\"text-decoration\":[\"underline\",\"overline\",\"line-through\",\"no-underline\"],\"text-decoration-style\":[{decoration:[\"solid\",\"dashed\",\"dotted\",\"double\",\"none\",\"wavy\"]}],\"text-decoration-thickness\":[{decoration:[\"auto\",\"from-font\",Sw,_w]}],\"underline-offset\":[{\"underline-offset\":[\"auto\",Sw,Pw]}],\"text-decoration-color\":[{decoration:[e]}],\"text-transform\":[\"uppercase\",\"lowercase\",\"capitalize\",\"normal-case\"],\"text-overflow\":[\"truncate\",\"text-ellipsis\",\"text-clip\"],\"text-wrap\":[{text:[\"wrap\",\"nowrap\",\"balance\",\"pretty\"]}],indent:[{indent:T()}],\"vertical-align\":[{align:[\"baseline\",\"top\",\"middle\",\"bottom\",\"text-top\",\"text-bottom\",\"sub\",\"super\",Pw]}],whitespace:[{whitespace:[\"normal\",\"nowrap\",\"pre\",\"pre-line\",\"pre-wrap\",\"break-spaces\"]}],break:[{break:[\"normal\",\"words\",\"all\",\"keep\"]}],hyphens:[{hyphens:[\"none\",\"manual\",\"auto\"]}],content:[{content:[\"none\",Pw]}],\"bg-attachment\":[{bg:[\"fixed\",\"local\",\"scroll\"]}],\"bg-clip\":[{\"bg-clip\":[\"border\",\"padding\",\"content\",\"text\"]}],\"bg-opacity\":[{\"bg-opacity\":[v]}],\"bg-origin\":[{\"bg-origin\":[\"border\",\"padding\",\"content\"]}],\"bg-position\":[{bg:[\"bottom\",\"center\",\"left\",\"left-bottom\",\"left-top\",\"right\",\"right-bottom\",\"right-top\",\"top\",Mw]}],\"bg-repeat\":[{bg:[\"no-repeat\",{repeat:[\"\",\"x\",\"y\",\"round\",\"space\"]}]}],\"bg-size\":[{bg:[\"auto\",\"cover\",\"contain\",Iw]}],\"bg-image\":[{bg:[\"none\",{\"gradient-to\":[\"t\",\"tr\",\"r\",\"br\",\"b\",\"bl\",\"l\",\"tl\"]},Rw]}],\"bg-color\":[{bg:[e]}],\"gradient-from-pos\":[{from:[f]}],\"gradient-via-pos\":[{via:[f]}],\"gradient-to-pos\":[{to:[f]}],\"gradient-from\":[{from:[h]}],\"gradient-via\":[{via:[h]}],\"gradient-to\":[{to:[h]}],rounded:[{rounded:[o]}],\"rounded-s\":[{\"rounded-s\":[o]}],\"rounded-e\":[{\"rounded-e\":[o]}],\"rounded-t\":[{\"rounded-t\":[o]}],\"rounded-r\":[{\"rounded-r\":[o]}],\"rounded-b\":[{\"rounded-b\":[o]}],\"rounded-l\":[{\"rounded-l\":[o]}],\"rounded-ss\":[{\"rounded-ss\":[o]}],\"rounded-se\":[{\"rounded-se\":[o]}],\"rounded-ee\":[{\"rounded-ee\":[o]}],\"rounded-es\":[{\"rounded-es\":[o]}],\"rounded-tl\":[{\"rounded-tl\":[o]}],\"rounded-tr\":[{\"rounded-tr\":[o]}],\"rounded-br\":[{\"rounded-br\":[o]}],\"rounded-bl\":[{\"rounded-bl\":[o]}],\"border-w\":[{border:[s]}],\"border-w-x\":[{\"border-x\":[s]}],\"border-w-y\":[{\"border-y\":[s]}],\"border-w-s\":[{\"border-s\":[s]}],\"border-w-e\":[{\"border-e\":[s]}],\"border-w-t\":[{\"border-t\":[s]}],\"border-w-r\":[{\"border-r\":[s]}],\"border-w-b\":[{\"border-b\":[s]}],\"border-w-l\":[{\"border-l\":[s]}],\"border-opacity\":[{\"border-opacity\":[v]}],\"border-style\":[{border:[\"solid\",\"dashed\",\"dotted\",\"double\",\"none\",\"hidden\"]}],\"divide-x\":[{\"divide-x\":[s]}],\"divide-x-reverse\":[\"divide-x-reverse\"],\"divide-y\":[{\"divide-y\":[s]}],\"divide-y-reverse\":[\"divide-y-reverse\"],\"divide-opacity\":[{\"divide-opacity\":[v]}],\"divide-style\":[{divide:[\"solid\",\"dashed\",\"dotted\",\"double\",\"none\"]}],\"border-color\":[{border:[a]}],\"border-color-x\":[{\"border-x\":[a]}],\"border-color-y\":[{\"border-y\":[a]}],\"border-color-s\":[{\"border-s\":[a]}],\"border-color-e\":[{\"border-e\":[a]}],\"border-color-t\":[{\"border-t\":[a]}],\"border-color-r\":[{\"border-r\":[a]}],\"border-color-b\":[{\"border-b\":[a]}],\"border-color-l\":[{\"border-l\":[a]}],\"divide-color\":[{divide:[a]}],\"outline-style\":[{outline:[\"\",\"solid\",\"dashed\",\"dotted\",\"double\",\"none\"]}],\"outline-offset\":[{\"outline-offset\":[Sw,Pw]}],\"outline-w\":[{outline:[Sw,_w]}],\"outline-color\":[{outline:[e]}],\"ring-w\":[{ring:E()}],\"ring-w-inset\":[\"ring-inset\"],\"ring-color\":[{ring:[e]}],\"ring-opacity\":[{\"ring-opacity\":[v]}],\"ring-offset-w\":[{\"ring-offset\":[Sw,_w]}],\"ring-offset-color\":[{\"ring-offset\":[e]}],shadow:[{shadow:[\"\",\"inner\",\"none\",$w,Lw]}],\"shadow-color\":[{shadow:[Bw]}],opacity:[{opacity:[v]}],\"mix-blend\":[{\"mix-blend\":[\"normal\",\"multiply\",\"screen\",\"overlay\",\"darken\",\"lighten\",\"color-dodge\",\"color-burn\",\"hard-light\",\"soft-light\",\"difference\",\"exclusion\",\"hue\",\"saturation\",\"color\",\"luminosity\",\"plus-lighter\",\"plus-darker\"]}],\"bg-blend\":[{\"bg-blend\":[\"normal\",\"multiply\",\"screen\",\"overlay\",\"darken\",\"lighten\",\"color-dodge\",\"color-burn\",\"hard-light\",\"soft-light\",\"difference\",\"exclusion\",\"hue\",\"saturation\",\"color\",\"luminosity\"]}],filter:[{filter:[\"\",\"none\"]}],blur:[{blur:[n]}],brightness:[{brightness:[r]}],contrast:[{contrast:[l]}],\"drop-shadow\":[{\"drop-shadow\":[\"\",\"none\",$w,Pw]}],grayscale:[{grayscale:[c]}],\"hue-rotate\":[{\"hue-rotate\":[u]}],invert:[{invert:[d]}],saturate:[{saturate:[y]}],sepia:[{sepia:[w]}],\"backdrop-filter\":[{\"backdrop-filter\":[\"\",\"none\"]}],\"backdrop-blur\":[{\"backdrop-blur\":[n]}],\"backdrop-brightness\":[{\"backdrop-brightness\":[r]}],\"backdrop-contrast\":[{\"backdrop-contrast\":[l]}],\"backdrop-grayscale\":[{\"backdrop-grayscale\":[c]}],\"backdrop-hue-rotate\":[{\"backdrop-hue-rotate\":[u]}],\"backdrop-invert\":[{\"backdrop-invert\":[d]}],\"backdrop-opacity\":[{\"backdrop-opacity\":[v]}],\"backdrop-saturate\":[{\"backdrop-saturate\":[y]}],\"backdrop-sepia\":[{\"backdrop-sepia\":[w]}],\"border-collapse\":[{border:[\"collapse\",\"separate\"]}],\"border-spacing\":[{\"border-spacing\":[i]}],\"border-spacing-x\":[{\"border-spacing-x\":[i]}],\"border-spacing-y\":[{\"border-spacing-y\":[i]}],\"table-layout\":[{table:[\"auto\",\"fixed\"]}],caption:[{caption:[\"top\",\"bottom\"]}],transition:[{transition:[\"none\",\"all\",\"\",\"colors\",\"opacity\",\"shadow\",\"transform\",Pw]}],duration:[{duration:P()}],ease:[{ease:[\"linear\",\"in\",\"out\",\"in-out\",Pw]}],delay:[{delay:P()}],animate:[{animate:[\"none\",\"spin\",\"ping\",\"pulse\",\"bounce\",Pw]}],transform:[{transform:[\"\",\"gpu\",\"none\"]}],scale:[{scale:[O]}],\"scale-x\":[{\"scale-x\":[O]}],\"scale-y\":[{\"scale-y\":[O]}],rotate:[{rotate:[Aw,Pw]}],\"translate-x\":[{\"translate-x\":[S]}],\"translate-y\":[{\"translate-y\":[S]}],\"skew-x\":[{\"skew-x\":[x]}],\"skew-y\":[{\"skew-y\":[x]}],\"transform-origin\":[{origin:[\"center\",\"top\",\"top-right\",\"right\",\"bottom-right\",\"bottom\",\"bottom-left\",\"left\",\"top-left\",Pw]}],accent:[{accent:[\"auto\",e]}],appearance:[{appearance:[\"none\",\"auto\"]}],cursor:[{cursor:[\"auto\",\"default\",\"pointer\",\"wait\",\"text\",\"move\",\"help\",\"not-allowed\",\"none\",\"context-menu\",\"progress\",\"cell\",\"crosshair\",\"vertical-text\",\"alias\",\"copy\",\"no-drop\",\"grab\",\"grabbing\",\"all-scroll\",\"col-resize\",\"row-resize\",\"n-resize\",\"e-resize\",\"s-resize\",\"w-resize\",\"ne-resize\",\"nw-resize\",\"se-resize\",\"sw-resize\",\"ew-resize\",\"ns-resize\",\"nesw-resize\",\"nwse-resize\",\"zoom-in\",\"zoom-out\",Pw]}],\"caret-color\":[{caret:[e]}],\"pointer-events\":[{\"pointer-events\":[\"none\",\"auto\"]}],resize:[{resize:[\"none\",\"y\",\"x\",\"\"]}],\"scroll-behavior\":[{scroll:[\"auto\",\"smooth\"]}],\"scroll-m\":[{\"scroll-m\":T()}],\"scroll-mx\":[{\"scroll-mx\":T()}],\"scroll-my\":[{\"scroll-my\":T()}],\"scroll-ms\":[{\"scroll-ms\":T()}],\"scroll-me\":[{\"scroll-me\":T()}],\"scroll-mt\":[{\"scroll-mt\":T()}],\"scroll-mr\":[{\"scroll-mr\":T()}],\"scroll-mb\":[{\"scroll-mb\":T()}],\"scroll-ml\":[{\"scroll-ml\":T()}],\"scroll-p\":[{\"scroll-p\":T()}],\"scroll-px\":[{\"scroll-px\":T()}],\"scroll-py\":[{\"scroll-py\":T()}],\"scroll-ps\":[{\"scroll-ps\":T()}],\"scroll-pe\":[{\"scroll-pe\":T()}],\"scroll-pt\":[{\"scroll-pt\":T()}],\"scroll-pr\":[{\"scroll-pr\":T()}],\"scroll-pb\":[{\"scroll-pb\":T()}],\"scroll-pl\":[{\"scroll-pl\":T()}],\"snap-align\":[{snap:[\"start\",\"end\",\"center\",\"align-none\"]}],\"snap-stop\":[{snap:[\"normal\",\"always\"]}],\"snap-type\":[{snap:[\"none\",\"x\",\"y\",\"both\"]}],\"snap-strictness\":[{snap:[\"mandatory\",\"proximity\"]}],touch:[{touch:[\"auto\",\"none\",\"manipulation\"]}],\"touch-x\":[{\"touch-pan\":[\"x\",\"left\",\"right\"]}],\"touch-y\":[{\"touch-pan\":[\"y\",\"up\",\"down\"]}],\"touch-pz\":[\"touch-pinch-zoom\"],select:[{select:[\"none\",\"text\",\"all\",\"auto\"]}],\"will-change\":[{\"will-change\":[\"auto\",\"scroll\",\"contents\",\"transform\",Pw]}],fill:[{fill:[e,\"none\"]}],\"stroke-w\":[{stroke:[Sw,_w,Ew]}],stroke:[{stroke:[e,\"none\"]}],sr:[\"sr-only\",\"not-sr-only\"],\"forced-color-adjust\":[{\"forced-color-adjust\":[\"auto\",\"none\"]}]},conflictingClassGroups:{overflow:[\"overflow-x\",\"overflow-y\"],overscroll:[\"overscroll-x\",\"overscroll-y\"],inset:[\"inset-x\",\"inset-y\",\"start\",\"end\",\"top\",\"right\",\"bottom\",\"left\"],\"inset-x\":[\"right\",\"left\"],\"inset-y\":[\"top\",\"bottom\"],flex:[\"basis\",\"grow\",\"shrink\"],gap:[\"gap-x\",\"gap-y\"],p:[\"px\",\"py\",\"ps\",\"pe\",\"pt\",\"pr\",\"pb\",\"pl\"],px:[\"pr\",\"pl\"],py:[\"pt\",\"pb\"],m:[\"mx\",\"my\",\"ms\",\"me\",\"mt\",\"mr\",\"mb\",\"ml\"],mx:[\"mr\",\"ml\"],my:[\"mt\",\"mb\"],size:[\"w\",\"h\"],\"font-size\":[\"leading\"],\"fvn-normal\":[\"fvn-ordinal\",\"fvn-slashed-zero\",\"fvn-figure\",\"fvn-spacing\",\"fvn-fraction\"],\"fvn-ordinal\":[\"fvn-normal\"],\"fvn-slashed-zero\":[\"fvn-normal\"],\"fvn-figure\":[\"fvn-normal\"],\"fvn-spacing\":[\"fvn-normal\"],\"fvn-fraction\":[\"fvn-normal\"],\"line-clamp\":[\"display\",\"overflow\"],rounded:[\"rounded-s\",\"rounded-e\",\"rounded-t\",\"rounded-r\",\"rounded-b\",\"rounded-l\",\"rounded-ss\",\"rounded-se\",\"rounded-ee\",\"rounded-es\",\"rounded-tl\",\"rounded-tr\",\"rounded-br\",\"rounded-bl\"],\"rounded-s\":[\"rounded-ss\",\"rounded-es\"],\"rounded-e\":[\"rounded-se\",\"rounded-ee\"],\"rounded-t\":[\"rounded-tl\",\"rounded-tr\"],\"rounded-r\":[\"rounded-tr\",\"rounded-br\"],\"rounded-b\":[\"rounded-br\",\"rounded-bl\"],\"rounded-l\":[\"rounded-tl\",\"rounded-bl\"],\"border-spacing\":[\"border-spacing-x\",\"border-spacing-y\"],\"border-w\":[\"border-w-s\",\"border-w-e\",\"border-w-t\",\"border-w-r\",\"border-w-b\",\"border-w-l\"],\"border-w-x\":[\"border-w-r\",\"border-w-l\"],\"border-w-y\":[\"border-w-t\",\"border-w-b\"],\"border-color\":[\"border-color-s\",\"border-color-e\",\"border-color-t\",\"border-color-r\",\"border-color-b\",\"border-color-l\"],\"border-color-x\":[\"border-color-r\",\"border-color-l\"],\"border-color-y\":[\"border-color-t\",\"border-color-b\"],\"scroll-m\":[\"scroll-mx\",\"scroll-my\",\"scroll-ms\",\"scroll-me\",\"scroll-mt\",\"scroll-mr\",\"scroll-mb\",\"scroll-ml\"],\"scroll-mx\":[\"scroll-mr\",\"scroll-ml\"],\"scroll-my\":[\"scroll-mt\",\"scroll-mb\"],\"scroll-p\":[\"scroll-px\",\"scroll-py\",\"scroll-ps\",\"scroll-pe\",\"scroll-pt\",\"scroll-pr\",\"scroll-pb\",\"scroll-pl\"],\"scroll-px\":[\"scroll-pr\",\"scroll-pl\"],\"scroll-py\":[\"scroll-pt\",\"scroll-pb\"],touch:[\"touch-x\",\"touch-y\",\"touch-pz\"],\"touch-x\":[\"touch\"],\"touch-y\":[\"touch\"],\"touch-pz\":[\"touch\"]},conflictingClassGroupModifiers:{\"font-size\":[\"leading\"]}}},Hw=(e,t,n)=>{void 0!==n&&(e[t]=n)},Vw=(e,t)=>{if(t)for(const n in t)Hw(e,n,t[n])},qw=(e,t)=>{if(t)for(const n in t){const r=t[n];void 0!==r&&(e[n]=(e[n]||[]).concat(r))}},Ww=((e,...t)=>\"function\"==typeof e?fw(Zw,e,...t):fw((()=>((e,{cacheSize:t,prefix:n,separator:r,experimentalParseClassName:a,extend:o={},override:i={}})=>{Hw(e,\"cacheSize\",t),Hw(e,\"prefix\",n),Hw(e,\"separator\",r),Hw(e,\"experimentalParseClassName\",a);for(const t in i)Vw(e[t],i[t]);for(const t in o)qw(e[t],o[t]);return e})(Zw(),e)),...t))({extend:{classGroups:{\"font-size\":[\"text-3xs\",\"text-xxs\"],\"font-weight\":[\"font-sidebar\",\"font-sidebar-active\"]}}}),{cva:Xw,cx:Gw}=(e=>{const t=function(){for(var t=arguments.length,n=new Array(t),r=0;r{const r=Object.fromEntries(Object.entries(e||{}).filter((e=>{let[t]=e;return![\"class\",\"className\"].includes(t)})));return t(n.map((e=>e(r))),null==e?void 0:e.class,null==e?void 0:e.className)}},cva:e=>n=>{var r;if(null==(null==e?void 0:e.variants))return t(null==e?void 0:e.base,null==n?void 0:n.class,null==n?void 0:n.className);const{variants:a,defaultVariants:o}=e,i=Object.keys(a).map((e=>{const t=null==n?void 0:n[e],r=null==o?void 0:o[e],i=KO(t)||KO(r);return a[e][i]})),s={...o,...n&&Object.entries(n).reduce(((e,t)=>{let[n,r]=t;return void 0===r?e:{...e,[n]:r}}),{})},l=null==e||null===(r=e.compoundVariants)||void 0===r?void 0:r.reduce(((e,t)=>{let{class:n,className:r,...a}=t;return Object.entries(a).every((e=>{let[t,n]=e;const r=s[t];return Array.isArray(n)?n.includes(r):r===n}))?[...e,n,r]:e}),[]);return t(null==e?void 0:e.base,i,l,null==n?void 0:n.class,null==n?void 0:n.className)},cx:t}})({hooks:{onComplete:e=>Ww(e)}});function Yw(){const e=Zf(),t=zg((()=>{const{class:t,style:n,...r}=e;return{class:t||\"\",style:n,rest:r}}));return{cx:function(...e){return{class:Gw(...e,t.value.class),style:t.value.style,...t.value.rest}},stylingAttrsCx:function(...e){return{class:Gw(...e,t.value.class),style:t.value.style}},otherAttrs:zg((()=>t.value.rest))}}const Kw={solid:[\"scalar-button-solid\",\"bg-b-btn text-c-btn focus-visible:border-c-btn active:bg-b-btn hover:bg-h-btn outline-offset-1\"],outlined:[\"scalar-button-outlined\",\"active:bg-btn-1 border border-solid border-border bg-b-1 text-c-1 hover:bg-b-2\"],ghost:[\"scalar-button-ghost\",\"bg-transparent text-c-3 active:text-c-1 hover:text-c-1\"],gradient:[\"scalar-button-gradient\",\"border bg-b-1.5 bg-linear-to-b from-b-1 to-b-2 hover:bg-linear-to-t\",\"dark:bg-linear-to-t dark:hover:bg-linear-to-b\"],danger:[\"scalar-button-danger\",\"bg-c-danger text-white active:brightness-90 hover:brightness-90\"]},Jw={class:\"circular-loader\"},ex=nf({inheritAttrs:!1,__name:\"ScalarLoading\",props:{loader:{},size:{}},setup(e){const{cx:t}=Yw(),n=Xw({variants:{size:{xs:\"size-3\",sm:\"size-3.5\",md:\"size-4\",lg:\"size-5\",xl:\"size-6\",\"2xl\":\"size-8\",\"3xl\":\"size-10\",full:\"size-full\"}},defaultVariants:{size:\"full\"}});return(e,r)=>e.loader?(ag(),lg(\"div\",id(Sg({key:0},Hp(t)(\"loader-wrapper\",Hp(n)({size:e.size})))),[(ag(),lg(\"svg\",{class:od([\"svg-loader\",{\"icon-is-valid\":e.loader.isValid,\"icon-is-invalid\":e.loader.isInvalid}]),viewBox:\"0 0 100 100\",xmlns:\"http://www.w3.org/2000/svg\",\"xmlns:xlink\":\"http://www.w3.org/1999/xlink\"},[r[0]||(r[0]=yg('',5)),fg(\"g\",Jw,[fg(\"circle\",{class:od([\"loader-path\",{\"loader-path-off\":!e.loader.isLoading}]),cx:\"50\",cy:\"50\",fill:\"none\",r:\"20\",\"stroke-width\":\"3\"},null,2)])],2))],16)):Og(\"\",!0)}}),tx=(e,t)=>{const n=e.__vccOpts||e;for(const[e,r]of t)n[e]=r;return n},nx=tx(ex,[[\"__scopeId\",\"data-v-27df5cd8\"]]),rx={key:3,class:\"centered\"},ax=nf({inheritAttrs:!1,__name:\"ScalarButton\",props:{is:{default:\"button\"},variant:{default:\"solid\"},size:{default:\"md\"},disabled:{type:Boolean},icon:{type:[Object,Function]},loader:{}},setup(e){const t=Xw({base:\"scalar-button flex cursor-pointer items-center justify-center rounded font-medium -outline-offset-1\",variants:{disabled:{true:\"bg-b-2 text-color-3 shadow-none\"},size:{xs:\"px-2 py-1 text-xs leading-5\",sm:\"px-3.5 py-2 text-sm leading-5\",md:\"px-5 py-3 text-sm leading-5\"},variant:Kw},compoundVariants:[{disabled:!0,variant:[\"solid\",\"outlined\",\"ghost\",\"gradient\",\"danger\"],class:\"bg-b-2 text-c-3 shadow-none hover:bg-b-[_] cursor-not-allowed active:bg-b-[_] hover:text-c-[_] active:text-c-[_]\"},{disabled:!0,variant:[\"gradient\"],class:\"to-b-1.5 bg-linear-to-b hover:bg-linear-to-b dark:hover:bg-linear-to-t\"}]}),n=Xw({base:\"shrink-0\",variants:{size:{xs:\"size-2.75 -ml-0.25 mr-1\",sm:\"size-3.25 -ml-0.5 mr-1.5\",md:\"size-3.5 -ml-0.5 mr-1.5\"}}}),r=Xw({variants:{size:{xs:\"size-4\",sm:\"size-5\",md:\"size-6\"}}}),{cx:a}=Yw();return(e,o)=>(ag(),cg($f(e.is),Sg({\"aria-disabled\":e.disabled||void 0,type:\"button\"===e.is?\"button\":void 0},Hp(a)(Hp(t)({disabled:e.disabled,size:e.size,variant:e.variant}),{relative:e.loader?.isActive})),{default:Ah((()=>[e.$slots.icon||e.icon?(ag(),lg(\"div\",{key:0,class:od([Hp(n)({size:e.size}),{invisible:e.loader?.isActive}])},[Rf(e.$slots,\"icon\",{},(()=>[(ag(),cg($f(e.icon),{class:\"size-full\"}))]))],2)):Og(\"\",!0),e.loader?(ag(),lg(\"span\",{key:1,class:od({invisible:e.loader?.isActive})},[Rf(e.$slots,\"default\")],2)):Rf(e.$slots,\"default\",{key:2}),e.loader?.isActive?(ag(),lg(\"div\",rx,[mg(Hp(nx),{class:od(Hp(r)({size:e.size})),loader:e.loader},null,8,[\"class\",\"loader\"])])):Og(\"\",!0)])),_:3},16,[\"aria-disabled\",\"type\"]))}}),ox=Symbol(),ix=nf({inheritAttrs:!1,__name:\"ScalarCard\",props:{label:{}},setup(e){const{id:t}=(()=>{const e=zp();return um(ox,e),{id:e}})(),n=zg((()=>e.label?{\"aria-label\":e.label}:t.value?{\"aria-labelledby\":t.value}:{})),{cx:r}=Yw();return(e,t)=>(ag(),lg(\"div\",Sg({role:\"group\"},{...n.value,...Hp(r)(\"scalar-card bg-b-2 flex flex-col divide-y rounded-lg border *:first:rounded-t-[inherit] *:last:rounded-b-[inherit]\")}),[Rf(e.$slots,\"default\")],16))}}),sx=nf({inheritAttrs:!1,__name:\"ScalarCardSection\",setup(e){const{cx:t}=Yw();return(e,n)=>(ag(),lg(\"div\",id(gg(Hp(t)(\"scalar-card-content flex overflow-auto\"))),[Rf(e.$slots,\"default\")],16))}}),lx=nf({inheritAttrs:!1,__name:\"ScalarCardFooter\",setup(e){const{cx:t}=Yw();return(e,n)=>(ag(),cg(sx,id(gg(Hp(t)(\"scalar-card-footer\"))),{default:Ah((()=>[Rf(e.$slots,\"default\")])),_:3},16))}}),cx=[\"id\"],ux={key:0,class:\"flex\"},dx=nf({inheritAttrs:!1,__name:\"ScalarCardHeader\",setup(e){const{cx:t}=Yw(),n=rf();return(e=>{const t=dm(ox,void 0);t&&(t.value=e)})(n),(e,r)=>(ag(),cg(sx,id(gg(Hp(t)(\"scalar-card-header leading-[22px] font-medium py-[6.75px] px-3 shrink-0\"))),{default:Ah((()=>[fg(\"div\",{id:Hp(n),class:\"scalar-card-header-title min-w-0 flex-1 truncate\"},[Rf(e.$slots,\"default\")],8,cx),e.$slots.actions?(ag(),lg(\"div\",ux,[Rf(e.$slots,\"actions\")])):Og(\"\",!0)])),_:3},16))}});function px(e={}){const t=zg((()=>e.label?{\"aria-label\":e.label}:{\"aria-hidden\":!0,role:\"presentation\"}));return{bind:zg((()=>({width:\"1em\",height:\"1em\",...t.value}))),weight:zg((()=>e.weight??\"regular\"))}}const hx={key:0},fx={key:1},mx={key:2},gx={key:3},vx={key:4},bx={key:5},yx=nf({name:\"ScalarIconArrowUpRight\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",hx,[...t[0]||(t[0]=[fg(\"path\",{d:\"M204,64V168a12,12,0,0,1-24,0V93L72.49,200.49a12,12,0,0,1-17-17L163,76H88a12,12,0,0,1,0-24H192A12,12,0,0,1,204,64Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",fx,[...t[1]||(t[1]=[fg(\"path\",{d:\"M192,64V168L88,64Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M192,56H88a8,8,0,0,0-5.66,13.66L128.69,116,58.34,186.34a8,8,0,0,0,11.32,11.32L140,127.31l46.34,46.35A8,8,0,0,0,200,168V64A8,8,0,0,0,192,56Zm-8,92.69-38.34-38.34h0L107.31,72H184Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",mx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M200,64V168a8,8,0,0,1-13.66,5.66L140,127.31,69.66,197.66a8,8,0,0,1-11.32-11.32L128.69,116,82.34,69.66A8,8,0,0,1,88,56H192A8,8,0,0,1,200,64Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",gx,[...t[3]||(t[3]=[fg(\"path\",{d:\"M198,64V168a6,6,0,0,1-12,0V78.48L68.24,196.24a6,6,0,0,1-8.48-8.48L177.52,70H88a6,6,0,0,1,0-12H192A6,6,0,0,1,198,64Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",vx,[...t[4]||(t[4]=[fg(\"path\",{d:\"M200,64V168a8,8,0,0,1-16,0V83.31L69.66,197.66a8,8,0,0,1-11.32-11.32L172.69,72H88a8,8,0,0,1,0-16H192A8,8,0,0,1,200,64Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",bx,[...t[5]||(t[5]=[fg(\"path\",{d:\"M196,64V168a4,4,0,0,1-8,0V73.66L66.83,194.83a4,4,0,0,1-5.66-5.66L182.34,68H88a4,4,0,0,1,0-8H192A4,4,0,0,1,196,64Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Ox={key:0},wx={key:1},xx={key:2},kx={key:3},Sx={key:4},_x={key:5},Tx=nf({name:\"ScalarIconBook\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Ox,[...t[0]||(t[0]=[fg(\"path\",{d:\"M208,20H72A36,36,0,0,0,36,56V224a12,12,0,0,0,12,12H192a12,12,0,0,0,0-24H60v-4a12,12,0,0,1,12-12H208a12,12,0,0,0,12-12V32A12,12,0,0,0,208,20ZM196,172H72a35.59,35.59,0,0,0-12,2.06V56A12,12,0,0,1,72,44H196Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",wx,[...t[1]||(t[1]=[fg(\"path\",{d:\"M208,32V192H72a24,24,0,0,0-24,24V56A24,24,0,0,1,72,32Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M208,24H72A32,32,0,0,0,40,56V224a8,8,0,0,0,8,8H192a8,8,0,0,0,0-16H56a16,16,0,0,1,16-16H208a8,8,0,0,0,8-8V32A8,8,0,0,0,208,24Zm-8,160H72a31.82,31.82,0,0,0-16,4.29V56A16,16,0,0,1,72,40H200Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",xx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,32V192a8,8,0,0,1-8,8H72a16,16,0,0,0-16,16H192a8,8,0,0,1,0,16H48a8,8,0,0,1-8-8V56A32,32,0,0,1,72,24H208A8,8,0,0,1,216,32Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",kx,[...t[3]||(t[3]=[fg(\"path\",{d:\"M208,26H72A30,30,0,0,0,42,56V224a6,6,0,0,0,6,6H192a6,6,0,0,0,0-12H54v-2a18,18,0,0,1,18-18H208a6,6,0,0,0,6-6V32A6,6,0,0,0,208,26Zm-6,160H72a29.87,29.87,0,0,0-18,6V56A18,18,0,0,1,72,38H202Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Sx,[...t[4]||(t[4]=[fg(\"path\",{d:\"M208,24H72A32,32,0,0,0,40,56V224a8,8,0,0,0,8,8H192a8,8,0,0,0,0-16H56a16,16,0,0,1,16-16H208a8,8,0,0,0,8-8V32A8,8,0,0,0,208,24Zm-8,160H72a31.82,31.82,0,0,0-16,4.29V56A16,16,0,0,1,72,40H200Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",_x,[...t[5]||(t[5]=[fg(\"path\",{d:\"M208,28H72A28,28,0,0,0,44,56V224a4,4,0,0,0,4,4H192a4,4,0,0,0,0-8H52v-4a20,20,0,0,1,20-20H208a4,4,0,0,0,4-4V32A4,4,0,0,0,208,28Zm-4,160H72a27.94,27.94,0,0,0-20,8.42V56A20,20,0,0,1,72,36H204Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Ex={key:0},Ax={key:1},Cx={key:2},Px={key:3},$x={key:4},Dx={key:5},Ix=nf({name:\"ScalarIconBookOpenText\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Ex,[...t[0]||(t[0]=[fg(\"path\",{d:\"M232,44H160a43.86,43.86,0,0,0-32,13.85A43.86,43.86,0,0,0,96,44H24A12,12,0,0,0,12,56V200a12,12,0,0,0,12,12H96a20,20,0,0,1,20,20,12,12,0,0,0,24,0,20,20,0,0,1,20-20h72a12,12,0,0,0,12-12V56A12,12,0,0,0,232,44ZM96,188H36V68H96a20,20,0,0,1,20,20V192.81A43.79,43.79,0,0,0,96,188Zm124,0H160a43.71,43.71,0,0,0-20,4.83V88a20,20,0,0,1,20-20h60ZM164,96h32a12,12,0,0,1,0,24H164a12,12,0,0,1,0-24Zm44,52a12,12,0,0,1-12,12H164a12,12,0,0,1,0-24h32A12,12,0,0,1,208,148Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Ax,[...t[1]||(t[1]=[fg(\"path\",{d:\"M232,56V200H160a32,32,0,0,0-32,32V88a32,32,0,0,1,32-32Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M232,48H160a40,40,0,0,0-32,16A40,40,0,0,0,96,48H24a8,8,0,0,0-8,8V200a8,8,0,0,0,8,8H96a24,24,0,0,1,24,24,8,8,0,0,0,16,0,24,24,0,0,1,24-24h72a8,8,0,0,0,8-8V56A8,8,0,0,0,232,48ZM96,192H32V64H96a24,24,0,0,1,24,24V200A39.81,39.81,0,0,0,96,192Zm128,0H160a39.81,39.81,0,0,0-24,8V88a24,24,0,0,1,24-24h64ZM160,88h40a8,8,0,0,1,0,16H160a8,8,0,0,1,0-16Zm48,40a8,8,0,0,1-8,8H160a8,8,0,0,1,0-16h40A8,8,0,0,1,208,128Zm0,32a8,8,0,0,1-8,8H160a8,8,0,0,1,0-16h40A8,8,0,0,1,208,160Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Cx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M232,48H168a32,32,0,0,0-32,32v87.73a8.17,8.17,0,0,1-7.47,8.25,8,8,0,0,1-8.53-8V80A32,32,0,0,0,88,48H24a8,8,0,0,0-8,8V200a8,8,0,0,0,8,8H96a24,24,0,0,1,24,23.94,7.9,7.9,0,0,0,5.12,7.55A8,8,0,0,0,136,232a24,24,0,0,1,24-24h72a8,8,0,0,0,8-8V56A8,8,0,0,0,232,48ZM208,168H168.27a8.17,8.17,0,0,1-8.25-7.47,8,8,0,0,1,8-8.53h39.73a8.17,8.17,0,0,1,8.25,7.47A8,8,0,0,1,208,168Zm0-32H168.27a8.17,8.17,0,0,1-8.25-7.47,8,8,0,0,1,8-8.53h39.73a8.17,8.17,0,0,1,8.25,7.47A8,8,0,0,1,208,136Zm0-32H168.27A8.17,8.17,0,0,1,160,96.53,8,8,0,0,1,168,88h39.73A8.17,8.17,0,0,1,216,95.47,8,8,0,0,1,208,104Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Px,[...t[3]||(t[3]=[fg(\"path\",{d:\"M232,50H160a38,38,0,0,0-32,17.55A38,38,0,0,0,96,50H24a6,6,0,0,0-6,6V200a6,6,0,0,0,6,6H96a26,26,0,0,1,26,26,6,6,0,0,0,12,0,26,26,0,0,1,26-26h72a6,6,0,0,0,6-6V56A6,6,0,0,0,232,50ZM96,194H30V62H96a26,26,0,0,1,26,26V204.31A37.86,37.86,0,0,0,96,194Zm130,0H160a37.87,37.87,0,0,0-26,10.32V88a26,26,0,0,1,26-26h66ZM160,90h40a6,6,0,0,1,0,12H160a6,6,0,0,1,0-12Zm46,38a6,6,0,0,1-6,6H160a6,6,0,0,1,0-12h40A6,6,0,0,1,206,128Zm0,32a6,6,0,0,1-6,6H160a6,6,0,0,1,0-12h40A6,6,0,0,1,206,160Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",$x,[...t[4]||(t[4]=[fg(\"path\",{d:\"M232,48H160a40,40,0,0,0-32,16A40,40,0,0,0,96,48H24a8,8,0,0,0-8,8V200a8,8,0,0,0,8,8H96a24,24,0,0,1,24,24,8,8,0,0,0,16,0,24,24,0,0,1,24-24h72a8,8,0,0,0,8-8V56A8,8,0,0,0,232,48ZM96,192H32V64H96a24,24,0,0,1,24,24V200A39.81,39.81,0,0,0,96,192Zm128,0H160a39.81,39.81,0,0,0-24,8V88a24,24,0,0,1,24-24h64ZM160,88h40a8,8,0,0,1,0,16H160a8,8,0,0,1,0-16Zm48,40a8,8,0,0,1-8,8H160a8,8,0,0,1,0-16h40A8,8,0,0,1,208,128Zm0,32a8,8,0,0,1-8,8H160a8,8,0,0,1,0-16h40A8,8,0,0,1,208,160Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Dx,[...t[5]||(t[5]=[fg(\"path\",{d:\"M232,52H160a36,36,0,0,0-32,19.54A36,36,0,0,0,96,52H24a4,4,0,0,0-4,4V200a4,4,0,0,0,4,4H96a28,28,0,0,1,28,28,4,4,0,0,0,8,0,28,28,0,0,1,28-28h72a4,4,0,0,0,4-4V56A4,4,0,0,0,232,52ZM96,196H28V60H96a28,28,0,0,1,28,28V209.4A35.94,35.94,0,0,0,96,196Zm132,0H160a35.94,35.94,0,0,0-28,13.41V88a28,28,0,0,1,28-28h68ZM160,92h40a4,4,0,0,1,0,8H160a4,4,0,0,1,0-8Zm44,36a4,4,0,0,1-4,4H160a4,4,0,0,1,0-8h40A4,4,0,0,1,204,128Zm0,32a4,4,0,0,1-4,4H160a4,4,0,0,1,0-8h40A4,4,0,0,1,204,160Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Mx={key:0},Nx={key:1},Rx={key:2},Lx={key:3},Bx={key:4},jx={key:5},Ux=nf({name:\"ScalarIconBracketsCurly\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Mx,[...t[0]||(t[0]=[fg(\"path\",{d:\"M54.8,119.49A35.06,35.06,0,0,1,49.05,128a35.06,35.06,0,0,1,5.75,8.51C60,147.24,60,159.83,60,172c0,25.94,1.84,32,20,32a12,12,0,0,1,0,24c-19.14,0-32.2-6.9-38.8-20.51C36,196.76,36,184.17,36,172c0-25.94-1.84-32-20-32a12,12,0,0,1,0-24c18.16,0,20-6.06,20-32,0-12.17,0-24.76,5.2-35.49C47.8,34.9,60.86,28,80,28a12,12,0,0,1,0,24c-18.16,0-20,6.06-20,32C60,96.17,60,108.76,54.8,119.49ZM240,116c-18.16,0-20-6.06-20-32,0-12.17,0-24.76-5.2-35.49C208.2,34.9,195.14,28,176,28a12,12,0,0,0,0,24c18.16,0,20,6.06,20,32,0,12.17,0,24.76,5.2,35.49A35.06,35.06,0,0,0,207,128a35.06,35.06,0,0,0-5.75,8.51C196,147.24,196,159.83,196,172c0,25.94-1.84,32-20,32a12,12,0,0,0,0,24c19.14,0,32.2-6.9,38.8-20.51C220,196.76,220,184.17,220,172c0-25.94,1.84-32,20-32a12,12,0,0,0,0-24Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Nx,[...t[1]||(t[1]=[fg(\"path\",{d:\"M240,128c-64,0,0,88-64,88H80c-64,0,0-88-64-88,64,0,0-88,64-88h96C240,40,176,128,240,128Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M43.18,128a29.78,29.78,0,0,1,8,10.26c4.8,9.9,4.8,22,4.8,33.74,0,24.31,1,36,24,36a8,8,0,0,1,0,16c-17.48,0-29.32-6.14-35.2-18.26-4.8-9.9-4.8-22-4.8-33.74,0-24.31-1-36-24-36a8,8,0,0,1,0-16c23,0,24-11.69,24-36,0-11.72,0-23.84,4.8-33.74C50.68,38.14,62.52,32,80,32a8,8,0,0,1,0,16C57,48,56,59.69,56,84c0,11.72,0,23.84-4.8,33.74A29.78,29.78,0,0,1,43.18,128ZM240,120c-23,0-24-11.69-24-36,0-11.72,0-23.84-4.8-33.74C205.32,38.14,193.48,32,176,32a8,8,0,0,0,0,16c23,0,24,11.69,24,36,0,11.72,0,23.84,4.8,33.74a29.78,29.78,0,0,0,8,10.26,29.78,29.78,0,0,0-8,10.26c-4.8,9.9-4.8,22-4.8,33.74,0,24.31-1,36-24,36a8,8,0,0,0,0,16c17.48,0,29.32-6.14,35.2-18.26,4.8-9.9,4.8-22,4.8-33.74,0-24.31,1-36,24-36a8,8,0,0,0,0-16Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Rx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM88,155.84c.29,14.26.41,20.16,16,20.16a8,8,0,0,1,0,16c-31.27,0-31.72-22.43-32-35.84C71.71,141.9,71.59,136,56,136a8,8,0,0,1,0-16c15.59,0,15.71-5.9,16-20.16C72.28,86.43,72.73,64,104,64a8,8,0,0,1,0,16c-15.59,0-15.71,5.9-16,20.16-.17,8.31-.41,20.09-8,27.84C87.59,135.75,87.83,147.53,88,155.84ZM200,136c-15.59,0-15.71,5.9-16,20.16-.28,13.41-.73,35.84-32,35.84a8,8,0,0,1,0-16c15.59,0,15.71-5.9,16-20.16.17-8.31.41-20.09,8-27.84-7.6-7.75-7.84-19.53-8-27.84C167.71,85.9,167.59,80,152,80a8,8,0,0,1,0-16c31.27,0,31.72,22.43,32,35.84.29,14.26.41,20.16,16,20.16a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Lx,[...t[3]||(t[3]=[fg(\"path\",{d:\"M39.91,128a27.68,27.68,0,0,1,9.49,11.13C54,148.62,54,160.51,54,172c0,24.27,1.21,38,26,38a6,6,0,0,1,0,12c-16.88,0-27.81-5.6-33.4-17.13C42,195.38,42,183.49,42,172c0-24.27-1.21-38-26-38a6,6,0,0,1,0-12c24.79,0,26-13.73,26-38,0-11.49,0-23.38,4.6-32.87C52.19,39.6,63.12,34,80,34a6,6,0,0,1,0,12C55.21,46,54,59.73,54,84c0,11.49,0,23.38-4.6,32.87A27.68,27.68,0,0,1,39.91,128ZM240,122c-24.79,0-26-13.73-26-38,0-11.49,0-23.38-4.6-32.87C203.81,39.6,192.88,34,176,34a6,6,0,0,0,0,12c24.79,0,26,13.73,26,38,0,11.49,0,23.38,4.6,32.87A27.68,27.68,0,0,0,216.09,128a27.68,27.68,0,0,0-9.49,11.13C202,148.62,202,160.51,202,172c0,24.27-1.21,38-26,38a6,6,0,0,0,0,12c16.88,0,27.81-5.6,33.4-17.13,4.6-9.49,4.6-21.38,4.6-32.87,0-24.27,1.21-38,26-38a6,6,0,0,0,0-12Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Bx,[...t[4]||(t[4]=[fg(\"path\",{d:\"M43.18,128a29.78,29.78,0,0,1,8,10.26c4.8,9.9,4.8,22,4.8,33.74,0,24.31,1,36,24,36a8,8,0,0,1,0,16c-17.48,0-29.32-6.14-35.2-18.26-4.8-9.9-4.8-22-4.8-33.74,0-24.31-1-36-24-36a8,8,0,0,1,0-16c23,0,24-11.69,24-36,0-11.72,0-23.84,4.8-33.74C50.68,38.14,62.52,32,80,32a8,8,0,0,1,0,16C57,48,56,59.69,56,84c0,11.72,0,23.84-4.8,33.74A29.78,29.78,0,0,1,43.18,128ZM240,120c-23,0-24-11.69-24-36,0-11.72,0-23.84-4.8-33.74C205.32,38.14,193.48,32,176,32a8,8,0,0,0,0,16c23,0,24,11.69,24,36,0,11.72,0,23.84,4.8,33.74a29.78,29.78,0,0,0,8,10.26,29.78,29.78,0,0,0-8,10.26c-4.8,9.9-4.8,22-4.8,33.74,0,24.31-1,36-24,36a8,8,0,0,0,0,16c17.48,0,29.32-6.14,35.2-18.26,4.8-9.9,4.8-22,4.8-33.74,0-24.31,1-36,24-36a8,8,0,0,0,0-16Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",jx,[...t[5]||(t[5]=[fg(\"path\",{d:\"M35.89,128C52,136.23,52,155.64,52,172c0,24.8,1.35,40,28,40a4,4,0,0,1,0,8c-36,0-36-26.61-36-48,0-24.8-1.35-40-28-40a4,4,0,0,1,0-8c26.65,0,28-15.2,28-40,0-21.39,0-48,36-48a4,4,0,0,1,0,8C53.35,44,52,59.2,52,84,52,100.36,52,119.77,35.89,128ZM240,124c-26.65,0-28-15.2-28-40,0-21.39,0-48-36-48a4,4,0,0,0,0,8c26.65,0,28,15.2,28,40,0,16.36,0,35.77,16.11,44C204,136.23,204,155.64,204,172c0,24.8-1.35,40-28,40a4,4,0,0,0,0,8c36,0,36-26.61,36-48,0-24.8,1.35-40,28-40a4,4,0,0,0,0-8Z\"},null,-1)])])):Og(\"\",!0)],16))}}),zx={key:0},Fx={key:1},Qx={key:2},Zx={key:3},Hx={key:4},Vx={key:5},qx=nf({name:\"ScalarIconCaretDown\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",zx,[...t[0]||(t[0]=[fg(\"path\",{d:\"M216.49,104.49l-80,80a12,12,0,0,1-17,0l-80-80a12,12,0,0,1,17-17L128,159l71.51-71.52a12,12,0,0,1,17,17Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Fx,[...t[1]||(t[1]=[fg(\"path\",{d:\"M208,96l-80,80L48,96Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M215.39,92.94A8,8,0,0,0,208,88H48a8,8,0,0,0-5.66,13.66l80,80a8,8,0,0,0,11.32,0l80-80A8,8,0,0,0,215.39,92.94ZM128,164.69,67.31,104H188.69Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Qx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Zx,[...t[3]||(t[3]=[fg(\"path\",{d:\"M212.24,100.24l-80,80a6,6,0,0,1-8.48,0l-80-80a6,6,0,0,1,8.48-8.48L128,167.51l75.76-75.75a6,6,0,0,1,8.48,8.48Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Hx,[...t[4]||(t[4]=[fg(\"path\",{d:\"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Vx,[...t[5]||(t[5]=[fg(\"path\",{d:\"M210.83,98.83l-80,80a4,4,0,0,1-5.66,0l-80-80a4,4,0,0,1,5.66-5.66L128,170.34l77.17-77.17a4,4,0,1,1,5.66,5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Wx={key:0},Xx={key:1},Gx={key:2},Yx={key:3},Kx={key:4},Jx={key:5},ek=nf({name:\"ScalarIconCaretRight\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Wx,[...t[0]||(t[0]=[fg(\"path\",{d:\"M184.49,136.49l-80,80a12,12,0,0,1-17-17L159,128,87.51,56.49a12,12,0,1,1,17-17l80,80A12,12,0,0,1,184.49,136.49Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Xx,[...t[1]||(t[1]=[fg(\"path\",{d:\"M176,128,96,208V48Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M181.66,122.34l-80-80A8,8,0,0,0,88,48V208a8,8,0,0,0,13.66,5.66l80-80A8,8,0,0,0,181.66,122.34ZM104,188.69V67.31L164.69,128Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Gx,[...t[2]||(t[2]=[fg(\"path\",{d:\"M181.66,133.66l-80,80A8,8,0,0,1,88,208V48a8,8,0,0,1,13.66-5.66l80,80A8,8,0,0,1,181.66,133.66Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Yx,[...t[3]||(t[3]=[fg(\"path\",{d:\"M180.24,132.24l-80,80a6,6,0,0,1-8.48-8.48L167.51,128,91.76,52.24a6,6,0,0,1,8.48-8.48l80,80A6,6,0,0,1,180.24,132.24Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Kx,[...t[4]||(t[4]=[fg(\"path\",{d:\"M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Jx,[...t[5]||(t[5]=[fg(\"path\",{d:\"M178.83,130.83l-80,80a4,4,0,0,1-5.66-5.66L170.34,128,93.17,50.83a4,4,0,0,1,5.66-5.66l80,80A4,4,0,0,1,178.83,130.83Z\"},null,-1)])])):Og(\"\",!0)],16))}}),tk={key:0},nk={key:1},rk={key:2},ak={key:3},ok={key:4},ik={key:5},sk=nf({name:\"ScalarIconCaretUpDown\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",tk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M184.49,167.51a12,12,0,0,1,0,17l-48,48a12,12,0,0,1-17,0l-48-48a12,12,0,0,1,17-17L128,207l39.51-39.52A12,12,0,0,1,184.49,167.51Zm-96-79L128,49l39.51,39.52a12,12,0,0,0,17-17l-48-48a12,12,0,0,0-17,0l-48,48a12,12,0,0,0,17,17Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",nk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M80,176h96l-48,48ZM128,32,80,80h96Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M176,168H80a8,8,0,0,0-5.66,13.66l48,48a8,8,0,0,0,11.32,0l48-48A8,8,0,0,0,176,168Zm-48,44.69L99.31,184h57.38ZM80,88h96a8,8,0,0,0,5.66-13.66l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,80,88Zm48-44.69L156.69,72H99.31Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",rk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M72.61,83.06a8,8,0,0,1,1.73-8.72l48-48a8,8,0,0,1,11.32,0l48,48A8,8,0,0,1,176,88H80A8,8,0,0,1,72.61,83.06ZM176,168H80a8,8,0,0,0-5.66,13.66l48,48a8,8,0,0,0,11.32,0l48-48A8,8,0,0,0,176,168Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",ak,[...t[3]||(t[3]=[fg(\"path\",{d:\"M180.24,171.76a6,6,0,0,1,0,8.48l-48,48a6,6,0,0,1-8.48,0l-48-48a6,6,0,0,1,8.48-8.48L128,215.51l43.76-43.75A6,6,0,0,1,180.24,171.76Zm-96-87.52L128,40.49l43.76,43.75a6,6,0,0,0,8.48-8.48l-48-48a6,6,0,0,0-8.48,0l-48,48a6,6,0,0,0,8.48,8.48Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",ok,[...t[4]||(t[4]=[fg(\"path\",{d:\"M181.66,170.34a8,8,0,0,1,0,11.32l-48,48a8,8,0,0,1-11.32,0l-48-48a8,8,0,0,1,11.32-11.32L128,212.69l42.34-42.35A8,8,0,0,1,181.66,170.34Zm-96-84.68L128,43.31l42.34,42.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,85.66Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",ik,[...t[5]||(t[5]=[fg(\"path\",{d:\"M178.83,173.17a4,4,0,0,1,0,5.66l-48,48a4,4,0,0,1-5.66,0l-48-48a4,4,0,0,1,5.66-5.66L128,218.34l45.17-45.17A4,4,0,0,1,178.83,173.17Zm-96-90.34L128,37.66l45.17,45.17a4,4,0,1,0,5.66-5.66l-48-48a4,4,0,0,0-5.66,0l-48,48a4,4,0,0,0,5.66,5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),lk={key:0},ck={key:1},uk={key:2},dk={key:3},pk={key:4},hk={key:5},fk=nf({name:\"ScalarIconCheck\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",lk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",ck,[...t[1]||(t[1]=[fg(\"path\",{d:\"M232,56V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",uk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",dk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M228.24,76.24l-128,128a6,6,0,0,1-8.48,0l-56-56a6,6,0,0,1,8.48-8.48L96,191.51,219.76,67.76a6,6,0,0,1,8.48,8.48Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",pk,[...t[4]||(t[4]=[fg(\"path\",{d:\"M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",hk,[...t[5]||(t[5]=[fg(\"path\",{d:\"M226.83,74.83l-128,128a4,4,0,0,1-5.66,0l-56-56a4,4,0,0,1,5.66-5.66L96,194.34,221.17,69.17a4,4,0,1,1,5.66,5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),mk={key:0},gk={key:1},vk={key:2},bk={key:3},yk={key:4},Ok={key:5},wk=nf({name:\"ScalarIconCopy\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",mk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",gk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,40V168H168V88H88V40Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",vk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-8,128H176V88a8,8,0,0,0-8-8H96V48H208Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",bk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M216,34H88a6,6,0,0,0-6,6V82H40a6,6,0,0,0-6,6V216a6,6,0,0,0,6,6H168a6,6,0,0,0,6-6V174h42a6,6,0,0,0,6-6V40A6,6,0,0,0,216,34ZM162,210H46V94H162Zm48-48H174V88a6,6,0,0,0-6-6H94V46H210Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",yk,[...t[4]||(t[4]=[fg(\"path\",{d:\"M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Ok,[...t[5]||(t[5]=[fg(\"path\",{d:\"M216,36H88a4,4,0,0,0-4,4V84H40a4,4,0,0,0-4,4V216a4,4,0,0,0,4,4H168a4,4,0,0,0,4-4V172h44a4,4,0,0,0,4-4V40A4,4,0,0,0,216,36ZM164,212H44V92H164Zm48-48H172V88a4,4,0,0,0-4-4H92V44H212Z\"},null,-1)])])):Og(\"\",!0)],16))}}),xk={key:0},kk={key:1},Sk={key:2},_k={key:3},Tk={key:4},Ek={key:5},Ak=nf({name:\"ScalarIconDiscordLogo\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",xk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M108,136a16,16,0,1,1-16-16A16,16,0,0,1,108,136Zm56-16a16,16,0,1,0,16,16A16,16,0,0,0,164,120Zm76.07,76.56-67,29.71A20.15,20.15,0,0,1,146,214.9l-8.54-23.13c-3.13.14-6.27.24-9.45.24s-6.32-.1-9.45-.24L110,214.9a20.19,20.19,0,0,1-27.08,11.37l-67-29.71A19.93,19.93,0,0,1,4.62,173.41L34.15,57A20,20,0,0,1,50.37,42.19l36.06-5.93A20.26,20.26,0,0,1,109.22,51.1l4.41,17.41c4.74-.33,9.52-.51,14.37-.51s9.63.18,14.37.51l4.41-17.41a20.25,20.25,0,0,1,22.79-14.84l36.06,5.93A20,20,0,0,1,221.85,57l29.53,116.38A19.93,19.93,0,0,1,240.07,196.56ZM227.28,176,199.23,65.46l-30.07-4.94-2.84,11.17c2.9.58,5.78,1.2,8.61,1.92a12,12,0,1,1-5.86,23.27A168.43,168.43,0,0,0,128,92a168.43,168.43,0,0,0-41.07,4.88,12,12,0,0,1-5.86-23.27c2.83-.72,5.71-1.34,8.61-1.92L86.85,60.52,56.77,65.46,28.72,176l60.22,26.7,5-13.57c-4.37-.76-8.67-1.65-12.88-2.71a12,12,0,0,1,5.86-23.28A168.43,168.43,0,0,0,128,168a168.43,168.43,0,0,0,41.07-4.88,12,12,0,0,1,5.86,23.28c-4.21,1.06-8.51,1.95-12.88,2.71l5,13.57Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",kk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M235.21,185.59l-67,29.7a8.15,8.15,0,0,1-11-4.56L147,183.06a190.5,190.5,0,0,1-19,.94,190.5,190.5,0,0,1-19-.94L98.75,210.73a8.15,8.15,0,0,1-11,4.56l-67-29.7a8,8,0,0,1-4.55-9.24L45.77,60A8.08,8.08,0,0,1,52.31,54l36.06-5.92a8.1,8.1,0,0,1,9.21,6l5,19.63a192.32,192.32,0,0,1,50.88,0l5-19.63a8.1,8.1,0,0,1,9.21-6L203.69,54A8.08,8.08,0,0,1,210.23,60l29.53,116.37A8,8,0,0,1,235.21,185.59Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M104,140a12,12,0,1,1-12-12A12,12,0,0,1,104,140Zm60-12a12,12,0,1,0,12,12A12,12,0,0,0,164,128Zm74.45,64.9-67,29.71a16.17,16.17,0,0,1-21.71-9.1l-8.11-22q-6.72.45-13.63.46t-13.63-.46l-8.11,22a16.18,16.18,0,0,1-21.71,9.1l-67-29.71a15.94,15.94,0,0,1-9.06-18.51L38,58A16.08,16.08,0,0,1,51,46.13l36.06-5.92a16.21,16.21,0,0,1,18.26,11.88l3.26,12.83Q118.11,64,128,64t19.4.92l3.26-12.83a16.22,16.22,0,0,1,18.26-11.88L205,46.13A16.08,16.08,0,0,1,218,58l29.53,116.38A15.94,15.94,0,0,1,238.45,192.9ZM232,178.28,202.47,62s0,0-.08,0L166.33,56a.17.17,0,0,0-.17,0l-2.83,11.14c5,.94,10,2.06,14.83,3.42A8,8,0,0,1,176,86.31a8.09,8.09,0,0,1-2.16-.3A172.25,172.25,0,0,0,128,80a172.25,172.25,0,0,0-45.84,6,8,8,0,1,1-4.32-15.4c4.82-1.36,9.78-2.48,14.82-3.42L89.83,56a.21.21,0,0,0-.12,0h0L53.61,61.92a.24.24,0,0,0-.09,0L24,178.33,91,208a.21.21,0,0,0,.22,0L98,189.72a173.2,173.2,0,0,1-20.14-4.32A8,8,0,0,1,82.16,170,171.85,171.85,0,0,0,128,176a171.85,171.85,0,0,0,45.84-6,8,8,0,0,1,4.32,15.41A173.2,173.2,0,0,1,158,189.72L164.75,208a.22.22,0,0,0,.21,0Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Sk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M247.51,174.39,218,58a16.08,16.08,0,0,0-13-11.88l-36.06-5.92a16.22,16.22,0,0,0-18.26,11.88l-.21.85a4,4,0,0,0,3.27,4.93,155.62,155.62,0,0,1,24.41,5.62,8.2,8.2,0,0,1,5.62,9.7,8,8,0,0,1-10.19,5.64,155.4,155.4,0,0,0-90.8-.1,8.22,8.22,0,0,1-10.28-4.81,8,8,0,0,1,5.08-10.33,156.85,156.85,0,0,1,24.72-5.72,4,4,0,0,0,3.27-4.93l-.21-.85A16.21,16.21,0,0,0,87.08,40.21L51,46.13A16.08,16.08,0,0,0,38,58L8.49,174.39a15.94,15.94,0,0,0,9.06,18.51l67,29.71a16.17,16.17,0,0,0,21.71-9.1l3.49-9.45a4,4,0,0,0-3.27-5.35,158.13,158.13,0,0,1-28.63-6.2,8.2,8.2,0,0,1-5.61-9.67,8,8,0,0,1,10.2-5.66,155.59,155.59,0,0,0,91.12,0,8,8,0,0,1,10.19,5.65,8.19,8.19,0,0,1-5.61,9.68,157.84,157.84,0,0,1-28.62,6.2,4,4,0,0,0-3.27,5.35l3.49,9.45a16.18,16.18,0,0,0,21.71,9.1l67-29.71A15.94,15.94,0,0,0,247.51,174.39ZM92,152a12,12,0,1,1,12-12A12,12,0,0,1,92,152Zm72,0a12,12,0,1,1,12-12A12,12,0,0,1,164,152Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",_k,[...t[3]||(t[3]=[fg(\"path\",{d:\"M102,140a10,10,0,1,1-10-10A10,10,0,0,1,102,140Zm62-10a10,10,0,1,0,10,10A10,10,0,0,0,164,130Zm73.64,61.08-67,29.71a14.43,14.43,0,0,1-5.77,1.21,14.13,14.13,0,0,1-13.25-9.18L143,189.43c-4.93.37-9.92.58-15,.58s-10.06-.21-15-.58l-8.63,23.39A14.13,14.13,0,0,1,91.13,222a14.43,14.43,0,0,1-5.77-1.21l-67-29.71a14,14,0,0,1-7.93-16.2L40,58.5A14.07,14.07,0,0,1,51.34,48.11L87.4,42.19a14.19,14.19,0,0,1,16,10.39l3.69,14.53a197.5,197.5,0,0,1,41.82,0l3.69-14.53a14.19,14.19,0,0,1,16-10.39l36.06,5.92A14.07,14.07,0,0,1,216,58.5l29.53,116.38A14,14,0,0,1,237.64,191.08Zm-3.7-13.25L204.41,61.45a2.08,2.08,0,0,0-1.7-1.5L166.65,54a2.13,2.13,0,0,0-2.42,1.5l-3.36,13.24a169.28,169.28,0,0,1,16.75,3.76A6,6,0,0,1,176,84.31a5.71,5.71,0,0,1-1.62-.23A174.26,174.26,0,0,0,128,78a174.26,174.26,0,0,0-46.38,6.08,6,6,0,1,1-3.24-11.55,169.28,169.28,0,0,1,16.75-3.76L91.77,55.53A2.12,2.12,0,0,0,89.35,54L53.29,60a2.08,2.08,0,0,0-1.7,1.5L22.06,177.83a2,2,0,0,0,1.16,2.28l67,29.7a2.19,2.19,0,0,0,1.76,0,2.07,2.07,0,0,0,1.14-1.17l7.58-20.55a171.46,171.46,0,0,1-22.33-4.64,6,6,0,1,1,3.24-11.55A174.26,174.26,0,0,0,128,178a174.26,174.26,0,0,0,46.38-6.08,6,6,0,1,1,3.24,11.55,171.46,171.46,0,0,1-22.33,4.64l7.58,20.55a2.07,2.07,0,0,0,1.14,1.17,2.19,2.19,0,0,0,1.76,0l67-29.7A2,2,0,0,0,233.94,177.83Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Tk,[...t[4]||(t[4]=[fg(\"path\",{d:\"M104,140a12,12,0,1,1-12-12A12,12,0,0,1,104,140Zm60-12a12,12,0,1,0,12,12A12,12,0,0,0,164,128Zm74.45,64.9-67,29.71a16.17,16.17,0,0,1-21.71-9.1l-8.11-22q-6.72.45-13.63.46t-13.63-.46l-8.11,22a16.18,16.18,0,0,1-21.71,9.1l-67-29.71a15.93,15.93,0,0,1-9.06-18.51L38,58A16.07,16.07,0,0,1,51,46.14l36.06-5.93a16.22,16.22,0,0,1,18.26,11.88l3.26,12.84Q118.11,64,128,64t19.4.93l3.26-12.84a16.21,16.21,0,0,1,18.26-11.88L205,46.14A16.07,16.07,0,0,1,218,58l29.53,116.38A15.93,15.93,0,0,1,238.45,192.9ZM232,178.28,202.47,62s0,0-.08,0L166.33,56a.17.17,0,0,0-.17,0l-2.83,11.14c5,.94,10,2.06,14.83,3.42A8,8,0,0,1,176,86.31a8.09,8.09,0,0,1-2.16-.3A172.25,172.25,0,0,0,128,80a172.25,172.25,0,0,0-45.84,6,8,8,0,1,1-4.32-15.4c4.82-1.36,9.78-2.48,14.82-3.42L89.83,56s0,0-.12,0h0L53.61,61.93a.17.17,0,0,0-.09,0L24,178.33,91,208a.23.23,0,0,0,.22,0L98,189.72a173.2,173.2,0,0,1-20.14-4.32A8,8,0,0,1,82.16,170,171.85,171.85,0,0,0,128,176a171.85,171.85,0,0,0,45.84-6,8,8,0,0,1,4.32,15.41A173.2,173.2,0,0,1,158,189.72L164.75,208a.22.22,0,0,0,.21,0Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Ek,[...t[5]||(t[5]=[fg(\"path\",{d:\"M100,140a8,8,0,1,1-8-8A8,8,0,0,1,100,140Zm64-8a8,8,0,1,0,8,8A8,8,0,0,0,164,132Zm72.83,57.25-67,29.71a12.36,12.36,0,0,1-5,1,12.13,12.13,0,0,1-11.38-7.88l-9.15-24.81c-5.36.45-10.81.69-16.34.69s-11-.24-16.34-.69l-9.15,24.81A12.13,12.13,0,0,1,91.13,220a12.36,12.36,0,0,1-5-1l-67-29.71a12,12,0,0,1-6.8-13.88L41.9,59a12.06,12.06,0,0,1,9.77-8.91l36.06-5.92a12.18,12.18,0,0,1,13.73,8.91l4.12,16.22a195.47,195.47,0,0,1,44.84,0l4.12-16.22a12.18,12.18,0,0,1,13.73-8.91l36.06,5.92A12.06,12.06,0,0,1,214.1,59l29.53,116.38A12,12,0,0,1,236.83,189.25Zm-1-11.91L206.35,61A4.07,4.07,0,0,0,203,58L167,52.05a4.15,4.15,0,0,0-4.69,3L158.4,70.38a166.74,166.74,0,0,1,18.68,4.08,4,4,0,1,1-2.16,7.7A176.21,176.21,0,0,0,128,76a176.21,176.21,0,0,0-46.92,6.16,4,4,0,1,1-2.16-7.7A166.74,166.74,0,0,1,97.6,70.38L93.71,55a4.15,4.15,0,0,0-4.69-3L53,58a4.07,4.07,0,0,0-3.31,3L20.12,177.34a4,4,0,0,0,2.29,4.59l67,29.71a4.16,4.16,0,0,0,3.35,0A4,4,0,0,0,95,209.35l8.45-22.88a171.49,171.49,0,0,1-24.53-4.92,4,4,0,0,1,2.16-7.71A176.21,176.21,0,0,0,128,180a176.21,176.21,0,0,0,46.92-6.16,4,4,0,0,1,2.16,7.71,171.49,171.49,0,0,1-24.53,4.92L161,209.35a4,4,0,0,0,2.23,2.32,4.16,4.16,0,0,0,3.35,0l67-29.71A4,4,0,0,0,235.88,177.34Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Ck={key:0},Pk={key:1},$k={key:2},Dk={key:3},Ik={key:4},Mk={key:5},Nk=nf({name:\"ScalarIconEnvelopeSimple\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Ck,[...t[0]||(t[0]=[fg(\"path\",{d:\"M224,44H32A12,12,0,0,0,20,56V192a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V56A12,12,0,0,0,224,44ZM193.15,68,128,127.72,62.85,68ZM44,188V83.28l75.89,69.57a12,12,0,0,0,16.22,0L212,83.28V188Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Pk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,56l-96,88L32,56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",$k,[...t[2]||(t[2]=[fg(\"path\",{d:\"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48Zm-8,144H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Dk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M224,50H32a6,6,0,0,0-6,6V192a14,14,0,0,0,14,14H216a14,14,0,0,0,14-14V56A6,6,0,0,0,224,50ZM208.58,62,128,135.86,47.42,62ZM216,194H40a2,2,0,0,1-2-2V69.64l86,78.78a6,6,0,0,0,8.1,0L218,69.64V192A2,2,0,0,1,216,194Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Ik,[...t[4]||(t[4]=[fg(\"path\",{d:\"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Mk,[...t[5]||(t[5]=[fg(\"path\",{d:\"M224,52H32a4,4,0,0,0-4,4V192a12,12,0,0,0,12,12H216a12,12,0,0,0,12-12V56A4,4,0,0,0,224,52Zm-10.28,8L128,138.57,42.28,60ZM216,196H40a4,4,0,0,1-4-4V65.09L125.3,147a4,4,0,0,0,5.4,0L220,65.09V192A4,4,0,0,1,216,196Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Rk={key:0},Lk={key:1},Bk={key:2},jk={key:3},Uk={key:4},zk={key:5},Fk=nf({name:\"ScalarIconEye\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Rk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M251,123.13c-.37-.81-9.13-20.26-28.48-39.61C196.63,57.67,164,44,128,44S59.37,57.67,33.51,83.52C14.16,102.87,5.4,122.32,5,123.13a12.08,12.08,0,0,0,0,9.75c.37.82,9.13,20.26,28.49,39.61C59.37,198.34,92,212,128,212s68.63-13.66,94.48-39.51c19.36-19.35,28.12-38.79,28.49-39.61A12.08,12.08,0,0,0,251,123.13Zm-46.06,33C183.47,177.27,157.59,188,128,188s-55.47-10.73-76.91-31.88A130.36,130.36,0,0,1,29.52,128,130.45,130.45,0,0,1,51.09,99.89C72.54,78.73,98.41,68,128,68s55.46,10.73,76.91,31.89A130.36,130.36,0,0,1,226.48,128,130.45,130.45,0,0,1,204.91,156.12ZM128,84a44,44,0,1,0,44,44A44.05,44.05,0,0,0,128,84Zm0,64a20,20,0,1,1,20-20A20,20,0,0,1,128,148Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Lk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M128,56C48,56,16,128,16,128s32,72,112,72,112-72,112-72S208,56,128,56Zm0,112a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M247.31,124.76c-.35-.79-8.82-19.58-27.65-38.41C194.57,61.26,162.88,48,128,48S61.43,61.26,36.34,86.35C17.51,105.18,9,124,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208s66.57-13.26,91.66-38.34c18.83-18.83,27.3-37.61,27.65-38.4A8,8,0,0,0,247.31,124.76ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.47,133.47,0,0,1,25,128,133.33,133.33,0,0,1,48.07,97.25C70.33,75.19,97.22,64,128,64s57.67,11.19,79.93,33.25A133.46,133.46,0,0,1,231.05,128C223.84,141.46,192.43,192,128,192Zm0-112a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Bk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M247.31,124.76c-.35-.79-8.82-19.58-27.65-38.41C194.57,61.26,162.88,48,128,48S61.43,61.26,36.34,86.35C17.51,105.18,9,124,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208s66.57-13.26,91.66-38.34c18.83-18.83,27.3-37.61,27.65-38.4A8,8,0,0,0,247.31,124.76ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",jk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M245.48,125.57c-.34-.78-8.66-19.23-27.24-37.81C201,70.54,171.38,50,128,50S55,70.54,37.76,87.76c-18.58,18.58-26.9,37-27.24,37.81a6,6,0,0,0,0,4.88c.34.77,8.66,19.22,27.24,37.8C55,185.47,84.62,206,128,206s73-20.53,90.24-37.75c18.58-18.58,26.9-37,27.24-37.8A6,6,0,0,0,245.48,125.57ZM128,194c-31.38,0-58.78-11.42-81.45-33.93A134.77,134.77,0,0,1,22.69,128,134.56,134.56,0,0,1,46.55,95.94C69.22,73.42,96.62,62,128,62s58.78,11.42,81.45,33.94A134.56,134.56,0,0,1,233.31,128C226.94,140.21,195,194,128,194Zm0-112a46,46,0,1,0,46,46A46.06,46.06,0,0,0,128,82Zm0,80a34,34,0,1,1,34-34A34,34,0,0,1,128,162Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Uk,[...t[4]||(t[4]=[fg(\"path\",{d:\"M247.31,124.76c-.35-.79-8.82-19.58-27.65-38.41C194.57,61.26,162.88,48,128,48S61.43,61.26,36.34,86.35C17.51,105.18,9,124,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208s66.57-13.26,91.66-38.34c18.83-18.83,27.3-37.61,27.65-38.4A8,8,0,0,0,247.31,124.76ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.47,133.47,0,0,1,25,128,133.33,133.33,0,0,1,48.07,97.25C70.33,75.19,97.22,64,128,64s57.67,11.19,79.93,33.25A133.46,133.46,0,0,1,231.05,128C223.84,141.46,192.43,192,128,192Zm0-112a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",zk,[...t[5]||(t[5]=[fg(\"path\",{d:\"M243.66,126.38c-.34-.76-8.52-18.89-26.83-37.2C199.87,72.22,170.7,52,128,52S56.13,72.22,39.17,89.18c-18.31,18.31-26.49,36.44-26.83,37.2a4.08,4.08,0,0,0,0,3.25c.34.77,8.52,18.89,26.83,37.2,17,17,46.14,37.17,88.83,37.17s71.87-20.21,88.83-37.17c18.31-18.31,26.49-36.43,26.83-37.2A4.08,4.08,0,0,0,243.66,126.38Zm-32.7,35c-23.07,23-51,34.62-83,34.62s-59.89-11.65-83-34.62A135.71,135.71,0,0,1,20.44,128,135.69,135.69,0,0,1,45,94.62C68.11,71.65,96,60,128,60s59.89,11.65,83,34.62A135.79,135.79,0,0,1,235.56,128,135.71,135.71,0,0,1,211,161.38ZM128,84a44,44,0,1,0,44,44A44.05,44.05,0,0,0,128,84Zm0,80a36,36,0,1,1,36-36A36,36,0,0,1,128,164Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Qk={key:0},Zk={key:1},Hk={key:2},Vk={key:3},qk={key:4},Wk={key:5},Xk=nf({name:\"ScalarIconEyeSlash\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Qk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M56.88,31.93A12,12,0,1,0,39.12,48.07l16,17.65C20.67,88.66,5.72,121.58,5,123.13a12.08,12.08,0,0,0,0,9.75c.37.82,9.13,20.26,28.49,39.61C59.37,198.34,92,212,128,212a131.34,131.34,0,0,0,51-10l20.09,22.1a12,12,0,0,0,17.76-16.14ZM128,188c-29.59,0-55.47-10.73-76.91-31.88A130.69,130.69,0,0,1,29.52,128c5.27-9.31,18.79-29.9,42-44.29l90.09,99.11A109.33,109.33,0,0,1,128,188Zm123-55.12c-.36.81-9,20-28,39.16a12,12,0,1,1-17-16.9A130.48,130.48,0,0,0,226.48,128a130.36,130.36,0,0,0-21.57-28.12C183.46,78.73,157.59,68,128,68c-3.35,0-6.7.14-10,.42a12,12,0,1,1-2-23.91c3.93-.34,8-.51,12-.51,36,0,68.63,13.67,94.49,39.52,19.35,19.35,28.11,38.8,28.48,39.61A12.08,12.08,0,0,1,251,132.88Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Zk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M128,56C48,56,16,128,16,128s32,72,112,72,112-72,112-72S208,56,128,56Zm0,112a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M53.92,34.62A8,8,0,1,0,42.08,45.38L61.32,66.55C25,88.84,9.38,123.2,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208a127.11,127.11,0,0,0,52.07-10.83l22,24.21a8,8,0,1,0,11.84-10.76Zm47.33,75.84,41.67,45.85a32,32,0,0,1-41.67-45.85ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.16,133.16,0,0,1,25,128c4.69-8.79,19.66-33.39,47.35-49.38l18,19.75a48,48,0,0,0,63.66,70l14.73,16.2A112,112,0,0,1,128,192Zm6-95.43a8,8,0,0,1,3-15.72,48.16,48.16,0,0,1,38.77,42.64,8,8,0,0,1-7.22,8.71,6.39,6.39,0,0,1-.75,0,8,8,0,0,1-8-7.26A32.09,32.09,0,0,0,134,96.57Zm113.28,34.69c-.42.94-10.55,23.37-33.36,43.8a8,8,0,1,1-10.67-11.92A132.77,132.77,0,0,0,231.05,128a133.15,133.15,0,0,0-23.12-30.77C185.67,75.19,158.78,64,128,64a118.37,118.37,0,0,0-19.36,1.57A8,8,0,1,1,106,49.79,134,134,0,0,1,128,48c34.88,0,66.57,13.26,91.66,38.35,18.83,18.83,27.3,37.62,27.65,38.41A8,8,0,0,1,247.31,131.26Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Hk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M96.68,57.87a4,4,0,0,1,2.08-6.6A130.13,130.13,0,0,1,128,48c34.88,0,66.57,13.26,91.66,38.35,18.83,18.83,27.3,37.62,27.65,38.41a8,8,0,0,1,0,6.5c-.35.79-8.82,19.57-27.65,38.4q-4.28,4.26-8.79,8.07a4,4,0,0,1-5.55-.36ZM213.92,210.62a8,8,0,1,1-11.84,10.76L180,197.13A127.21,127.21,0,0,1,128,208c-34.88,0-66.57-13.26-91.66-38.34C17.51,150.83,9,132.05,8.69,131.26a8,8,0,0,1,0-6.5C9,124,17.51,105.18,36.34,86.35a135,135,0,0,1,25-19.78L42.08,45.38A8,8,0,1,1,53.92,34.62Zm-65.49-48.25-52.69-58a40,40,0,0,0,52.69,58Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Vk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M52.44,36A6,6,0,0,0,43.56,44L64.44,67c-37.28,21.9-53.23,57-53.92,58.57a6,6,0,0,0,0,4.88c.34.77,8.66,19.22,27.24,37.8C55,185.47,84.62,206,128,206a124.91,124.91,0,0,0,52.57-11.25l23,25.29a6,6,0,0,0,8.88-8.08Zm48.62,71.32,45,49.52a34,34,0,0,1-45-49.52ZM128,194c-31.38,0-58.78-11.42-81.45-33.93A134.57,134.57,0,0,1,22.69,128c4.29-8.2,20.1-35.18,50-51.91L92.89,98.3a46,46,0,0,0,61.35,67.48l17.81,19.6A113.47,113.47,0,0,1,128,194Zm6.4-99.4a6,6,0,0,1,2.25-11.79,46.17,46.17,0,0,1,37.15,40.87,6,6,0,0,1-5.42,6.53l-.56,0a6,6,0,0,1-6-5.45A34.1,34.1,0,0,0,134.4,94.6Zm111.08,35.85c-.41.92-10.37,23-32.86,43.12a6,6,0,1,1-8-8.94A134.07,134.07,0,0,0,233.31,128a134.67,134.67,0,0,0-23.86-32.07C186.78,73.42,159.38,62,128,62a120.19,120.19,0,0,0-19.69,1.6,6,6,0,1,1-2-11.83A131.12,131.12,0,0,1,128,50c43.38,0,73,20.54,90.24,37.76,18.58,18.58,26.9,37,27.24,37.81A6,6,0,0,1,245.48,130.45Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",qk,[...t[4]||(t[4]=[fg(\"path\",{d:\"M53.92,34.62A8,8,0,1,0,42.08,45.38L61.32,66.55C25,88.84,9.38,123.2,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208a127.11,127.11,0,0,0,52.07-10.83l22,24.21a8,8,0,1,0,11.84-10.76Zm47.33,75.84,41.67,45.85a32,32,0,0,1-41.67-45.85ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.16,133.16,0,0,1,25,128c4.69-8.79,19.66-33.39,47.35-49.38l18,19.75a48,48,0,0,0,63.66,70l14.73,16.2A112,112,0,0,1,128,192Zm6-95.43a8,8,0,0,1,3-15.72,48.16,48.16,0,0,1,38.77,42.64,8,8,0,0,1-7.22,8.71,6.39,6.39,0,0,1-.75,0,8,8,0,0,1-8-7.26A32.09,32.09,0,0,0,134,96.57Zm113.28,34.69c-.42.94-10.55,23.37-33.36,43.8a8,8,0,1,1-10.67-11.92A132.77,132.77,0,0,0,231.05,128a133.15,133.15,0,0,0-23.12-30.77C185.67,75.19,158.78,64,128,64a118.37,118.37,0,0,0-19.36,1.57A8,8,0,1,1,106,49.79,134,134,0,0,1,128,48c34.88,0,66.57,13.26,91.66,38.35,18.83,18.83,27.3,37.62,27.65,38.41A8,8,0,0,1,247.31,131.26Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",Wk,[...t[5]||(t[5]=[fg(\"path\",{d:\"M51,37.31A4,4,0,0,0,45,42.69L67.59,67.5C29.34,89,13,124.81,12.34,126.38a4.08,4.08,0,0,0,0,3.25c.34.77,8.52,18.89,26.83,37.2,17,17,46.14,37.17,88.83,37.17a122.59,122.59,0,0,0,53.06-11.69l24,26.38a4,4,0,1,0,5.92-5.38ZM149.1,157.16A36,36,0,0,1,101,104.22ZM128,196c-32,0-59.89-11.65-83-34.62A135.81,135.81,0,0,1,20.44,128c3.65-7.23,20.09-36.81,52.68-54.43l22.45,24.7a44,44,0,0,0,59,64.83l20.89,23A114.94,114.94,0,0,1,128,196Zm6.78-103.36a4,4,0,0,1,1.49-7.86,44.15,44.15,0,0,1,35.54,39.09,4,4,0,0,1-3.61,4.35l-.38,0a4,4,0,0,1-4-3.63A36.1,36.1,0,0,0,134.78,92.64Zm108.88,37c-.41.91-10.2,22.58-32.38,42.45a4,4,0,0,1-2.67,1,4,4,0,0,1-2.67-7A136.71,136.71,0,0,0,235.56,128,136.07,136.07,0,0,0,211,94.62C187.89,71.65,160,60,128,60a122,122,0,0,0-20,1.63,4,4,0,0,1-1.32-7.89A129.3,129.3,0,0,1,128,52c42.7,0,71.87,20.22,88.83,37.18,18.31,18.31,26.49,36.44,26.83,37.2A4.08,4.08,0,0,1,243.66,129.63Z\"},null,-1)])])):Og(\"\",!0)],16))}}),Gk={key:0},Yk={key:1},Kk={key:2},Jk={key:3},eS={key:4},tS={key:5},nS=nf({name:\"ScalarIconFileMd\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",Gk,[...t[0]||(t[0]=[fg(\"path\",{d:\"M100,152v56a12,12,0,0,1-24,0V190.07l-6.17,8.81a12,12,0,0,1-19.66,0L44,190.07V208a12,12,0,0,1-24,0V152a12,12,0,0,1,21.83-6.88L60,171.07l18.17-25.95A12,12,0,0,1,100,152Zm84,28a40,40,0,0,1-40,40H128a12,12,0,0,1-12-12V152a12,12,0,0,1,12-12h16A40,40,0,0,1,184,180Zm-24,0a16,16,0,0,0-16-16h-4v32h4A16,16,0,0,0,160,180Zm60-92V224a12,12,0,0,1-24,0V104H148a12,12,0,0,1-12-12V44H60v64a12,12,0,0,1-24,0V40A20,20,0,0,1,56,20h96a12,12,0,0,1,8.49,3.52l56,56A12,12,0,0,1,220,88Zm-60-8h23L160,57Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",Yk,[...t[1]||(t[1]=[fg(\"path\",{d:\"M208,88H152V32Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v72a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V224a8,8,0,0,0,16,0V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM144,144H128a8,8,0,0,0-8,8v56a8,8,0,0,0,8,8h16a36,36,0,0,0,0-72Zm0,56h-8V160h8a20,20,0,0,1,0,40Zm-40-48v56a8,8,0,0,1-16,0V177.38L74.55,196.59a8,8,0,0,1-13.1,0L48,177.38V208a8,8,0,0,1-16,0V152a8,8,0,0,1,14.55-4.59L68,178.05l21.45-30.64A8,8,0,0,1,104,152Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",Kk,[...t[2]||(t[2]=[fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v76a4,4,0,0,0,4,4H196a4,4,0,0,1,4,4V224a8,8,0,0,0,9.19,7.91,8.15,8.15,0,0,0,6.81-8.16V88A8,8,0,0,0,213.66,82.34ZM152,88V44l44,44Zm-8,56H128a8,8,0,0,0-8,8v56a8,8,0,0,0,8,8h15.32c19.66,0,36.21-15.48,36.67-35.13A36,36,0,0,0,144,144Zm-.49,56H136V160h8a20,20,0,0,1,20,20.77C163.58,191.59,154.34,200,143.51,200ZM104,152v55.73A8.17,8.17,0,0,1,96.53,216,8,8,0,0,1,88,208V177.38l-13.32,19a8.3,8.3,0,0,1-4.2,3.2,8,8,0,0,1-9-3L48,177.38v30.35A8.17,8.17,0,0,1,40.53,216,8,8,0,0,1,32,208V152.31a8.27,8.27,0,0,1,4.56-7.53,8,8,0,0,1,10,2.63L68,178.05l21.27-30.39a8.28,8.28,0,0,1,8.06-3.55A8,8,0,0,1,104,152Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Jk,[...t[3]||(t[3]=[fg(\"path\",{d:\"M212.24,83.76l-56-56A6,6,0,0,0,152,26H56A14,14,0,0,0,42,40v72a6,6,0,0,0,12,0V40a2,2,0,0,1,2-2h90V88a6,6,0,0,0,6,6h50V224a6,6,0,0,0,12,0V88A6,6,0,0,0,212.24,83.76ZM158,46.48,193.52,82H158ZM144,146H128a6,6,0,0,0-6,6v56a6,6,0,0,0,6,6h16a34,34,0,0,0,0-68Zm0,56H134V158h10a22,22,0,0,1,0,44Zm-42-50v56a6,6,0,0,1-12,0V171L72.92,195.44a6,6,0,0,1-9.84,0L46,171v37a6,6,0,0,1-12,0V152a6,6,0,0,1,10.92-3.44l23.08,33,23.08-33A6,6,0,0,1,102,152Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",eS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v72a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V224a8,8,0,0,0,16,0V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM144,144H128a8,8,0,0,0-8,8v56a8,8,0,0,0,8,8h16a36,36,0,0,0,0-72Zm0,56h-8V160h8a20,20,0,0,1,0,40Zm-40-48v56a8,8,0,0,1-16,0V177.38L74.55,196.59a8,8,0,0,1-13.1,0L48,177.38V208a8,8,0,0,1-16,0V152a8,8,0,0,1,14.55-4.59L68,178.05l21.45-30.64A8,8,0,0,1,104,152Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",tS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M210.83,85.17l-56-56A4,4,0,0,0,152,28H56A12,12,0,0,0,44,40v72a4,4,0,0,0,8,0V40a4,4,0,0,1,4-4h92V88a4,4,0,0,0,4,4h52V224a4,4,0,0,0,8,0V88A4,4,0,0,0,210.83,85.17ZM156,41.65,198.34,84H156ZM144,148H128a4,4,0,0,0-4,4v56a4,4,0,0,0,4,4h16a32,32,0,0,0,0-64Zm0,56H132V156h12a24,24,0,0,1,0,48Zm-44-52v56a4,4,0,0,1-8,0V164.69l-20.72,29.6a4,4,0,0,1-6.56,0L44,164.69V208a4,4,0,0,1-8,0V152a4,4,0,0,1,7.28-2.29L68,185l24.72-35.31A4,4,0,0,1,100,152Z\"},null,-1)])])):Og(\"\",!0)],16))}}),rS={key:0},aS={key:1},oS={key:2},iS={key:3},sS={key:4},lS={key:5},cS=nf({name:\"ScalarIconFileText\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",rS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M216.49,79.52l-56-56A12,12,0,0,0,152,20H56A20,20,0,0,0,36,40V216a20,20,0,0,0,20,20H200a20,20,0,0,0,20-20V88A12,12,0,0,0,216.49,79.52ZM160,57l23,23H160ZM60,212V44h76V92a12,12,0,0,0,12,12h48V212Zm112-80a12,12,0,0,1-12,12H96a12,12,0,0,1,0-24h64A12,12,0,0,1,172,132Zm0,40a12,12,0,0,1-12,12H96a12,12,0,0,1,0-24h64A12,12,0,0,1,172,172Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",aS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M208,88H152V32Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Zm-32-80a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,136Zm0,32a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,168Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",oS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,176H96a8,8,0,0,1,0-16h64a8,8,0,0,1,0,16Zm0-32H96a8,8,0,0,1,0-16h64a8,8,0,0,1,0,16Zm-8-56V44l44,44Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",iS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M212.24,83.76l-56-56A6,6,0,0,0,152,26H56A14,14,0,0,0,42,40V216a14,14,0,0,0,14,14H200a14,14,0,0,0,14-14V88A6,6,0,0,0,212.24,83.76ZM158,46.48,193.52,82H158ZM200,218H56a2,2,0,0,1-2-2V40a2,2,0,0,1,2-2h90V88a6,6,0,0,0,6,6h50V216A2,2,0,0,1,200,218Zm-34-82a6,6,0,0,1-6,6H96a6,6,0,0,1,0-12h64A6,6,0,0,1,166,136Zm0,32a6,6,0,0,1-6,6H96a6,6,0,0,1,0-12h64A6,6,0,0,1,166,168Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",sS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Zm-32-80a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,136Zm0,32a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,168Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",lS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M210.83,85.17l-56-56A4,4,0,0,0,152,28H56A12,12,0,0,0,44,40V216a12,12,0,0,0,12,12H200a12,12,0,0,0,12-12V88A4,4,0,0,0,210.83,85.17ZM156,41.65,198.34,84H156ZM200,220H56a4,4,0,0,1-4-4V40a4,4,0,0,1,4-4h92V88a4,4,0,0,0,4,4h52V216A4,4,0,0,1,200,220Zm-36-84a4,4,0,0,1-4,4H96a4,4,0,0,1,0-8h64A4,4,0,0,1,164,136Zm0,32a4,4,0,0,1-4,4H96a4,4,0,0,1,0-8h64A4,4,0,0,1,164,168Z\"},null,-1)])])):Og(\"\",!0)],16))}}),uS={key:0},dS={key:1},pS={key:2},hS={key:3},fS={key:4},mS={key:5},gS=nf({name:\"ScalarIconGavel\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",uS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M246.14,113.86l-16-16a20,20,0,0,0-23.06-3.75l-45.2-45.2a20,20,0,0,0-3.74-23.06l-16-16a20,20,0,0,0-28.28,0l-64,64a20,20,0,0,0,0,28.28l16,16a20,20,0,0,0,23,3.79L29.36,181.38a32,32,0,0,0,45.26,45.26L134,167.21a20,20,0,0,0,3.81,22.94l16,16a20,20,0,0,0,28.29,0l64-64a20,20,0,0,0,0-28.29ZM80,98.34,69.64,88,128,29.65,138.34,40ZM57.64,209.67a8,8,0,0,1-11.31-11.32l59.52-59.52,11.31,11.32Zm92.7-60.29-43.72-43.72,39-39,43.72,43.72Zm17.65,37L157.65,176,216,117.66,226.34,128Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",dS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M149.66,45.66l-64,64a8,8,0,0,1-11.32,0l-16-16a8,8,0,0,1,0-11.32l64-64a8,8,0,0,1,11.32,0l16,16A8,8,0,0,1,149.66,45.66Zm88,76.68-16-16a8,8,0,0,0-11.32,0l-64,64a8,8,0,0,0,0,11.32l16,16a8,8,0,0,0,11.32,0l64-64A8,8,0,0,0,237.66,122.34Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M243.32,116.69l-16-16a16,16,0,0,0-20.84-1.53L156.84,49.52a16,16,0,0,0-1.52-20.84l-16-16a16,16,0,0,0-22.63,0l-64,64a16,16,0,0,0,0,22.63l16,16a16,16,0,0,0,20.83,1.52L96.69,124,31.31,189.38A25,25,0,0,0,66.63,224.7L132,159.32l7.17,7.16a16,16,0,0,0,1.52,20.84l16,16a16,16,0,0,0,22.63,0l64-64A16,16,0,0,0,243.32,116.69ZM80,104,64,88l64-64,16,16ZM55.32,213.38a9,9,0,0,1-12.69,0,9,9,0,0,1,0-12.68L108,135.32,120.69,148ZM101,105.66,145.66,61,195,110.34,150.35,155ZM168,192l-16-16,4-4h0l56-56h0l4-4,16,16Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",pS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M52.69,99.31a16,16,0,0,1,0-22.63l64-64a16,16,0,0,1,22.63,22.63l-64,64a16,16,0,0,1-22.63,0Zm190.63,17.37a16,16,0,0,0-22.63,0l-64,64a16,16,0,0,0,0,22.63h0a16,16,0,0,0,22.63,0l64-64A16,16,0,0,0,243.32,116.68Zm-35.11-15.8L155.12,47.79a4,4,0,0,0-5.66,0L87.8,109.45a4,4,0,0,0,0,5.66L103,130.34,28.69,204.69a16,16,0,0,0,22.62,22.62L125.66,153l15.23,15.23a4,4,0,0,0,5.66,0l61.66-61.66A4,4,0,0,0,208.21,100.88Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",hS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M241.91,118.1l-16-16a14,14,0,0,0-19.55-.23L154.13,49.64a14,14,0,0,0-.23-19.55l-16-16a14,14,0,0,0-19.8,0l-64,64a14,14,0,0,0,0,19.8l16,16a14,14,0,0,0,19.55.23L99.52,124,32.73,190.79a23,23,0,0,0,32.48,32.49L132,156.49l9.87,9.87a14,14,0,0,0,.23,19.55l16,16a14,14,0,0,0,19.8,0l64-64A14,14,0,0,0,241.91,118.1Zm-91.56,39.76-52.21-52.2,47.52-47.52,52.2,52.2ZM78.59,105.41l-16-16a2,2,0,0,1,0-2.83l64-64a2,2,0,0,1,2.83,0l16,16a2,2,0,0,1,0,2.83l-64,64A2,2,0,0,1,78.59,105.41ZM56.73,214.8a11,11,0,0,1-15.52-15.52L108,132.49,123.52,148Zm176.69-85.38-64,64a2,2,0,0,1-2.83,0l-16-16a2,2,0,0,1,0-2.83l64-64a2,2,0,0,1,2.83,0l16,16A2,2,0,0,1,233.42,129.42Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",fS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M243.32,116.69l-16-16a16,16,0,0,0-20.84-1.53L156.84,49.52a16,16,0,0,0-1.52-20.84l-16-16a16,16,0,0,0-22.63,0l-64,64a16,16,0,0,0,0,22.63l16,16a16,16,0,0,0,20.83,1.52L96.69,124,31.31,189.38A25,25,0,0,0,66.63,224.7L132,159.32l7.17,7.16a16,16,0,0,0,1.52,20.84l16,16a16,16,0,0,0,22.63,0l64-64A16,16,0,0,0,243.32,116.69ZM80,104,64,88l64-64,16,16ZM55.32,213.38a9,9,0,0,1-12.69,0,9,9,0,0,1,0-12.68L108,135.32,120.69,148ZM101,105.66,145.66,61,195,110.34,150.35,155ZM168,192l-16-16,4-4h0l56-56h0l4-4,16,16Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",mS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M240.49,119.52l-16-16a12,12,0,0,0-17,0l-1.17,1.17-55-55,1.18-1.17a12,12,0,0,0,0-17l-16-16a12,12,0,0,0-17,0l-64,64a12,12,0,0,0,0,17l16,16a12,12,0,0,0,17,0l1.17-1.18L102.34,124l-68.2,68.21A21,21,0,0,0,63.8,221.87L132,153.66l12.69,12.69-1.18,1.17a12,12,0,0,0,0,17l16,16a12,12,0,0,0,17,0l64-64a12,12,0,0,0,0-17ZM77.17,106.83l-16-16a4,4,0,0,1,0-5.66l64-64a4,4,0,0,1,5.66,0l16,16a4,4,0,0,1,0,5.65l-64,64A4,4,0,0,1,77.17,106.83Zm-19,109.38A13,13,0,1,1,39.8,197.87L108,129.66,126.34,148ZM95.31,105.66l50.35-50.35,55,55-50.35,50.35Zm139.52,25.17-64,64a4,4,0,0,1-5.66,0l-16-16a4,4,0,0,1,0-5.65l64-64a4,4,0,0,1,5.66,0l16,16a4,4,0,0,1,0,5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),vS={key:0},bS={key:1},yS={key:2},OS={key:3},wS={key:4},xS={key:5},kS=nf({name:\"ScalarIconGear\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",vS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M128,76a52,52,0,1,0,52,52A52.06,52.06,0,0,0,128,76Zm0,80a28,28,0,1,1,28-28A28,28,0,0,1,128,156Zm92-27.21v-1.58l14-17.51a12,12,0,0,0,2.23-10.59A111.75,111.75,0,0,0,225,71.89,12,12,0,0,0,215.89,66L193.61,63.5l-1.11-1.11L190,40.1A12,12,0,0,0,184.11,31a111.67,111.67,0,0,0-27.23-11.27A12,12,0,0,0,146.3,22L128.79,36h-1.58L109.7,22a12,12,0,0,0-10.59-2.23A111.75,111.75,0,0,0,71.89,31.05,12,12,0,0,0,66,40.11L63.5,62.39,62.39,63.5,40.1,66A12,12,0,0,0,31,71.89,111.67,111.67,0,0,0,19.77,99.12,12,12,0,0,0,22,109.7l14,17.51v1.58L22,146.3a12,12,0,0,0-2.23,10.59,111.75,111.75,0,0,0,11.29,27.22A12,12,0,0,0,40.11,190l22.28,2.48,1.11,1.11L66,215.9A12,12,0,0,0,71.89,225a111.67,111.67,0,0,0,27.23,11.27A12,12,0,0,0,109.7,234l17.51-14h1.58l17.51,14a12,12,0,0,0,10.59,2.23A111.75,111.75,0,0,0,184.11,225a12,12,0,0,0,5.91-9.06l2.48-22.28,1.11-1.11L215.9,190a12,12,0,0,0,9.06-5.91,111.67,111.67,0,0,0,11.27-27.23A12,12,0,0,0,234,146.3Zm-24.12-4.89a70.1,70.1,0,0,1,0,8.2,12,12,0,0,0,2.61,8.22l12.84,16.05A86.47,86.47,0,0,1,207,166.86l-20.43,2.27a12,12,0,0,0-7.65,4,69,69,0,0,1-5.8,5.8,12,12,0,0,0-4,7.65L166.86,207a86.47,86.47,0,0,1-10.49,4.35l-16.05-12.85a12,12,0,0,0-7.5-2.62c-.24,0-.48,0-.72,0a70.1,70.1,0,0,1-8.2,0,12.06,12.06,0,0,0-8.22,2.6L99.63,211.33A86.47,86.47,0,0,1,89.14,207l-2.27-20.43a12,12,0,0,0-4-7.65,69,69,0,0,1-5.8-5.8,12,12,0,0,0-7.65-4L49,166.86a86.47,86.47,0,0,1-4.35-10.49l12.84-16.05a12,12,0,0,0,2.61-8.22,70.1,70.1,0,0,1,0-8.2,12,12,0,0,0-2.61-8.22L44.67,99.63A86.47,86.47,0,0,1,49,89.14l20.43-2.27a12,12,0,0,0,7.65-4,69,69,0,0,1,5.8-5.8,12,12,0,0,0,4-7.65L89.14,49a86.47,86.47,0,0,1,10.49-4.35l16.05,12.85a12.06,12.06,0,0,0,8.22,2.6,70.1,70.1,0,0,1,8.2,0,12,12,0,0,0,8.22-2.6l16.05-12.85A86.47,86.47,0,0,1,166.86,49l2.27,20.43a12,12,0,0,0,4,7.65,69,69,0,0,1,5.8,5.8,12,12,0,0,0,7.65,4L207,89.14a86.47,86.47,0,0,1,4.35,10.49l-12.84,16.05A12,12,0,0,0,195.88,123.9Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",bS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M207.86,123.18l16.78-21a99.14,99.14,0,0,0-10.07-24.29l-26.7-3a81,81,0,0,0-6.81-6.81l-3-26.71a99.43,99.43,0,0,0-24.3-10l-21,16.77a81.59,81.59,0,0,0-9.64,0l-21-16.78A99.14,99.14,0,0,0,77.91,41.43l-3,26.7a81,81,0,0,0-6.81,6.81l-26.71,3a99.43,99.43,0,0,0-10,24.3l16.77,21a81.59,81.59,0,0,0,0,9.64l-16.78,21a99.14,99.14,0,0,0,10.07,24.29l26.7,3a81,81,0,0,0,6.81,6.81l3,26.71a99.43,99.43,0,0,0,24.3,10l21-16.77a81.59,81.59,0,0,0,9.64,0l21,16.78a99.14,99.14,0,0,0,24.29-10.07l3-26.7a81,81,0,0,0,6.81-6.81l26.71-3a99.43,99.43,0,0,0,10-24.3l-16.77-21A81.59,81.59,0,0,0,207.86,123.18ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Zm88-29.84q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06Zm-16.1-6.5a73.93,73.93,0,0,1,0,8.68,8,8,0,0,0,1.74,5.48l14.19,17.73a91.57,91.57,0,0,1-6.23,15L187,173.11a8,8,0,0,0-5.1,2.64,74.11,74.11,0,0,1-6.14,6.14,8,8,0,0,0-2.64,5.1l-2.51,22.58a91.32,91.32,0,0,1-15,6.23l-17.74-14.19a8,8,0,0,0-5-1.75h-.48a73.93,73.93,0,0,1-8.68,0,8.06,8.06,0,0,0-5.48,1.74L100.45,215.8a91.57,91.57,0,0,1-15-6.23L82.89,187a8,8,0,0,0-2.64-5.1,74.11,74.11,0,0,1-6.14-6.14,8,8,0,0,0-5.1-2.64L46.43,170.6a91.32,91.32,0,0,1-6.23-15l14.19-17.74a8,8,0,0,0,1.74-5.48,73.93,73.93,0,0,1,0-8.68,8,8,0,0,0-1.74-5.48L40.2,100.45a91.57,91.57,0,0,1,6.23-15L69,82.89a8,8,0,0,0,5.1-2.64,74.11,74.11,0,0,1,6.14-6.14A8,8,0,0,0,82.89,69L85.4,46.43a91.32,91.32,0,0,1,15-6.23l17.74,14.19a8,8,0,0,0,5.48,1.74,73.93,73.93,0,0,1,8.68,0,8.06,8.06,0,0,0,5.48-1.74L155.55,40.2a91.57,91.57,0,0,1,15,6.23L173.11,69a8,8,0,0,0,2.64,5.1,74.11,74.11,0,0,1,6.14,6.14,8,8,0,0,0,5.1,2.64l22.58,2.51a91.32,91.32,0,0,1,6.23,15l-14.19,17.74A8,8,0,0,0,199.87,123.66Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",yS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",OS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M128,82a46,46,0,1,0,46,46A46.06,46.06,0,0,0,128,82Zm0,80a34,34,0,1,1,34-34A34,34,0,0,1,128,162ZM214,130.84c.06-1.89.06-3.79,0-5.68L229.33,106a6,6,0,0,0,1.11-5.29A105.34,105.34,0,0,0,219.76,74.9a6,6,0,0,0-4.53-3l-24.45-2.71q-1.93-2.07-4-4l-2.72-24.46a6,6,0,0,0-3-4.53,105.65,105.65,0,0,0-25.77-10.66A6,6,0,0,0,150,26.68l-19.2,15.37c-1.89-.06-3.79-.06-5.68,0L106,26.67a6,6,0,0,0-5.29-1.11A105.34,105.34,0,0,0,74.9,36.24a6,6,0,0,0-3,4.53L69.23,65.22q-2.07,1.94-4,4L40.76,72a6,6,0,0,0-4.53,3,105.65,105.65,0,0,0-10.66,25.77A6,6,0,0,0,26.68,106l15.37,19.2c-.06,1.89-.06,3.79,0,5.68L26.67,150.05a6,6,0,0,0-1.11,5.29A105.34,105.34,0,0,0,36.24,181.1a6,6,0,0,0,4.53,3l24.45,2.71q1.94,2.07,4,4L72,215.24a6,6,0,0,0,3,4.53,105.65,105.65,0,0,0,25.77,10.66,6,6,0,0,0,5.29-1.11L125.16,214c1.89.06,3.79.06,5.68,0l19.21,15.38a6,6,0,0,0,3.75,1.31,6.2,6.2,0,0,0,1.54-.2,105.34,105.34,0,0,0,25.76-10.68,6,6,0,0,0,3-4.53l2.71-24.45q2.07-1.93,4-4l24.46-2.72a6,6,0,0,0,4.53-3,105.49,105.49,0,0,0,10.66-25.77,6,6,0,0,0-1.11-5.29Zm-3.1,41.63-23.64,2.63a6,6,0,0,0-3.82,2,75.14,75.14,0,0,1-6.31,6.31,6,6,0,0,0-2,3.82l-2.63,23.63A94.28,94.28,0,0,1,155.14,218l-18.57-14.86a6,6,0,0,0-3.75-1.31h-.36a78.07,78.07,0,0,1-8.92,0,6,6,0,0,0-4.11,1.3L100.87,218a94.13,94.13,0,0,1-17.34-7.17L80.9,187.21a6,6,0,0,0-2-3.82,75.14,75.14,0,0,1-6.31-6.31,6,6,0,0,0-3.82-2l-23.63-2.63A94.28,94.28,0,0,1,38,155.14l14.86-18.57a6,6,0,0,0,1.3-4.11,78.07,78.07,0,0,1,0-8.92,6,6,0,0,0-1.3-4.11L38,100.87a94.13,94.13,0,0,1,7.17-17.34L68.79,80.9a6,6,0,0,0,3.82-2,75.14,75.14,0,0,1,6.31-6.31,6,6,0,0,0,2-3.82l2.63-23.63A94.28,94.28,0,0,1,100.86,38l18.57,14.86a6,6,0,0,0,4.11,1.3,78.07,78.07,0,0,1,8.92,0,6,6,0,0,0,4.11-1.3L155.13,38a94.13,94.13,0,0,1,17.34,7.17l2.63,23.64a6,6,0,0,0,2,3.82,75.14,75.14,0,0,1,6.31,6.31,6,6,0,0,0,3.82,2l23.63,2.63A94.28,94.28,0,0,1,218,100.86l-14.86,18.57a6,6,0,0,0-1.3,4.11,78.07,78.07,0,0,1,0,8.92,6,6,0,0,0,1.3,4.11L218,155.13A94.13,94.13,0,0,1,210.85,172.47Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",wS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Zm88-29.84q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.21,107.21,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.71,107.71,0,0,0-26.25-10.87,8,8,0,0,0-7.06,1.49L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.21,107.21,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06Zm-16.1-6.5a73.93,73.93,0,0,1,0,8.68,8,8,0,0,0,1.74,5.48l14.19,17.73a91.57,91.57,0,0,1-6.23,15L187,173.11a8,8,0,0,0-5.1,2.64,74.11,74.11,0,0,1-6.14,6.14,8,8,0,0,0-2.64,5.1l-2.51,22.58a91.32,91.32,0,0,1-15,6.23l-17.74-14.19a8,8,0,0,0-5-1.75h-.48a73.93,73.93,0,0,1-8.68,0,8,8,0,0,0-5.48,1.74L100.45,215.8a91.57,91.57,0,0,1-15-6.23L82.89,187a8,8,0,0,0-2.64-5.1,74.11,74.11,0,0,1-6.14-6.14,8,8,0,0,0-5.1-2.64L46.43,170.6a91.32,91.32,0,0,1-6.23-15l14.19-17.74a8,8,0,0,0,1.74-5.48,73.93,73.93,0,0,1,0-8.68,8,8,0,0,0-1.74-5.48L40.2,100.45a91.57,91.57,0,0,1,6.23-15L69,82.89a8,8,0,0,0,5.1-2.64,74.11,74.11,0,0,1,6.14-6.14A8,8,0,0,0,82.89,69L85.4,46.43a91.32,91.32,0,0,1,15-6.23l17.74,14.19a8,8,0,0,0,5.48,1.74,73.93,73.93,0,0,1,8.68,0,8,8,0,0,0,5.48-1.74L155.55,40.2a91.57,91.57,0,0,1,15,6.23L173.11,69a8,8,0,0,0,2.64,5.1,74.11,74.11,0,0,1,6.14,6.14,8,8,0,0,0,5.1,2.64l22.58,2.51a91.32,91.32,0,0,1,6.23,15l-14.19,17.74A8,8,0,0,0,199.87,123.66Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",xS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M128,84a44,44,0,1,0,44,44A44.05,44.05,0,0,0,128,84Zm0,80a36,36,0,1,1,36-36A36,36,0,0,1,128,164Zm83.93-32.49q.13-3.51,0-7l15.83-19.79a4,4,0,0,0,.75-3.53A103.64,103.64,0,0,0,218,75.9a4,4,0,0,0-3-2l-25.19-2.8c-1.58-1.71-3.24-3.37-4.95-4.95L182.07,41a4,4,0,0,0-2-3A104,104,0,0,0,154.82,27.5a4,4,0,0,0-3.53.74L131.51,44.07q-3.51-.14-7,0L104.7,28.24a4,4,0,0,0-3.53-.75A103.64,103.64,0,0,0,75.9,38a4,4,0,0,0-2,3l-2.8,25.19c-1.71,1.58-3.37,3.24-4.95,4.95L41,73.93a4,4,0,0,0-3,2A104,104,0,0,0,27.5,101.18a4,4,0,0,0,.74,3.53l15.83,19.78q-.14,3.51,0,7L28.24,151.3a4,4,0,0,0-.75,3.53A103.64,103.64,0,0,0,38,180.1a4,4,0,0,0,3,2l25.19,2.8c1.58,1.71,3.24,3.37,4.95,4.95l2.8,25.2a4,4,0,0,0,2,3,104,104,0,0,0,25.28,10.46,4,4,0,0,0,3.53-.74l19.78-15.83q3.51.13,7,0l19.79,15.83a4,4,0,0,0,2.5.88,4,4,0,0,0,1-.13A103.64,103.64,0,0,0,180.1,218a4,4,0,0,0,2-3l2.8-25.19c1.71-1.58,3.37-3.24,4.95-4.95l25.2-2.8a4,4,0,0,0,3-2,104,104,0,0,0,10.46-25.28,4,4,0,0,0-.74-3.53Zm.17,42.83-24.67,2.74a4,4,0,0,0-2.55,1.32,76.2,76.2,0,0,1-6.48,6.48,4,4,0,0,0-1.32,2.55l-2.74,24.66a95.45,95.45,0,0,1-19.64,8.15l-19.38-15.51a4,4,0,0,0-2.5-.87h-.24a73.67,73.67,0,0,1-9.16,0,4,4,0,0,0-2.74.87l-19.37,15.5a95.33,95.33,0,0,1-19.65-8.13l-2.74-24.67a4,4,0,0,0-1.32-2.55,76.2,76.2,0,0,1-6.48-6.48,4,4,0,0,0-2.55-1.32l-24.66-2.74a95.45,95.45,0,0,1-8.15-19.64l15.51-19.38a4,4,0,0,0,.87-2.74,77.76,77.76,0,0,1,0-9.16,4,4,0,0,0-.87-2.74l-15.5-19.37A95.33,95.33,0,0,1,43.9,81.66l24.67-2.74a4,4,0,0,0,2.55-1.32,76.2,76.2,0,0,1,6.48-6.48,4,4,0,0,0,1.32-2.55l2.74-24.66a95.45,95.45,0,0,1,19.64-8.15l19.38,15.51a4,4,0,0,0,2.74.87,73.67,73.67,0,0,1,9.16,0,4,4,0,0,0,2.74-.87l19.37-15.5a95.33,95.33,0,0,1,19.65,8.13l2.74,24.67a4,4,0,0,0,1.32,2.55,76.2,76.2,0,0,1,6.48,6.48,4,4,0,0,0,2.55,1.32l24.66,2.74a95.45,95.45,0,0,1,8.15,19.64l-15.51,19.38a4,4,0,0,0-.87,2.74,77.76,77.76,0,0,1,0,9.16,4,4,0,0,0,.87,2.74l15.5,19.37A95.33,95.33,0,0,1,212.1,174.34Z\"},null,-1)])])):Og(\"\",!0)],16))}}),SS={key:0},_S={key:1},TS={key:2},ES={key:3},AS={key:4},CS={key:5},PS=nf({name:\"ScalarIconGitBranch\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",SS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M236,64a36,36,0,1,0-48,33.94V112a4,4,0,0,1-4,4H96a27.8,27.8,0,0,0-4,.29V97.94a36,36,0,1,0-24,0v60.12a36,36,0,1,0,24,0V144a4,4,0,0,1,4-4h88a28,28,0,0,0,28-28V97.94A36.07,36.07,0,0,0,236,64ZM80,52A12,12,0,1,1,68,64,12,12,0,0,1,80,52Zm0,152a12,12,0,1,1,12-12A12,12,0,0,1,80,204ZM200,76a12,12,0,1,1,12-12A12,12,0,0,1,200,76Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",_S,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,64a24,24,0,1,1-24-24A24,24,0,0,1,224,64Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M232,64a32,32,0,1,0-40,31v17a8,8,0,0,1-8,8H96a23.84,23.84,0,0,0-8,1.38V95a32,32,0,1,0-16,0v66a32,32,0,1,0,16,0V144a8,8,0,0,1,8-8h88a24,24,0,0,0,24-24V95A32.06,32.06,0,0,0,232,64ZM64,64A16,16,0,1,1,80,80,16,16,0,0,1,64,64ZM96,192a16,16,0,1,1-16-16A16,16,0,0,1,96,192ZM200,80a16,16,0,1,1,16-16A16,16,0,0,1,200,80Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",TS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M232,64a32,32,0,1,0-40,31v17a8,8,0,0,1-8,8H96a23.84,23.84,0,0,0-8,1.38V95a32,32,0,1,0-16,0v66a32,32,0,1,0,16,0V144a8,8,0,0,1,8-8h88a24,24,0,0,0,24-24V95A32.06,32.06,0,0,0,232,64ZM64,64A16,16,0,1,1,80,80,16,16,0,0,1,64,64ZM96,192a16,16,0,1,1-16-16A16,16,0,0,1,96,192Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",ES,[...t[3]||(t[3]=[fg(\"path\",{d:\"M230,64a30,30,0,1,0-36,29.4V112a10,10,0,0,1-10,10H96a21.84,21.84,0,0,0-10,2.42v-31a30,30,0,1,0-12,0v69.2a30,30,0,1,0,12,0V144a10,10,0,0,1,10-10h88a22,22,0,0,0,22-22V93.4A30.05,30.05,0,0,0,230,64ZM62,64A18,18,0,1,1,80,82,18,18,0,0,1,62,64ZM98,192a18,18,0,1,1-18-18A18,18,0,0,1,98,192ZM200,82a18,18,0,1,1,18-18A18,18,0,0,1,200,82Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",AS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M232,64a32,32,0,1,0-40,31v17a8,8,0,0,1-8,8H96a23.84,23.84,0,0,0-8,1.38V95a32,32,0,1,0-16,0v66a32,32,0,1,0,16,0V144a8,8,0,0,1,8-8h88a24,24,0,0,0,24-24V95A32.06,32.06,0,0,0,232,64ZM64,64A16,16,0,1,1,80,80,16,16,0,0,1,64,64ZM96,192a16,16,0,1,1-16-16A16,16,0,0,1,96,192ZM200,80a16,16,0,1,1,16-16A16,16,0,0,1,200,80Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",CS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M228,64a28,28,0,1,0-32,27.71V112a12,12,0,0,1-12,12H96a19.91,19.91,0,0,0-12,4V91.71a28,28,0,1,0-8,0v72.58a28,28,0,1,0,8,0V144a12,12,0,0,1,12-12h88a20,20,0,0,0,20-20V91.71A28,28,0,0,0,228,64ZM60,64A20,20,0,1,1,80,84,20,20,0,0,1,60,64Zm40,128a20,20,0,1,1-20-20A20,20,0,0,1,100,192ZM200,84a20,20,0,1,1,20-20A20,20,0,0,1,200,84Z\"},null,-1)])])):Og(\"\",!0)],16))}}),$S={key:0},DS={key:1},IS={key:2},MS={key:3},NS={key:4},RS={key:5},LS=nf({name:\"ScalarIconGithubLogo\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",$S,[...t[0]||(t[0]=[fg(\"path\",{d:\"M212.62,75.17A63.7,63.7,0,0,0,206.39,26,12,12,0,0,0,196,20a63.71,63.71,0,0,0-50,24H126A63.71,63.71,0,0,0,76,20a12,12,0,0,0-10.39,6,63.7,63.7,0,0,0-6.23,49.17A61.5,61.5,0,0,0,52,104v8a60.1,60.1,0,0,0,45.76,58.28A43.66,43.66,0,0,0,92,192v4H76a20,20,0,0,1-20-20,44.05,44.05,0,0,0-44-44,12,12,0,0,0,0,24,20,20,0,0,1,20,20,44.05,44.05,0,0,0,44,44H92v12a12,12,0,0,0,24,0V192a20,20,0,0,1,40,0v40a12,12,0,0,0,24,0V192a43.66,43.66,0,0,0-5.76-21.72A60.1,60.1,0,0,0,220,112v-8A61.5,61.5,0,0,0,212.62,75.17ZM196,112a36,36,0,0,1-36,36H112a36,36,0,0,1-36-36v-8a37.87,37.87,0,0,1,6.13-20.12,11.65,11.65,0,0,0,1.58-11.49,39.9,39.9,0,0,1-.4-27.72,39.87,39.87,0,0,1,26.41,17.8A12,12,0,0,0,119.82,68h32.35a12,12,0,0,0,10.11-5.53,39.84,39.84,0,0,1,26.41-17.8,39.9,39.9,0,0,1-.4,27.72,12,12,0,0,0,1.61,11.53A37.85,37.85,0,0,1,196,104Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",DS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M208,104v8a48,48,0,0,1-48,48H136a32,32,0,0,1,32,32v40H104V192a32,32,0,0,1,32-32H112a48,48,0,0,1-48-48v-8a49.28,49.28,0,0,1,8.51-27.3A51.92,51.92,0,0,1,76,32a52,52,0,0,1,43.83,24h32.34A52,52,0,0,1,196,32a51.92,51.92,0,0,1,3.49,44.7A49.28,49.28,0,0,1,208,104Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M208.3,75.68A59.74,59.74,0,0,0,202.93,28,8,8,0,0,0,196,24a59.75,59.75,0,0,0-48,24H124A59.75,59.75,0,0,0,76,24a8,8,0,0,0-6.93,4,59.78,59.78,0,0,0-5.38,47.68A58.14,58.14,0,0,0,56,104v8a56.06,56.06,0,0,0,48.44,55.47A39.8,39.8,0,0,0,96,192v8H72a24,24,0,0,1-24-24A40,40,0,0,0,8,136a8,8,0,0,0,0,16,24,24,0,0,1,24,24,40,40,0,0,0,40,40H96v16a8,8,0,0,0,16,0V192a24,24,0,0,1,48,0v40a8,8,0,0,0,16,0V192a39.8,39.8,0,0,0-8.44-24.53A56.06,56.06,0,0,0,216,112v-8A58,58,0,0,0,208.3,75.68ZM200,112a40,40,0,0,1-40,40H112a40,40,0,0,1-40-40v-8a41.74,41.74,0,0,1,6.9-22.48A8,8,0,0,0,80,73.83a43.81,43.81,0,0,1,.79-33.58,43.88,43.88,0,0,1,32.32,20.06A8,8,0,0,0,119.82,64h32.35a8,8,0,0,0,6.74-3.69,43.87,43.87,0,0,1,32.32-20.06A43.81,43.81,0,0,1,192,73.83a8.09,8.09,0,0,0,1,7.65A41.76,41.76,0,0,1,200,104Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",IS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,104v8a56.06,56.06,0,0,1-48.44,55.47A39.8,39.8,0,0,1,176,192v40a8,8,0,0,1-8,8H104a8,8,0,0,1-8-8V216H72a40,40,0,0,1-40-40A24,24,0,0,0,8,152a8,8,0,0,1,0-16,40,40,0,0,1,40,40,24,24,0,0,0,24,24H96v-8a39.8,39.8,0,0,1,8.44-24.53A56.06,56.06,0,0,1,56,112v-8a58.14,58.14,0,0,1,7.69-28.32A59.78,59.78,0,0,1,69.07,28,8,8,0,0,1,76,24a59.75,59.75,0,0,1,48,24h24a59.75,59.75,0,0,1,48-24,8,8,0,0,1,6.93,4,59.74,59.74,0,0,1,5.37,47.68A58,58,0,0,1,216,104Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",MS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M206.13,75.92A57.79,57.79,0,0,0,201.2,29a6,6,0,0,0-5.2-3,57.77,57.77,0,0,0-47,24H123A57.77,57.77,0,0,0,76,26a6,6,0,0,0-5.2,3,57.79,57.79,0,0,0-4.93,46.92A55.88,55.88,0,0,0,58,104v8a54.06,54.06,0,0,0,50.45,53.87A37.85,37.85,0,0,0,98,192v10H72a26,26,0,0,1-26-26A38,38,0,0,0,8,138a6,6,0,0,0,0,12,26,26,0,0,1,26,26,38,38,0,0,0,38,38H98v18a6,6,0,0,0,12,0V192a26,26,0,0,1,52,0v40a6,6,0,0,0,12,0V192a37.85,37.85,0,0,0-10.45-26.13A54.06,54.06,0,0,0,214,112v-8A55.88,55.88,0,0,0,206.13,75.92ZM202,112a42,42,0,0,1-42,42H112a42,42,0,0,1-42-42v-8a43.86,43.86,0,0,1,7.3-23.69,6,6,0,0,0,.81-5.76,45.85,45.85,0,0,1,1.43-36.42,45.85,45.85,0,0,1,35.23,21.1A6,6,0,0,0,119.83,62h32.34a6,6,0,0,0,5.06-2.76,45.83,45.83,0,0,1,35.23-21.11,45.85,45.85,0,0,1,1.43,36.42,6,6,0,0,0,.79,5.74A43.78,43.78,0,0,1,202,104Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",NS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M208.31,75.68A59.78,59.78,0,0,0,202.93,28,8,8,0,0,0,196,24a59.75,59.75,0,0,0-48,24H124A59.75,59.75,0,0,0,76,24a8,8,0,0,0-6.93,4,59.78,59.78,0,0,0-5.38,47.68A58.14,58.14,0,0,0,56,104v8a56.06,56.06,0,0,0,48.44,55.47A39.8,39.8,0,0,0,96,192v8H72a24,24,0,0,1-24-24A40,40,0,0,0,8,136a8,8,0,0,0,0,16,24,24,0,0,1,24,24,40,40,0,0,0,40,40H96v16a8,8,0,0,0,16,0V192a24,24,0,0,1,48,0v40a8,8,0,0,0,16,0V192a39.8,39.8,0,0,0-8.44-24.53A56.06,56.06,0,0,0,216,112v-8A58.14,58.14,0,0,0,208.31,75.68ZM200,112a40,40,0,0,1-40,40H112a40,40,0,0,1-40-40v-8a41.74,41.74,0,0,1,6.9-22.48A8,8,0,0,0,80,73.83a43.81,43.81,0,0,1,.79-33.58,43.88,43.88,0,0,1,32.32,20.06A8,8,0,0,0,119.82,64h32.35a8,8,0,0,0,6.74-3.69,43.87,43.87,0,0,1,32.32-20.06A43.81,43.81,0,0,1,192,73.83a8.09,8.09,0,0,0,1,7.65A41.72,41.72,0,0,1,200,104Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",RS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M203.94,76.16A55.73,55.73,0,0,0,199.46,30,4,4,0,0,0,196,28a55.78,55.78,0,0,0-46,24H122A55.78,55.78,0,0,0,76,28a4,4,0,0,0-3.46,2,55.73,55.73,0,0,0-4.48,46.16A53.78,53.78,0,0,0,60,104v8a52.06,52.06,0,0,0,52,52h1.41A36,36,0,0,0,100,192v12H72a28,28,0,0,1-28-28A36,36,0,0,0,8,140a4,4,0,0,0,0,8,28,28,0,0,1,28,28,36,36,0,0,0,36,36h28v20a4,4,0,0,0,8,0V192a28,28,0,0,1,56,0v40a4,4,0,0,0,8,0V192a36,36,0,0,0-13.41-28H160a52.06,52.06,0,0,0,52-52v-8A53.78,53.78,0,0,0,203.94,76.16ZM204,112a44.05,44.05,0,0,1-44,44H112a44.05,44.05,0,0,1-44-44v-8a45.76,45.76,0,0,1,7.71-24.89,4,4,0,0,0,.53-3.84,47.82,47.82,0,0,1,2.1-39.21,47.8,47.8,0,0,1,38.12,22.1A4,4,0,0,0,119.83,60h32.34a4,4,0,0,0,3.37-1.84,47.8,47.8,0,0,1,38.12-22.1,47.82,47.82,0,0,1,2.1,39.21,4,4,0,0,0,.53,3.83A45.85,45.85,0,0,1,204,104Z\"},null,-1)])])):Og(\"\",!0)],16))}}),BS={key:0},jS={key:1},US={key:2},zS={key:3},FS={key:4},QS={key:5},ZS=nf({name:\"ScalarIconGlobeSimple\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",BS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm83.13,96H179.56a144.3,144.3,0,0,0-21.35-66.36A84.22,84.22,0,0,1,211.13,116ZM128,207c-9.36-10.81-24.46-33.13-27.45-67h54.94a119.74,119.74,0,0,1-17.11,52.77A108.61,108.61,0,0,1,128,207Zm-27.45-91a119.74,119.74,0,0,1,17.11-52.77A108.61,108.61,0,0,1,128,49c9.36,10.81,24.46,33.13,27.45,67ZM97.79,49.64A144.3,144.3,0,0,0,76.44,116H44.87A84.22,84.22,0,0,1,97.79,49.64ZM44.87,140H76.44a144.3,144.3,0,0,0,21.35,66.36A84.22,84.22,0,0,1,44.87,140Zm113.34,66.36A144.3,144.3,0,0,0,179.56,140h31.57A84.22,84.22,0,0,1,158.21,206.36Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",jS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M128,24h0A104,104,0,1,0,232,128,104.12,104.12,0,0,0,128,24Zm87.62,96H175.79C174,83.49,159.94,57.67,148.41,42.4A88.19,88.19,0,0,1,215.63,120ZM96.23,136h63.54c-2.31,41.61-22.23,67.11-31.77,77C118.45,203.1,98.54,177.6,96.23,136Zm0-16C98.54,78.39,118.46,52.89,128,43c9.55,9.93,29.46,35.43,31.77,77Zm11.36-77.6C96.06,57.67,82,83.49,80.21,120H40.37A88.19,88.19,0,0,1,107.59,42.4ZM40.37,136H80.21c1.82,36.51,15.85,62.33,27.38,77.6A88.19,88.19,0,0,1,40.37,136Zm108,77.6c11.53-15.27,25.56-41.09,27.38-77.6h39.84A88.19,88.19,0,0,1,148.41,213.6Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",US,[...t[2]||(t[2]=[fg(\"path\",{d:\"M128,24h0A104,104,0,1,0,232,128,104.12,104.12,0,0,0,128,24Zm87.62,96H175.79C174,83.49,159.94,57.67,148.41,42.4A88.19,88.19,0,0,1,215.63,120ZM96.23,136h63.54c-2.31,41.61-22.23,67.11-31.77,77C118.45,203.1,98.54,177.6,96.23,136Zm0-16C98.54,78.39,118.46,52.89,128,43c9.55,9.93,29.46,35.43,31.77,77Zm52.18,93.6c11.53-15.27,25.56-41.09,27.38-77.6h39.84A88.19,88.19,0,0,1,148.41,213.6Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",zS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm89.8,96H173.89c-1.54-40.77-18.48-68.23-30.43-82.67A90.19,90.19,0,0,1,217.8,122ZM128,215.83a110,110,0,0,1-15.19-19.45A128.37,128.37,0,0,1,94.13,134h67.74a128.37,128.37,0,0,1-18.68,62.38A110,110,0,0,1,128,215.83ZM94.13,122a128.37,128.37,0,0,1,18.68-62.38A110,110,0,0,1,128,40.17a110,110,0,0,1,15.19,19.45A128.37,128.37,0,0,1,161.87,122Zm18.41-82.67c-12,14.44-28.89,41.9-30.43,82.67H38.2A90.19,90.19,0,0,1,112.54,39.33ZM38.2,134H82.11c1.54,40.77,18.48,68.23,30.43,82.67A90.19,90.19,0,0,1,38.2,134Zm105.26,82.67c11.95-14.44,28.89-41.9,30.43-82.67H217.8A90.19,90.19,0,0,1,143.46,216.67Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",FS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M128,24h0A104,104,0,1,0,232,128,104.12,104.12,0,0,0,128,24Zm87.62,96H175.79C174,83.49,159.94,57.67,148.41,42.4A88.19,88.19,0,0,1,215.63,120ZM96.23,136h63.54c-2.31,41.61-22.23,67.11-31.77,77C118.45,203.1,98.54,177.6,96.23,136Zm0-16C98.54,78.39,118.46,52.89,128,43c9.55,9.93,29.46,35.43,31.77,77Zm11.36-77.6C96.06,57.67,82,83.49,80.21,120H40.37A88.19,88.19,0,0,1,107.59,42.4ZM40.37,136H80.21c1.82,36.51,15.85,62.33,27.38,77.6A88.19,88.19,0,0,1,40.37,136Zm108,77.6c11.53-15.27,25.56-41.09,27.38-77.6h39.84A88.19,88.19,0,0,1,148.41,213.6Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",QS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M128,28h0A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm91.9,96h-48c-1.15-45.55-21.74-74.52-33.48-87.4A92.14,92.14,0,0,1,219.91,124ZM128,218.61c-8.32-8-34.57-37.13-35.93-86.61h71.86C162.57,181.48,136.32,210.61,128,218.61ZM92.07,124C93.43,74.52,119.68,45.39,128,37.39c8.32,8,34.57,37.13,35.93,86.61Zm25.47-87.4C105.8,49.48,85.21,78.45,84.06,124h-48A92.14,92.14,0,0,1,117.54,36.6ZM36.09,132h48c1.15,45.55,21.74,74.52,33.48,87.4A92.14,92.14,0,0,1,36.09,132Zm102.37,87.4c11.74-12.88,32.33-41.85,33.48-87.4h48A92.14,92.14,0,0,1,138.46,219.4Z\"},null,-1)])])):Og(\"\",!0)],16))}}),HS={key:0},VS={key:1},qS={key:2},WS={key:3},XS={key:4},GS={key:5},YS=nf({name:\"ScalarIconHash\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",HS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M224,84H180.2l7.61-41.85a12,12,0,0,0-23.62-4.3L155.8,84H116.2l7.61-41.85a12,12,0,1,0-23.62-4.3L91.8,84H48a12,12,0,0,0,0,24H87.44l-7.27,40H32a12,12,0,0,0,0,24H75.8l-7.61,41.85a12,12,0,0,0,9.66,14A11.43,11.43,0,0,0,80,228a12,12,0,0,0,11.8-9.86L100.2,172h39.6l-7.61,41.85a12,12,0,0,0,9.66,14,11.43,11.43,0,0,0,2.16.2,12,12,0,0,0,11.8-9.86L164.2,172H208a12,12,0,0,0,0-24H168.56l7.27-40H224a12,12,0,0,0,0-24Zm-79.83,64H104.56l7.27-40h39.61Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",VS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M165.82,96l-11.64,64h-64l11.64-64Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M224,88H175.4l8.47-46.57a8,8,0,0,0-15.74-2.86l-9,49.43H111.4l8.47-46.57a8,8,0,0,0-15.74-2.86L95.14,88H48a8,8,0,0,0,0,16H92.23L83.5,152H32a8,8,0,0,0,0,16H80.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,80,224a8,8,0,0,0,7.86-6.57l9-49.43H144.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,144,224a8,8,0,0,0,7.86-6.57l9-49.43H208a8,8,0,0,0,0-16H163.77l8.73-48H224a8,8,0,0,0,0-16Zm-76.5,64H99.77l8.73-48h47.73Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",qS,[...t[2]||(t[2]=[fg(\"path\",{d:\"M116.25,112h31.5l-8,32h-31.5ZM224,48V208a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32H208A16,16,0,0,1,224,48Zm-16,56a8,8,0,0,0-8-8H168.25l7.51-30.06a8,8,0,0,0-15.52-3.88L151.75,96h-31.5l7.51-30.06a8,8,0,0,0-15.52-3.88L103.75,96H64a8,8,0,0,0,0,16H99.75l-8,32H56a8,8,0,0,0,0,16H87.75l-7.51,30.06a8,8,0,0,0,5.82,9.7,8.13,8.13,0,0,0,2,.24,8,8,0,0,0,7.75-6.06L104.25,160h31.5l-7.51,30.06a8,8,0,0,0,5.82,9.7A8.13,8.13,0,0,0,136,200a8,8,0,0,0,7.75-6.06L152.25,160H192a8,8,0,0,0,0-16H156.25l8-32H200A8,8,0,0,0,208,104Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",WS,[...t[3]||(t[3]=[fg(\"path\",{d:\"M224,90H173l8.89-48.93a6,6,0,1,0-11.8-2.14L160.81,90H109l8.89-48.93a6,6,0,0,0-11.8-2.14L96.81,90H48a6,6,0,0,0,0,12H94.63l-9.46,52H32a6,6,0,0,0,0,12H83L74.1,214.93a6,6,0,0,0,4.83,7A5.64,5.64,0,0,0,80,222a6,6,0,0,0,5.89-4.93L95.19,166H147l-8.89,48.93a6,6,0,0,0,4.83,7,5.64,5.64,0,0,0,1.08.1,6,6,0,0,0,5.89-4.93L159.19,166H208a6,6,0,0,0,0-12H161.37l9.46-52H224a6,6,0,0,0,0-12Zm-74.83,64H97.37l9.46-52h51.8Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",XS,[...t[4]||(t[4]=[fg(\"path\",{d:\"M224,88H175.4l8.47-46.57a8,8,0,0,0-15.74-2.86l-9,49.43H111.4l8.47-46.57a8,8,0,0,0-15.74-2.86L95.14,88H48a8,8,0,0,0,0,16H92.23L83.5,152H32a8,8,0,0,0,0,16H80.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,80,224a8,8,0,0,0,7.86-6.57l9-49.43H144.6l-8.47,46.57a8,8,0,0,0,6.44,9.3A7.79,7.79,0,0,0,144,224a8,8,0,0,0,7.86-6.57l9-49.43H208a8,8,0,0,0,0-16H163.77l8.73-48H224a8,8,0,0,0,0-16Zm-76.5,64H99.77l8.73-48h47.73Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",GS,[...t[5]||(t[5]=[fg(\"path\",{d:\"M224,92H170.61l9.33-51.28a4,4,0,1,0-7.88-1.44L162.48,92H106.61l9.33-51.28a4,4,0,1,0-7.88-1.44L98.48,92H48a4,4,0,0,0,0,8H97L86.84,156H32a4,4,0,0,0,0,8H85.39l-9.33,51.28a4,4,0,0,0,3.22,4.65A3.65,3.65,0,0,0,80,220a4,4,0,0,0,3.94-3.29L93.52,164h55.87l-9.33,51.28a4,4,0,0,0,3.22,4.65,3.65,3.65,0,0,0,.72.07,4,4,0,0,0,3.94-3.29L157.52,164H208a4,4,0,0,0,0-8H159l10.19-56H224a4,4,0,0,0,0-8Zm-73.16,64H95l10.19-56H161Z\"},null,-1)])])):Og(\"\",!0)],16))}}),KS={key:0},JS={key:1},e_={key:2},t_={key:3},n_={key:4},r_={key:5},a_=nf({name:\"ScalarIconHouse\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",KS,[...t[0]||(t[0]=[fg(\"path\",{d:\"M222.14,105.85l-80-80a20,20,0,0,0-28.28,0l-80,80A19.86,19.86,0,0,0,28,120v96a12,12,0,0,0,12,12h64a12,12,0,0,0,12-12V164h24v52a12,12,0,0,0,12,12h64a12,12,0,0,0,12-12V120A19.86,19.86,0,0,0,222.14,105.85ZM204,204H164V152a12,12,0,0,0-12-12H104a12,12,0,0,0-12,12v52H52V121.65l76-76,76,76Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",JS,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,120v96H152V152H104v64H40V120a8,8,0,0,1,2.34-5.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,216,120Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M219.31,108.68l-80-80a16,16,0,0,0-22.62,0l-80,80A15.87,15.87,0,0,0,32,120v96a8,8,0,0,0,8,8h64a8,8,0,0,0,8-8V160h32v56a8,8,0,0,0,8,8h64a8,8,0,0,0,8-8V120A15.87,15.87,0,0,0,219.31,108.68ZM208,208H160V152a8,8,0,0,0-8-8H104a8,8,0,0,0-8,8v56H48V120l80-80,80,80Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",e_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M224,120v96a8,8,0,0,1-8,8H160a8,8,0,0,1-8-8V164a4,4,0,0,0-4-4H108a4,4,0,0,0-4,4v52a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V120a16,16,0,0,1,4.69-11.31l80-80a16,16,0,0,1,22.62,0l80,80A16,16,0,0,1,224,120Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",t_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M217.9,110.1l-80-80a14,14,0,0,0-19.8,0l-80,80A13.92,13.92,0,0,0,34,120v96a6,6,0,0,0,6,6h64a6,6,0,0,0,6-6V158h36v58a6,6,0,0,0,6,6h64a6,6,0,0,0,6-6V120A13.92,13.92,0,0,0,217.9,110.1ZM210,210H158V152a6,6,0,0,0-6-6H104a6,6,0,0,0-6,6v58H46V120a2,2,0,0,1,.58-1.42l80-80a2,2,0,0,1,2.84,0l80,80A2,2,0,0,1,210,120Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",n_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M219.31,108.68l-80-80a16,16,0,0,0-22.62,0l-80,80A15.87,15.87,0,0,0,32,120v96a8,8,0,0,0,8,8h64a8,8,0,0,0,8-8V160h32v56a8,8,0,0,0,8,8h64a8,8,0,0,0,8-8V120A15.87,15.87,0,0,0,219.31,108.68ZM208,208H160V152a8,8,0,0,0-8-8H104a8,8,0,0,0-8,8v56H48V120l80-80,80,80Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",r_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M216.49,111.51l-80-80a12,12,0,0,0-17,0l-80,80A12,12,0,0,0,36,120v96a4,4,0,0,0,4,4h64a4,4,0,0,0,4-4V156h40v60a4,4,0,0,0,4,4h64a4,4,0,0,0,4-4V120A12,12,0,0,0,216.49,111.51ZM212,212H156V152a4,4,0,0,0-4-4H104a4,4,0,0,0-4,4v60H44V120a4,4,0,0,1,1.17-2.83l80-80a4,4,0,0,1,5.66,0l80,80A4,4,0,0,1,212,120Z\"},null,-1)])])):Og(\"\",!0)],16))}}),o_={key:0},i_={key:1},s_={key:2},l_={key:3},c_={key:4},u_={key:5},d_=nf({name:\"ScalarIconInfo\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",o_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",i_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",s_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",l_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M142,176a6,6,0,0,1-6,6,14,14,0,0,1-14-14V128a2,2,0,0,0-2-2,6,6,0,0,1,0-12,14,14,0,0,1,14,14v40a2,2,0,0,0,2,2A6,6,0,0,1,142,176ZM124,94a10,10,0,1,0-10-10A10,10,0,0,0,124,94Zm106,34A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",c_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",u_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M140,176a4,4,0,0,1-4,4,12,12,0,0,1-12-12V128a4,4,0,0,0-4-4,4,4,0,0,1,0-8,12,12,0,0,1,12,12v40a4,4,0,0,0,4,4A4,4,0,0,1,140,176ZM124,92a8,8,0,1,0-8-8A8,8,0,0,0,124,92Zm104,36A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z\"},null,-1)])])):Og(\"\",!0)],16))}}),p_={key:0},h_={key:1},f_={key:2},m_={key:3},g_={key:4},v_={key:5},b_=nf({name:\"ScalarIconLink\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",p_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M117.18,188.74a12,12,0,0,1,0,17l-5.12,5.12A58.26,58.26,0,0,1,70.6,228h0A58.62,58.62,0,0,1,29.14,127.92L63.89,93.17a58.64,58.64,0,0,1,98.56,28.11,12,12,0,1,1-23.37,5.44,34.65,34.65,0,0,0-58.22-16.58L46.11,144.89A34.62,34.62,0,0,0,70.57,204h0a34.41,34.41,0,0,0,24.49-10.14l5.11-5.12A12,12,0,0,1,117.18,188.74ZM226.83,45.17a58.65,58.65,0,0,0-82.93,0l-5.11,5.11a12,12,0,0,0,17,17l5.12-5.12a34.63,34.63,0,1,1,49,49L175.1,145.86A34.39,34.39,0,0,1,150.61,156h0a34.63,34.63,0,0,1-33.69-26.72,12,12,0,0,0-23.38,5.44A58.64,58.64,0,0,0,150.56,180h.05a58.28,58.28,0,0,0,41.47-17.17l34.75-34.75a58.62,58.62,0,0,0,0-82.91Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",h_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M218.34,119.6,183.6,154.34a46.58,46.58,0,0,1-44.31,12.26c-.31.34-.62.67-.95,1L103.6,202.34A46.63,46.63,0,1,1,37.66,136.4L72.4,101.66A46.6,46.6,0,0,1,116.71,89.4c.31-.34.62-.67,1-1L152.4,53.66a46.63,46.63,0,0,1,65.94,65.94Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M240,88.23a54.43,54.43,0,0,1-16,37L189.25,160a54.27,54.27,0,0,1-38.63,16h-.05A54.63,54.63,0,0,1,96,119.84a8,8,0,0,1,16,.45A38.62,38.62,0,0,0,150.58,160h0a38.39,38.39,0,0,0,27.31-11.31l34.75-34.75a38.63,38.63,0,0,0-54.63-54.63l-11,11A8,8,0,0,1,135.7,59l11-11A54.65,54.65,0,0,1,224,48,54.86,54.86,0,0,1,240,88.23ZM109,185.66l-11,11A38.41,38.41,0,0,1,70.6,208h0a38.63,38.63,0,0,1-27.29-65.94L78,107.31A38.63,38.63,0,0,1,144,135.71a8,8,0,0,0,7.78,8.22H152a8,8,0,0,0,8-7.78A54.86,54.86,0,0,0,144,96a54.65,54.65,0,0,0-77.27,0L32,130.75A54.62,54.62,0,0,0,70.56,224h0a54.28,54.28,0,0,0,38.64-16l11-11A8,8,0,0,0,109,185.66Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",f_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM115.7,192.49a43.31,43.31,0,0,1-55-66.43l25.37-25.37a43.35,43.35,0,0,1,61.25,0,42.9,42.9,0,0,1,9.95,15.43,8,8,0,1,1-15,5.6A27.33,27.33,0,0,0,97.37,112L72,137.37a27.32,27.32,0,0,0,34.68,41.91,8,8,0,1,1,9,13.21Zm79.61-62.55-25.37,25.37A43,43,0,0,1,139.32,168h0a43.35,43.35,0,0,1-40.53-28.12,8,8,0,1,1,15-5.6A27.35,27.35,0,0,0,139.28,152h0a27.14,27.14,0,0,0,19.32-8L184,118.63a27.32,27.32,0,0,0-34.68-41.91,8,8,0,1,1-9-13.21,43.32,43.32,0,0,1,55,66.43Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",m_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M238,88.18a52.42,52.42,0,0,1-15.4,35.66l-34.75,34.75A52.28,52.28,0,0,1,150.62,174h-.05A52.63,52.63,0,0,1,98,119.9a6,6,0,0,1,6-5.84h.17a6,6,0,0,1,5.83,6.16A40.62,40.62,0,0,0,150.58,162h0a40.4,40.4,0,0,0,28.73-11.9l34.75-34.74A40.63,40.63,0,0,0,156.63,57.9l-11,11a6,6,0,0,1-8.49-8.49l11-11a52.62,52.62,0,0,1,74.43,0A52.83,52.83,0,0,1,238,88.18Zm-127.62,98.9-11,11A40.36,40.36,0,0,1,70.6,210h0a40.63,40.63,0,0,1-28.7-69.36L76.62,105.9A40.63,40.63,0,0,1,146,135.77a6,6,0,0,0,5.83,6.16H152a6,6,0,0,0,6-5.84A52.63,52.63,0,0,0,68.14,97.42L33.38,132.16A52.63,52.63,0,0,0,70.56,222h0a52.26,52.26,0,0,0,37.22-15.42l11-11a6,6,0,1,0-8.49-8.48Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",g_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M240,88.23a54.43,54.43,0,0,1-16,37L189.25,160a54.27,54.27,0,0,1-38.63,16h-.05A54.63,54.63,0,0,1,96,119.84a8,8,0,0,1,16,.45A38.62,38.62,0,0,0,150.58,160h0a38.39,38.39,0,0,0,27.31-11.31l34.75-34.75a38.63,38.63,0,0,0-54.63-54.63l-11,11A8,8,0,0,1,135.7,59l11-11A54.65,54.65,0,0,1,224,48,54.86,54.86,0,0,1,240,88.23ZM109,185.66l-11,11A38.41,38.41,0,0,1,70.6,208h0a38.63,38.63,0,0,1-27.29-65.94L78,107.31A38.63,38.63,0,0,1,144,135.71a8,8,0,0,0,16,.45A54.86,54.86,0,0,0,144,96a54.65,54.65,0,0,0-77.27,0L32,130.75A54.62,54.62,0,0,0,70.56,224h0a54.28,54.28,0,0,0,38.64-16l11-11A8,8,0,0,0,109,185.66Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",v_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M236,88.12a50.44,50.44,0,0,1-14.81,34.31l-34.75,34.74A50.33,50.33,0,0,1,150.62,172h-.05A50.63,50.63,0,0,1,100,120a4,4,0,0,1,4-3.89h.11a4,4,0,0,1,3.89,4.11A42.64,42.64,0,0,0,150.58,164h0a42.32,42.32,0,0,0,30.14-12.49l34.75-34.74a42.63,42.63,0,1,0-60.29-60.28l-11,11a4,4,0,0,1-5.66-5.65l11-11A50.64,50.64,0,0,1,236,88.12ZM111.78,188.49l-11,11A42.33,42.33,0,0,1,70.6,212h0a42.63,42.63,0,0,1-30.11-72.77l34.75-34.74A42.63,42.63,0,0,1,148,135.82a4,4,0,0,0,8,.23A50.64,50.64,0,0,0,69.55,98.83L34.8,133.57A50.63,50.63,0,0,0,70.56,220h0a50.33,50.33,0,0,0,35.81-14.83l11-11a4,4,0,1,0-5.65-5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),y_={key:0},O_={key:1},w_={key:2},x_={key:3},k_={key:4},S_={key:5},__=nf({name:\"ScalarIconList\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",y_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128ZM40,76H216a12,12,0,0,0,0-24H40a12,12,0,0,0,0,24ZM216,180H40a12,12,0,0,0,0,24H216a12,12,0,0,0,0-24Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",O_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,64V192H40V64Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",w_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM192,184H64a8,8,0,0,1,0-16H192a8,8,0,0,1,0,16Zm0-48H64a8,8,0,0,1,0-16H192a8,8,0,0,1,0,16Zm0-48H64a8,8,0,0,1,0-16H192a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",x_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M222,128a6,6,0,0,1-6,6H40a6,6,0,0,1,0-12H216A6,6,0,0,1,222,128ZM40,70H216a6,6,0,0,0,0-12H40a6,6,0,0,0,0,12ZM216,186H40a6,6,0,0,0,0,12H216a6,6,0,0,0,0-12Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",k_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",S_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M220,128a4,4,0,0,1-4,4H40a4,4,0,0,1,0-8H216A4,4,0,0,1,220,128ZM40,68H216a4,4,0,0,0,0-8H40a4,4,0,0,0,0,8ZM216,188H40a4,4,0,0,0,0,8H216a4,4,0,0,0,0-8Z\"},null,-1)])])):Og(\"\",!0)],16))}}),T_={key:0},E_={key:1},A_={key:2},C_={key:3},P_={key:4},$_={key:5},D_=nf({name:\"ScalarIconLockSimple\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",T_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M208,76H180V56A52,52,0,0,0,76,56V76H48A20,20,0,0,0,28,96V208a20,20,0,0,0,20,20H208a20,20,0,0,0,20-20V96A20,20,0,0,0,208,76ZM100,56a28,28,0,0,1,56,0V76H100ZM204,204H52V100H204Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",E_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,96V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8H208A8,8,0,0,1,216,96Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96ZM208,208H48V96H208V208Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",A_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",C_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M208,82H174V56a46,46,0,0,0-92,0V82H48A14,14,0,0,0,34,96V208a14,14,0,0,0,14,14H208a14,14,0,0,0,14-14V96A14,14,0,0,0,208,82ZM94,56a34,34,0,0,1,68,0V82H94ZM210,208a2,2,0,0,1-2,2H48a2,2,0,0,1-2-2V96a2,2,0,0,1,2-2H208a2,2,0,0,1,2,2Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",P_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96ZM208,208H48V96H208V208Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",$_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M208,84H172V56a44,44,0,0,0-88,0V84H48A12,12,0,0,0,36,96V208a12,12,0,0,0,12,12H208a12,12,0,0,0,12-12V96A12,12,0,0,0,208,84ZM92,56a36,36,0,0,1,72,0V84H92ZM212,208a4,4,0,0,1-4,4H48a4,4,0,0,1-4-4V96a4,4,0,0,1,4-4H208a4,4,0,0,1,4,4Z\"},null,-1)])])):Og(\"\",!0)],16))}}),I_={key:0},M_={key:1},N_={key:2},R_={key:3},L_={key:4},B_={key:5},j_=nf({name:\"ScalarIconMagnifyingGlass\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",I_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M232.49,215.51,185,168a92.12,92.12,0,1,0-17,17l47.53,47.54a12,12,0,0,0,17-17ZM44,112a68,68,0,1,1,68,68A68.07,68.07,0,0,1,44,112Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",M_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M192,112a80,80,0,1,1-80-80A80,80,0,0,1,192,112Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M229.66,218.34,179.6,168.28a88.21,88.21,0,1,0-11.32,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",N_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M168,112a56,56,0,1,1-56-56A56,56,0,0,1,168,112Zm61.66,117.66a8,8,0,0,1-11.32,0l-50.06-50.07a88,88,0,1,1,11.32-11.31l50.06,50.06A8,8,0,0,1,229.66,229.66ZM112,184a72,72,0,1,0-72-72A72.08,72.08,0,0,0,112,184Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",R_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M228.24,219.76l-51.38-51.38a86.15,86.15,0,1,0-8.48,8.48l51.38,51.38a6,6,0,0,0,8.48-8.48ZM38,112a74,74,0,1,1,74,74A74.09,74.09,0,0,1,38,112Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",L_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",B_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M226.83,221.17l-52.7-52.7a84.1,84.1,0,1,0-5.66,5.66l52.7,52.7a4,4,0,0,0,5.66-5.66ZM36,112a76,76,0,1,1,76,76A76.08,76.08,0,0,1,36,112Z\"},null,-1)])])):Og(\"\",!0)],16))}}),U_={key:0},z_={key:1},F_={key:2},Q_={key:3},Z_={key:4},H_={key:5},V_=nf({name:\"ScalarIconPencilSimple\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",U_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M230.14,70.54,185.46,25.85a20,20,0,0,0-28.29,0L33.86,149.17A19.85,19.85,0,0,0,28,163.31V208a20,20,0,0,0,20,20H92.69a19.86,19.86,0,0,0,14.14-5.86L230.14,98.82a20,20,0,0,0,0-28.28ZM91,204H52V165l84-84,39,39ZM192,103,153,64l18.34-18.34,39,39Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",z_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M221.66,90.34,192,120,136,64l29.66-29.66a8,8,0,0,1,11.31,0L221.66,79A8,8,0,0,1,221.66,90.34Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63ZM92.69,208H48V163.31l88-88L180.69,120ZM192,108.68,147.31,64l24-24L216,84.68Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",F_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63ZM192,108.68,147.31,64l24-24L216,84.68Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",Q_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M225.9,74.78,181.21,30.09a14,14,0,0,0-19.8,0L38.1,153.41a13.94,13.94,0,0,0-4.1,9.9V208a14,14,0,0,0,14,14H92.69a13.94,13.94,0,0,0,9.9-4.1L225.9,94.58a14,14,0,0,0,0-19.8ZM94.1,209.41a2,2,0,0,1-1.41.59H48a2,2,0,0,1-2-2V163.31a2,2,0,0,1,.59-1.41L136,72.48,183.51,120ZM217.41,86.1,192,111.51,144.49,64,169.9,38.58a2,2,0,0,1,2.83,0l44.68,44.69a2,2,0,0,1,0,2.83Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Z_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63ZM92.69,208H48V163.31l88-88L180.69,120ZM192,108.68,147.31,64l24-24L216,84.68Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",H_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M224.49,76.2,179.8,31.51a12,12,0,0,0-17,0L133.17,61.17h0L39.52,154.83A11.9,11.9,0,0,0,36,163.31V208a12,12,0,0,0,12,12H92.69a12,12,0,0,0,8.48-3.51L224.48,93.17a12,12,0,0,0,0-17Zm-129,134.63A4,4,0,0,1,92.69,212H48a4,4,0,0,1-4-4V163.31a4,4,0,0,1,1.17-2.83L136,69.65,186.34,120ZM218.83,87.51,192,114.34,141.66,64l26.82-26.83a4,4,0,0,1,5.66,0l44.69,44.68a4,4,0,0,1,0,5.66Z\"},null,-1)])])):Og(\"\",!0)],16))}}),q_={key:0},W_={key:1},X_={key:2},G_={key:3},Y_={key:4},K_={key:5},J_=nf({name:\"ScalarIconPlay\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",q_,[...t[0]||(t[0]=[fg(\"path\",{d:\"M234.49,111.07,90.41,22.94A20,20,0,0,0,60,39.87V216.13a20,20,0,0,0,30.41,16.93l144.08-88.13a19.82,19.82,0,0,0,0-33.86ZM84,208.85V47.15L216.16,128Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",W_,[...t[1]||(t[1]=[fg(\"path\",{d:\"M228.23,134.69,84.15,222.81A8,8,0,0,1,72,216.12V39.88a8,8,0,0,1,12.15-6.69l144.08,88.12A7.82,7.82,0,0,1,228.23,134.69Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M232.4,114.49,88.32,26.35a16,16,0,0,0-16.2-.3A15.86,15.86,0,0,0,64,39.87V216.13A15.94,15.94,0,0,0,80,232a16.07,16.07,0,0,0,8.36-2.35L232.4,141.51a15.81,15.81,0,0,0,0-27ZM80,215.94V40l143.83,88Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",X_,[...t[2]||(t[2]=[fg(\"path\",{d:\"M240,128a15.74,15.74,0,0,1-7.6,13.51L88.32,229.65a16,16,0,0,1-16.2.3A15.86,15.86,0,0,1,64,216.13V39.87a15.86,15.86,0,0,1,8.12-13.82,16,16,0,0,1,16.2.3L232.4,114.49A15.74,15.74,0,0,1,240,128Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",G_,[...t[3]||(t[3]=[fg(\"path\",{d:\"M231.36,116.19,87.28,28.06a14,14,0,0,0-14.18-.27A13.69,13.69,0,0,0,66,39.87V216.13a13.69,13.69,0,0,0,7.1,12.08,14,14,0,0,0,14.18-.27l144.08-88.13a13.82,13.82,0,0,0,0-23.62Zm-6.26,13.38L81,217.7a2,2,0,0,1-2.06,0,1.78,1.78,0,0,1-1-1.61V39.87a1.78,1.78,0,0,1,1-1.61A2.06,2.06,0,0,1,80,38a2,2,0,0,1,1,.31L225.1,126.43a1.82,1.82,0,0,1,0,3.14Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",Y_,[...t[4]||(t[4]=[fg(\"path\",{d:\"M232.4,114.49,88.32,26.35a16,16,0,0,0-16.2-.3A15.86,15.86,0,0,0,64,39.87V216.13A15.94,15.94,0,0,0,80,232a16.07,16.07,0,0,0,8.36-2.35L232.4,141.51a15.81,15.81,0,0,0,0-27ZM80,215.94V40l143.83,88Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",K_,[...t[5]||(t[5]=[fg(\"path\",{d:\"M230.32,117.9,86.24,29.79a11.91,11.91,0,0,0-12.17-.23A11.71,11.71,0,0,0,68,39.89V216.11a11.71,11.71,0,0,0,6.07,10.33,11.91,11.91,0,0,0,12.17-.23L230.32,138.1a11.82,11.82,0,0,0,0-20.2Zm-4.18,13.37L82.06,219.39a4,4,0,0,1-4.07.07,3.77,3.77,0,0,1-2-3.35V39.89a3.77,3.77,0,0,1,2-3.35,4,4,0,0,1,4.07.07l144.08,88.12a3.8,3.8,0,0,1,0,6.54Z\"},null,-1)])])):Og(\"\",!0)],16))}}),eT={key:0},tT={key:1},nT={key:2},rT={key:3},aT={key:4},oT={key:5},iT=nf({name:\"ScalarIconPlus\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",eT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",tT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",nT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",rT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",aT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",oT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z\"},null,-1)])])):Og(\"\",!0)],16))}}),sT={key:0},lT={key:1},cT={key:2},uT={key:3},dT={key:4},pT={key:5},hT=nf({name:\"ScalarIconScroll\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",sT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M92,92a12,12,0,0,1,12-12h60a12,12,0,0,1,0,24H104A12,12,0,0,1,92,92Zm12,52h60a12,12,0,0,0,0-24H104a12,12,0,0,0,0,24Zm132,48a36,36,0,0,1-36,36H88a36,36,0,0,1-36-36V64a12,12,0,0,0-24,0c0,3.73,3.35,6.51,3.38,6.54l-.18-.14h0A12,12,0,1,1,16.81,89.59h0C15.49,88.62,4,79.55,4,64A36,36,0,0,1,40,28H176a36,36,0,0,1,36,36V164h4a12,12,0,0,1,7.2,2.4C224.51,167.38,236,176.45,236,192ZM92.62,172.2A12,12,0,0,1,104,164h84V64a12,12,0,0,0-12-12H73.94A35.88,35.88,0,0,1,76,64V192a12,12,0,0,0,24,0c0-3.58-3.17-6.38-3.2-6.4A12,12,0,0,1,92.62,172.2ZM212,192a7.69,7.69,0,0,0-1.24-4h-87a30.32,30.32,0,0,1,.26,4,35.84,35.84,0,0,1-2.06,12H200A12,12,0,0,0,212,192Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",lT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M200,176H104s8,6,8,16a24,24,0,0,1-48,0V64A24,24,0,0,0,40,40H176a24,24,0,0,1,24,24Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M96,104a8,8,0,0,1,8-8h64a8,8,0,0,1,0,16H104A8,8,0,0,1,96,104Zm8,40h64a8,8,0,0,0,0-16H104a8,8,0,0,0,0,16Zm128,48a32,32,0,0,1-32,32H88a32,32,0,0,1-32-32V64a16,16,0,0,0-32,0c0,5.74,4.83,9.62,4.88,9.66h0A8,8,0,0,1,24,88a7.89,7.89,0,0,1-4.79-1.61h0C18.05,85.54,8,77.61,8,64A32,32,0,0,1,40,32H176a32,32,0,0,1,32,32V168h8a8,8,0,0,1,4.8,1.6C222,170.46,232,178.39,232,192ZM96.26,173.48A8.07,8.07,0,0,1,104,168h88V64a16,16,0,0,0-16-16H67.69A31.71,31.71,0,0,1,72,64V192a16,16,0,0,0,32,0c0-5.74-4.83-9.62-4.88-9.66A7.82,7.82,0,0,1,96.26,173.48ZM216,192a12.58,12.58,0,0,0-3.23-8h-94a26.92,26.92,0,0,1,1.21,8,31.82,31.82,0,0,1-4.29,16H200A16,16,0,0,0,216,192Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",cT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M220.8,169.6A8,8,0,0,0,216,168h-8V64a32,32,0,0,0-32-32H40A32,32,0,0,0,8,64C8,77.61,18.05,85.54,19.2,86.4h0A7.89,7.89,0,0,0,24,88a8,8,0,0,0,4.87-14.33h0C28.83,73.62,24,69.74,24,64a16,16,0,0,1,32,0V192a32,32,0,0,0,32,32H200a32,32,0,0,0,32-32C232,178.39,222,170.46,220.8,169.6ZM104,96h64a8,8,0,0,1,0,16H104a8,8,0,0,1,0-16Zm-8,40a8,8,0,0,1,8-8h64a8,8,0,0,1,0,16H104A8,8,0,0,1,96,136Zm104,72H107.71A31.82,31.82,0,0,0,112,192a26.92,26.92,0,0,0-1.21-8h102a12.58,12.58,0,0,1,3.23,8A16,16,0,0,1,200,208Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",uT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M98,136a6,6,0,0,1,6-6h64a6,6,0,0,1,0,12H104A6,6,0,0,1,98,136Zm6-26h64a6,6,0,0,0,0-12H104a6,6,0,0,0,0,12Zm126,82a30,30,0,0,1-30,30H88a30,30,0,0,1-30-30V64a18,18,0,0,0-36,0c0,6.76,5.58,11.19,5.64,11.23A6,6,0,1,1,20.4,84.8C20,84.48,10,76.85,10,64A30,30,0,0,1,40,34H176a30,30,0,0,1,30,30V170h10a6,6,0,0,1,3.6,1.2C220,171.52,230,179.15,230,192Zm-124,0c0-6.76-5.59-11.19-5.64-11.23A6,6,0,0,1,104,170h90V64a18,18,0,0,0-18-18H64a29.82,29.82,0,0,1,6,18V192a18,18,0,0,0,36,0Zm112,0a14.94,14.94,0,0,0-4.34-10H115.88A24.83,24.83,0,0,1,118,192a29.87,29.87,0,0,1-6,18h88A18,18,0,0,0,218,192Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",dT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M96,104a8,8,0,0,1,8-8h64a8,8,0,0,1,0,16H104A8,8,0,0,1,96,104Zm8,40h64a8,8,0,0,0,0-16H104a8,8,0,0,0,0,16Zm128,48a32,32,0,0,1-32,32H88a32,32,0,0,1-32-32V64a16,16,0,0,0-32,0c0,5.74,4.83,9.62,4.88,9.66h0A8,8,0,0,1,24,88a7.89,7.89,0,0,1-4.79-1.61h0C18.05,85.54,8,77.61,8,64A32,32,0,0,1,40,32H176a32,32,0,0,1,32,32V168h8a8,8,0,0,1,4.8,1.6C222,170.46,232,178.39,232,192ZM96.26,173.48A8.07,8.07,0,0,1,104,168h88V64a16,16,0,0,0-16-16H67.69A31.71,31.71,0,0,1,72,64V192a16,16,0,0,0,32,0c0-5.74-4.83-9.62-4.88-9.66A7.82,7.82,0,0,1,96.26,173.48ZM216,192a12.58,12.58,0,0,0-3.23-8h-94a26.92,26.92,0,0,1,1.21,8,31.82,31.82,0,0,1-4.29,16H200A16,16,0,0,0,216,192Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",pT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M100,104a4,4,0,0,1,4-4h64a4,4,0,0,1,0,8H104A4,4,0,0,1,100,104Zm4,36h64a4,4,0,0,0,0-8H104a4,4,0,0,0,0,8Zm124,52a28,28,0,0,1-28,28H88a28,28,0,0,1-28-28V64a20,20,0,0,0-40,0c0,7.78,6.34,12.75,6.4,12.8a4,4,0,1,1-4.8,6.4C21.21,82.91,12,75.86,12,64A28,28,0,0,1,40,36H176a28,28,0,0,1,28,28V172h12a4,4,0,0,1,2.4.8C218.79,173.09,228,180.14,228,192Zm-120,0c0-7.78-6.34-12.75-6.4-12.8A4,4,0,0,1,104,172h92V64a20,20,0,0,0-20-20H59.57A27.9,27.9,0,0,1,68,64V192a20,20,0,0,0,40,0Zm112,0c0-6-3.74-10.3-5.5-12H112.61A23.31,23.31,0,0,1,116,192a27.94,27.94,0,0,1-8.42,20H200A20,20,0,0,0,220,192Z\"},null,-1)])])):Og(\"\",!0)],16))}}),fT={key:0},mT={key:1},gT={key:2},vT={key:3},bT={key:4},yT={key:5},OT=nf({name:\"ScalarIconSwap\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",fT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M228,48V152a20,20,0,0,1-20,20H112.92a12,12,0,0,1-17.41,16.49l-20-20a12,12,0,0,1,0-17l20-20A12,12,0,0,1,112.92,148H204V52H100a12,12,0,0,1-24,0V48A20,20,0,0,1,96,28H208A20,20,0,0,1,228,48ZM168,192a12,12,0,0,0-12,12H52V108h91.08a12,12,0,0,0,17.41,16.49l20-20a12,12,0,0,0,0-17l-20-20A12,12,0,0,0,143.08,84H48a20,20,0,0,0-20,20V208a20,20,0,0,0,20,20H160a20,20,0,0,0,20-20v-4A12,12,0,0,0,168,192Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",mT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,48V152a8,8,0,0,1-8,8H168v48a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V104a8,8,0,0,1,8-8H88V48a8,8,0,0,1,8-8H208A8,8,0,0,1,216,48Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M224,48V152a16,16,0,0,1-16,16H99.31l10.35,10.34a8,8,0,0,1-11.32,11.32l-24-24a8,8,0,0,1,0-11.32l24-24a8,8,0,0,1,11.32,11.32L99.31,152H208V48H96v8a8,8,0,0,1-16,0V48A16,16,0,0,1,96,32H208A16,16,0,0,1,224,48ZM168,192a8,8,0,0,0-8,8v8H48V104H156.69l-10.35,10.34a8,8,0,0,0,11.32,11.32l24-24a8,8,0,0,0,0-11.32l-24-24a8,8,0,0,0-11.32,11.32L156.69,88H48a16,16,0,0,0-16,16V208a16,16,0,0,0,16,16H160a16,16,0,0,0,16-16v-8A8,8,0,0,0,168,192Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",gT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M224,48V152a16,16,0,0,1-16,16H112v16a8,8,0,0,1-13.66,5.66l-24-24a8,8,0,0,1,0-11.32l24-24A8,8,0,0,1,112,136v16h96V48H96v8a8,8,0,0,1-16,0V48A16,16,0,0,1,96,32H208A16,16,0,0,1,224,48ZM168,192a8,8,0,0,0-8,8v8H48V104h96v16a8,8,0,0,0,13.66,5.66l24-24a8,8,0,0,0,0-11.32l-24-24A8,8,0,0,0,144,72V88H48a16,16,0,0,0-16,16V208a16,16,0,0,0,16,16H160a16,16,0,0,0,16-16v-8A8,8,0,0,0,168,192Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",vT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M222,48V152a14,14,0,0,1-14,14H94.49l13.75,13.76a6,6,0,1,1-8.48,8.48l-24-24a6,6,0,0,1,0-8.48l24-24a6,6,0,0,1,8.48,8.48L94.49,154H208a2,2,0,0,0,2-2V48a2,2,0,0,0-2-2H96a2,2,0,0,0-2,2v8a6,6,0,0,1-12,0V48A14,14,0,0,1,96,34H208A14,14,0,0,1,222,48ZM168,194a6,6,0,0,0-6,6v8a2,2,0,0,1-2,2H48a2,2,0,0,1-2-2V104a2,2,0,0,1,2-2H161.51l-13.75,13.76a6,6,0,1,0,8.48,8.48l24-24a6,6,0,0,0,0-8.48l-24-24a6,6,0,0,0-8.48,8.48L161.51,90H48a14,14,0,0,0-14,14V208a14,14,0,0,0,14,14H160a14,14,0,0,0,14-14v-8A6,6,0,0,0,168,194Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",bT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M224,48V152a16,16,0,0,1-16,16H99.31l10.35,10.34a8,8,0,0,1-11.32,11.32l-24-24a8,8,0,0,1,0-11.32l24-24a8,8,0,0,1,11.32,11.32L99.31,152H208V48H96v8a8,8,0,0,1-16,0V48A16,16,0,0,1,96,32H208A16,16,0,0,1,224,48ZM168,192a8,8,0,0,0-8,8v8H48V104H156.69l-10.35,10.34a8,8,0,0,0,11.32,11.32l24-24a8,8,0,0,0,0-11.32l-24-24a8,8,0,0,0-11.32,11.32L156.69,88H48a16,16,0,0,0-16,16V208a16,16,0,0,0,16,16H160a16,16,0,0,0,16-16v-8A8,8,0,0,0,168,192Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",yT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M220,48V152a12,12,0,0,1-12,12H89.66l17.17,17.17a4,4,0,0,1-5.66,5.66l-24-24a4,4,0,0,1,0-5.66l24-24a4,4,0,0,1,5.66,5.66L89.66,156H208a4,4,0,0,0,4-4V48a4,4,0,0,0-4-4H96a4,4,0,0,0-4,4v8a4,4,0,0,1-8,0V48A12,12,0,0,1,96,36H208A12,12,0,0,1,220,48ZM168,196a4,4,0,0,0-4,4v8a4,4,0,0,1-4,4H48a4,4,0,0,1-4-4V104a4,4,0,0,1,4-4H166.34l-17.17,17.17a4,4,0,0,0,5.66,5.66l24-24a4,4,0,0,0,0-5.66l-24-24a4,4,0,0,0-5.66,5.66L166.34,92H48a12,12,0,0,0-12,12V208a12,12,0,0,0,12,12H160a12,12,0,0,0,12-12v-8A4,4,0,0,0,168,196Z\"},null,-1)])])):Og(\"\",!0)],16))}}),wT={key:0},xT={key:1},kT={key:2},ST={key:3},_T={key:4},TT={key:5},ET=nf({name:\"ScalarIconTag\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",wT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M246.15,133.18,146.83,33.86A19.85,19.85,0,0,0,132.69,28H40A12,12,0,0,0,28,40v92.69a19.85,19.85,0,0,0,5.86,14.14l99.32,99.32a20,20,0,0,0,28.28,0l84.69-84.69A20,20,0,0,0,246.15,133.18Zm-98.83,93.17L52,131V52h79l95.32,95.32ZM104,88A16,16,0,1,1,88,72,16,16,0,0,1,104,88Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",xT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M237.66,153,153,237.66a8,8,0,0,1-11.31,0L42.34,138.34A8,8,0,0,1,40,132.69V40h92.69a8,8,0,0,1,5.65,2.34l99.32,99.32A8,8,0,0,1,237.66,153Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M243.31,136,144,36.69A15.86,15.86,0,0,0,132.69,32H40a8,8,0,0,0-8,8v92.69A15.86,15.86,0,0,0,36.69,144L136,243.31a16,16,0,0,0,22.63,0l84.68-84.68a16,16,0,0,0,0-22.63Zm-96,96L48,132.69V48h84.69L232,147.31ZM96,84A12,12,0,1,1,84,72,12,12,0,0,1,96,84Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",kT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M243.31,136,144,36.69A15.86,15.86,0,0,0,132.69,32H40a8,8,0,0,0-8,8v92.69A15.86,15.86,0,0,0,36.69,144L136,243.31a16,16,0,0,0,22.63,0l84.68-84.68a16,16,0,0,0,0-22.63ZM84,96A12,12,0,1,1,96,84,12,12,0,0,1,84,96Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",ST,[...t[3]||(t[3]=[fg(\"path\",{d:\"M241.91,137.42,142.59,38.1a13.94,13.94,0,0,0-9.9-4.1H40a6,6,0,0,0-6,6v92.69a13.94,13.94,0,0,0,4.1,9.9l99.32,99.32a14,14,0,0,0,19.8,0l84.69-84.69A14,14,0,0,0,241.91,137.42Zm-8.49,11.31-84.69,84.69a2,2,0,0,1-2.83,0L46.59,134.1a2,2,0,0,1-.59-1.41V46h86.69a2,2,0,0,1,1.41.59l99.32,99.31A2,2,0,0,1,233.42,148.73ZM94,84A10,10,0,1,1,84,74,10,10,0,0,1,94,84Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",_T,[...t[4]||(t[4]=[fg(\"path\",{d:\"M243.31,136,144,36.69A15.86,15.86,0,0,0,132.69,32H40a8,8,0,0,0-8,8v92.69A15.86,15.86,0,0,0,36.69,144L136,243.31a16,16,0,0,0,22.63,0l84.68-84.68a16,16,0,0,0,0-22.63Zm-96,96L48,132.69V48h84.69L232,147.31ZM96,84A12,12,0,1,1,84,72,12,12,0,0,1,96,84Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",TT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M240.49,138.83,141.17,39.51A11.93,11.93,0,0,0,132.69,36H40a4,4,0,0,0-4,4v92.69a11.93,11.93,0,0,0,3.51,8.48l99.32,99.32a12,12,0,0,0,17,0l84.69-84.69a12,12,0,0,0,0-17Zm-5.66,11.31-84.69,84.69a4,4,0,0,1-5.65,0L45.17,135.51A4,4,0,0,1,44,132.69V44h88.69a4,4,0,0,1,2.82,1.17l99.32,99.32A4,4,0,0,1,234.83,150.14ZM92,84a8,8,0,1,1-8-8A8,8,0,0,1,92,84Z\"},null,-1)])])):Og(\"\",!0)],16))}}),AT={key:0},CT={key:1},PT={key:2},$T={key:3},DT={key:4},IT={key:5},MT=nf({name:\"ScalarIconTerminalWindow\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",AT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M72.5,150.63,100.79,128,72.5,105.37a12,12,0,1,1,15-18.74l40,32a12,12,0,0,1,0,18.74l-40,32a12,12,0,0,1-15-18.74ZM144,172h32a12,12,0,0,0,0-24H144a12,12,0,0,0,0,24ZM236,56V200a20,20,0,0,1-20,20H40a20,20,0,0,1-20-20V56A20,20,0,0,1,40,36H216A20,20,0,0,1,236,56Zm-24,4H44V196H212Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",CT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,56V200a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8H216A8,8,0,0,1,224,56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M128,128a8,8,0,0,1-3,6.25l-40,32a8,8,0,1,1-10-12.5L107.19,128,75,102.25a8,8,0,1,1,10-12.5l40,32A8,8,0,0,1,128,128Zm48,24H136a8,8,0,0,0,0,16h40a8,8,0,0,0,0-16Zm56-96V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56ZM216,200V56H40V200H216Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",PT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm-91,94.25-40,32a8,8,0,1,1-10-12.5L107.19,128,75,102.25a8,8,0,1,1,10-12.5l40,32a8,8,0,0,1,0,12.5ZM176,168H136a8,8,0,0,1,0-16h40a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",$T,[...t[3]||(t[3]=[fg(\"path\",{d:\"M126,128a6,6,0,0,1-2.25,4.69l-40,32a6,6,0,0,1-7.5-9.38L110.4,128,76.25,100.69a6,6,0,1,1,7.5-9.38l40,32A6,6,0,0,1,126,128Zm50,26H136a6,6,0,0,0,0,12h40a6,6,0,0,0,0-12Zm54-98V200a14,14,0,0,1-14,14H40a14,14,0,0,1-14-14V56A14,14,0,0,1,40,42H216A14,14,0,0,1,230,56Zm-12,0a2,2,0,0,0-2-2H40a2,2,0,0,0-2,2V200a2,2,0,0,0,2,2H216a2,2,0,0,0,2-2Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",DT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M128,128a8,8,0,0,1-3,6.25l-40,32a8,8,0,1,1-10-12.5L107.19,128,75,102.25a8,8,0,1,1,10-12.5l40,32A8,8,0,0,1,128,128Zm48,24H136a8,8,0,0,0,0,16h40a8,8,0,0,0,0-16Zm56-96V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56ZM216,200V56H40V200H216Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",IT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M122.5,124.88a4,4,0,0,1,0,6.24l-40,32a4,4,0,0,1-5-6.24L113.6,128,77.5,99.12a4,4,0,0,1,5-6.24ZM176,156H136a4,4,0,0,0,0,8h40a4,4,0,0,0,0-8ZM228,56V200a12,12,0,0,1-12,12H40a12,12,0,0,1-12-12V56A12,12,0,0,1,40,44H216A12,12,0,0,1,228,56Zm-8,0a4,4,0,0,0-4-4H40a4,4,0,0,0-4,4V200a4,4,0,0,0,4,4H216a4,4,0,0,0,4-4Z\"},null,-1)])])):Og(\"\",!0)],16))}}),NT={key:0},RT={key:1},LT={key:2},BT={key:3},jT={key:4},UT={key:5},zT=nf({name:\"ScalarIconTextAlignLeft\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",NT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M28,64A12,12,0,0,1,40,52H216a12,12,0,0,1,0,24H40A12,12,0,0,1,28,64Zm12,52H168a12,12,0,0,0,0-24H40a12,12,0,0,0,0,24Zm176,16H40a12,12,0,0,0,0,24H216a12,12,0,0,0,0-24Zm-48,40H40a12,12,0,0,0,0,24H168a12,12,0,0,0,0-24Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",RT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,64V168a16,16,0,0,1-16,16H40V64Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M32,64a8,8,0,0,1,8-8H216a8,8,0,0,1,0,16H40A8,8,0,0,1,32,64Zm8,48H168a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16Zm176,24H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Zm-48,40H40a8,8,0,0,0,0,16H168a8,8,0,0,0,0-16Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",LT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM160,184H64a8,8,0,0,1,0-16h96a8,8,0,0,1,0,16Zm32-32H64a8,8,0,0,1,0-16H192a8,8,0,0,1,0,16ZM56,112a8,8,0,0,1,8-8h96a8,8,0,0,1,0,16H64A8,8,0,0,1,56,112ZM192,88H64a8,8,0,0,1,0-16H192a8,8,0,0,1,0,16Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",BT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M34,64a6,6,0,0,1,6-6H216a6,6,0,0,1,0,12H40A6,6,0,0,1,34,64Zm6,46H168a6,6,0,0,0,0-12H40a6,6,0,0,0,0,12Zm176,28H40a6,6,0,0,0,0,12H216a6,6,0,0,0,0-12Zm-48,40H40a6,6,0,0,0,0,12H168a6,6,0,0,0,0-12Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",jT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M32,64a8,8,0,0,1,8-8H216a8,8,0,0,1,0,16H40A8,8,0,0,1,32,64Zm8,48H168a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16Zm176,24H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Zm-48,40H40a8,8,0,0,0,0,16H168a8,8,0,0,0,0-16Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",UT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M36,64a4,4,0,0,1,4-4H216a4,4,0,0,1,0,8H40A4,4,0,0,1,36,64Zm4,44H168a4,4,0,0,0,0-8H40a4,4,0,0,0,0,8Zm176,32H40a4,4,0,0,0,0,8H216a4,4,0,0,0,0-8Zm-48,40H40a4,4,0,0,0,0,8H168a4,4,0,0,0,0-8Z\"},null,-1)])])):Og(\"\",!0)],16))}}),FT={key:0},QT={key:1},ZT={key:2},HT={key:3},VT={key:4},qT={key:5},WT=nf({name:\"ScalarIconTrash\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",FT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M216,48H180V36A28,28,0,0,0,152,8H104A28,28,0,0,0,76,36V48H40a12,12,0,0,0,0,24h4V208a20,20,0,0,0,20,20H192a20,20,0,0,0,20-20V72h4a12,12,0,0,0,0-24ZM100,36a4,4,0,0,1,4-4h48a4,4,0,0,1,4,4V48H100Zm88,168H68V72H188ZM116,104v64a12,12,0,0,1-24,0V104a12,12,0,0,1,24,0Zm48,0v64a12,12,0,0,1-24,0V104a12,12,0,0,1,24,0Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",QT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M200,56V208a8,8,0,0,1-8,8H64a8,8,0,0,1-8-8V56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",ZT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm0-120H96V40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",HT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M216,50H174V40a22,22,0,0,0-22-22H104A22,22,0,0,0,82,40V50H40a6,6,0,0,0,0,12H50V208a14,14,0,0,0,14,14H192a14,14,0,0,0,14-14V62h10a6,6,0,0,0,0-12ZM94,40a10,10,0,0,1,10-10h48a10,10,0,0,1,10,10V50H94ZM194,208a2,2,0,0,1-2,2H64a2,2,0,0,1-2-2V62H194ZM110,104v64a6,6,0,0,1-12,0V104a6,6,0,0,1,12,0Zm48,0v64a6,6,0,0,1-12,0V104a6,6,0,0,1,12,0Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",VT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",qT,[...t[5]||(t[5]=[fg(\"path\",{d:\"M216,52H172V40a20,20,0,0,0-20-20H104A20,20,0,0,0,84,40V52H40a4,4,0,0,0,0,8H52V208a12,12,0,0,0,12,12H192a12,12,0,0,0,12-12V60h12a4,4,0,0,0,0-8ZM92,40a12,12,0,0,1,12-12h48a12,12,0,0,1,12,12V52H92ZM196,208a4,4,0,0,1-4,4H64a4,4,0,0,1-4-4V60H196ZM108,104v64a4,4,0,0,1-8,0V104a4,4,0,0,1,8,0Zm48,0v64a4,4,0,0,1-8,0V104a4,4,0,0,1,8,0Z\"},null,-1)])])):Og(\"\",!0)],16))}}),XT={key:0},GT={key:1},YT={key:2},KT={key:3},JT={key:4},eE={key:5},tE=nf({name:\"ScalarIconWarning\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",XT,[...t[0]||(t[0]=[fg(\"path\",{d:\"M240.26,186.1,152.81,34.23h0a28.74,28.74,0,0,0-49.62,0L15.74,186.1a27.45,27.45,0,0,0,0,27.71A28.31,28.31,0,0,0,40.55,228h174.9a28.31,28.31,0,0,0,24.79-14.19A27.45,27.45,0,0,0,240.26,186.1Zm-20.8,15.7a4.46,4.46,0,0,1-4,2.2H40.55a4.46,4.46,0,0,1-4-2.2,3.56,3.56,0,0,1,0-3.73L124,46.2a4.77,4.77,0,0,1,8,0l87.44,151.87A3.56,3.56,0,0,1,219.46,201.8ZM116,136V104a12,12,0,0,1,24,0v32a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,176Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",GT,[...t[1]||(t[1]=[fg(\"path\",{d:\"M215.46,216H40.54C27.92,216,20,202.79,26.13,192.09L113.59,40.22c6.3-11,22.52-11,28.82,0l87.46,151.87C236,202.79,228.08,216,215.46,216Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",YT,[...t[2]||(t[2]=[fg(\"path\",{d:\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM120,104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm8,88a12,12,0,1,1,12-12A12,12,0,0,1,128,192Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",KT,[...t[3]||(t[3]=[fg(\"path\",{d:\"M235.07,189.09,147.61,37.22h0a22.75,22.75,0,0,0-39.22,0L20.93,189.09a21.53,21.53,0,0,0,0,21.72A22.35,22.35,0,0,0,40.55,222h174.9a22.35,22.35,0,0,0,19.6-11.19A21.53,21.53,0,0,0,235.07,189.09ZM224.66,204.8a10.46,10.46,0,0,1-9.21,5.2H40.55a10.46,10.46,0,0,1-9.21-5.2,9.51,9.51,0,0,1,0-9.72L118.79,43.21a10.75,10.75,0,0,1,18.42,0l87.46,151.87A9.51,9.51,0,0,1,224.66,204.8ZM122,144V104a6,6,0,0,1,12,0v40a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,180Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",JT,[...t[4]||(t[4]=[fg(\"path\",{d:\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",eE,[...t[5]||(t[5]=[fg(\"path\",{d:\"M233.34,190.09,145.88,38.22h0a20.75,20.75,0,0,0-35.76,0L22.66,190.09a19.52,19.52,0,0,0,0,19.71A20.36,20.36,0,0,0,40.54,220H215.46a20.36,20.36,0,0,0,17.86-10.2A19.52,19.52,0,0,0,233.34,190.09ZM226.4,205.8a12.47,12.47,0,0,1-10.94,6.2H40.54a12.47,12.47,0,0,1-10.94-6.2,11.45,11.45,0,0,1,0-11.72L117.05,42.21a12.76,12.76,0,0,1,21.9,0L226.4,194.08A11.45,11.45,0,0,1,226.4,205.8ZM124,144V104a4,4,0,0,1,8,0v40a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,180Z\"},null,-1)])])):Og(\"\",!0)],16))}}),nE={key:0},rE={key:1},aE={key:2},oE={key:3},iE={key:4},sE={key:5},lE=nf({name:\"ScalarIconWarningCircle\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",nE,[...t[0]||(t[0]=[fg(\"path\",{d:\"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",rE,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",aE,[...t[2]||(t[2]=[fg(\"path\",{d:\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",oE,[...t[3]||(t[3]=[fg(\"path\",{d:\"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm0,192a90,90,0,1,1,90-90A90.1,90.1,0,0,1,128,218Zm-6-82V80a6,6,0,0,1,12,0v56a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,172Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",iE,[...t[4]||(t[4]=[fg(\"path\",{d:\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",sE,[...t[5]||(t[5]=[fg(\"path\",{d:\"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm0,192a92,92,0,1,1,92-92A92.1,92.1,0,0,1,128,220Zm-4-84V80a4,4,0,0,1,8,0v56a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,172Z\"},null,-1)])])):Og(\"\",!0)],16))}}),cE={key:0},uE={key:1},dE={key:2},pE={key:3},hE={key:4},fE={key:5},mE=nf({name:\"ScalarIconWarningOctagon\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",cE,[...t[0]||(t[0]=[fg(\"path\",{d:\"M116,132V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0ZM236,91.55v72.9a19.86,19.86,0,0,1-5.86,14.14l-51.55,51.55A19.85,19.85,0,0,1,164.45,236H91.55a19.85,19.85,0,0,1-14.14-5.86L25.86,178.59A19.86,19.86,0,0,1,20,164.45V91.55a19.86,19.86,0,0,1,5.86-14.14L77.41,25.86A19.85,19.85,0,0,1,91.55,20h72.9a19.85,19.85,0,0,1,14.14,5.86l51.55,51.55A19.86,19.86,0,0,1,236,91.55Zm-24,1.66L162.79,44H93.21L44,93.21v69.58L93.21,212h69.58L212,162.79ZM128,156a16,16,0,1,0,16,16A16,16,0,0,0,128,156Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",uE,[...t[1]||(t[1]=[fg(\"path\",{d:\"M224,91.55v72.9a8,8,0,0,1-2.34,5.66l-51.55,51.55a8,8,0,0,1-5.66,2.34H91.55a8,8,0,0,1-5.66-2.34L34.34,170.11A8,8,0,0,1,32,164.45V91.55a8,8,0,0,1,2.34-5.66L85.89,34.34A8,8,0,0,1,91.55,32h72.9a8,8,0,0,1,5.66,2.34l51.55,51.55A8,8,0,0,1,224,91.55Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",dE,[...t[2]||(t[2]=[fg(\"path\",{d:\"M227.31,80.23,175.77,28.69A16.13,16.13,0,0,0,164.45,24H91.55a16.13,16.13,0,0,0-11.32,4.69L28.69,80.23A16.13,16.13,0,0,0,24,91.55v72.9a16.13,16.13,0,0,0,4.69,11.32l51.54,51.54A16.13,16.13,0,0,0,91.55,232h72.9a16.13,16.13,0,0,0,11.32-4.69l51.54-51.54A16.13,16.13,0,0,0,232,164.45V91.55A16.13,16.13,0,0,0,227.31,80.23ZM120,80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",pE,[...t[3]||(t[3]=[fg(\"path\",{d:\"M122,136V80a6,6,0,0,1,12,0v56a6,6,0,0,1-12,0ZM230,91.55v72.9a13.92,13.92,0,0,1-4.1,9.9L174.35,225.9a13.92,13.92,0,0,1-9.9,4.1H91.55a13.92,13.92,0,0,1-9.9-4.1L30.1,174.35a13.92,13.92,0,0,1-4.1-9.9V91.55a13.92,13.92,0,0,1,4.1-9.9L81.65,30.1a13.92,13.92,0,0,1,9.9-4.1h72.9a13.92,13.92,0,0,1,9.9,4.1L225.9,81.65A13.92,13.92,0,0,1,230,91.55Zm-12,0a2,2,0,0,0-.59-1.42L165.87,38.59a2,2,0,0,0-1.42-.59H91.55a2,2,0,0,0-1.41.59L38.58,90.13A2,2,0,0,0,38,91.55v72.9a2,2,0,0,0,.59,1.42l51.54,51.54a2,2,0,0,0,1.42.59h72.9a2,2,0,0,0,1.41-.59l51.56-51.54a2,2,0,0,0,.58-1.42ZM128,162a10,10,0,1,0,10,10A10,10,0,0,0,128,162Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",hE,[...t[4]||(t[4]=[fg(\"path\",{d:\"M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",fE,[...t[5]||(t[5]=[fg(\"path\",{d:\"M124,136V80a4,4,0,0,1,8,0v56a4,4,0,0,1-8,0ZM228,91.55v72.9a12,12,0,0,1-3.51,8.49l-51.55,51.55a12,12,0,0,1-8.49,3.51H91.55a12,12,0,0,1-8.49-3.51L31.51,172.94A12,12,0,0,1,28,164.45V91.55a12,12,0,0,1,3.51-8.49L83.06,31.51A12,12,0,0,1,91.55,28h72.9a12,12,0,0,1,8.49,3.51l51.55,51.55A12,12,0,0,1,228,91.55Zm-8,0a4,4,0,0,0-1.17-2.83L167.28,37.17A4.06,4.06,0,0,0,164.45,36H91.55a4.06,4.06,0,0,0-2.83,1.17L37.17,88.72A4,4,0,0,0,36,91.55v72.9a4,4,0,0,0,1.17,2.83l51.55,51.55A4.06,4.06,0,0,0,91.55,220h72.9a4.06,4.06,0,0,0,2.83-1.17l51.55-51.55a4,4,0,0,0,1.17-2.83ZM128,164a8,8,0,1,0,8,8A8,8,0,0,0,128,164Z\"},null,-1)])])):Og(\"\",!0)],16))}}),gE={key:0},vE={key:1},bE={key:2},yE={key:3},OE={key:4},wE={key:5},xE=nf({name:\"ScalarIconWebhooksLogo\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",gE,[...t[0]||(t[0]=[fg(\"path\",{d:\"M192,180H118.71a56,56,0,1,1-104.6-37.46,12,12,0,1,1,21.37,10.92A31.64,31.64,0,0,0,32,168a32,32,0,0,0,64,0,12,12,0,0,1,12-12h84a12,12,0,0,1,0,24Zm0-68a55.9,55.9,0,0,0-18.45,3.12L138.22,57.71a12,12,0,0,0-20.44,12.58l40.94,66.52a12,12,0,0,0,16.52,3.93,32,32,0,1,1,19.68,59.13A12,12,0,0,0,196,223.82a10.05,10.05,0,0,0,1.09,0A56,56,0,0,0,192,112ZM57.71,178.22a12,12,0,0,0,16.51-3.93l40.94-66.52a12,12,0,0,0-3.92-16.51,32,32,0,1,1,45.28-41.8,12,12,0,1,0,21.37-10.92A56,56,0,1,0,89.1,104.32L53.78,161.71A12,12,0,0,0,57.71,178.22Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",vE,[...t[1]||(t[1]=[fg(\"path\",{d:\"M128,104a40,40,0,1,1,40-40A40,40,0,0,1,128,104Zm64,24a40,40,0,1,0,40,40A40,40,0,0,0,192,128ZM64,128a40,40,0,1,0,40,40A40,40,0,0,0,64,128Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M178.16,176H111.32A48,48,0,1,1,25.6,139.19a8,8,0,0,1,12.8,9.61A31.69,31.69,0,0,0,32,168a32,32,0,0,0,64,0,8,8,0,0,1,8-8h74.16a16,16,0,1,1,0,16ZM64,184a16,16,0,0,0,14.08-23.61l35.77-58.14a8,8,0,0,0-2.62-11,32,32,0,1,1,46.1-40.06A8,8,0,1,0,172,44.79a48,48,0,1,0-75.62,55.33L64.44,152c-.15,0-.29,0-.44,0a16,16,0,0,0,0,32Zm128-64a48.18,48.18,0,0,0-18,3.49L142.08,71.6A16,16,0,1,0,128,80l.44,0,35.78,58.15a8,8,0,0,0,11,2.61A32,32,0,1,1,192,200a8,8,0,0,0,0,16,48,48,0,0,0,0-96Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",bE,[...t[2]||(t[2]=[fg(\"path\",{d:\"M50.15,160,89.07,92.57l-2.24-3.88a48,48,0,1,1,85.05-44.17,8.17,8.17,0,0,1-3.19,10.4,8,8,0,0,1-11.35-3.72,32,32,0,1,0-56.77,29.3.57.57,0,0,1,.08.13l13.83,23.94a8,8,0,0,1,0,8L77.86,176a16,16,0,0,1-27.71-16Zm141-40H178.81L141.86,56a16,16,0,0,0-27.71,16l34.64,60a8,8,0,0,0,6.92,4h35.63c17.89,0,32.95,14.64,32.66,32.53A32,32,0,0,1,192.31,200a8.23,8.23,0,0,0-8.28,7.33,8,8,0,0,0,8,8.67,48.05,48.05,0,0,0,48-48.93C239.49,140.79,217.48,120,191.19,120ZM208,167.23c-.4-8.61-7.82-15.23-16.43-15.23H114.81a8,8,0,0,0-6.93,4L91.72,184h0a32,32,0,1,1-53.47-35,8.2,8.2,0,0,0-.92-11,8,8,0,0,0-11.72,1.17A47.63,47.63,0,0,0,16,167.54,48,48,0,0,0,105.55,192v0l4.62-8H192A16,16,0,0,0,208,167.23Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",yE,[...t[3]||(t[3]=[fg(\"path\",{d:\"M179.37,174H109.6a46,46,0,1,1-82.4-33.61,6,6,0,0,1,9.6,7.21A33.68,33.68,0,0,0,30,168a34,34,0,0,0,68,0,6,6,0,0,1,6-6h75.37a14,14,0,1,1,0,12ZM64,182a14,14,0,0,0,11.73-21.62l36.42-59.18a6,6,0,0,0-2-8.25,34,34,0,1,1,49-42.57,6,6,0,1,0,11-4.79A46,46,0,1,0,99,99.7L65.52,154.08c-.5-.05-1-.08-1.52-.08a14,14,0,0,0,0,28Zm128-60a46,46,0,0,0-18.8,4L139.73,71.61A14,14,0,1,0,128,78a12.79,12.79,0,0,0,1.52-.09l36.4,59.17a6.05,6.05,0,0,0,3.73,2.69,6,6,0,0,0,4.53-.73A34,34,0,1,1,192,202a6,6,0,0,0,0,12,46,46,0,0,0,0-92Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",OE,[...t[4]||(t[4]=[fg(\"path\",{d:\"M178.16,176H111.32A48,48,0,1,1,25.6,139.19a8,8,0,0,1,12.8,9.61A31.69,31.69,0,0,0,32,168a32,32,0,0,0,64,0,8,8,0,0,1,8-8h74.16a16,16,0,1,1,0,16ZM64,184a16,16,0,0,0,14.08-23.61l35.77-58.14a8,8,0,0,0-2.62-11,32,32,0,1,1,46.1-40.06A8,8,0,1,0,172,44.79a48,48,0,1,0-75.62,55.33L64.44,152c-.15,0-.29,0-.44,0a16,16,0,0,0,0,32Zm128-64a48.18,48.18,0,0,0-18,3.49L142.08,71.6A16,16,0,1,0,128,80l.44,0,35.78,58.15a8,8,0,0,0,11,2.61A32,32,0,1,1,192,200a8,8,0,0,0,0,16,48,48,0,0,0,0-96Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",wE,[...t[5]||(t[5]=[fg(\"path\",{d:\"M180.7,172H107.81a44,44,0,1,1-79-30.41,4,4,0,0,1,6.4,4.81A35.67,35.67,0,0,0,28,168a36,36,0,0,0,72,0,4,4,0,0,1,4-4h76.7a12,12,0,1,1,0,8ZM64,180a12,12,0,0,0,9.33-19.54l37.11-60.3a4,4,0,0,0-1.31-5.51A36,36,0,1,1,161,49.58a4,4,0,1,0,7.33-3.19,44,44,0,1,0-66.71,52.83l-35.1,57.05A11.58,11.58,0,0,0,64,156a12,12,0,0,0,0,24Zm128-56a44,44,0,0,0-19.56,4.58l-35.11-57A12,12,0,1,0,128,76a12.24,12.24,0,0,0,2.52-.27L167.63,136a4,4,0,0,0,5.5,1.31A36,36,0,1,1,192,204a4,4,0,0,0,0,8,44,44,0,0,0,0-88Z\"},null,-1)])])):Og(\"\",!0)],16))}}),kE={key:0},SE={key:1},_E={key:2},TE={key:3},EE={key:4},AE={key:5},CE=nf({name:\"ScalarIconX\",props:{label:{},weight:{}},setup(e){const t=e,{bind:n,weight:r}=px(t);return(e,t)=>(ag(),lg(\"svg\",Sg({xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 256 256\",fill:\"currentColor\"},Hp(n)),[Rf(e.$slots,\"default\"),\"bold\"===Hp(r)?(ag(),lg(\"g\",kE,[...t[0]||(t[0]=[fg(\"path\",{d:\"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z\"},null,-1)])])):\"duotone\"===Hp(r)?(ag(),lg(\"g\",SE,[...t[1]||(t[1]=[fg(\"path\",{d:\"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",opacity:\"0.2\"},null,-1),fg(\"path\",{d:\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\"},null,-1)])])):\"fill\"===Hp(r)?(ag(),lg(\"g\",_E,[...t[2]||(t[2]=[fg(\"path\",{d:\"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM181.66,170.34a8,8,0,0,1-11.32,11.32L128,139.31,85.66,181.66a8,8,0,0,1-11.32-11.32L116.69,128,74.34,85.66A8,8,0,0,1,85.66,74.34L128,116.69l42.34-42.35a8,8,0,0,1,11.32,11.32L139.31,128Z\"},null,-1)])])):\"light\"===Hp(r)?(ag(),lg(\"g\",TE,[...t[3]||(t[3]=[fg(\"path\",{d:\"M204.24,195.76a6,6,0,1,1-8.48,8.48L128,136.49,60.24,204.24a6,6,0,0,1-8.48-8.48L119.51,128,51.76,60.24a6,6,0,0,1,8.48-8.48L128,119.51l67.76-67.75a6,6,0,0,1,8.48,8.48L136.49,128Z\"},null,-1)])])):\"regular\"===Hp(r)?(ag(),lg(\"g\",EE,[...t[4]||(t[4]=[fg(\"path\",{d:\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\"},null,-1)])])):\"thin\"===Hp(r)?(ag(),lg(\"g\",AE,[...t[5]||(t[5]=[fg(\"path\",{d:\"M202.83,197.17a4,4,0,0,1-5.66,5.66L128,133.66,58.83,202.83a4,4,0,0,1-5.66-5.66L122.34,128,53.17,58.83a4,4,0,0,1,5.66-5.66L128,122.34l69.17-69.17a4,4,0,1,1,5.66,5.66L133.66,128Z\"},null,-1)])])):Og(\"\",!0)],16))}}),PE=nf({__name:\"ScalarCheckbox\",props:{selected:{type:Boolean},type:{default:\"checkbox\"}},setup:e=>(e,t)=>(ag(),lg(\"div\",{class:od([\"flex size-4 items-center justify-center p-0.75\",[e.selected?\"bg-c-accent text-b-1\":\"text-transparent shadow-border\",\"checkbox\"===e.type?\"rounded\":\"rounded-full\"]])},[e.selected?(ag(),cg(Hp(fk),{key:0,class:\"size-3\",weight:\"bold\"})):Og(\"\",!0)],2))}),$E=Symbol(),DE=nf({inheritAttrs:!1,__name:\"ScalarFormInput\",props:{is:{default:\"button\"}},setup(e){const{cx:t}=Yw(),n=dm($E,!1),r=Xw({base:[\"bg-b-1.5 flex items-center text-c-2 gap-0.75 px-3 py-2.5 \",\"outline-offset-[-1px] has-[:focus-visible]:outline\"],variants:{grouped:{true:\"first:rounded-t-[inherit] last:rounded-b-[inherit]\",false:\"rounded border\"},button:{true:\"cursor-pointer hover:bg-b-2\"}}});return(e,a)=>(ag(),cg($f(e.is),Sg({type:\"button\"===e.is?\"button\":void 0},Hp(t)(Hp(r)({button:\"button\"===e.is,grouped:Hp(n)}))),{default:Ah((()=>[Rf(e.$slots,\"default\")])),_:3},16,[\"type\"]))}}),IE={class:\"flex-1 text-left min-w-0 truncate\"},ME=[\"type\"],NE=nf({inheritAttrs:!1,__name:\"ScalarCheckboxInput\",props:Wf({type:{default:\"checkbox\"}},{modelValue:{type:Boolean},modelModifiers:{}}),emits:[\"update:modelValue\"],setup(e){const t=zm(e,\"modelValue\"),{stylingAttrsCx:n,otherAttrs:r}=Yw();return(e,a)=>(ag(),cg(Hp(DE),Sg({is:\"label\"},Hp(n)(\"cursor-pointer gap-2 hover:bg-b-2\",{\"text-c-1\":t.value})),{default:Ah((()=>[mg(PE,{class:\"shrink-0\",selected:t.value,type:e.type},null,8,[\"selected\",\"type\"]),fg(\"div\",IE,[Rf(e.$slots,\"default\")]),Ch(fg(\"input\",Sg({\"onUpdate:modelValue\":a[0]||(a[0]=e=>t.value=e),class:\"sr-only\",type:e.type},Hp(r)),null,16,ME),[[Wv,t.value]])])),_:3},16))}}),RE=nf({inheritAttrs:!1,__name:\"ScalarFormInputGroup\",props:{is:{default:\"div\"}},setup(e){const{cx:t}=Yw();return um($E,!0),(e,n)=>(ag(),cg($f(e.is),id(gg(Hp(t)(\"flex flex-col border rounded divide-y\"))),{default:Ah((()=>[Rf(e.$slots,\"default\")])),_:3},16))}}),LE=nf({__name:\"ScalarCheckboxRadioGroup\",props:Wf({options:{default:()=>[]}},{modelValue:{},modelModifiers:{}}),emits:[\"update:modelValue\"],setup(e){const t=zm(e,\"modelValue\"),n=rf();return(e,r)=>(ag(),cg(Hp(RE),null,{default:Ah((()=>[(ag(!0),lg(Km,null,Mf(e.options,(e=>(ag(),cg(NE,{key:e.value,modelValue:t.value?.value===e.value,name:Hp(n),type:\"radio\",value:e.value,\"onUpdate:modelValue\":n=>t.value=n?e:void 0},{default:Ah((()=>[bg(pd(e.label),1)])),_:2},1032,[\"modelValue\",\"name\",\"value\",\"onUpdate:modelValue\"])))),128))])),_:1}))}});let BE=class{constructor(e,t,n){this.property=e,this.normal=t,n&&(this.space=n)}};function jE(e,t){const n={},r={};let a=-1;for(;++a\"xlink:\"+t.slice(5).toLowerCase(),properties:{xLinkActuate:null,xLinkArcRole:null,xLinkHref:null,xLinkRole:null,xLinkShow:null,xLinkTitle:null,xLinkType:null}}),aA=nA({space:\"xml\",transform:(e,t)=>\"xml:\"+t.slice(3).toLowerCase(),properties:{xmlLang:null,xmlBase:null,xmlSpace:null}});function oA(e,t){return t in e?e[t]:t}function iA(e,t){return oA(e,t.toLowerCase())}const sA=nA({space:\"xmlns\",attributes:{xmlnsxlink:\"xmlns:xlink\"},transform:iA,properties:{xmlns:null,xmlnsXLink:null}}),lA=nA({transform:(e,t)=>\"role\"===t?t:\"aria-\"+t.slice(4).toLowerCase(),properties:{ariaActiveDescendant:null,ariaAtomic:ZE,ariaAutoComplete:null,ariaBusy:ZE,ariaChecked:ZE,ariaColCount:VE,ariaColIndex:VE,ariaColSpan:VE,ariaControls:qE,ariaCurrent:null,ariaDescribedBy:qE,ariaDetails:null,ariaDisabled:ZE,ariaDropEffect:qE,ariaErrorMessage:null,ariaExpanded:ZE,ariaFlowTo:qE,ariaGrabbed:ZE,ariaHasPopup:null,ariaHidden:ZE,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:qE,ariaLevel:VE,ariaLive:null,ariaModal:ZE,ariaMultiLine:ZE,ariaMultiSelectable:ZE,ariaOrientation:null,ariaOwns:qE,ariaPlaceholder:null,ariaPosInSet:VE,ariaPressed:ZE,ariaReadOnly:ZE,ariaRelevant:null,ariaRequired:ZE,ariaRoleDescription:qE,ariaRowCount:VE,ariaRowIndex:VE,ariaRowSpan:VE,ariaSelected:ZE,ariaSetSize:VE,ariaSort:null,ariaValueMax:VE,ariaValueMin:VE,ariaValueNow:VE,ariaValueText:null,role:null}}),cA=nA({space:\"html\",attributes:{acceptcharset:\"accept-charset\",classname:\"class\",htmlfor:\"for\",httpequiv:\"http-equiv\"},transform:iA,mustUseProperty:[\"checked\",\"multiple\",\"muted\",\"selected\"],properties:{abbr:null,accept:WE,acceptCharset:qE,accessKey:qE,action:null,allow:null,allowFullScreen:QE,allowPaymentRequest:QE,allowUserMedia:QE,alt:null,as:null,async:QE,autoCapitalize:null,autoComplete:qE,autoFocus:QE,autoPlay:QE,blocking:qE,capture:null,charSet:null,checked:QE,cite:null,className:qE,cols:VE,colSpan:null,content:null,contentEditable:ZE,controls:QE,controlsList:qE,coords:VE|WE,crossOrigin:null,data:null,dateTime:null,decoding:null,default:QE,defer:QE,dir:null,dirName:null,disabled:QE,download:HE,draggable:ZE,encType:null,enterKeyHint:null,fetchPriority:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:QE,formTarget:null,headers:qE,height:VE,hidden:QE,high:VE,href:null,hrefLang:null,htmlFor:qE,httpEquiv:qE,id:null,imageSizes:null,imageSrcSet:null,inert:QE,inputMode:null,integrity:null,is:null,isMap:QE,itemId:null,itemProp:qE,itemRef:qE,itemScope:QE,itemType:qE,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:QE,low:VE,manifest:null,max:null,maxLength:VE,media:null,method:null,min:null,minLength:VE,multiple:QE,muted:QE,name:null,nonce:null,noModule:QE,noValidate:QE,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforeMatch:null,onBeforePrint:null,onBeforeToggle:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextLost:null,onContextMenu:null,onContextRestored:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onScrollEnd:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:QE,optimum:VE,pattern:null,ping:qE,placeholder:null,playsInline:QE,popover:null,popoverTarget:null,popoverTargetAction:null,poster:null,preload:null,readOnly:QE,referrerPolicy:null,rel:qE,required:QE,reversed:QE,rows:VE,rowSpan:VE,sandbox:qE,scope:null,scoped:QE,seamless:QE,selected:QE,shadowRootClonable:QE,shadowRootDelegatesFocus:QE,shadowRootMode:null,shape:null,size:VE,sizes:null,slot:null,span:VE,spellCheck:ZE,src:null,srcDoc:null,srcLang:null,srcSet:null,start:VE,step:null,style:null,tabIndex:VE,target:null,title:null,translate:null,type:null,typeMustMatch:QE,useMap:null,value:ZE,width:VE,wrap:null,writingSuggestions:null,align:null,aLink:null,archive:qE,axis:null,background:null,bgColor:null,border:VE,borderColor:null,bottomMargin:VE,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:QE,declare:QE,event:null,face:null,frame:null,frameBorder:null,hSpace:VE,leftMargin:VE,link:null,longDesc:null,lowSrc:null,marginHeight:VE,marginWidth:VE,noResize:QE,noHref:QE,noShade:QE,noWrap:QE,object:null,profile:null,prompt:null,rev:null,rightMargin:VE,rules:null,scheme:null,scrolling:ZE,standby:null,summary:null,text:null,topMargin:VE,valueType:null,version:null,vAlign:null,vLink:null,vSpace:VE,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:QE,disableRemotePlayback:QE,prefix:null,property:null,results:VE,security:null,unselectable:null}}),uA=nA({space:\"svg\",attributes:{accentHeight:\"accent-height\",alignmentBaseline:\"alignment-baseline\",arabicForm:\"arabic-form\",baselineShift:\"baseline-shift\",capHeight:\"cap-height\",className:\"class\",clipPath:\"clip-path\",clipRule:\"clip-rule\",colorInterpolation:\"color-interpolation\",colorInterpolationFilters:\"color-interpolation-filters\",colorProfile:\"color-profile\",colorRendering:\"color-rendering\",crossOrigin:\"crossorigin\",dataType:\"datatype\",dominantBaseline:\"dominant-baseline\",enableBackground:\"enable-background\",fillOpacity:\"fill-opacity\",fillRule:\"fill-rule\",floodColor:\"flood-color\",floodOpacity:\"flood-opacity\",fontFamily:\"font-family\",fontSize:\"font-size\",fontSizeAdjust:\"font-size-adjust\",fontStretch:\"font-stretch\",fontStyle:\"font-style\",fontVariant:\"font-variant\",fontWeight:\"font-weight\",glyphName:\"glyph-name\",glyphOrientationHorizontal:\"glyph-orientation-horizontal\",glyphOrientationVertical:\"glyph-orientation-vertical\",hrefLang:\"hreflang\",horizAdvX:\"horiz-adv-x\",horizOriginX:\"horiz-origin-x\",horizOriginY:\"horiz-origin-y\",imageRendering:\"image-rendering\",letterSpacing:\"letter-spacing\",lightingColor:\"lighting-color\",markerEnd:\"marker-end\",markerMid:\"marker-mid\",markerStart:\"marker-start\",navDown:\"nav-down\",navDownLeft:\"nav-down-left\",navDownRight:\"nav-down-right\",navLeft:\"nav-left\",navNext:\"nav-next\",navPrev:\"nav-prev\",navRight:\"nav-right\",navUp:\"nav-up\",navUpLeft:\"nav-up-left\",navUpRight:\"nav-up-right\",onAbort:\"onabort\",onActivate:\"onactivate\",onAfterPrint:\"onafterprint\",onBeforePrint:\"onbeforeprint\",onBegin:\"onbegin\",onCancel:\"oncancel\",onCanPlay:\"oncanplay\",onCanPlayThrough:\"oncanplaythrough\",onChange:\"onchange\",onClick:\"onclick\",onClose:\"onclose\",onCopy:\"oncopy\",onCueChange:\"oncuechange\",onCut:\"oncut\",onDblClick:\"ondblclick\",onDrag:\"ondrag\",onDragEnd:\"ondragend\",onDragEnter:\"ondragenter\",onDragExit:\"ondragexit\",onDragLeave:\"ondragleave\",onDragOver:\"ondragover\",onDragStart:\"ondragstart\",onDrop:\"ondrop\",onDurationChange:\"ondurationchange\",onEmptied:\"onemptied\",onEnd:\"onend\",onEnded:\"onended\",onError:\"onerror\",onFocus:\"onfocus\",onFocusIn:\"onfocusin\",onFocusOut:\"onfocusout\",onHashChange:\"onhashchange\",onInput:\"oninput\",onInvalid:\"oninvalid\",onKeyDown:\"onkeydown\",onKeyPress:\"onkeypress\",onKeyUp:\"onkeyup\",onLoad:\"onload\",onLoadedData:\"onloadeddata\",onLoadedMetadata:\"onloadedmetadata\",onLoadStart:\"onloadstart\",onMessage:\"onmessage\",onMouseDown:\"onmousedown\",onMouseEnter:\"onmouseenter\",onMouseLeave:\"onmouseleave\",onMouseMove:\"onmousemove\",onMouseOut:\"onmouseout\",onMouseOver:\"onmouseover\",onMouseUp:\"onmouseup\",onMouseWheel:\"onmousewheel\",onOffline:\"onoffline\",onOnline:\"ononline\",onPageHide:\"onpagehide\",onPageShow:\"onpageshow\",onPaste:\"onpaste\",onPause:\"onpause\",onPlay:\"onplay\",onPlaying:\"onplaying\",onPopState:\"onpopstate\",onProgress:\"onprogress\",onRateChange:\"onratechange\",onRepeat:\"onrepeat\",onReset:\"onreset\",onResize:\"onresize\",onScroll:\"onscroll\",onSeeked:\"onseeked\",onSeeking:\"onseeking\",onSelect:\"onselect\",onShow:\"onshow\",onStalled:\"onstalled\",onStorage:\"onstorage\",onSubmit:\"onsubmit\",onSuspend:\"onsuspend\",onTimeUpdate:\"ontimeupdate\",onToggle:\"ontoggle\",onUnload:\"onunload\",onVolumeChange:\"onvolumechange\",onWaiting:\"onwaiting\",onZoom:\"onzoom\",overlinePosition:\"overline-position\",overlineThickness:\"overline-thickness\",paintOrder:\"paint-order\",panose1:\"panose-1\",pointerEvents:\"pointer-events\",referrerPolicy:\"referrerpolicy\",renderingIntent:\"rendering-intent\",shapeRendering:\"shape-rendering\",stopColor:\"stop-color\",stopOpacity:\"stop-opacity\",strikethroughPosition:\"strikethrough-position\",strikethroughThickness:\"strikethrough-thickness\",strokeDashArray:\"stroke-dasharray\",strokeDashOffset:\"stroke-dashoffset\",strokeLineCap:\"stroke-linecap\",strokeLineJoin:\"stroke-linejoin\",strokeMiterLimit:\"stroke-miterlimit\",strokeOpacity:\"stroke-opacity\",strokeWidth:\"stroke-width\",tabIndex:\"tabindex\",textAnchor:\"text-anchor\",textDecoration:\"text-decoration\",textRendering:\"text-rendering\",transformOrigin:\"transform-origin\",typeOf:\"typeof\",underlinePosition:\"underline-position\",underlineThickness:\"underline-thickness\",unicodeBidi:\"unicode-bidi\",unicodeRange:\"unicode-range\",unitsPerEm:\"units-per-em\",vAlphabetic:\"v-alphabetic\",vHanging:\"v-hanging\",vIdeographic:\"v-ideographic\",vMathematical:\"v-mathematical\",vectorEffect:\"vector-effect\",vertAdvY:\"vert-adv-y\",vertOriginX:\"vert-origin-x\",vertOriginY:\"vert-origin-y\",wordSpacing:\"word-spacing\",writingMode:\"writing-mode\",xHeight:\"x-height\",playbackOrder:\"playbackorder\",timelineBegin:\"timelinebegin\"},transform:oA,properties:{about:XE,accentHeight:VE,accumulate:null,additive:null,alignmentBaseline:null,alphabetic:VE,amplitude:VE,arabicForm:null,ascent:VE,attributeName:null,attributeType:null,azimuth:VE,bandwidth:null,baselineShift:null,baseFrequency:null,baseProfile:null,bbox:null,begin:null,bias:VE,by:null,calcMode:null,capHeight:VE,className:qE,clip:null,clipPath:null,clipPathUnits:null,clipRule:null,color:null,colorInterpolation:null,colorInterpolationFilters:null,colorProfile:null,colorRendering:null,content:null,contentScriptType:null,contentStyleType:null,crossOrigin:null,cursor:null,cx:null,cy:null,d:null,dataType:null,defaultAction:null,descent:VE,diffuseConstant:VE,direction:null,display:null,dur:null,divisor:VE,dominantBaseline:null,download:QE,dx:null,dy:null,edgeMode:null,editable:null,elevation:VE,enableBackground:null,end:null,event:null,exponent:VE,externalResourcesRequired:null,fill:null,fillOpacity:VE,fillRule:null,filter:null,filterRes:null,filterUnits:null,floodColor:null,floodOpacity:null,focusable:null,focusHighlight:null,fontFamily:null,fontSize:null,fontSizeAdjust:null,fontStretch:null,fontStyle:null,fontVariant:null,fontWeight:null,format:null,fr:null,from:null,fx:null,fy:null,g1:WE,g2:WE,glyphName:WE,glyphOrientationHorizontal:null,glyphOrientationVertical:null,glyphRef:null,gradientTransform:null,gradientUnits:null,handler:null,hanging:VE,hatchContentUnits:null,hatchUnits:null,height:null,href:null,hrefLang:null,horizAdvX:VE,horizOriginX:VE,horizOriginY:VE,id:null,ideographic:VE,imageRendering:null,initialVisibility:null,in:null,in2:null,intercept:VE,k:VE,k1:VE,k2:VE,k3:VE,k4:VE,kernelMatrix:XE,kernelUnitLength:null,keyPoints:null,keySplines:null,keyTimes:null,kerning:null,lang:null,lengthAdjust:null,letterSpacing:null,lightingColor:null,limitingConeAngle:VE,local:null,markerEnd:null,markerMid:null,markerStart:null,markerHeight:null,markerUnits:null,markerWidth:null,mask:null,maskContentUnits:null,maskUnits:null,mathematical:null,max:null,media:null,mediaCharacterEncoding:null,mediaContentEncodings:null,mediaSize:VE,mediaTime:null,method:null,min:null,mode:null,name:null,navDown:null,navDownLeft:null,navDownRight:null,navLeft:null,navNext:null,navPrev:null,navRight:null,navUp:null,navUpLeft:null,navUpRight:null,numOctaves:null,observer:null,offset:null,onAbort:null,onActivate:null,onAfterPrint:null,onBeforePrint:null,onBegin:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnd:null,onEnded:null,onError:null,onFocus:null,onFocusIn:null,onFocusOut:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadStart:null,onMessage:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onMouseWheel:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRepeat:null,onReset:null,onResize:null,onScroll:null,onSeeked:null,onSeeking:null,onSelect:null,onShow:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnload:null,onVolumeChange:null,onWaiting:null,onZoom:null,opacity:null,operator:null,order:null,orient:null,orientation:null,origin:null,overflow:null,overlay:null,overlinePosition:VE,overlineThickness:VE,paintOrder:null,panose1:null,path:null,pathLength:VE,patternContentUnits:null,patternTransform:null,patternUnits:null,phase:null,ping:qE,pitch:null,playbackOrder:null,pointerEvents:null,points:null,pointsAtX:VE,pointsAtY:VE,pointsAtZ:VE,preserveAlpha:null,preserveAspectRatio:null,primitiveUnits:null,propagate:null,property:XE,r:null,radius:null,referrerPolicy:null,refX:null,refY:null,rel:XE,rev:XE,renderingIntent:null,repeatCount:null,repeatDur:null,requiredExtensions:XE,requiredFeatures:XE,requiredFonts:XE,requiredFormats:XE,resource:null,restart:null,result:null,rotate:null,rx:null,ry:null,scale:null,seed:null,shapeRendering:null,side:null,slope:null,snapshotTime:null,specularConstant:VE,specularExponent:VE,spreadMethod:null,spacing:null,startOffset:null,stdDeviation:null,stemh:null,stemv:null,stitchTiles:null,stopColor:null,stopOpacity:null,strikethroughPosition:VE,strikethroughThickness:VE,string:null,stroke:null,strokeDashArray:XE,strokeDashOffset:null,strokeLineCap:null,strokeLineJoin:null,strokeMiterLimit:VE,strokeOpacity:VE,strokeWidth:null,style:null,surfaceScale:VE,syncBehavior:null,syncBehaviorDefault:null,syncMaster:null,syncTolerance:null,syncToleranceDefault:null,systemLanguage:XE,tabIndex:VE,tableValues:null,target:null,targetX:VE,targetY:VE,textAnchor:null,textDecoration:null,textRendering:null,textLength:null,timelineBegin:null,title:null,transformBehavior:null,type:null,typeOf:XE,to:null,transform:null,transformOrigin:null,u1:null,u2:null,underlinePosition:VE,underlineThickness:VE,unicode:null,unicodeBidi:null,unicodeRange:null,unitsPerEm:VE,values:null,vAlphabetic:VE,vMathematical:VE,vectorEffect:null,vHanging:VE,vIdeographic:VE,version:null,vertAdvY:VE,vertOriginX:VE,vertOriginY:VE,viewBox:null,viewTarget:null,visibility:null,width:null,widths:null,wordSpacing:null,writingMode:null,x:null,x1:null,x2:null,xChannelSelector:null,xHeight:VE,y:null,y1:null,y2:null,yChannelSelector:null,z:null,zoomAndPan:null}}),dA=/^data[-\\w.:]+$/i,pA=/-[a-z]/g,hA=/[A-Z]/g;function fA(e,t){const n=UE(t);let r=t,a=zE;if(n in e.normal)return e.property[e.normal[n]];if(n.length>4&&\"data\"===n.slice(0,4)&&dA.test(t)){if(\"-\"===t.charAt(4)){const e=t.slice(5).replace(pA,gA);r=\"data\"+e.charAt(0).toUpperCase()+e.slice(1)}else{const e=t.slice(4);if(!pA.test(e)){let n=e.replace(hA,mA);\"-\"!==n.charAt(0)&&(n=\"-\"+n),t=\"data\"+n}}a=JE}return new a(r,t)}function mA(e){return\"-\"+e.toLowerCase()}function gA(e){return e.charAt(1).toUpperCase()}const vA=jE([aA,rA,sA,lA,cA],\"html\"),bA=jE([aA,rA,sA,lA,uA],\"svg\");function yA(e){const t=[],n=String(e||\"\");let r=n.indexOf(\",\"),a=0,o=!1;for(;!o;){-1===r&&(r=n.length,o=!0);const e=n.slice(a,r).trim();!e&&o||t.push(e),a=r+1,r=n.indexOf(\",\",a)}return t}function OA(e,t){const n=t||{};return(\"\"===e[e.length-1]?[...e,\"\"]:e).join((n.padRight?\" \":\"\")+\",\"+(!1===n.padLeft?\"\":\" \")).trim()}const wA=/[#.]/g;function xA(e){const t=String(e||\"\").trim();return t?t.split(/[ \\t\\n\\r\\f]+/g):[]}function kA(e){return e.join(\" \").trim()}const SA=new Set([\"button\",\"menu\",\"reset\",\"submit\"]),_A={}.hasOwnProperty;function TA(e,t,n){const r=n&&function(e){const t={};let n=-1;for(;++n-1&&ee)return{line:t+1,column:e-(t>0?n[t-1]:0)+1,offset:e}},toOffset:function(e){const t=e&&e.line,r=e&&e.column;if(\"number\"==typeof t&&\"number\"==typeof r&&!Number.isNaN(t)&&!Number.isNaN(r)&&t-1 in n){const e=(n[t-2]||0)+r-1||0;if(e>-1&&e=55296&&e<=57343}function GA(e){return 32!==e&&10!==e&&13!==e&&9!==e&&12!==e&&e>=1&&e<=31||e>=127&&e<=159}function YA(e){return e>=64976&&e<=65007||FA.has(e)}var KA,JA,eC,tC;(JA=KA||(KA={})).controlCharacterInInputStream=\"control-character-in-input-stream\",JA.noncharacterInInputStream=\"noncharacter-in-input-stream\",JA.surrogateInInputStream=\"surrogate-in-input-stream\",JA.nonVoidHtmlElementStartTagWithTrailingSolidus=\"non-void-html-element-start-tag-with-trailing-solidus\",JA.endTagWithAttributes=\"end-tag-with-attributes\",JA.endTagWithTrailingSolidus=\"end-tag-with-trailing-solidus\",JA.unexpectedSolidusInTag=\"unexpected-solidus-in-tag\",JA.unexpectedNullCharacter=\"unexpected-null-character\",JA.unexpectedQuestionMarkInsteadOfTagName=\"unexpected-question-mark-instead-of-tag-name\",JA.invalidFirstCharacterOfTagName=\"invalid-first-character-of-tag-name\",JA.unexpectedEqualsSignBeforeAttributeName=\"unexpected-equals-sign-before-attribute-name\",JA.missingEndTagName=\"missing-end-tag-name\",JA.unexpectedCharacterInAttributeName=\"unexpected-character-in-attribute-name\",JA.unknownNamedCharacterReference=\"unknown-named-character-reference\",JA.missingSemicolonAfterCharacterReference=\"missing-semicolon-after-character-reference\",JA.unexpectedCharacterAfterDoctypeSystemIdentifier=\"unexpected-character-after-doctype-system-identifier\",JA.unexpectedCharacterInUnquotedAttributeValue=\"unexpected-character-in-unquoted-attribute-value\",JA.eofBeforeTagName=\"eof-before-tag-name\",JA.eofInTag=\"eof-in-tag\",JA.missingAttributeValue=\"missing-attribute-value\",JA.missingWhitespaceBetweenAttributes=\"missing-whitespace-between-attributes\",JA.missingWhitespaceAfterDoctypePublicKeyword=\"missing-whitespace-after-doctype-public-keyword\",JA.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers=\"missing-whitespace-between-doctype-public-and-system-identifiers\",JA.missingWhitespaceAfterDoctypeSystemKeyword=\"missing-whitespace-after-doctype-system-keyword\",JA.missingQuoteBeforeDoctypePublicIdentifier=\"missing-quote-before-doctype-public-identifier\",JA.missingQuoteBeforeDoctypeSystemIdentifier=\"missing-quote-before-doctype-system-identifier\",JA.missingDoctypePublicIdentifier=\"missing-doctype-public-identifier\",JA.missingDoctypeSystemIdentifier=\"missing-doctype-system-identifier\",JA.abruptDoctypePublicIdentifier=\"abrupt-doctype-public-identifier\",JA.abruptDoctypeSystemIdentifier=\"abrupt-doctype-system-identifier\",JA.cdataInHtmlContent=\"cdata-in-html-content\",JA.incorrectlyOpenedComment=\"incorrectly-opened-comment\",JA.eofInScriptHtmlCommentLikeText=\"eof-in-script-html-comment-like-text\",JA.eofInDoctype=\"eof-in-doctype\",JA.nestedComment=\"nested-comment\",JA.abruptClosingOfEmptyComment=\"abrupt-closing-of-empty-comment\",JA.eofInComment=\"eof-in-comment\",JA.incorrectlyClosedComment=\"incorrectly-closed-comment\",JA.eofInCdata=\"eof-in-cdata\",JA.absenceOfDigitsInNumericCharacterReference=\"absence-of-digits-in-numeric-character-reference\",JA.nullCharacterReference=\"null-character-reference\",JA.surrogateCharacterReference=\"surrogate-character-reference\",JA.characterReferenceOutsideUnicodeRange=\"character-reference-outside-unicode-range\",JA.controlCharacterReference=\"control-character-reference\",JA.noncharacterCharacterReference=\"noncharacter-character-reference\",JA.missingWhitespaceBeforeDoctypeName=\"missing-whitespace-before-doctype-name\",JA.missingDoctypeName=\"missing-doctype-name\",JA.invalidCharacterSequenceAfterDoctypeName=\"invalid-character-sequence-after-doctype-name\",JA.duplicateAttribute=\"duplicate-attribute\",JA.nonConformingDoctype=\"non-conforming-doctype\",JA.missingDoctype=\"missing-doctype\",JA.misplacedDoctype=\"misplaced-doctype\",JA.endTagWithoutMatchingOpenElement=\"end-tag-without-matching-open-element\",JA.closingOfElementWithOpenChildElements=\"closing-of-element-with-open-child-elements\",JA.disallowedContentInNoscriptInHead=\"disallowed-content-in-noscript-in-head\",JA.openElementsLeftAfterEof=\"open-elements-left-after-eof\",JA.abandonedHeadElementChild=\"abandoned-head-element-child\",JA.misplacedStartTagForHeadElement=\"misplaced-start-tag-for-head-element\",JA.nestedNoscriptInHead=\"nested-noscript-in-head\",JA.eofInElementThatCanContainOnlyText=\"eof-in-element-that-can-contain-only-text\";class nC{constructor(e){this.handler=e,this.html=\"\",this.pos=-1,this.lastGapPos=-2,this.gapStack=[],this.skipNextNewLine=!1,this.lastChunkWritten=!1,this.endOfChunkHit=!1,this.bufferWaterline=65536,this.isEol=!1,this.lineStartPos=0,this.droppedBufferSize=0,this.line=1,this.lastErrOffset=-1}get col(){return this.pos-this.lineStartPos+Number(this.lastGapPos!==this.pos)}get offset(){return this.droppedBufferSize+this.pos}getError(e,t){const{line:n,col:r,offset:a}=this,o=r+t,i=a+t;return{code:e,startLine:n,endLine:n,startCol:o,endCol:o,startOffset:i,endOffset:i}}_err(e){this.handler.onParseError&&this.lastErrOffset!==this.offset&&(this.lastErrOffset=this.offset,this.handler.onParseError(this.getError(e,0)))}_addGap(){this.gapStack.push(this.lastGapPos),this.lastGapPos=this.pos}_processSurrogate(e){if(this.pos!==this.html.length-1){const t=this.html.charCodeAt(this.pos+1);if(function(e){return e>=56320&&e<=57343}(t))return this.pos++,this._addGap(),1024*(e-55296)+9216+t}else if(!this.lastChunkWritten)return this.endOfChunkHit=!0,ZA.EOF;return this._err(KA.surrogateInInputStream),e}willDropParsedChunk(){return this.pos>this.bufferWaterline}dropParsedChunk(){this.willDropParsedChunk()&&(this.html=this.html.substring(this.pos),this.lineStartPos-=this.pos,this.droppedBufferSize+=this.pos,this.pos=0,this.lastGapPos=-2,this.gapStack.length=0)}write(e,t){this.html.length>0?this.html+=e:this.html=e,this.endOfChunkHit=!1,this.lastChunkWritten=t}insertHtmlAtCurrentPos(e){this.html=this.html.substring(0,this.pos+1)+e+this.html.substring(this.pos+1),this.endOfChunkHit=!1}startsWith(e,t){if(this.pos+e.length>this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,!1;if(t)return this.html.startsWith(e,this.pos);for(let t=0;t=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,ZA.EOF;const n=this.html.charCodeAt(t);return n===ZA.CARRIAGE_RETURN?ZA.LINE_FEED:n}advance(){if(this.pos++,this.isEol&&(this.isEol=!1,this.line++,this.lineStartPos=this.pos),this.pos>=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,ZA.EOF;let e=this.html.charCodeAt(this.pos);return e===ZA.CARRIAGE_RETURN?(this.isEol=!0,this.skipNextNewLine=!0,ZA.LINE_FEED):e===ZA.LINE_FEED&&(this.isEol=!0,this.skipNextNewLine)?(this.line--,this.skipNextNewLine=!1,this._addGap(),this.advance()):(this.skipNextNewLine=!1,XA(e)&&(e=this._processSurrogate(e)),null===this.handler.onParseError||e>31&&e<127||e===ZA.LINE_FEED||e===ZA.CARRIAGE_RETURN||e>159&&e<64976||this._checkForProblematicCharacters(e),e)}_checkForProblematicCharacters(e){GA(e)?this._err(KA.controlCharacterInInputStream):YA(e)&&this._err(KA.noncharacterInInputStream)}retreat(e){for(this.pos-=e;this.pos=0;n--)if(e.attrs[n].name===t)return e.attrs[n].value;return null}(tC=eC||(eC={}))[tC.CHARACTER=0]=\"CHARACTER\",tC[tC.NULL_CHARACTER=1]=\"NULL_CHARACTER\",tC[tC.WHITESPACE_CHARACTER=2]=\"WHITESPACE_CHARACTER\",tC[tC.START_TAG=3]=\"START_TAG\",tC[tC.END_TAG=4]=\"END_TAG\",tC[tC.COMMENT=5]=\"COMMENT\",tC[tC.DOCTYPE=6]=\"DOCTYPE\",tC[tC.EOF=7]=\"EOF\",tC[tC.HIBERNATION=8]=\"HIBERNATION\";const aC=new Uint16Array('ᵁ<Õıʊҝջאٵ۞ޢߖࠏ੊ઑඡ๭༉༦჊ረዡᐕᒝᓃᓟᔥ\\0\\0\\0\\0\\0\\0ᕫᛍᦍᰒᷝ὾⁠↰⊍⏀⏻⑂⠤⤒ⴈ⹈⿎〖㊺㘹㞬㣾㨨㩱㫠㬮ࠀEMabcfglmnoprstu\\\\bfms„‹•˜¦³¹ÈÏlig耻Æ䃆P耻&䀦cute耻Á䃁reve;䄂Āiyx}rc耻Â䃂;䐐r;쀀𝔄rave耻À䃀pha;䎑acr;䄀d;橓Āgp¡on;䄄f;쀀𝔸plyFunction;恡ing耻Å䃅Ācs¾Ãr;쀀𝒜ign;扔ilde耻Ã䃃ml耻Ä䃄ЀaceforsuåûþėĜĢħĪĀcrêòkslash;或Ŷöø;櫧ed;挆y;䐑ƀcrtąċĔause;戵noullis;愬a;䎒r;쀀𝔅pf;쀀𝔹eve;䋘còēmpeq;扎܀HOacdefhilorsuōőŖƀƞƢƵƷƺǜȕɳɸɾcy;䐧PY耻©䂩ƀcpyŝŢźute;䄆Ā;iŧŨ拒talDifferentialD;慅leys;愭ȀaeioƉƎƔƘron;䄌dil耻Ç䃇rc;䄈nint;戰ot;䄊ĀdnƧƭilla;䂸terDot;䂷òſi;䎧rcleȀDMPTLJNjǑǖot;抙inus;抖lus;投imes;抗oĀcsǢǸkwiseContourIntegral;戲eCurlyĀDQȃȏoubleQuote;思uote;怙ȀlnpuȞȨɇɕonĀ;eȥȦ户;橴ƀgitȯȶȺruent;扡nt;戯ourIntegral;戮ĀfrɌɎ;愂oduct;成nterClockwiseContourIntegral;戳oss;樯cr;쀀𝒞pĀ;Cʄʅ拓ap;才րDJSZacefiosʠʬʰʴʸˋ˗ˡ˦̳ҍĀ;oŹʥtrahd;椑cy;䐂cy;䐅cy;䐏ƀgrsʿ˄ˇger;怡r;憡hv;櫤Āayː˕ron;䄎;䐔lĀ;t˝˞戇a;䎔r;쀀𝔇Āaf˫̧Ācm˰̢riticalȀADGT̖̜̀̆cute;䂴oŴ̋̍;䋙bleAcute;䋝rave;䁠ilde;䋜ond;拄ferentialD;慆Ѱ̽\\0\\0\\0͔͂\\0Ѕf;쀀𝔻ƀ;DE͈͉͍䂨ot;惜qual;扐blèCDLRUVͣͲ΂ϏϢϸontourIntegraìȹoɴ͹\\0\\0ͻ»͉nArrow;懓Āeo·ΤftƀARTΐΖΡrrow;懐ightArrow;懔eåˊngĀLRΫτeftĀARγιrrow;柸ightArrow;柺ightArrow;柹ightĀATϘϞrrow;懒ee;抨pɁϩ\\0\\0ϯrrow;懑ownArrow;懕erticalBar;戥ǹABLRTaВЪаўѿͼrrowƀ;BUНОТ憓ar;椓pArrow;懵reve;䌑eft˒к\\0ц\\0ѐightVector;楐eeVector;楞ectorĀ;Bљњ憽ar;楖ightǔѧ\\0ѱeeVector;楟ectorĀ;BѺѻ懁ar;楗eeĀ;A҆҇护rrow;憧ĀctҒҗr;쀀𝒟rok;䄐ࠀNTacdfglmopqstuxҽӀӄӋӞӢӧӮӵԡԯԶՒ՝ՠեG;䅊H耻Ð䃐cute耻É䃉ƀaiyӒӗӜron;䄚rc耻Ê䃊;䐭ot;䄖r;쀀𝔈rave耻È䃈ement;戈ĀapӺӾcr;䄒tyɓԆ\\0\\0ԒmallSquare;旻erySmallSquare;斫ĀgpԦԪon;䄘f;쀀𝔼silon;䎕uĀaiԼՉlĀ;TՂՃ橵ilde;扂librium;懌Āci՗՚r;愰m;橳a;䎗ml耻Ë䃋Āipժկsts;戃onentialE;慇ʀcfiosօֈ֍ֲ׌y;䐤r;쀀𝔉lledɓ֗\\0\\0֣mallSquare;旼erySmallSquare;斪Ͱֺ\\0ֿ\\0\\0ׄf;쀀𝔽All;戀riertrf;愱cò׋؀JTabcdfgorstר׬ׯ׺؀ؒؖ؛؝أ٬ٲcy;䐃耻>䀾mmaĀ;d׷׸䎓;䏜reve;䄞ƀeiy؇،ؐdil;䄢rc;䄜;䐓ot;䄠r;쀀𝔊;拙pf;쀀𝔾eater̀EFGLSTصلَٖٛ٦qualĀ;Lؾؿ扥ess;招ullEqual;执reater;檢ess;扷lantEqual;橾ilde;扳cr;쀀𝒢;扫ЀAacfiosuڅڋږڛڞڪھۊRDcy;䐪Āctڐڔek;䋇;䁞irc;䄤r;愌lbertSpace;愋ǰگ\\0ڲf;愍izontalLine;攀Āctۃۅòکrok;䄦mpńېۘownHumðįqual;扏܀EJOacdfgmnostuۺ۾܃܇܎ܚܞܡܨ݄ݸދޏޕcy;䐕lig;䄲cy;䐁cute耻Í䃍Āiyܓܘrc耻Î䃎;䐘ot;䄰r;愑rave耻Ì䃌ƀ;apܠܯܿĀcgܴܷr;䄪inaryI;慈lieóϝǴ݉\\0ݢĀ;eݍݎ戬Āgrݓݘral;戫section;拂isibleĀCTݬݲomma;恣imes;恢ƀgptݿރވon;䄮f;쀀𝕀a;䎙cr;愐ilde;䄨ǫޚ\\0ޞcy;䐆l耻Ï䃏ʀcfosuެ޷޼߂ߐĀiyޱ޵rc;䄴;䐙r;쀀𝔍pf;쀀𝕁ǣ߇\\0ߌr;쀀𝒥rcy;䐈kcy;䐄΀HJacfosߤߨ߽߬߱ࠂࠈcy;䐥cy;䐌ppa;䎚Āey߶߻dil;䄶;䐚r;쀀𝔎pf;쀀𝕂cr;쀀𝒦րJTaceflmostࠥࠩࠬࡐࡣ঳সে্਷ੇcy;䐉耻<䀼ʀcmnpr࠷࠼ࡁࡄࡍute;䄹bda;䎛g;柪lacetrf;愒r;憞ƀaeyࡗ࡜ࡡron;䄽dil;䄻;䐛Āfsࡨ॰tԀACDFRTUVarࡾࢩࢱࣦ࣠ࣼयज़ΐ४Ānrࢃ࢏gleBracket;柨rowƀ;BR࢙࢚࢞憐ar;懤ightArrow;懆eiling;挈oǵࢷ\\0ࣃbleBracket;柦nǔࣈ\\0࣒eeVector;楡ectorĀ;Bࣛࣜ懃ar;楙loor;挊ightĀAV࣯ࣵrrow;憔ector;楎Āerँगeƀ;AVउऊऐ抣rrow;憤ector;楚iangleƀ;BEतथऩ抲ar;槏qual;抴pƀDTVषूौownVector;楑eeVector;楠ectorĀ;Bॖॗ憿ar;楘ectorĀ;B॥०憼ar;楒ightáΜs̀EFGLSTॾঋকঝঢভqualGreater;拚ullEqual;扦reater;扶ess;檡lantEqual;橽ilde;扲r;쀀𝔏Ā;eঽা拘ftarrow;懚idot;䄿ƀnpw৔ਖਛgȀLRlr৞৷ਂਐeftĀAR০৬rrow;柵ightArrow;柷ightArrow;柶eftĀarγਊightáοightáϊf;쀀𝕃erĀLRਢਬeftArrow;憙ightArrow;憘ƀchtਾੀੂòࡌ;憰rok;䅁;扪Ѐacefiosuਗ਼੝੠੷੼અઋ઎p;椅y;䐜Ādl੥੯iumSpace;恟lintrf;愳r;쀀𝔐nusPlus;戓pf;쀀𝕄cò੶;䎜ҀJacefostuણધભીଔଙඑ඗ඞcy;䐊cute;䅃ƀaey઴હાron;䅇dil;䅅;䐝ƀgswે૰଎ativeƀMTV૓૟૨ediumSpace;怋hiĀcn૦૘ë૙eryThiî૙tedĀGL૸ଆreaterGreateòٳessLesóੈLine;䀊r;쀀𝔑ȀBnptଢନଷ଺reak;恠BreakingSpace;䂠f;愕ڀ;CDEGHLNPRSTV୕ୖ୪୼஡௫ఄ౞಄ದ೘ൡඅ櫬Āou୛୤ngruent;扢pCap;扭oubleVerticalBar;戦ƀlqxஃஊ஛ement;戉ualĀ;Tஒஓ扠ilde;쀀≂̸ists;戄reater΀;EFGLSTஶஷ஽௉௓௘௥扯qual;扱ullEqual;쀀≧̸reater;쀀≫̸ess;批lantEqual;쀀⩾̸ilde;扵umpń௲௽ownHump;쀀≎̸qual;쀀≏̸eĀfsఊధtTriangleƀ;BEచఛడ拪ar;쀀⧏̸qual;括s̀;EGLSTవశ఼ౄోౘ扮qual;扰reater;扸ess;쀀≪̸lantEqual;쀀⩽̸ilde;扴estedĀGL౨౹reaterGreater;쀀⪢̸essLess;쀀⪡̸recedesƀ;ESಒಓಛ技qual;쀀⪯̸lantEqual;拠ĀeiಫಹverseElement;戌ghtTriangleƀ;BEೋೌ೒拫ar;쀀⧐̸qual;拭ĀquೝഌuareSuĀbp೨೹setĀ;E೰ೳ쀀⊏̸qual;拢ersetĀ;Eഃആ쀀⊐̸qual;拣ƀbcpഓതൎsetĀ;Eഛഞ쀀⊂⃒qual;抈ceedsȀ;ESTലള഻െ抁qual;쀀⪰̸lantEqual;拡ilde;쀀≿̸ersetĀ;E൘൛쀀⊃⃒qual;抉ildeȀ;EFT൮൯൵ൿ扁qual;扄ullEqual;扇ilde;扉erticalBar;戤cr;쀀𝒩ilde耻Ñ䃑;䎝܀Eacdfgmoprstuvලෂ෉෕ෛ෠෧෼ขภยา฿ไlig;䅒cute耻Ó䃓Āiy෎ීrc耻Ô䃔;䐞blac;䅐r;쀀𝔒rave耻Ò䃒ƀaei෮ෲ෶cr;䅌ga;䎩cron;䎟pf;쀀𝕆enCurlyĀDQฎบoubleQuote;怜uote;怘;橔Āclวฬr;쀀𝒪ash耻Ø䃘iŬื฼de耻Õ䃕es;樷ml耻Ö䃖erĀBP๋๠Āar๐๓r;怾acĀek๚๜;揞et;掴arenthesis;揜Ҁacfhilors๿ງຊຏຒດຝະ໼rtialD;戂y;䐟r;쀀𝔓i;䎦;䎠usMinus;䂱Āipຢອncareplanåڝf;愙Ȁ;eio຺ູ໠໤檻cedesȀ;EST່້໏໚扺qual;檯lantEqual;扼ilde;找me;怳Ādp໩໮uct;戏ortionĀ;aȥ໹l;戝Āci༁༆r;쀀𝒫;䎨ȀUfos༑༖༛༟OT耻\"䀢r;쀀𝔔pf;愚cr;쀀𝒬؀BEacefhiorsu༾གྷཇའཱིྦྷྪྭ႖ႩႴႾarr;椐G耻®䂮ƀcnrཎནབute;䅔g;柫rĀ;tཛྷཝ憠l;椖ƀaeyཧཬཱron;䅘dil;䅖;䐠Ā;vླྀཹ愜erseĀEUྂྙĀlq྇ྎement;戋uilibrium;懋pEquilibrium;楯r»ཹo;䎡ghtЀACDFTUVa࿁࿫࿳ဢဨၛႇϘĀnr࿆࿒gleBracket;柩rowƀ;BL࿜࿝࿡憒ar;懥eftArrow;懄eiling;按oǵ࿹\\0စbleBracket;柧nǔည\\0နeeVector;楝ectorĀ;Bဝသ懂ar;楕loor;挋Āerိ၃eƀ;AVဵံြ抢rrow;憦ector;楛iangleƀ;BEၐၑၕ抳ar;槐qual;抵pƀDTVၣၮၸownVector;楏eeVector;楜ectorĀ;Bႂႃ憾ar;楔ectorĀ;B႑႒懀ar;楓Āpuႛ႞f;愝ndImplies;楰ightarrow;懛ĀchႹႼr;愛;憱leDelayed;槴ڀHOacfhimoqstuფჱჷჽᄙᄞᅑᅖᅡᅧᆵᆻᆿĀCcჩხHcy;䐩y;䐨FTcy;䐬cute;䅚ʀ;aeiyᄈᄉᄎᄓᄗ檼ron;䅠dil;䅞rc;䅜;䐡r;쀀𝔖ortȀDLRUᄪᄴᄾᅉownArrow»ОeftArrow»࢚ightArrow»࿝pArrow;憑gma;䎣allCircle;战pf;쀀𝕊ɲᅭ\\0\\0ᅰt;戚areȀ;ISUᅻᅼᆉᆯ斡ntersection;抓uĀbpᆏᆞsetĀ;Eᆗᆘ抏qual;抑ersetĀ;Eᆨᆩ抐qual;抒nion;抔cr;쀀𝒮ar;拆ȀbcmpᇈᇛሉላĀ;sᇍᇎ拐etĀ;Eᇍᇕqual;抆ĀchᇠህeedsȀ;ESTᇭᇮᇴᇿ扻qual;檰lantEqual;扽ilde;承Tháྌ;我ƀ;esሒሓሣ拑rsetĀ;Eሜም抃qual;抇et»ሓրHRSacfhiorsሾቄ቉ቕ቞ቱቶኟዂወዑORN耻Þ䃞ADE;愢ĀHc቎ቒcy;䐋y;䐦Ābuቚቜ;䀉;䎤ƀaeyብቪቯron;䅤dil;䅢;䐢r;쀀𝔗Āeiቻ኉Dzኀ\\0ኇefore;戴a;䎘Ācn኎ኘkSpace;쀀  Space;怉ldeȀ;EFTካኬኲኼ戼qual;扃ullEqual;扅ilde;扈pf;쀀𝕋ipleDot;惛Āctዖዛr;쀀𝒯rok;䅦ૡዷጎጚጦ\\0ጬጱ\\0\\0\\0\\0\\0ጸጽ፷ᎅ\\0᏿ᐄᐊᐐĀcrዻጁute耻Ú䃚rĀ;oጇገ憟cir;楉rǣጓ\\0጖y;䐎ve;䅬Āiyጞጣrc耻Û䃛;䐣blac;䅰r;쀀𝔘rave耻Ù䃙acr;䅪Ādiፁ፩erĀBPፈ፝Āarፍፐr;䁟acĀekፗፙ;揟et;掵arenthesis;揝onĀ;P፰፱拃lus;抎Āgp፻፿on;䅲f;쀀𝕌ЀADETadps᎕ᎮᎸᏄϨᏒᏗᏳrrowƀ;BDᅐᎠᎤar;椒ownArrow;懅ownArrow;憕quilibrium;楮eeĀ;AᏋᏌ报rrow;憥ownáϳerĀLRᏞᏨeftArrow;憖ightArrow;憗iĀ;lᏹᏺ䏒on;䎥ing;䅮cr;쀀𝒰ilde;䅨ml耻Ü䃜ҀDbcdefosvᐧᐬᐰᐳᐾᒅᒊᒐᒖash;披ar;櫫y;䐒ashĀ;lᐻᐼ抩;櫦Āerᑃᑅ;拁ƀbtyᑌᑐᑺar;怖Ā;iᑏᑕcalȀBLSTᑡᑥᑪᑴar;戣ine;䁼eparator;杘ilde;所ThinSpace;怊r;쀀𝔙pf;쀀𝕍cr;쀀𝒱dash;抪ʀcefosᒧᒬᒱᒶᒼirc;䅴dge;拀r;쀀𝔚pf;쀀𝕎cr;쀀𝒲Ȁfiosᓋᓐᓒᓘr;쀀𝔛;䎞pf;쀀𝕏cr;쀀𝒳ҀAIUacfosuᓱᓵᓹᓽᔄᔏᔔᔚᔠcy;䐯cy;䐇cy;䐮cute耻Ý䃝Āiyᔉᔍrc;䅶;䐫r;쀀𝔜pf;쀀𝕐cr;쀀𝒴ml;䅸ЀHacdefosᔵᔹᔿᕋᕏᕝᕠᕤcy;䐖cute;䅹Āayᕄᕉron;䅽;䐗ot;䅻Dzᕔ\\0ᕛoWidtè૙a;䎖r;愨pf;愤cr;쀀𝒵௡ᖃᖊᖐ\\0ᖰᖶᖿ\\0\\0\\0\\0ᗆᗛᗫᙟ᙭\\0ᚕ᚛ᚲᚹ\\0ᚾcute耻á䃡reve;䄃̀;Ediuyᖜᖝᖡᖣᖨᖭ戾;쀀∾̳;房rc耻â䃢te肻´̆;䐰lig耻æ䃦Ā;r²ᖺ;쀀𝔞rave耻à䃠ĀepᗊᗖĀfpᗏᗔsym;愵èᗓha;䎱ĀapᗟcĀclᗤᗧr;䄁g;樿ɤᗰ\\0\\0ᘊʀ;adsvᗺᗻᗿᘁᘇ戧nd;橕;橜lope;橘;橚΀;elmrszᘘᘙᘛᘞᘿᙏᙙ戠;榤e»ᘙsdĀ;aᘥᘦ戡ѡᘰᘲᘴᘶᘸᘺᘼᘾ;榨;榩;榪;榫;榬;榭;榮;榯tĀ;vᙅᙆ戟bĀ;dᙌᙍ抾;榝Āptᙔᙗh;戢»¹arr;捼Āgpᙣᙧon;䄅f;쀀𝕒΀;Eaeiop዁ᙻᙽᚂᚄᚇᚊ;橰cir;橯;扊d;手s;䀧roxĀ;e዁ᚒñᚃing耻å䃥ƀctyᚡᚦᚨr;쀀𝒶;䀪mpĀ;e዁ᚯñʈilde耻ã䃣ml耻ä䃤Āciᛂᛈoninôɲnt;樑ࠀNabcdefiklnoprsu᛭ᛱᜰ᜼ᝃᝈ᝸᝽០៦ᠹᡐᜍ᤽᥈ᥰot;櫭Ācrᛶ᜞kȀcepsᜀᜅᜍᜓong;扌psilon;䏶rime;怵imĀ;e᜚᜛戽q;拍Ŷᜢᜦee;抽edĀ;gᜬᜭ挅e»ᜭrkĀ;t፜᜷brk;掶Āoyᜁᝁ;䐱quo;怞ʀcmprtᝓ᝛ᝡᝤᝨausĀ;eĊĉptyv;榰séᜌnoõēƀahwᝯ᝱ᝳ;䎲;愶een;扬r;쀀𝔟g΀costuvwឍឝឳេ៕៛៞ƀaiuបពរðݠrc;旯p»፱ƀdptឤឨឭot;樀lus;樁imes;樂ɱឹ\\0\\0ើcup;樆ar;昅riangleĀdu៍្own;施p;斳plus;樄eåᑄåᒭarow;植ƀako៭ᠦᠵĀcn៲ᠣkƀlst៺֫᠂ozenge;槫riangleȀ;dlr᠒᠓᠘᠝斴own;斾eft;旂ight;斸k;搣Ʊᠫ\\0ᠳƲᠯ\\0ᠱ;斒;斑4;斓ck;斈ĀeoᠾᡍĀ;qᡃᡆ쀀=⃥uiv;쀀≡⃥t;挐Ȁptwxᡙᡞᡧᡬf;쀀𝕓Ā;tᏋᡣom»Ꮜtie;拈؀DHUVbdhmptuvᢅᢖᢪᢻᣗᣛᣬ᣿ᤅᤊᤐᤡȀLRlrᢎᢐᢒᢔ;敗;敔;敖;敓ʀ;DUduᢡᢢᢤᢦᢨ敐;敦;敩;敤;敧ȀLRlrᢳᢵᢷᢹ;敝;敚;敜;教΀;HLRhlrᣊᣋᣍᣏᣑᣓᣕ救;敬;散;敠;敫;敢;敟ox;槉ȀLRlrᣤᣦᣨᣪ;敕;敒;攐;攌ʀ;DUduڽ᣷᣹᣻᣽;敥;敨;攬;攴inus;抟lus;択imes;抠ȀLRlrᤙᤛᤝ᤟;敛;敘;攘;攔΀;HLRhlrᤰᤱᤳᤵᤷ᤻᤹攂;敪;敡;敞;攼;攤;攜Āevģ᥂bar耻¦䂦Ȁceioᥑᥖᥚᥠr;쀀𝒷mi;恏mĀ;e᜚᜜lƀ;bhᥨᥩᥫ䁜;槅sub;柈Ŭᥴ᥾lĀ;e᥹᥺怢t»᥺pƀ;Eeįᦅᦇ;檮Ā;qۜۛೡᦧ\\0᧨ᨑᨕᨲ\\0ᨷᩐ\\0\\0᪴\\0\\0᫁\\0\\0ᬡᬮ᭍᭒\\0᯽\\0ᰌƀcpr᦭ᦲ᧝ute;䄇̀;abcdsᦿᧀᧄ᧊᧕᧙戩nd;橄rcup;橉Āau᧏᧒p;橋p;橇ot;橀;쀀∩︀Āeo᧢᧥t;恁îړȀaeiu᧰᧻ᨁᨅǰ᧵\\0᧸s;橍on;䄍dil耻ç䃧rc;䄉psĀ;sᨌᨍ橌m;橐ot;䄋ƀdmnᨛᨠᨦil肻¸ƭptyv;榲t脀¢;eᨭᨮ䂢räƲr;쀀𝔠ƀceiᨽᩀᩍy;䑇ckĀ;mᩇᩈ朓ark»ᩈ;䏇r΀;Ecefms᩟᩠ᩢᩫ᪤᪪᪮旋;槃ƀ;elᩩᩪᩭ䋆q;扗eɡᩴ\\0\\0᪈rrowĀlr᩼᪁eft;憺ight;憻ʀRSacd᪒᪔᪖᪚᪟»ཇ;擈st;抛irc;抚ash;抝nint;樐id;櫯cir;槂ubsĀ;u᪻᪼晣it»᪼ˬ᫇᫔᫺\\0ᬊonĀ;eᫍᫎ䀺Ā;qÇÆɭ᫙\\0\\0᫢aĀ;t᫞᫟䀬;䁀ƀ;fl᫨᫩᫫戁îᅠeĀmx᫱᫶ent»᫩eóɍǧ᫾\\0ᬇĀ;dኻᬂot;橭nôɆƀfryᬐᬔᬗ;쀀𝕔oäɔ脀©;sŕᬝr;愗Āaoᬥᬩrr;憵ss;朗Ācuᬲᬷr;쀀𝒸Ābpᬼ᭄Ā;eᭁᭂ櫏;櫑Ā;eᭉᭊ櫐;櫒dot;拯΀delprvw᭠᭬᭷ᮂᮬᯔ᯹arrĀlr᭨᭪;椸;椵ɰ᭲\\0\\0᭵r;拞c;拟arrĀ;p᭿ᮀ憶;椽̀;bcdosᮏᮐᮖᮡᮥᮨ截rcap;橈Āauᮛᮞp;橆p;橊ot;抍r;橅;쀀∪︀Ȁalrv᮵ᮿᯞᯣrrĀ;mᮼᮽ憷;椼yƀevwᯇᯔᯘqɰᯎ\\0\\0ᯒreã᭳uã᭵ee;拎edge;拏en耻¤䂤earrowĀlrᯮ᯳eft»ᮀight»ᮽeäᯝĀciᰁᰇoninôǷnt;戱lcty;挭ঀAHabcdefhijlorstuwz᰸᰻᰿ᱝᱩᱵᲊᲞᲬᲷ᳻᳿ᴍᵻᶑᶫᶻ᷆᷍rò΁ar;楥Ȁglrs᱈ᱍ᱒᱔ger;怠eth;愸òᄳhĀ;vᱚᱛ怐»ऊūᱡᱧarow;椏aã̕Āayᱮᱳron;䄏;䐴ƀ;ao̲ᱼᲄĀgrʿᲁr;懊tseq;橷ƀglmᲑᲔᲘ耻°䂰ta;䎴ptyv;榱ĀirᲣᲨsht;楿;쀀𝔡arĀlrᲳᲵ»ࣜ»သʀaegsv᳂͸᳖᳜᳠mƀ;oș᳊᳔ndĀ;ș᳑uit;晦amma;䏝in;拲ƀ;io᳧᳨᳸䃷de脀÷;o᳧ᳰntimes;拇nø᳷cy;䑒cɯᴆ\\0\\0ᴊrn;挞op;挍ʀlptuwᴘᴝᴢᵉᵕlar;䀤f;쀀𝕕ʀ;emps̋ᴭᴷᴽᵂqĀ;d͒ᴳot;扑inus;戸lus;戔quare;抡blebarwedgåúnƀadhᄮᵝᵧownarrowóᲃarpoonĀlrᵲᵶefôᲴighôᲶŢᵿᶅkaro÷གɯᶊ\\0\\0ᶎrn;挟op;挌ƀcotᶘᶣᶦĀryᶝᶡ;쀀𝒹;䑕l;槶rok;䄑Ādrᶰᶴot;拱iĀ;fᶺ᠖斿Āah᷀᷃ròЩaòྦangle;榦Āci᷒ᷕy;䑟grarr;柿ऀDacdefglmnopqrstuxḁḉḙḸոḼṉṡṾấắẽỡἪἷὄ὎὚ĀDoḆᴴoôᲉĀcsḎḔute耻é䃩ter;橮ȀaioyḢḧḱḶron;䄛rĀ;cḭḮ扖耻ê䃪lon;払;䑍ot;䄗ĀDrṁṅot;扒;쀀𝔢ƀ;rsṐṑṗ檚ave耻è䃨Ā;dṜṝ檖ot;檘Ȁ;ilsṪṫṲṴ檙nters;揧;愓Ā;dṹṺ檕ot;檗ƀapsẅẉẗcr;䄓tyƀ;svẒẓẕ戅et»ẓpĀ1;ẝẤijạả;怄;怅怃ĀgsẪẬ;䅋p;怂ĀgpẴẸon;䄙f;쀀𝕖ƀalsỄỎỒrĀ;sỊị拕l;槣us;橱iƀ;lvỚớở䎵on»ớ;䏵ȀcsuvỪỳἋἣĀioữḱrc»Ḯɩỹ\\0\\0ỻíՈantĀglἂἆtr»ṝess»Ṻƀaeiἒ἖Ἒls;䀽st;扟vĀ;DȵἠD;橸parsl;槥ĀDaἯἳot;打rr;楱ƀcdiἾὁỸr;愯oô͒ĀahὉὋ;䎷耻ð䃰Āmrὓὗl耻ë䃫o;悬ƀcipὡὤὧl;䀡sôծĀeoὬὴctatioîՙnentialåչৡᾒ\\0ᾞ\\0ᾡᾧ\\0\\0ῆῌ\\0ΐ\\0ῦῪ \\0 ⁚llingdotseñṄy;䑄male;晀ƀilrᾭᾳ῁lig;耀ffiɩᾹ\\0\\0᾽g;耀ffig;耀ffl;쀀𝔣lig;耀filig;쀀fjƀaltῙ῜ῡt;晭ig;耀flns;斱of;䆒ǰ΅\\0ῳf;쀀𝕗ĀakֿῷĀ;vῼ´拔;櫙artint;樍Āao‌⁕Ācs‑⁒ႉ‸⁅⁈\\0⁐β•‥‧‪‬\\0‮耻½䂽;慓耻¼䂼;慕;慙;慛Ƴ‴\\0‶;慔;慖ʴ‾⁁\\0\\0⁃耻¾䂾;慗;慜5;慘ƶ⁌\\0⁎;慚;慝8;慞l;恄wn;挢cr;쀀𝒻ࢀEabcdefgijlnorstv₂₉₟₥₰₴⃰⃵⃺⃿℃ℒℸ̗ℾ⅒↞Ā;lٍ₇;檌ƀcmpₐₕ₝ute;䇵maĀ;dₜ᳚䎳;檆reve;䄟Āiy₪₮rc;䄝;䐳ot;䄡Ȁ;lqsؾق₽⃉ƀ;qsؾٌ⃄lanô٥Ȁ;cdl٥⃒⃥⃕c;檩otĀ;o⃜⃝檀Ā;l⃢⃣檂;檄Ā;e⃪⃭쀀⋛︀s;檔r;쀀𝔤Ā;gٳ؛mel;愷cy;䑓Ȁ;Eajٚℌℎℐ;檒;檥;檤ȀEaesℛℝ℩ℴ;扩pĀ;p℣ℤ檊rox»ℤĀ;q℮ℯ檈Ā;q℮ℛim;拧pf;쀀𝕘Āci⅃ⅆr;愊mƀ;el٫ⅎ⅐;檎;檐茀>;cdlqr׮ⅠⅪⅮⅳⅹĀciⅥⅧ;檧r;橺ot;拗Par;榕uest;橼ʀadelsↄⅪ←ٖ↛ǰ↉\\0↎proø₞r;楸qĀlqؿ↖lesó₈ií٫Āen↣↭rtneqq;쀀≩︀Å↪ԀAabcefkosy⇄⇇⇱⇵⇺∘∝∯≨≽ròΠȀilmr⇐⇔⇗⇛rsðᒄf»․ilôکĀdr⇠⇤cy;䑊ƀ;cwࣴ⇫⇯ir;楈;憭ar;意irc;䄥ƀalr∁∎∓rtsĀ;u∉∊晥it»∊lip;怦con;抹r;쀀𝔥sĀew∣∩arow;椥arow;椦ʀamopr∺∾≃≞≣rr;懿tht;戻kĀlr≉≓eftarrow;憩ightarrow;憪f;쀀𝕙bar;怕ƀclt≯≴≸r;쀀𝒽asè⇴rok;䄧Ābp⊂⊇ull;恃hen»ᱛૡ⊣\\0⊪\\0⊸⋅⋎\\0⋕⋳\\0\\0⋸⌢⍧⍢⍿\\0⎆⎪⎴cute耻í䃭ƀ;iyݱ⊰⊵rc耻î䃮;䐸Ācx⊼⊿y;䐵cl耻¡䂡ĀfrΟ⋉;쀀𝔦rave耻ì䃬Ȁ;inoܾ⋝⋩⋮Āin⋢⋦nt;樌t;戭fin;槜ta;愩lig;䄳ƀaop⋾⌚⌝ƀcgt⌅⌈⌗r;䄫ƀelpܟ⌏⌓inåގarôܠh;䄱f;抷ed;䆵ʀ;cfotӴ⌬⌱⌽⍁are;愅inĀ;t⌸⌹戞ie;槝doô⌙ʀ;celpݗ⍌⍐⍛⍡al;抺Āgr⍕⍙eróᕣã⍍arhk;樗rod;樼Ȁcgpt⍯⍲⍶⍻y;䑑on;䄯f;쀀𝕚a;䎹uest耻¿䂿Āci⎊⎏r;쀀𝒾nʀ;EdsvӴ⎛⎝⎡ӳ;拹ot;拵Ā;v⎦⎧拴;拳Ā;iݷ⎮lde;䄩ǫ⎸\\0⎼cy;䑖l耻ï䃯̀cfmosu⏌⏗⏜⏡⏧⏵Āiy⏑⏕rc;䄵;䐹r;쀀𝔧ath;䈷pf;쀀𝕛ǣ⏬\\0⏱r;쀀𝒿rcy;䑘kcy;䑔Ѐacfghjos␋␖␢␧␭␱␵␻ppaĀ;v␓␔䎺;䏰Āey␛␠dil;䄷;䐺r;쀀𝔨reen;䄸cy;䑅cy;䑜pf;쀀𝕜cr;쀀𝓀஀ABEHabcdefghjlmnoprstuv⑰⒁⒆⒍⒑┎┽╚▀♎♞♥♹♽⚚⚲⛘❝❨➋⟀⠁⠒ƀart⑷⑺⑼rò৆òΕail;椛arr;椎Ā;gঔ⒋;檋ar;楢ॣ⒥\\0⒪\\0⒱\\0\\0\\0\\0\\0⒵Ⓔ\\0ⓆⓈⓍ\\0⓹ute;䄺mptyv;榴raîࡌbda;䎻gƀ;dlࢎⓁⓃ;榑åࢎ;檅uo耻«䂫rЀ;bfhlpst࢙ⓞⓦⓩ⓫⓮⓱⓵Ā;f࢝ⓣs;椟s;椝ë≒p;憫l;椹im;楳l;憢ƀ;ae⓿─┄檫il;椙Ā;s┉┊檭;쀀⪭︀ƀabr┕┙┝rr;椌rk;杲Āak┢┬cĀek┨┪;䁻;䁛Āes┱┳;榋lĀdu┹┻;榏;榍Ȁaeuy╆╋╖╘ron;䄾Ādi═╔il;䄼ìࢰâ┩;䐻Ȁcqrs╣╦╭╽a;椶uoĀ;rนᝆĀdu╲╷har;楧shar;楋h;憲ʀ;fgqs▋▌উ◳◿扤tʀahlrt▘▤▷◂◨rrowĀ;t࢙□aé⓶arpoonĀdu▯▴own»њp»०eftarrows;懇ightƀahs◍◖◞rrowĀ;sࣴࢧarpoonó྘quigarro÷⇰hreetimes;拋ƀ;qs▋ও◺lanôবʀ;cdgsব☊☍☝☨c;檨otĀ;o☔☕橿Ā;r☚☛檁;檃Ā;e☢☥쀀⋚︀s;檓ʀadegs☳☹☽♉♋pproøⓆot;拖qĀgq♃♅ôউgtò⒌ôছiíলƀilr♕࣡♚sht;楼;쀀𝔩Ā;Eজ♣;檑š♩♶rĀdu▲♮Ā;l॥♳;楪lk;斄cy;䑙ʀ;achtੈ⚈⚋⚑⚖rò◁orneòᴈard;楫ri;旺Āio⚟⚤dot;䅀ustĀ;a⚬⚭掰che»⚭ȀEaes⚻⚽⛉⛔;扨pĀ;p⛃⛄檉rox»⛄Ā;q⛎⛏檇Ā;q⛎⚻im;拦Ѐabnoptwz⛩⛴⛷✚✯❁❇❐Ānr⛮⛱g;柬r;懽rëࣁgƀlmr⛿✍✔eftĀar০✇ightá৲apsto;柼ightá৽parrowĀlr✥✩efô⓭ight;憬ƀafl✶✹✽r;榅;쀀𝕝us;樭imes;樴š❋❏st;戗áፎƀ;ef❗❘᠀旊nge»❘arĀ;l❤❥䀨t;榓ʀachmt❳❶❼➅➇ròࢨorneòᶌarĀ;d྘➃;業;怎ri;抿̀achiqt➘➝ੀ➢➮➻quo;怹r;쀀𝓁mƀ;egল➪➬;檍;檏Ābu┪➳oĀ;rฟ➹;怚rok;䅂萀<;cdhilqrࠫ⟒☹⟜⟠⟥⟪⟰Āci⟗⟙;檦r;橹reå◲mes;拉arr;楶uest;橻ĀPi⟵⟹ar;榖ƀ;ef⠀भ᠛旃rĀdu⠇⠍shar;楊har;楦Āen⠗⠡rtneqq;쀀≨︀Å⠞܀Dacdefhilnopsu⡀⡅⢂⢎⢓⢠⢥⢨⣚⣢⣤ઃ⣳⤂Dot;戺Ȁclpr⡎⡒⡣⡽r耻¯䂯Āet⡗⡙;時Ā;e⡞⡟朠se»⡟Ā;sျ⡨toȀ;dluျ⡳⡷⡻owîҌefôएðᏑker;斮Āoy⢇⢌mma;権;䐼ash;怔asuredangle»ᘦr;쀀𝔪o;愧ƀcdn⢯⢴⣉ro耻µ䂵Ȁ;acdᑤ⢽⣀⣄sôᚧir;櫰ot肻·Ƶusƀ;bd⣒ᤃ⣓戒Ā;uᴼ⣘;横ţ⣞⣡p;櫛ò−ðઁĀdp⣩⣮els;抧f;쀀𝕞Āct⣸⣽r;쀀𝓂pos»ᖝƀ;lm⤉⤊⤍䎼timap;抸ఀGLRVabcdefghijlmoprstuvw⥂⥓⥾⦉⦘⧚⧩⨕⨚⩘⩝⪃⪕⪤⪨⬄⬇⭄⭿⮮ⰴⱧⱼ⳩Āgt⥇⥋;쀀⋙̸Ā;v⥐௏쀀≫⃒ƀelt⥚⥲⥶ftĀar⥡⥧rrow;懍ightarrow;懎;쀀⋘̸Ā;v⥻ే쀀≪⃒ightarrow;懏ĀDd⦎⦓ash;抯ash;抮ʀbcnpt⦣⦧⦬⦱⧌la»˞ute;䅄g;쀀∠⃒ʀ;Eiop඄⦼⧀⧅⧈;쀀⩰̸d;쀀≋̸s;䅉roø඄urĀ;a⧓⧔普lĀ;s⧓ସdz⧟\\0⧣p肻 ଷmpĀ;e௹ఀʀaeouy⧴⧾⨃⨐⨓ǰ⧹\\0⧻;橃on;䅈dil;䅆ngĀ;dൾ⨊ot;쀀⩭̸p;橂;䐽ash;怓΀;Aadqsxஒ⨩⨭⨻⩁⩅⩐rr;懗rĀhr⨳⨶k;椤Ā;oᏲᏰot;쀀≐̸uiöୣĀei⩊⩎ar;椨í஘istĀ;s஠டr;쀀𝔫ȀEest௅⩦⩹⩼ƀ;qs஼⩭௡ƀ;qs஼௅⩴lanô௢ií௪Ā;rஶ⪁»ஷƀAap⪊⪍⪑rò⥱rr;憮ar;櫲ƀ;svྍ⪜ྌĀ;d⪡⪢拼;拺cy;䑚΀AEadest⪷⪺⪾⫂⫅⫶⫹rò⥦;쀀≦̸rr;憚r;急Ȁ;fqs఻⫎⫣⫯tĀar⫔⫙rro÷⫁ightarro÷⪐ƀ;qs఻⪺⫪lanôౕĀ;sౕ⫴»శiíౝĀ;rవ⫾iĀ;eచథiäඐĀpt⬌⬑f;쀀𝕟膀¬;in⬙⬚⬶䂬nȀ;Edvஉ⬤⬨⬮;쀀⋹̸ot;쀀⋵̸ǡஉ⬳⬵;拷;拶iĀ;vಸ⬼ǡಸ⭁⭃;拾;拽ƀaor⭋⭣⭩rȀ;ast୻⭕⭚⭟lleì୻l;쀀⫽⃥;쀀∂̸lint;樔ƀ;ceಒ⭰⭳uåಥĀ;cಘ⭸Ā;eಒ⭽ñಘȀAait⮈⮋⮝⮧rò⦈rrƀ;cw⮔⮕⮙憛;쀀⤳̸;쀀↝̸ghtarrow»⮕riĀ;eೋೖ΀chimpqu⮽⯍⯙⬄୸⯤⯯Ȁ;cerല⯆ഷ⯉uå൅;쀀𝓃ortɭ⬅\\0\\0⯖ará⭖mĀ;e൮⯟Ā;q൴൳suĀbp⯫⯭å೸åഋƀbcp⯶ⰑⰙȀ;Ees⯿ⰀഢⰄ抄;쀀⫅̸etĀ;eഛⰋqĀ;qണⰀcĀ;eലⰗñസȀ;EesⰢⰣൟⰧ抅;쀀⫆̸etĀ;e൘ⰮqĀ;qൠⰣȀgilrⰽⰿⱅⱇìௗlde耻ñ䃱çృiangleĀlrⱒⱜeftĀ;eచⱚñదightĀ;eೋⱥñ೗Ā;mⱬⱭ䎽ƀ;esⱴⱵⱹ䀣ro;愖p;怇ҀDHadgilrsⲏⲔⲙⲞⲣⲰⲶⳓⳣash;抭arr;椄p;쀀≍⃒ash;抬ĀetⲨⲬ;쀀≥⃒;쀀>⃒nfin;槞ƀAetⲽⳁⳅrr;椂;쀀≤⃒Ā;rⳊⳍ쀀<⃒ie;쀀⊴⃒ĀAtⳘⳜrr;椃rie;쀀⊵⃒im;쀀∼⃒ƀAan⳰⳴ⴂrr;懖rĀhr⳺⳽k;椣Ā;oᏧᏥear;椧ቓ᪕\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ⴭ\\0ⴸⵈⵠⵥ⵲ⶄᬇ\\0\\0ⶍⶫ\\0ⷈⷎ\\0ⷜ⸙⸫⸾⹃Ācsⴱ᪗ute耻ó䃳ĀiyⴼⵅrĀ;c᪞ⵂ耻ô䃴;䐾ʀabios᪠ⵒⵗLjⵚlac;䅑v;樸old;榼lig;䅓Ācr⵩⵭ir;榿;쀀𝔬ͯ⵹\\0\\0⵼\\0ⶂn;䋛ave耻ò䃲;槁Ābmⶈ෴ar;榵Ȁacitⶕ⶘ⶥⶨrò᪀Āir⶝ⶠr;榾oss;榻nå๒;槀ƀaeiⶱⶵⶹcr;䅍ga;䏉ƀcdnⷀⷅǍron;䎿;榶pf;쀀𝕠ƀaelⷔ⷗ǒr;榷rp;榹΀;adiosvⷪⷫⷮ⸈⸍⸐⸖戨rò᪆Ȁ;efmⷷⷸ⸂⸅橝rĀ;oⷾⷿ愴f»ⷿ耻ª䂪耻º䂺gof;抶r;橖lope;橗;橛ƀclo⸟⸡⸧ò⸁ash耻ø䃸l;折iŬⸯ⸴de耻õ䃵esĀ;aǛ⸺s;樶ml耻ö䃶bar;挽ૡ⹞\\0⹽\\0⺀⺝\\0⺢⺹\\0\\0⻋ຜ\\0⼓\\0\\0⼫⾼\\0⿈rȀ;astЃ⹧⹲຅脀¶;l⹭⹮䂶leìЃɩ⹸\\0\\0⹻m;櫳;櫽y;䐿rʀcimpt⺋⺏⺓ᡥ⺗nt;䀥od;䀮il;怰enk;怱r;쀀𝔭ƀimo⺨⺰⺴Ā;v⺭⺮䏆;䏕maô੶ne;明ƀ;tv⺿⻀⻈䏀chfork»´;䏖Āau⻏⻟nĀck⻕⻝kĀ;h⇴⻛;愎ö⇴sҀ;abcdemst⻳⻴ᤈ⻹⻽⼄⼆⼊⼎䀫cir;樣ir;樢Āouᵀ⼂;樥;橲n肻±ຝim;樦wo;樧ƀipu⼙⼠⼥ntint;樕f;쀀𝕡nd耻£䂣Ԁ;Eaceinosu່⼿⽁⽄⽇⾁⾉⾒⽾⾶;檳p;檷uå໙Ā;c໎⽌̀;acens່⽙⽟⽦⽨⽾pproø⽃urlyeñ໙ñ໎ƀaes⽯⽶⽺pprox;檹qq;檵im;拨iíໟmeĀ;s⾈ຮ怲ƀEas⽸⾐⽺ð⽵ƀdfp໬⾙⾯ƀals⾠⾥⾪lar;挮ine;挒urf;挓Ā;t໻⾴ï໻rel;抰Āci⿀⿅r;쀀𝓅;䏈ncsp;怈̀fiopsu⿚⋢⿟⿥⿫⿱r;쀀𝔮pf;쀀𝕢rime;恗cr;쀀𝓆ƀaeo⿸〉〓tĀei⿾々rnionóڰnt;樖stĀ;e【】䀿ñἙô༔઀ABHabcdefhilmnoprstux぀けさすムㄎㄫㅇㅢㅲㆎ㈆㈕㈤㈩㉘㉮㉲㊐㊰㊷ƀartぇおがròႳòϝail;検aròᱥar;楤΀cdenqrtとふへみわゔヌĀeuねぱ;쀀∽̱te;䅕iãᅮmptyv;榳gȀ;del࿑らるろ;榒;榥å࿑uo耻»䂻rր;abcfhlpstw࿜ガクシスゼゾダッデナp;極Ā;f࿠ゴs;椠;椳s;椞ë≝ð✮l;楅im;楴l;憣;憝Āaiパフil;椚oĀ;nホボ戶aló༞ƀabrョリヮrò៥rk;杳ĀakンヽcĀekヹ・;䁽;䁝Āes㄂㄄;榌lĀduㄊㄌ;榎;榐Ȁaeuyㄗㄜㄧㄩron;䅙Ādiㄡㄥil;䅗ì࿲âヺ;䑀Ȁclqsㄴㄷㄽㅄa;椷dhar;楩uoĀ;rȎȍh;憳ƀacgㅎㅟངlȀ;ipsླྀㅘㅛႜnåႻarôྩt;断ƀilrㅩဣㅮsht;楽;쀀𝔯ĀaoㅷㆆrĀduㅽㅿ»ѻĀ;l႑ㆄ;楬Ā;vㆋㆌ䏁;䏱ƀgns㆕ㇹㇼht̀ahlrstㆤㆰ㇂㇘㇤㇮rrowĀ;t࿜ㆭaéトarpoonĀduㆻㆿowîㅾp»႒eftĀah㇊㇐rrowó࿪arpoonóՑightarrows;應quigarro÷ニhreetimes;拌g;䋚ingdotseñἲƀahm㈍㈐㈓rò࿪aòՑ;怏oustĀ;a㈞㈟掱che»㈟mid;櫮Ȁabpt㈲㈽㉀㉒Ānr㈷㈺g;柭r;懾rëဃƀafl㉇㉊㉎r;榆;쀀𝕣us;樮imes;樵Āap㉝㉧rĀ;g㉣㉤䀩t;榔olint;樒arò㇣Ȁachq㉻㊀Ⴜ㊅quo;怺r;쀀𝓇Ābu・㊊oĀ;rȔȓƀhir㊗㊛㊠reåㇸmes;拊iȀ;efl㊪ၙᠡ㊫方tri;槎luhar;楨;愞ൡ㋕㋛㋟㌬㌸㍱\\0㍺㎤\\0\\0㏬㏰\\0㐨㑈㑚㒭㒱㓊㓱\\0㘖\\0\\0㘳cute;䅛quï➺Ԁ;Eaceinpsyᇭ㋳㋵㋿㌂㌋㌏㌟㌦㌩;檴ǰ㋺\\0㋼;檸on;䅡uåᇾĀ;dᇳ㌇il;䅟rc;䅝ƀEas㌖㌘㌛;檶p;檺im;择olint;樓iíሄ;䑁otƀ;be㌴ᵇ㌵担;橦΀Aacmstx㍆㍊㍗㍛㍞㍣㍭rr;懘rĀhr㍐㍒ë∨Ā;oਸ਼਴t耻§䂧i;䀻war;椩mĀin㍩ðnuóñt;朶rĀ;o㍶⁕쀀𝔰Ȁacoy㎂㎆㎑㎠rp;景Āhy㎋㎏cy;䑉;䑈rtɭ㎙\\0\\0㎜iäᑤaraì⹯耻­䂭Āgm㎨㎴maƀ;fv㎱㎲㎲䏃;䏂Ѐ;deglnprካ㏅㏉㏎㏖㏞㏡㏦ot;橪Ā;q኱ኰĀ;E㏓㏔檞;檠Ā;E㏛㏜檝;檟e;扆lus;樤arr;楲aròᄽȀaeit㏸㐈㐏㐗Āls㏽㐄lsetmé㍪hp;樳parsl;槤Ādlᑣ㐔e;挣Ā;e㐜㐝檪Ā;s㐢㐣檬;쀀⪬︀ƀflp㐮㐳㑂tcy;䑌Ā;b㐸㐹䀯Ā;a㐾㐿槄r;挿f;쀀𝕤aĀdr㑍ЂesĀ;u㑔㑕晠it»㑕ƀcsu㑠㑹㒟Āau㑥㑯pĀ;sᆈ㑫;쀀⊓︀pĀ;sᆴ㑵;쀀⊔︀uĀbp㑿㒏ƀ;esᆗᆜ㒆etĀ;eᆗ㒍ñᆝƀ;esᆨᆭ㒖etĀ;eᆨ㒝ñᆮƀ;afᅻ㒦ְrť㒫ֱ»ᅼaròᅈȀcemt㒹㒾㓂㓅r;쀀𝓈tmîñiì㐕aræᆾĀar㓎㓕rĀ;f㓔ឿ昆Āan㓚㓭ightĀep㓣㓪psiloîỠhé⺯s»⡒ʀbcmnp㓻㕞ሉ㖋㖎Ҁ;Edemnprs㔎㔏㔑㔕㔞㔣㔬㔱㔶抂;櫅ot;檽Ā;dᇚ㔚ot;櫃ult;櫁ĀEe㔨㔪;櫋;把lus;檿arr;楹ƀeiu㔽㕒㕕tƀ;en㔎㕅㕋qĀ;qᇚ㔏eqĀ;q㔫㔨m;櫇Ābp㕚㕜;櫕;櫓c̀;acensᇭ㕬㕲㕹㕻㌦pproø㋺urlyeñᇾñᇳƀaes㖂㖈㌛pproø㌚qñ㌗g;晪ڀ123;Edehlmnps㖩㖬㖯ሜ㖲㖴㗀㗉㗕㗚㗟㗨㗭耻¹䂹耻²䂲耻³䂳;櫆Āos㖹㖼t;檾ub;櫘Ā;dሢ㗅ot;櫄sĀou㗏㗒l;柉b;櫗arr;楻ult;櫂ĀEe㗤㗦;櫌;抋lus;櫀ƀeiu㗴㘉㘌tƀ;enሜ㗼㘂qĀ;qሢ㖲eqĀ;q㗧㗤m;櫈Ābp㘑㘓;櫔;櫖ƀAan㘜㘠㘭rr;懙rĀhr㘦㘨ë∮Ā;oਫ਩war;椪lig耻ß䃟௡㙑㙝㙠ዎ㙳㙹\\0㙾㛂\\0\\0\\0\\0\\0㛛㜃\\0㜉㝬\\0\\0\\0㞇ɲ㙖\\0\\0㙛get;挖;䏄rë๟ƀaey㙦㙫㙰ron;䅥dil;䅣;䑂lrec;挕r;쀀𝔱Ȁeiko㚆㚝㚵㚼Dz㚋\\0㚑eĀ4fኄኁaƀ;sv㚘㚙㚛䎸ym;䏑Ācn㚢㚲kĀas㚨㚮pproø዁im»ኬsðኞĀas㚺㚮ð዁rn耻þ䃾Ǭ̟㛆⋧es膀×;bd㛏㛐㛘䃗Ā;aᤏ㛕r;樱;樰ƀeps㛡㛣㜀á⩍Ȁ;bcf҆㛬㛰㛴ot;挶ir;櫱Ā;o㛹㛼쀀𝕥rk;櫚á㍢rime;怴ƀaip㜏㜒㝤dåቈ΀adempst㜡㝍㝀㝑㝗㝜㝟ngleʀ;dlqr㜰㜱㜶㝀㝂斵own»ᶻeftĀ;e⠀㜾ñम;扜ightĀ;e㊪㝋ñၚot;旬inus;樺lus;樹b;槍ime;樻ezium;揢ƀcht㝲㝽㞁Āry㝷㝻;쀀𝓉;䑆cy;䑛rok;䅧Āio㞋㞎xô᝷headĀlr㞗㞠eftarro÷ࡏightarrow»ཝऀAHabcdfghlmoprstuw㟐㟓㟗㟤㟰㟼㠎㠜㠣㠴㡑㡝㡫㢩㣌㣒㣪㣶ròϭar;楣Ācr㟜㟢ute耻ú䃺òᅐrǣ㟪\\0㟭y;䑞ve;䅭Āiy㟵㟺rc耻û䃻;䑃ƀabh㠃㠆㠋ròᎭlac;䅱aòᏃĀir㠓㠘sht;楾;쀀𝔲rave耻ù䃹š㠧㠱rĀlr㠬㠮»ॗ»ႃlk;斀Āct㠹㡍ɯ㠿\\0\\0㡊rnĀ;e㡅㡆挜r»㡆op;挏ri;旸Āal㡖㡚cr;䅫肻¨͉Āgp㡢㡦on;䅳f;쀀𝕦̀adhlsuᅋ㡸㡽፲㢑㢠ownáᎳarpoonĀlr㢈㢌efô㠭ighô㠯iƀ;hl㢙㢚㢜䏅»ᏺon»㢚parrows;懈ƀcit㢰㣄㣈ɯ㢶\\0\\0㣁rnĀ;e㢼㢽挝r»㢽op;挎ng;䅯ri;旹cr;쀀𝓊ƀdir㣙㣝㣢ot;拰lde;䅩iĀ;f㜰㣨»᠓Āam㣯㣲rò㢨l耻ü䃼angle;榧ހABDacdeflnoprsz㤜㤟㤩㤭㦵㦸㦽㧟㧤㧨㧳㧹㧽㨁㨠ròϷarĀ;v㤦㤧櫨;櫩asèϡĀnr㤲㤷grt;榜΀eknprst㓣㥆㥋㥒㥝㥤㦖appá␕othinçẖƀhir㓫⻈㥙opô⾵Ā;hᎷ㥢ïㆍĀiu㥩㥭gmá㎳Ābp㥲㦄setneqĀ;q㥽㦀쀀⊊︀;쀀⫋︀setneqĀ;q㦏㦒쀀⊋︀;쀀⫌︀Āhr㦛㦟etá㚜iangleĀlr㦪㦯eft»थight»ၑy;䐲ash»ံƀelr㧄㧒㧗ƀ;beⷪ㧋㧏ar;抻q;扚lip;拮Ābt㧜ᑨaòᑩr;쀀𝔳tré㦮suĀbp㧯㧱»ജ»൙pf;쀀𝕧roð໻tré㦴Ācu㨆㨋r;쀀𝓋Ābp㨐㨘nĀEe㦀㨖»㥾nĀEe㦒㨞»㦐igzag;榚΀cefoprs㨶㨻㩖㩛㩔㩡㩪irc;䅵Ādi㩀㩑Ābg㩅㩉ar;機eĀ;qᗺ㩏;扙erp;愘r;쀀𝔴pf;쀀𝕨Ā;eᑹ㩦atèᑹcr;쀀𝓌ૣណ㪇\\0㪋\\0㪐㪛\\0\\0㪝㪨㪫㪯\\0\\0㫃㫎\\0㫘ៜ៟tré៑r;쀀𝔵ĀAa㪔㪗ròσrò৶;䎾ĀAa㪡㪤ròθrò৫að✓is;拻ƀdptឤ㪵㪾Āfl㪺ឩ;쀀𝕩imåឲĀAa㫇㫊ròώròਁĀcq㫒ីr;쀀𝓍Āpt៖㫜ré។Ѐacefiosu㫰㫽㬈㬌㬑㬕㬛㬡cĀuy㫶㫻te耻ý䃽;䑏Āiy㬂㬆rc;䅷;䑋n耻¥䂥r;쀀𝔶cy;䑗pf;쀀𝕪cr;쀀𝓎Ācm㬦㬩y;䑎l耻ÿ䃿Ԁacdefhiosw㭂㭈㭔㭘㭤㭩㭭㭴㭺㮀cute;䅺Āay㭍㭒ron;䅾;䐷ot;䅼Āet㭝㭡træᕟa;䎶r;쀀𝔷cy;䐶grarr;懝pf;쀀𝕫cr;쀀𝓏Ājn㮅㮇;怍j;怌'.split(\"\").map((e=>e.charCodeAt(0)))),oC=new Map([[0,65533],[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]);var iC,sC,lC,cC,uC,dC,pC,hC,fC,mC,gC,vC,bC,yC,OC,wC,xC,kC;function SC(e){return e>=iC.ZERO&&e<=iC.NINE}function _C(e){return e===iC.EQUALS||function(e){return e>=iC.UPPER_A&&e<=iC.UPPER_Z||e>=iC.LOWER_A&&e<=iC.LOWER_Z||SC(e)}(e)}(sC=iC||(iC={}))[sC.NUM=35]=\"NUM\",sC[sC.SEMI=59]=\"SEMI\",sC[sC.EQUALS=61]=\"EQUALS\",sC[sC.ZERO=48]=\"ZERO\",sC[sC.NINE=57]=\"NINE\",sC[sC.LOWER_A=97]=\"LOWER_A\",sC[sC.LOWER_F=102]=\"LOWER_F\",sC[sC.LOWER_X=120]=\"LOWER_X\",sC[sC.LOWER_Z=122]=\"LOWER_Z\",sC[sC.UPPER_A=65]=\"UPPER_A\",sC[sC.UPPER_F=70]=\"UPPER_F\",sC[sC.UPPER_Z=90]=\"UPPER_Z\",(cC=lC||(lC={}))[cC.VALUE_LENGTH=49152]=\"VALUE_LENGTH\",cC[cC.BRANCH_LENGTH=16256]=\"BRANCH_LENGTH\",cC[cC.JUMP_TABLE=127]=\"JUMP_TABLE\",(dC=uC||(uC={}))[dC.EntityStart=0]=\"EntityStart\",dC[dC.NumericStart=1]=\"NumericStart\",dC[dC.NumericDecimal=2]=\"NumericDecimal\",dC[dC.NumericHex=3]=\"NumericHex\",dC[dC.NamedEntity=4]=\"NamedEntity\",(hC=pC||(pC={}))[hC.Legacy=0]=\"Legacy\",hC[hC.Strict=1]=\"Strict\",hC[hC.Attribute=2]=\"Attribute\";class TC{constructor(e,t,n){this.decodeTree=e,this.emitCodePoint=t,this.errors=n,this.state=uC.EntityStart,this.consumed=1,this.result=0,this.treeIndex=0,this.excess=1,this.decodeMode=pC.Strict}startEntity(e){this.decodeMode=e,this.state=uC.EntityStart,this.result=0,this.treeIndex=0,this.excess=1,this.consumed=1}write(e,t){switch(this.state){case uC.EntityStart:return e.charCodeAt(t)===iC.NUM?(this.state=uC.NumericStart,this.consumed+=1,this.stateNumericStart(e,t+1)):(this.state=uC.NamedEntity,this.stateNamedEntity(e,t));case uC.NumericStart:return this.stateNumericStart(e,t);case uC.NumericDecimal:return this.stateNumericDecimal(e,t);case uC.NumericHex:return this.stateNumericHex(e,t);case uC.NamedEntity:return this.stateNamedEntity(e,t)}}stateNumericStart(e,t){return t>=e.length?-1:(32|e.charCodeAt(t))===iC.LOWER_X?(this.state=uC.NumericHex,this.consumed+=1,this.stateNumericHex(e,t+1)):(this.state=uC.NumericDecimal,this.stateNumericDecimal(e,t))}addToNumericResult(e,t,n,r){if(t!==n){const a=n-t;this.result=this.result*Math.pow(r,a)+Number.parseInt(e.substr(t,a),r),this.consumed+=a}}stateNumericHex(e,t){const n=t;for(;t=iC.UPPER_A&&r<=iC.UPPER_F||r>=iC.LOWER_A&&r<=iC.LOWER_F)))return this.addToNumericResult(e,n,t,16),this.emitNumericEntity(a,3);t+=1}var r;return this.addToNumericResult(e,n,t,16),-1}stateNumericDecimal(e,t){const n=t;for(;t=55296&&e<=57343||e>1114111?65533:null!==(t=oC.get(e))&&void 0!==t?t:e}(this.result),this.consumed),this.errors&&(e!==iC.SEMI&&this.errors.missingSemicolonAfterCharacterReference(),this.errors.validateNumericCharacterReference(this.result)),this.consumed}stateNamedEntity(e,t){const{decodeTree:n}=this;let r=n[this.treeIndex],a=(r&lC.VALUE_LENGTH)>>14;for(;t>14,0!==a){if(o===iC.SEMI)return this.emitNamedEntityData(this.treeIndex,a,this.consumed+this.excess);this.decodeMode!==pC.Strict&&(this.result=this.treeIndex,this.consumed+=this.excess,this.excess=0)}}return-1}emitNotTerminatedNamedEntity(){var e;const{result:t,decodeTree:n}=this,r=(n[t]&lC.VALUE_LENGTH)>>14;return this.emitNamedEntityData(t,r,this.consumed),null===(e=this.errors)||void 0===e||e.missingSemicolonAfterCharacterReference(),this.consumed}emitNamedEntityData(e,t,n){const{decodeTree:r}=this;return this.emitCodePoint(1===t?r[e]&~lC.VALUE_LENGTH:r[e+1],n),3===t&&this.emitCodePoint(r[e+2],n),n}end(){var e;switch(this.state){case uC.NamedEntity:return 0===this.result||this.decodeMode===pC.Attribute&&this.result!==this.treeIndex?0:this.emitNotTerminatedNamedEntity();case uC.NumericDecimal:return this.emitNumericEntity(0,2);case uC.NumericHex:return this.emitNumericEntity(0,3);case uC.NumericStart:return null===(e=this.errors)||void 0===e||e.absenceOfDigitsInNumericCharacterReference(this.consumed),0;case uC.EntityStart:return 0}}}function EC(e,t,n,r){const a=(t&lC.BRANCH_LENGTH)>>7,o=t&lC.JUMP_TABLE;if(0===a)return 0!==o&&r===o?n:-1;if(o){const t=r-o;return t<0||t>=a?-1:e[n+t]-1}let i=n,s=i+a-1;for(;i<=s;){const t=i+s>>>1,n=e[t];if(nr))return e[t+a];s=t-1}}return-1}(mC=fC||(fC={})).HTML=\"http://www.w3.org/1999/xhtml\",mC.MATHML=\"http://www.w3.org/1998/Math/MathML\",mC.SVG=\"http://www.w3.org/2000/svg\",mC.XLINK=\"http://www.w3.org/1999/xlink\",mC.XML=\"http://www.w3.org/XML/1998/namespace\",mC.XMLNS=\"http://www.w3.org/2000/xmlns/\",(vC=gC||(gC={})).TYPE=\"type\",vC.ACTION=\"action\",vC.ENCODING=\"encoding\",vC.PROMPT=\"prompt\",vC.NAME=\"name\",vC.COLOR=\"color\",vC.FACE=\"face\",vC.SIZE=\"size\",(yC=bC||(bC={})).NO_QUIRKS=\"no-quirks\",yC.QUIRKS=\"quirks\",yC.LIMITED_QUIRKS=\"limited-quirks\",(wC=OC||(OC={})).A=\"a\",wC.ADDRESS=\"address\",wC.ANNOTATION_XML=\"annotation-xml\",wC.APPLET=\"applet\",wC.AREA=\"area\",wC.ARTICLE=\"article\",wC.ASIDE=\"aside\",wC.B=\"b\",wC.BASE=\"base\",wC.BASEFONT=\"basefont\",wC.BGSOUND=\"bgsound\",wC.BIG=\"big\",wC.BLOCKQUOTE=\"blockquote\",wC.BODY=\"body\",wC.BR=\"br\",wC.BUTTON=\"button\",wC.CAPTION=\"caption\",wC.CENTER=\"center\",wC.CODE=\"code\",wC.COL=\"col\",wC.COLGROUP=\"colgroup\",wC.DD=\"dd\",wC.DESC=\"desc\",wC.DETAILS=\"details\",wC.DIALOG=\"dialog\",wC.DIR=\"dir\",wC.DIV=\"div\",wC.DL=\"dl\",wC.DT=\"dt\",wC.EM=\"em\",wC.EMBED=\"embed\",wC.FIELDSET=\"fieldset\",wC.FIGCAPTION=\"figcaption\",wC.FIGURE=\"figure\",wC.FONT=\"font\",wC.FOOTER=\"footer\",wC.FOREIGN_OBJECT=\"foreignObject\",wC.FORM=\"form\",wC.FRAME=\"frame\",wC.FRAMESET=\"frameset\",wC.H1=\"h1\",wC.H2=\"h2\",wC.H3=\"h3\",wC.H4=\"h4\",wC.H5=\"h5\",wC.H6=\"h6\",wC.HEAD=\"head\",wC.HEADER=\"header\",wC.HGROUP=\"hgroup\",wC.HR=\"hr\",wC.HTML=\"html\",wC.I=\"i\",wC.IMG=\"img\",wC.IMAGE=\"image\",wC.INPUT=\"input\",wC.IFRAME=\"iframe\",wC.KEYGEN=\"keygen\",wC.LABEL=\"label\",wC.LI=\"li\",wC.LINK=\"link\",wC.LISTING=\"listing\",wC.MAIN=\"main\",wC.MALIGNMARK=\"malignmark\",wC.MARQUEE=\"marquee\",wC.MATH=\"math\",wC.MENU=\"menu\",wC.META=\"meta\",wC.MGLYPH=\"mglyph\",wC.MI=\"mi\",wC.MO=\"mo\",wC.MN=\"mn\",wC.MS=\"ms\",wC.MTEXT=\"mtext\",wC.NAV=\"nav\",wC.NOBR=\"nobr\",wC.NOFRAMES=\"noframes\",wC.NOEMBED=\"noembed\",wC.NOSCRIPT=\"noscript\",wC.OBJECT=\"object\",wC.OL=\"ol\",wC.OPTGROUP=\"optgroup\",wC.OPTION=\"option\",wC.P=\"p\",wC.PARAM=\"param\",wC.PLAINTEXT=\"plaintext\",wC.PRE=\"pre\",wC.RB=\"rb\",wC.RP=\"rp\",wC.RT=\"rt\",wC.RTC=\"rtc\",wC.RUBY=\"ruby\",wC.S=\"s\",wC.SCRIPT=\"script\",wC.SEARCH=\"search\",wC.SECTION=\"section\",wC.SELECT=\"select\",wC.SOURCE=\"source\",wC.SMALL=\"small\",wC.SPAN=\"span\",wC.STRIKE=\"strike\",wC.STRONG=\"strong\",wC.STYLE=\"style\",wC.SUB=\"sub\",wC.SUMMARY=\"summary\",wC.SUP=\"sup\",wC.TABLE=\"table\",wC.TBODY=\"tbody\",wC.TEMPLATE=\"template\",wC.TEXTAREA=\"textarea\",wC.TFOOT=\"tfoot\",wC.TD=\"td\",wC.TH=\"th\",wC.THEAD=\"thead\",wC.TITLE=\"title\",wC.TR=\"tr\",wC.TRACK=\"track\",wC.TT=\"tt\",wC.U=\"u\",wC.UL=\"ul\",wC.SVG=\"svg\",wC.VAR=\"var\",wC.WBR=\"wbr\",wC.XMP=\"xmp\",(kC=xC||(xC={}))[kC.UNKNOWN=0]=\"UNKNOWN\",kC[kC.A=1]=\"A\",kC[kC.ADDRESS=2]=\"ADDRESS\",kC[kC.ANNOTATION_XML=3]=\"ANNOTATION_XML\",kC[kC.APPLET=4]=\"APPLET\",kC[kC.AREA=5]=\"AREA\",kC[kC.ARTICLE=6]=\"ARTICLE\",kC[kC.ASIDE=7]=\"ASIDE\",kC[kC.B=8]=\"B\",kC[kC.BASE=9]=\"BASE\",kC[kC.BASEFONT=10]=\"BASEFONT\",kC[kC.BGSOUND=11]=\"BGSOUND\",kC[kC.BIG=12]=\"BIG\",kC[kC.BLOCKQUOTE=13]=\"BLOCKQUOTE\",kC[kC.BODY=14]=\"BODY\",kC[kC.BR=15]=\"BR\",kC[kC.BUTTON=16]=\"BUTTON\",kC[kC.CAPTION=17]=\"CAPTION\",kC[kC.CENTER=18]=\"CENTER\",kC[kC.CODE=19]=\"CODE\",kC[kC.COL=20]=\"COL\",kC[kC.COLGROUP=21]=\"COLGROUP\",kC[kC.DD=22]=\"DD\",kC[kC.DESC=23]=\"DESC\",kC[kC.DETAILS=24]=\"DETAILS\",kC[kC.DIALOG=25]=\"DIALOG\",kC[kC.DIR=26]=\"DIR\",kC[kC.DIV=27]=\"DIV\",kC[kC.DL=28]=\"DL\",kC[kC.DT=29]=\"DT\",kC[kC.EM=30]=\"EM\",kC[kC.EMBED=31]=\"EMBED\",kC[kC.FIELDSET=32]=\"FIELDSET\",kC[kC.FIGCAPTION=33]=\"FIGCAPTION\",kC[kC.FIGURE=34]=\"FIGURE\",kC[kC.FONT=35]=\"FONT\",kC[kC.FOOTER=36]=\"FOOTER\",kC[kC.FOREIGN_OBJECT=37]=\"FOREIGN_OBJECT\",kC[kC.FORM=38]=\"FORM\",kC[kC.FRAME=39]=\"FRAME\",kC[kC.FRAMESET=40]=\"FRAMESET\",kC[kC.H1=41]=\"H1\",kC[kC.H2=42]=\"H2\",kC[kC.H3=43]=\"H3\",kC[kC.H4=44]=\"H4\",kC[kC.H5=45]=\"H5\",kC[kC.H6=46]=\"H6\",kC[kC.HEAD=47]=\"HEAD\",kC[kC.HEADER=48]=\"HEADER\",kC[kC.HGROUP=49]=\"HGROUP\",kC[kC.HR=50]=\"HR\",kC[kC.HTML=51]=\"HTML\",kC[kC.I=52]=\"I\",kC[kC.IMG=53]=\"IMG\",kC[kC.IMAGE=54]=\"IMAGE\",kC[kC.INPUT=55]=\"INPUT\",kC[kC.IFRAME=56]=\"IFRAME\",kC[kC.KEYGEN=57]=\"KEYGEN\",kC[kC.LABEL=58]=\"LABEL\",kC[kC.LI=59]=\"LI\",kC[kC.LINK=60]=\"LINK\",kC[kC.LISTING=61]=\"LISTING\",kC[kC.MAIN=62]=\"MAIN\",kC[kC.MALIGNMARK=63]=\"MALIGNMARK\",kC[kC.MARQUEE=64]=\"MARQUEE\",kC[kC.MATH=65]=\"MATH\",kC[kC.MENU=66]=\"MENU\",kC[kC.META=67]=\"META\",kC[kC.MGLYPH=68]=\"MGLYPH\",kC[kC.MI=69]=\"MI\",kC[kC.MO=70]=\"MO\",kC[kC.MN=71]=\"MN\",kC[kC.MS=72]=\"MS\",kC[kC.MTEXT=73]=\"MTEXT\",kC[kC.NAV=74]=\"NAV\",kC[kC.NOBR=75]=\"NOBR\",kC[kC.NOFRAMES=76]=\"NOFRAMES\",kC[kC.NOEMBED=77]=\"NOEMBED\",kC[kC.NOSCRIPT=78]=\"NOSCRIPT\",kC[kC.OBJECT=79]=\"OBJECT\",kC[kC.OL=80]=\"OL\",kC[kC.OPTGROUP=81]=\"OPTGROUP\",kC[kC.OPTION=82]=\"OPTION\",kC[kC.P=83]=\"P\",kC[kC.PARAM=84]=\"PARAM\",kC[kC.PLAINTEXT=85]=\"PLAINTEXT\",kC[kC.PRE=86]=\"PRE\",kC[kC.RB=87]=\"RB\",kC[kC.RP=88]=\"RP\",kC[kC.RT=89]=\"RT\",kC[kC.RTC=90]=\"RTC\",kC[kC.RUBY=91]=\"RUBY\",kC[kC.S=92]=\"S\",kC[kC.SCRIPT=93]=\"SCRIPT\",kC[kC.SEARCH=94]=\"SEARCH\",kC[kC.SECTION=95]=\"SECTION\",kC[kC.SELECT=96]=\"SELECT\",kC[kC.SOURCE=97]=\"SOURCE\",kC[kC.SMALL=98]=\"SMALL\",kC[kC.SPAN=99]=\"SPAN\",kC[kC.STRIKE=100]=\"STRIKE\",kC[kC.STRONG=101]=\"STRONG\",kC[kC.STYLE=102]=\"STYLE\",kC[kC.SUB=103]=\"SUB\",kC[kC.SUMMARY=104]=\"SUMMARY\",kC[kC.SUP=105]=\"SUP\",kC[kC.TABLE=106]=\"TABLE\",kC[kC.TBODY=107]=\"TBODY\",kC[kC.TEMPLATE=108]=\"TEMPLATE\",kC[kC.TEXTAREA=109]=\"TEXTAREA\",kC[kC.TFOOT=110]=\"TFOOT\",kC[kC.TD=111]=\"TD\",kC[kC.TH=112]=\"TH\",kC[kC.THEAD=113]=\"THEAD\",kC[kC.TITLE=114]=\"TITLE\",kC[kC.TR=115]=\"TR\",kC[kC.TRACK=116]=\"TRACK\",kC[kC.TT=117]=\"TT\",kC[kC.U=118]=\"U\",kC[kC.UL=119]=\"UL\",kC[kC.SVG=120]=\"SVG\",kC[kC.VAR=121]=\"VAR\",kC[kC.WBR=122]=\"WBR\",kC[kC.XMP=123]=\"XMP\";const AC=new Map([[OC.A,xC.A],[OC.ADDRESS,xC.ADDRESS],[OC.ANNOTATION_XML,xC.ANNOTATION_XML],[OC.APPLET,xC.APPLET],[OC.AREA,xC.AREA],[OC.ARTICLE,xC.ARTICLE],[OC.ASIDE,xC.ASIDE],[OC.B,xC.B],[OC.BASE,xC.BASE],[OC.BASEFONT,xC.BASEFONT],[OC.BGSOUND,xC.BGSOUND],[OC.BIG,xC.BIG],[OC.BLOCKQUOTE,xC.BLOCKQUOTE],[OC.BODY,xC.BODY],[OC.BR,xC.BR],[OC.BUTTON,xC.BUTTON],[OC.CAPTION,xC.CAPTION],[OC.CENTER,xC.CENTER],[OC.CODE,xC.CODE],[OC.COL,xC.COL],[OC.COLGROUP,xC.COLGROUP],[OC.DD,xC.DD],[OC.DESC,xC.DESC],[OC.DETAILS,xC.DETAILS],[OC.DIALOG,xC.DIALOG],[OC.DIR,xC.DIR],[OC.DIV,xC.DIV],[OC.DL,xC.DL],[OC.DT,xC.DT],[OC.EM,xC.EM],[OC.EMBED,xC.EMBED],[OC.FIELDSET,xC.FIELDSET],[OC.FIGCAPTION,xC.FIGCAPTION],[OC.FIGURE,xC.FIGURE],[OC.FONT,xC.FONT],[OC.FOOTER,xC.FOOTER],[OC.FOREIGN_OBJECT,xC.FOREIGN_OBJECT],[OC.FORM,xC.FORM],[OC.FRAME,xC.FRAME],[OC.FRAMESET,xC.FRAMESET],[OC.H1,xC.H1],[OC.H2,xC.H2],[OC.H3,xC.H3],[OC.H4,xC.H4],[OC.H5,xC.H5],[OC.H6,xC.H6],[OC.HEAD,xC.HEAD],[OC.HEADER,xC.HEADER],[OC.HGROUP,xC.HGROUP],[OC.HR,xC.HR],[OC.HTML,xC.HTML],[OC.I,xC.I],[OC.IMG,xC.IMG],[OC.IMAGE,xC.IMAGE],[OC.INPUT,xC.INPUT],[OC.IFRAME,xC.IFRAME],[OC.KEYGEN,xC.KEYGEN],[OC.LABEL,xC.LABEL],[OC.LI,xC.LI],[OC.LINK,xC.LINK],[OC.LISTING,xC.LISTING],[OC.MAIN,xC.MAIN],[OC.MALIGNMARK,xC.MALIGNMARK],[OC.MARQUEE,xC.MARQUEE],[OC.MATH,xC.MATH],[OC.MENU,xC.MENU],[OC.META,xC.META],[OC.MGLYPH,xC.MGLYPH],[OC.MI,xC.MI],[OC.MO,xC.MO],[OC.MN,xC.MN],[OC.MS,xC.MS],[OC.MTEXT,xC.MTEXT],[OC.NAV,xC.NAV],[OC.NOBR,xC.NOBR],[OC.NOFRAMES,xC.NOFRAMES],[OC.NOEMBED,xC.NOEMBED],[OC.NOSCRIPT,xC.NOSCRIPT],[OC.OBJECT,xC.OBJECT],[OC.OL,xC.OL],[OC.OPTGROUP,xC.OPTGROUP],[OC.OPTION,xC.OPTION],[OC.P,xC.P],[OC.PARAM,xC.PARAM],[OC.PLAINTEXT,xC.PLAINTEXT],[OC.PRE,xC.PRE],[OC.RB,xC.RB],[OC.RP,xC.RP],[OC.RT,xC.RT],[OC.RTC,xC.RTC],[OC.RUBY,xC.RUBY],[OC.S,xC.S],[OC.SCRIPT,xC.SCRIPT],[OC.SEARCH,xC.SEARCH],[OC.SECTION,xC.SECTION],[OC.SELECT,xC.SELECT],[OC.SOURCE,xC.SOURCE],[OC.SMALL,xC.SMALL],[OC.SPAN,xC.SPAN],[OC.STRIKE,xC.STRIKE],[OC.STRONG,xC.STRONG],[OC.STYLE,xC.STYLE],[OC.SUB,xC.SUB],[OC.SUMMARY,xC.SUMMARY],[OC.SUP,xC.SUP],[OC.TABLE,xC.TABLE],[OC.TBODY,xC.TBODY],[OC.TEMPLATE,xC.TEMPLATE],[OC.TEXTAREA,xC.TEXTAREA],[OC.TFOOT,xC.TFOOT],[OC.TD,xC.TD],[OC.TH,xC.TH],[OC.THEAD,xC.THEAD],[OC.TITLE,xC.TITLE],[OC.TR,xC.TR],[OC.TRACK,xC.TRACK],[OC.TT,xC.TT],[OC.U,xC.U],[OC.UL,xC.UL],[OC.SVG,xC.SVG],[OC.VAR,xC.VAR],[OC.WBR,xC.WBR],[OC.XMP,xC.XMP]]);function CC(e){var t;return null!==(t=AC.get(e))&&void 0!==t?t:xC.UNKNOWN}const PC=xC,$C={[fC.HTML]:new Set([PC.ADDRESS,PC.APPLET,PC.AREA,PC.ARTICLE,PC.ASIDE,PC.BASE,PC.BASEFONT,PC.BGSOUND,PC.BLOCKQUOTE,PC.BODY,PC.BR,PC.BUTTON,PC.CAPTION,PC.CENTER,PC.COL,PC.COLGROUP,PC.DD,PC.DETAILS,PC.DIR,PC.DIV,PC.DL,PC.DT,PC.EMBED,PC.FIELDSET,PC.FIGCAPTION,PC.FIGURE,PC.FOOTER,PC.FORM,PC.FRAME,PC.FRAMESET,PC.H1,PC.H2,PC.H3,PC.H4,PC.H5,PC.H6,PC.HEAD,PC.HEADER,PC.HGROUP,PC.HR,PC.HTML,PC.IFRAME,PC.IMG,PC.INPUT,PC.LI,PC.LINK,PC.LISTING,PC.MAIN,PC.MARQUEE,PC.MENU,PC.META,PC.NAV,PC.NOEMBED,PC.NOFRAMES,PC.NOSCRIPT,PC.OBJECT,PC.OL,PC.P,PC.PARAM,PC.PLAINTEXT,PC.PRE,PC.SCRIPT,PC.SECTION,PC.SELECT,PC.SOURCE,PC.STYLE,PC.SUMMARY,PC.TABLE,PC.TBODY,PC.TD,PC.TEMPLATE,PC.TEXTAREA,PC.TFOOT,PC.TH,PC.THEAD,PC.TITLE,PC.TR,PC.TRACK,PC.UL,PC.WBR,PC.XMP]),[fC.MATHML]:new Set([PC.MI,PC.MO,PC.MN,PC.MS,PC.MTEXT,PC.ANNOTATION_XML]),[fC.SVG]:new Set([PC.TITLE,PC.FOREIGN_OBJECT,PC.DESC]),[fC.XLINK]:new Set,[fC.XML]:new Set,[fC.XMLNS]:new Set},DC=new Set([PC.H1,PC.H2,PC.H3,PC.H4,PC.H5,PC.H6]);var IC,MC;OC.STYLE,OC.SCRIPT,OC.XMP,OC.IFRAME,OC.NOEMBED,OC.NOFRAMES,OC.PLAINTEXT,(MC=IC||(IC={}))[MC.DATA=0]=\"DATA\",MC[MC.RCDATA=1]=\"RCDATA\",MC[MC.RAWTEXT=2]=\"RAWTEXT\",MC[MC.SCRIPT_DATA=3]=\"SCRIPT_DATA\",MC[MC.PLAINTEXT=4]=\"PLAINTEXT\",MC[MC.TAG_OPEN=5]=\"TAG_OPEN\",MC[MC.END_TAG_OPEN=6]=\"END_TAG_OPEN\",MC[MC.TAG_NAME=7]=\"TAG_NAME\",MC[MC.RCDATA_LESS_THAN_SIGN=8]=\"RCDATA_LESS_THAN_SIGN\",MC[MC.RCDATA_END_TAG_OPEN=9]=\"RCDATA_END_TAG_OPEN\",MC[MC.RCDATA_END_TAG_NAME=10]=\"RCDATA_END_TAG_NAME\",MC[MC.RAWTEXT_LESS_THAN_SIGN=11]=\"RAWTEXT_LESS_THAN_SIGN\",MC[MC.RAWTEXT_END_TAG_OPEN=12]=\"RAWTEXT_END_TAG_OPEN\",MC[MC.RAWTEXT_END_TAG_NAME=13]=\"RAWTEXT_END_TAG_NAME\",MC[MC.SCRIPT_DATA_LESS_THAN_SIGN=14]=\"SCRIPT_DATA_LESS_THAN_SIGN\",MC[MC.SCRIPT_DATA_END_TAG_OPEN=15]=\"SCRIPT_DATA_END_TAG_OPEN\",MC[MC.SCRIPT_DATA_END_TAG_NAME=16]=\"SCRIPT_DATA_END_TAG_NAME\",MC[MC.SCRIPT_DATA_ESCAPE_START=17]=\"SCRIPT_DATA_ESCAPE_START\",MC[MC.SCRIPT_DATA_ESCAPE_START_DASH=18]=\"SCRIPT_DATA_ESCAPE_START_DASH\",MC[MC.SCRIPT_DATA_ESCAPED=19]=\"SCRIPT_DATA_ESCAPED\",MC[MC.SCRIPT_DATA_ESCAPED_DASH=20]=\"SCRIPT_DATA_ESCAPED_DASH\",MC[MC.SCRIPT_DATA_ESCAPED_DASH_DASH=21]=\"SCRIPT_DATA_ESCAPED_DASH_DASH\",MC[MC.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN=22]=\"SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN\",MC[MC.SCRIPT_DATA_ESCAPED_END_TAG_OPEN=23]=\"SCRIPT_DATA_ESCAPED_END_TAG_OPEN\",MC[MC.SCRIPT_DATA_ESCAPED_END_TAG_NAME=24]=\"SCRIPT_DATA_ESCAPED_END_TAG_NAME\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPE_START=25]=\"SCRIPT_DATA_DOUBLE_ESCAPE_START\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPED=26]=\"SCRIPT_DATA_DOUBLE_ESCAPED\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPED_DASH=27]=\"SCRIPT_DATA_DOUBLE_ESCAPED_DASH\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH=28]=\"SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN=29]=\"SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN\",MC[MC.SCRIPT_DATA_DOUBLE_ESCAPE_END=30]=\"SCRIPT_DATA_DOUBLE_ESCAPE_END\",MC[MC.BEFORE_ATTRIBUTE_NAME=31]=\"BEFORE_ATTRIBUTE_NAME\",MC[MC.ATTRIBUTE_NAME=32]=\"ATTRIBUTE_NAME\",MC[MC.AFTER_ATTRIBUTE_NAME=33]=\"AFTER_ATTRIBUTE_NAME\",MC[MC.BEFORE_ATTRIBUTE_VALUE=34]=\"BEFORE_ATTRIBUTE_VALUE\",MC[MC.ATTRIBUTE_VALUE_DOUBLE_QUOTED=35]=\"ATTRIBUTE_VALUE_DOUBLE_QUOTED\",MC[MC.ATTRIBUTE_VALUE_SINGLE_QUOTED=36]=\"ATTRIBUTE_VALUE_SINGLE_QUOTED\",MC[MC.ATTRIBUTE_VALUE_UNQUOTED=37]=\"ATTRIBUTE_VALUE_UNQUOTED\",MC[MC.AFTER_ATTRIBUTE_VALUE_QUOTED=38]=\"AFTER_ATTRIBUTE_VALUE_QUOTED\",MC[MC.SELF_CLOSING_START_TAG=39]=\"SELF_CLOSING_START_TAG\",MC[MC.BOGUS_COMMENT=40]=\"BOGUS_COMMENT\",MC[MC.MARKUP_DECLARATION_OPEN=41]=\"MARKUP_DECLARATION_OPEN\",MC[MC.COMMENT_START=42]=\"COMMENT_START\",MC[MC.COMMENT_START_DASH=43]=\"COMMENT_START_DASH\",MC[MC.COMMENT=44]=\"COMMENT\",MC[MC.COMMENT_LESS_THAN_SIGN=45]=\"COMMENT_LESS_THAN_SIGN\",MC[MC.COMMENT_LESS_THAN_SIGN_BANG=46]=\"COMMENT_LESS_THAN_SIGN_BANG\",MC[MC.COMMENT_LESS_THAN_SIGN_BANG_DASH=47]=\"COMMENT_LESS_THAN_SIGN_BANG_DASH\",MC[MC.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH=48]=\"COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH\",MC[MC.COMMENT_END_DASH=49]=\"COMMENT_END_DASH\",MC[MC.COMMENT_END=50]=\"COMMENT_END\",MC[MC.COMMENT_END_BANG=51]=\"COMMENT_END_BANG\",MC[MC.DOCTYPE=52]=\"DOCTYPE\",MC[MC.BEFORE_DOCTYPE_NAME=53]=\"BEFORE_DOCTYPE_NAME\",MC[MC.DOCTYPE_NAME=54]=\"DOCTYPE_NAME\",MC[MC.AFTER_DOCTYPE_NAME=55]=\"AFTER_DOCTYPE_NAME\",MC[MC.AFTER_DOCTYPE_PUBLIC_KEYWORD=56]=\"AFTER_DOCTYPE_PUBLIC_KEYWORD\",MC[MC.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER=57]=\"BEFORE_DOCTYPE_PUBLIC_IDENTIFIER\",MC[MC.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED=58]=\"DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED\",MC[MC.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED=59]=\"DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED\",MC[MC.AFTER_DOCTYPE_PUBLIC_IDENTIFIER=60]=\"AFTER_DOCTYPE_PUBLIC_IDENTIFIER\",MC[MC.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS=61]=\"BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS\",MC[MC.AFTER_DOCTYPE_SYSTEM_KEYWORD=62]=\"AFTER_DOCTYPE_SYSTEM_KEYWORD\",MC[MC.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER=63]=\"BEFORE_DOCTYPE_SYSTEM_IDENTIFIER\",MC[MC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED=64]=\"DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED\",MC[MC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED=65]=\"DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED\",MC[MC.AFTER_DOCTYPE_SYSTEM_IDENTIFIER=66]=\"AFTER_DOCTYPE_SYSTEM_IDENTIFIER\",MC[MC.BOGUS_DOCTYPE=67]=\"BOGUS_DOCTYPE\",MC[MC.CDATA_SECTION=68]=\"CDATA_SECTION\",MC[MC.CDATA_SECTION_BRACKET=69]=\"CDATA_SECTION_BRACKET\",MC[MC.CDATA_SECTION_END=70]=\"CDATA_SECTION_END\",MC[MC.CHARACTER_REFERENCE=71]=\"CHARACTER_REFERENCE\",MC[MC.AMBIGUOUS_AMPERSAND=72]=\"AMBIGUOUS_AMPERSAND\";const NC={DATA:IC.DATA,RCDATA:IC.RCDATA,RAWTEXT:IC.RAWTEXT,SCRIPT_DATA:IC.SCRIPT_DATA,PLAINTEXT:IC.PLAINTEXT,CDATA_SECTION:IC.CDATA_SECTION};function RC(e){return e>=ZA.LATIN_CAPITAL_A&&e<=ZA.LATIN_CAPITAL_Z}function LC(e){return function(e){return e>=ZA.LATIN_SMALL_A&&e<=ZA.LATIN_SMALL_Z}(e)||RC(e)}function BC(e){return LC(e)||function(e){return e>=ZA.DIGIT_0&&e<=ZA.DIGIT_9}(e)}function jC(e){return e+32}function UC(e){return e===ZA.SPACE||e===ZA.LINE_FEED||e===ZA.TABULATION||e===ZA.FORM_FEED}function zC(e){return UC(e)||e===ZA.SOLIDUS||e===ZA.GREATER_THAN_SIGN}class FC{constructor(e,t){this.options=e,this.handler=t,this.paused=!1,this.inLoop=!1,this.inForeignNode=!1,this.lastStartTagName=\"\",this.active=!1,this.state=IC.DATA,this.returnState=IC.DATA,this.entityStartPos=0,this.consumedAfterSnapshot=-1,this.currentCharacterToken=null,this.currentToken=null,this.currentAttr={name:\"\",value:\"\"},this.preprocessor=new nC(t),this.currentLocation=this.getCurrentLocation(-1),this.entityDecoder=new TC(aC,((e,t)=>{this.preprocessor.pos=this.entityStartPos+t-1,this._flushCodePointConsumedAsCharacterReference(e)}),t.onParseError?{missingSemicolonAfterCharacterReference:()=>{this._err(KA.missingSemicolonAfterCharacterReference,1)},absenceOfDigitsInNumericCharacterReference:e=>{this._err(KA.absenceOfDigitsInNumericCharacterReference,this.entityStartPos-this.preprocessor.pos+e)},validateNumericCharacterReference:e=>{const t=function(e){return e===ZA.NULL?KA.nullCharacterReference:e>1114111?KA.characterReferenceOutsideUnicodeRange:XA(e)?KA.surrogateCharacterReference:YA(e)?KA.noncharacterCharacterReference:GA(e)||e===ZA.CARRIAGE_RETURN?KA.controlCharacterReference:null}(e);t&&this._err(t,1)}}:void 0)}_err(e,t=0){var n,r;null===(r=(n=this.handler).onParseError)||void 0===r||r.call(n,this.preprocessor.getError(e,t))}getCurrentLocation(e){return this.options.sourceCodeLocationInfo?{startLine:this.preprocessor.line,startCol:this.preprocessor.col-e,startOffset:this.preprocessor.offset-e,endLine:-1,endCol:-1,endOffset:-1}:null}_runParsingLoop(){if(!this.inLoop){for(this.inLoop=!0;this.active&&!this.paused;){this.consumedAfterSnapshot=0;const e=this._consume();this._ensureHibernation()||this._callState(e)}this.inLoop=!1}}pause(){this.paused=!0}resume(e){if(!this.paused)throw new Error(\"Parser was already resumed\");this.paused=!1,this.inLoop||(this._runParsingLoop(),this.paused||null==e||e())}write(e,t,n){this.active=!0,this.preprocessor.write(e,t),this._runParsingLoop(),this.paused||null==n||n()}insertHtmlAtCurrentPos(e){this.active=!0,this.preprocessor.insertHtmlAtCurrentPos(e),this._runParsingLoop()}_ensureHibernation(){return!!this.preprocessor.endOfChunkHit&&(this.preprocessor.retreat(this.consumedAfterSnapshot),this.consumedAfterSnapshot=0,this.active=!1,!0)}_consume(){return this.consumedAfterSnapshot++,this.preprocessor.advance()}_advanceBy(e){this.consumedAfterSnapshot+=e;for(let t=0;t0&&this._err(KA.endTagWithAttributes),e.selfClosing&&this._err(KA.endTagWithTrailingSolidus),this.handler.onEndTag(e)),this.preprocessor.dropParsedChunk()}emitCurrentComment(e){this.prepareToken(e),this.handler.onComment(e),this.preprocessor.dropParsedChunk()}emitCurrentDoctype(e){this.prepareToken(e),this.handler.onDoctype(e),this.preprocessor.dropParsedChunk()}_emitCurrentCharacterToken(e){if(this.currentCharacterToken){switch(e&&this.currentCharacterToken.location&&(this.currentCharacterToken.location.endLine=e.startLine,this.currentCharacterToken.location.endCol=e.startCol,this.currentCharacterToken.location.endOffset=e.startOffset),this.currentCharacterToken.type){case eC.CHARACTER:this.handler.onCharacter(this.currentCharacterToken);break;case eC.NULL_CHARACTER:this.handler.onNullCharacter(this.currentCharacterToken);break;case eC.WHITESPACE_CHARACTER:this.handler.onWhitespaceCharacter(this.currentCharacterToken)}this.currentCharacterToken=null}}_emitEOFToken(){const e=this.getCurrentLocation(0);e&&(e.endLine=e.startLine,e.endCol=e.startCol,e.endOffset=e.startOffset),this._emitCurrentCharacterToken(e),this.handler.onEof({type:eC.EOF,location:e}),this.active=!1}_appendCharToCurrentCharacterToken(e,t){if(this.currentCharacterToken){if(this.currentCharacterToken.type===e)return void(this.currentCharacterToken.chars+=t);this.currentLocation=this.getCurrentLocation(0),this._emitCurrentCharacterToken(this.currentLocation),this.preprocessor.dropParsedChunk()}this._createCharacterToken(e,t)}_emitCodePoint(e){const t=UC(e)?eC.WHITESPACE_CHARACTER:e===ZA.NULL?eC.NULL_CHARACTER:eC.CHARACTER;this._appendCharToCurrentCharacterToken(t,String.fromCodePoint(e))}_emitChars(e){this._appendCharToCurrentCharacterToken(eC.CHARACTER,e)}_startCharacterReference(){this.returnState=this.state,this.state=IC.CHARACTER_REFERENCE,this.entityStartPos=this.preprocessor.pos,this.entityDecoder.startEntity(this._isCharacterReferenceInAttribute()?pC.Attribute:pC.Legacy)}_isCharacterReferenceInAttribute(){return this.returnState===IC.ATTRIBUTE_VALUE_DOUBLE_QUOTED||this.returnState===IC.ATTRIBUTE_VALUE_SINGLE_QUOTED||this.returnState===IC.ATTRIBUTE_VALUE_UNQUOTED}_flushCodePointConsumedAsCharacterReference(e){this._isCharacterReferenceInAttribute()?this.currentAttr.value+=String.fromCodePoint(e):this._emitCodePoint(e)}_callState(e){switch(this.state){case IC.DATA:this._stateData(e);break;case IC.RCDATA:this._stateRcdata(e);break;case IC.RAWTEXT:this._stateRawtext(e);break;case IC.SCRIPT_DATA:this._stateScriptData(e);break;case IC.PLAINTEXT:this._statePlaintext(e);break;case IC.TAG_OPEN:this._stateTagOpen(e);break;case IC.END_TAG_OPEN:this._stateEndTagOpen(e);break;case IC.TAG_NAME:this._stateTagName(e);break;case IC.RCDATA_LESS_THAN_SIGN:this._stateRcdataLessThanSign(e);break;case IC.RCDATA_END_TAG_OPEN:this._stateRcdataEndTagOpen(e);break;case IC.RCDATA_END_TAG_NAME:this._stateRcdataEndTagName(e);break;case IC.RAWTEXT_LESS_THAN_SIGN:this._stateRawtextLessThanSign(e);break;case IC.RAWTEXT_END_TAG_OPEN:this._stateRawtextEndTagOpen(e);break;case IC.RAWTEXT_END_TAG_NAME:this._stateRawtextEndTagName(e);break;case IC.SCRIPT_DATA_LESS_THAN_SIGN:this._stateScriptDataLessThanSign(e);break;case IC.SCRIPT_DATA_END_TAG_OPEN:this._stateScriptDataEndTagOpen(e);break;case IC.SCRIPT_DATA_END_TAG_NAME:this._stateScriptDataEndTagName(e);break;case IC.SCRIPT_DATA_ESCAPE_START:this._stateScriptDataEscapeStart(e);break;case IC.SCRIPT_DATA_ESCAPE_START_DASH:this._stateScriptDataEscapeStartDash(e);break;case IC.SCRIPT_DATA_ESCAPED:this._stateScriptDataEscaped(e);break;case IC.SCRIPT_DATA_ESCAPED_DASH:this._stateScriptDataEscapedDash(e);break;case IC.SCRIPT_DATA_ESCAPED_DASH_DASH:this._stateScriptDataEscapedDashDash(e);break;case IC.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:this._stateScriptDataEscapedLessThanSign(e);break;case IC.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:this._stateScriptDataEscapedEndTagOpen(e);break;case IC.SCRIPT_DATA_ESCAPED_END_TAG_NAME:this._stateScriptDataEscapedEndTagName(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPE_START:this._stateScriptDataDoubleEscapeStart(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPED:this._stateScriptDataDoubleEscaped(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPED_DASH:this._stateScriptDataDoubleEscapedDash(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH:this._stateScriptDataDoubleEscapedDashDash(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN:this._stateScriptDataDoubleEscapedLessThanSign(e);break;case IC.SCRIPT_DATA_DOUBLE_ESCAPE_END:this._stateScriptDataDoubleEscapeEnd(e);break;case IC.BEFORE_ATTRIBUTE_NAME:this._stateBeforeAttributeName(e);break;case IC.ATTRIBUTE_NAME:this._stateAttributeName(e);break;case IC.AFTER_ATTRIBUTE_NAME:this._stateAfterAttributeName(e);break;case IC.BEFORE_ATTRIBUTE_VALUE:this._stateBeforeAttributeValue(e);break;case IC.ATTRIBUTE_VALUE_DOUBLE_QUOTED:this._stateAttributeValueDoubleQuoted(e);break;case IC.ATTRIBUTE_VALUE_SINGLE_QUOTED:this._stateAttributeValueSingleQuoted(e);break;case IC.ATTRIBUTE_VALUE_UNQUOTED:this._stateAttributeValueUnquoted(e);break;case IC.AFTER_ATTRIBUTE_VALUE_QUOTED:this._stateAfterAttributeValueQuoted(e);break;case IC.SELF_CLOSING_START_TAG:this._stateSelfClosingStartTag(e);break;case IC.BOGUS_COMMENT:this._stateBogusComment(e);break;case IC.MARKUP_DECLARATION_OPEN:this._stateMarkupDeclarationOpen(e);break;case IC.COMMENT_START:this._stateCommentStart(e);break;case IC.COMMENT_START_DASH:this._stateCommentStartDash(e);break;case IC.COMMENT:this._stateComment(e);break;case IC.COMMENT_LESS_THAN_SIGN:this._stateCommentLessThanSign(e);break;case IC.COMMENT_LESS_THAN_SIGN_BANG:this._stateCommentLessThanSignBang(e);break;case IC.COMMENT_LESS_THAN_SIGN_BANG_DASH:this._stateCommentLessThanSignBangDash(e);break;case IC.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH:this._stateCommentLessThanSignBangDashDash(e);break;case IC.COMMENT_END_DASH:this._stateCommentEndDash(e);break;case IC.COMMENT_END:this._stateCommentEnd(e);break;case IC.COMMENT_END_BANG:this._stateCommentEndBang(e);break;case IC.DOCTYPE:this._stateDoctype(e);break;case IC.BEFORE_DOCTYPE_NAME:this._stateBeforeDoctypeName(e);break;case IC.DOCTYPE_NAME:this._stateDoctypeName(e);break;case IC.AFTER_DOCTYPE_NAME:this._stateAfterDoctypeName(e);break;case IC.AFTER_DOCTYPE_PUBLIC_KEYWORD:this._stateAfterDoctypePublicKeyword(e);break;case IC.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:this._stateBeforeDoctypePublicIdentifier(e);break;case IC.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:this._stateDoctypePublicIdentifierDoubleQuoted(e);break;case IC.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:this._stateDoctypePublicIdentifierSingleQuoted(e);break;case IC.AFTER_DOCTYPE_PUBLIC_IDENTIFIER:this._stateAfterDoctypePublicIdentifier(e);break;case IC.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:this._stateBetweenDoctypePublicAndSystemIdentifiers(e);break;case IC.AFTER_DOCTYPE_SYSTEM_KEYWORD:this._stateAfterDoctypeSystemKeyword(e);break;case IC.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:this._stateBeforeDoctypeSystemIdentifier(e);break;case IC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:this._stateDoctypeSystemIdentifierDoubleQuoted(e);break;case IC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:this._stateDoctypeSystemIdentifierSingleQuoted(e);break;case IC.AFTER_DOCTYPE_SYSTEM_IDENTIFIER:this._stateAfterDoctypeSystemIdentifier(e);break;case IC.BOGUS_DOCTYPE:this._stateBogusDoctype(e);break;case IC.CDATA_SECTION:this._stateCdataSection(e);break;case IC.CDATA_SECTION_BRACKET:this._stateCdataSectionBracket(e);break;case IC.CDATA_SECTION_END:this._stateCdataSectionEnd(e);break;case IC.CHARACTER_REFERENCE:this._stateCharacterReference();break;case IC.AMBIGUOUS_AMPERSAND:this._stateAmbiguousAmpersand(e);break;default:throw new Error(\"Unknown state\")}}_stateData(e){switch(e){case ZA.LESS_THAN_SIGN:this.state=IC.TAG_OPEN;break;case ZA.AMPERSAND:this._startCharacterReference();break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._emitCodePoint(e);break;case ZA.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateRcdata(e){switch(e){case ZA.AMPERSAND:this._startCharacterReference();break;case ZA.LESS_THAN_SIGN:this.state=IC.RCDATA_LESS_THAN_SIGN;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._emitChars(QA);break;case ZA.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateRawtext(e){switch(e){case ZA.LESS_THAN_SIGN:this.state=IC.RAWTEXT_LESS_THAN_SIGN;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._emitChars(QA);break;case ZA.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateScriptData(e){switch(e){case ZA.LESS_THAN_SIGN:this.state=IC.SCRIPT_DATA_LESS_THAN_SIGN;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._emitChars(QA);break;case ZA.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_statePlaintext(e){switch(e){case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._emitChars(QA);break;case ZA.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateTagOpen(e){if(LC(e))this._createStartTagToken(),this.state=IC.TAG_NAME,this._stateTagName(e);else switch(e){case ZA.EXCLAMATION_MARK:this.state=IC.MARKUP_DECLARATION_OPEN;break;case ZA.SOLIDUS:this.state=IC.END_TAG_OPEN;break;case ZA.QUESTION_MARK:this._err(KA.unexpectedQuestionMarkInsteadOfTagName),this._createCommentToken(1),this.state=IC.BOGUS_COMMENT,this._stateBogusComment(e);break;case ZA.EOF:this._err(KA.eofBeforeTagName),this._emitChars(\"<\"),this._emitEOFToken();break;default:this._err(KA.invalidFirstCharacterOfTagName),this._emitChars(\"<\"),this.state=IC.DATA,this._stateData(e)}}_stateEndTagOpen(e){if(LC(e))this._createEndTagToken(),this.state=IC.TAG_NAME,this._stateTagName(e);else switch(e){case ZA.GREATER_THAN_SIGN:this._err(KA.missingEndTagName),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofBeforeTagName),this._emitChars(\"\");break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.state=IC.SCRIPT_DATA_ESCAPED,this._emitChars(QA);break;case ZA.EOF:this._err(KA.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=IC.SCRIPT_DATA_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataEscapedLessThanSign(e){e===ZA.SOLIDUS?this.state=IC.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:LC(e)?(this._emitChars(\"<\"),this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPE_START,this._stateScriptDataDoubleEscapeStart(e)):(this._emitChars(\"<\"),this.state=IC.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataEscapedEndTagOpen(e){LC(e)?(this.state=IC.SCRIPT_DATA_ESCAPED_END_TAG_NAME,this._stateScriptDataEscapedEndTagName(e)):(this._emitChars(\"\");break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitChars(QA);break;case ZA.EOF:this._err(KA.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataDoubleEscapedLessThanSign(e){e===ZA.SOLIDUS?(this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPE_END,this._emitChars(\"/\")):(this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPED,this._stateScriptDataDoubleEscaped(e))}_stateScriptDataDoubleEscapeEnd(e){if(this.preprocessor.startsWith(WA,!1)&&zC(this.preprocessor.peek(6))){this._emitCodePoint(e);for(let e=0;e<6;e++)this._emitCodePoint(this._consume());this.state=IC.SCRIPT_DATA_ESCAPED}else this._ensureHibernation()||(this.state=IC.SCRIPT_DATA_DOUBLE_ESCAPED,this._stateScriptDataDoubleEscaped(e))}_stateBeforeAttributeName(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.SOLIDUS:case ZA.GREATER_THAN_SIGN:case ZA.EOF:this.state=IC.AFTER_ATTRIBUTE_NAME,this._stateAfterAttributeName(e);break;case ZA.EQUALS_SIGN:this._err(KA.unexpectedEqualsSignBeforeAttributeName),this._createAttr(\"=\"),this.state=IC.ATTRIBUTE_NAME;break;default:this._createAttr(\"\"),this.state=IC.ATTRIBUTE_NAME,this._stateAttributeName(e)}}_stateAttributeName(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:case ZA.SOLIDUS:case ZA.GREATER_THAN_SIGN:case ZA.EOF:this._leaveAttrName(),this.state=IC.AFTER_ATTRIBUTE_NAME,this._stateAfterAttributeName(e);break;case ZA.EQUALS_SIGN:this._leaveAttrName(),this.state=IC.BEFORE_ATTRIBUTE_VALUE;break;case ZA.QUOTATION_MARK:case ZA.APOSTROPHE:case ZA.LESS_THAN_SIGN:this._err(KA.unexpectedCharacterInAttributeName),this.currentAttr.name+=String.fromCodePoint(e);break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.currentAttr.name+=QA;break;default:this.currentAttr.name+=String.fromCodePoint(RC(e)?jC(e):e)}}_stateAfterAttributeName(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.SOLIDUS:this.state=IC.SELF_CLOSING_START_TAG;break;case ZA.EQUALS_SIGN:this.state=IC.BEFORE_ATTRIBUTE_VALUE;break;case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentTagToken();break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this._createAttr(\"\"),this.state=IC.ATTRIBUTE_NAME,this._stateAttributeName(e)}}_stateBeforeAttributeValue(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.QUOTATION_MARK:this.state=IC.ATTRIBUTE_VALUE_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:this.state=IC.ATTRIBUTE_VALUE_SINGLE_QUOTED;break;case ZA.GREATER_THAN_SIGN:this._err(KA.missingAttributeValue),this.state=IC.DATA,this.emitCurrentTagToken();break;default:this.state=IC.ATTRIBUTE_VALUE_UNQUOTED,this._stateAttributeValueUnquoted(e)}}_stateAttributeValueDoubleQuoted(e){switch(e){case ZA.QUOTATION_MARK:this.state=IC.AFTER_ATTRIBUTE_VALUE_QUOTED;break;case ZA.AMPERSAND:this._startCharacterReference();break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.currentAttr.value+=QA;break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAttributeValueSingleQuoted(e){switch(e){case ZA.APOSTROPHE:this.state=IC.AFTER_ATTRIBUTE_VALUE_QUOTED;break;case ZA.AMPERSAND:this._startCharacterReference();break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.currentAttr.value+=QA;break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAttributeValueUnquoted(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this._leaveAttrValue(),this.state=IC.BEFORE_ATTRIBUTE_NAME;break;case ZA.AMPERSAND:this._startCharacterReference();break;case ZA.GREATER_THAN_SIGN:this._leaveAttrValue(),this.state=IC.DATA,this.emitCurrentTagToken();break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this.currentAttr.value+=QA;break;case ZA.QUOTATION_MARK:case ZA.APOSTROPHE:case ZA.LESS_THAN_SIGN:case ZA.EQUALS_SIGN:case ZA.GRAVE_ACCENT:this._err(KA.unexpectedCharacterInUnquotedAttributeValue),this.currentAttr.value+=String.fromCodePoint(e);break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAfterAttributeValueQuoted(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this._leaveAttrValue(),this.state=IC.BEFORE_ATTRIBUTE_NAME;break;case ZA.SOLIDUS:this._leaveAttrValue(),this.state=IC.SELF_CLOSING_START_TAG;break;case ZA.GREATER_THAN_SIGN:this._leaveAttrValue(),this.state=IC.DATA,this.emitCurrentTagToken();break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this._err(KA.missingWhitespaceBetweenAttributes),this.state=IC.BEFORE_ATTRIBUTE_NAME,this._stateBeforeAttributeName(e)}}_stateSelfClosingStartTag(e){switch(e){case ZA.GREATER_THAN_SIGN:this.currentToken.selfClosing=!0,this.state=IC.DATA,this.emitCurrentTagToken();break;case ZA.EOF:this._err(KA.eofInTag),this._emitEOFToken();break;default:this._err(KA.unexpectedSolidusInTag),this.state=IC.BEFORE_ATTRIBUTE_NAME,this._stateBeforeAttributeName(e)}}_stateBogusComment(e){const t=this.currentToken;switch(e){case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentComment(t);break;case ZA.EOF:this.emitCurrentComment(t),this._emitEOFToken();break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.data+=QA;break;default:t.data+=String.fromCodePoint(e)}}_stateMarkupDeclarationOpen(e){this._consumeSequenceIfMatch(\"--\",!0)?(this._createCommentToken(3),this.state=IC.COMMENT_START):this._consumeSequenceIfMatch(qA,!1)?(this.currentLocation=this.getCurrentLocation(8),this.state=IC.DOCTYPE):this._consumeSequenceIfMatch(VA,!0)?this.inForeignNode?this.state=IC.CDATA_SECTION:(this._err(KA.cdataInHtmlContent),this._createCommentToken(8),this.currentToken.data=\"[CDATA[\",this.state=IC.BOGUS_COMMENT):this._ensureHibernation()||(this._err(KA.incorrectlyOpenedComment),this._createCommentToken(2),this.state=IC.BOGUS_COMMENT,this._stateBogusComment(e))}_stateCommentStart(e){switch(e){case ZA.HYPHEN_MINUS:this.state=IC.COMMENT_START_DASH;break;case ZA.GREATER_THAN_SIGN:{this._err(KA.abruptClosingOfEmptyComment),this.state=IC.DATA;const e=this.currentToken;this.emitCurrentComment(e);break}default:this.state=IC.COMMENT,this._stateComment(e)}}_stateCommentStartDash(e){const t=this.currentToken;switch(e){case ZA.HYPHEN_MINUS:this.state=IC.COMMENT_END;break;case ZA.GREATER_THAN_SIGN:this._err(KA.abruptClosingOfEmptyComment),this.state=IC.DATA,this.emitCurrentComment(t);break;case ZA.EOF:this._err(KA.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"-\",this.state=IC.COMMENT,this._stateComment(e)}}_stateComment(e){const t=this.currentToken;switch(e){case ZA.HYPHEN_MINUS:this.state=IC.COMMENT_END_DASH;break;case ZA.LESS_THAN_SIGN:t.data+=\"<\",this.state=IC.COMMENT_LESS_THAN_SIGN;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.data+=QA;break;case ZA.EOF:this._err(KA.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=String.fromCodePoint(e)}}_stateCommentLessThanSign(e){const t=this.currentToken;switch(e){case ZA.EXCLAMATION_MARK:t.data+=\"!\",this.state=IC.COMMENT_LESS_THAN_SIGN_BANG;break;case ZA.LESS_THAN_SIGN:t.data+=\"<\";break;default:this.state=IC.COMMENT,this._stateComment(e)}}_stateCommentLessThanSignBang(e){e===ZA.HYPHEN_MINUS?this.state=IC.COMMENT_LESS_THAN_SIGN_BANG_DASH:(this.state=IC.COMMENT,this._stateComment(e))}_stateCommentLessThanSignBangDash(e){e===ZA.HYPHEN_MINUS?this.state=IC.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH:(this.state=IC.COMMENT_END_DASH,this._stateCommentEndDash(e))}_stateCommentLessThanSignBangDashDash(e){e!==ZA.GREATER_THAN_SIGN&&e!==ZA.EOF&&this._err(KA.nestedComment),this.state=IC.COMMENT_END,this._stateCommentEnd(e)}_stateCommentEndDash(e){const t=this.currentToken;switch(e){case ZA.HYPHEN_MINUS:this.state=IC.COMMENT_END;break;case ZA.EOF:this._err(KA.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"-\",this.state=IC.COMMENT,this._stateComment(e)}}_stateCommentEnd(e){const t=this.currentToken;switch(e){case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentComment(t);break;case ZA.EXCLAMATION_MARK:this.state=IC.COMMENT_END_BANG;break;case ZA.HYPHEN_MINUS:t.data+=\"-\";break;case ZA.EOF:this._err(KA.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"--\",this.state=IC.COMMENT,this._stateComment(e)}}_stateCommentEndBang(e){const t=this.currentToken;switch(e){case ZA.HYPHEN_MINUS:t.data+=\"--!\",this.state=IC.COMMENT_END_DASH;break;case ZA.GREATER_THAN_SIGN:this._err(KA.incorrectlyClosedComment),this.state=IC.DATA,this.emitCurrentComment(t);break;case ZA.EOF:this._err(KA.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"--!\",this.state=IC.COMMENT,this._stateComment(e)}}_stateDoctype(e){switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this.state=IC.BEFORE_DOCTYPE_NAME;break;case ZA.GREATER_THAN_SIGN:this.state=IC.BEFORE_DOCTYPE_NAME,this._stateBeforeDoctypeName(e);break;case ZA.EOF:{this._err(KA.eofInDoctype),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this._emitEOFToken();break}default:this._err(KA.missingWhitespaceBeforeDoctypeName),this.state=IC.BEFORE_DOCTYPE_NAME,this._stateBeforeDoctypeName(e)}}_stateBeforeDoctypeName(e){if(RC(e))this._createDoctypeToken(String.fromCharCode(jC(e))),this.state=IC.DOCTYPE_NAME;else switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),this._createDoctypeToken(QA),this.state=IC.DOCTYPE_NAME;break;case ZA.GREATER_THAN_SIGN:{this._err(KA.missingDoctypeName),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this.state=IC.DATA;break}case ZA.EOF:{this._err(KA.eofInDoctype),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this._emitEOFToken();break}default:this._createDoctypeToken(String.fromCodePoint(e)),this.state=IC.DOCTYPE_NAME}}_stateDoctypeName(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this.state=IC.AFTER_DOCTYPE_NAME;break;case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.name+=QA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.name+=String.fromCodePoint(RC(e)?jC(e):e)}}_stateAfterDoctypeName(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._consumeSequenceIfMatch(\"public\",!1)?this.state=IC.AFTER_DOCTYPE_PUBLIC_KEYWORD:this._consumeSequenceIfMatch(\"system\",!1)?this.state=IC.AFTER_DOCTYPE_SYSTEM_KEYWORD:this._ensureHibernation()||(this._err(KA.invalidCharacterSequenceAfterDoctypeName),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e))}}_stateAfterDoctypePublicKeyword(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this.state=IC.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER;break;case ZA.QUOTATION_MARK:this._err(KA.missingWhitespaceAfterDoctypePublicKeyword),t.publicId=\"\",this.state=IC.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:this._err(KA.missingWhitespaceAfterDoctypePublicKeyword),t.publicId=\"\",this.state=IC.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;break;case ZA.GREATER_THAN_SIGN:this._err(KA.missingDoctypePublicIdentifier),t.forceQuirks=!0,this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypePublicIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBeforeDoctypePublicIdentifier(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.QUOTATION_MARK:t.publicId=\"\",this.state=IC.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:t.publicId=\"\",this.state=IC.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;break;case ZA.GREATER_THAN_SIGN:this._err(KA.missingDoctypePublicIdentifier),t.forceQuirks=!0,this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypePublicIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateDoctypePublicIdentifierDoubleQuoted(e){const t=this.currentToken;switch(e){case ZA.QUOTATION_MARK:this.state=IC.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.publicId+=QA;break;case ZA.GREATER_THAN_SIGN:this._err(KA.abruptDoctypePublicIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.publicId+=String.fromCodePoint(e)}}_stateDoctypePublicIdentifierSingleQuoted(e){const t=this.currentToken;switch(e){case ZA.APOSTROPHE:this.state=IC.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.publicId+=QA;break;case ZA.GREATER_THAN_SIGN:this._err(KA.abruptDoctypePublicIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.publicId+=String.fromCodePoint(e)}}_stateAfterDoctypePublicIdentifier(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this.state=IC.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS;break;case ZA.GREATER_THAN_SIGN:this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.QUOTATION_MARK:this._err(KA.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:this._err(KA.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBetweenDoctypePublicAndSystemIdentifiers(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.QUOTATION_MARK:t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateAfterDoctypeSystemKeyword(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:this.state=IC.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER;break;case ZA.QUOTATION_MARK:this._err(KA.missingWhitespaceAfterDoctypeSystemKeyword),t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:this._err(KA.missingWhitespaceAfterDoctypeSystemKeyword),t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case ZA.GREATER_THAN_SIGN:this._err(KA.missingDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBeforeDoctypeSystemIdentifier(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.QUOTATION_MARK:t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case ZA.APOSTROPHE:t.systemId=\"\",this.state=IC.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case ZA.GREATER_THAN_SIGN:this._err(KA.missingDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.DATA,this.emitCurrentDoctype(t);break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateDoctypeSystemIdentifierDoubleQuoted(e){const t=this.currentToken;switch(e){case ZA.QUOTATION_MARK:this.state=IC.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.systemId+=QA;break;case ZA.GREATER_THAN_SIGN:this._err(KA.abruptDoctypeSystemIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.systemId+=String.fromCodePoint(e)}}_stateDoctypeSystemIdentifierSingleQuoted(e){const t=this.currentToken;switch(e){case ZA.APOSTROPHE:this.state=IC.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter),t.systemId+=QA;break;case ZA.GREATER_THAN_SIGN:this._err(KA.abruptDoctypeSystemIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.systemId+=String.fromCodePoint(e)}}_stateAfterDoctypeSystemIdentifier(e){const t=this.currentToken;switch(e){case ZA.SPACE:case ZA.LINE_FEED:case ZA.TABULATION:case ZA.FORM_FEED:break;case ZA.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.EOF:this._err(KA.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(KA.unexpectedCharacterAfterDoctypeSystemIdentifier),this.state=IC.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBogusDoctype(e){const t=this.currentToken;switch(e){case ZA.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=IC.DATA;break;case ZA.NULL:this._err(KA.unexpectedNullCharacter);break;case ZA.EOF:this.emitCurrentDoctype(t),this._emitEOFToken()}}_stateCdataSection(e){switch(e){case ZA.RIGHT_SQUARE_BRACKET:this.state=IC.CDATA_SECTION_BRACKET;break;case ZA.EOF:this._err(KA.eofInCdata),this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateCdataSectionBracket(e){e===ZA.RIGHT_SQUARE_BRACKET?this.state=IC.CDATA_SECTION_END:(this._emitChars(\"]\"),this.state=IC.CDATA_SECTION,this._stateCdataSection(e))}_stateCdataSectionEnd(e){switch(e){case ZA.GREATER_THAN_SIGN:this.state=IC.DATA;break;case ZA.RIGHT_SQUARE_BRACKET:this._emitChars(\"]\");break;default:this._emitChars(\"]]\"),this.state=IC.CDATA_SECTION,this._stateCdataSection(e)}}_stateCharacterReference(){let e=this.entityDecoder.write(this.preprocessor.html,this.preprocessor.pos);if(e<0){if(!this.preprocessor.lastChunkWritten)return this.active=!1,this.preprocessor.pos=this.preprocessor.html.length-1,this.consumedAfterSnapshot=0,void(this.preprocessor.endOfChunkHit=!0);e=this.entityDecoder.end()}0===e?(this.preprocessor.pos=this.entityStartPos,this._flushCodePointConsumedAsCharacterReference(ZA.AMPERSAND),this.state=!this._isCharacterReferenceInAttribute()&&BC(this.preprocessor.peek(1))?IC.AMBIGUOUS_AMPERSAND:this.returnState):this.state=this.returnState}_stateAmbiguousAmpersand(e){BC(e)?this._flushCodePointConsumedAsCharacterReference(e):(e===ZA.SEMICOLON&&this._err(KA.unknownNamedCharacterReference),this.state=this.returnState,this._callState(e))}}const QC=new Set([xC.DD,xC.DT,xC.LI,xC.OPTGROUP,xC.OPTION,xC.P,xC.RB,xC.RP,xC.RT,xC.RTC]),ZC=new Set([...QC,xC.CAPTION,xC.COLGROUP,xC.TBODY,xC.TD,xC.TFOOT,xC.TH,xC.THEAD,xC.TR]),HC=new Set([xC.APPLET,xC.CAPTION,xC.HTML,xC.MARQUEE,xC.OBJECT,xC.TABLE,xC.TD,xC.TEMPLATE,xC.TH]),VC=new Set([...HC,xC.OL,xC.UL]),qC=new Set([...HC,xC.BUTTON]),WC=new Set([xC.ANNOTATION_XML,xC.MI,xC.MN,xC.MO,xC.MS,xC.MTEXT]),XC=new Set([xC.DESC,xC.FOREIGN_OBJECT,xC.TITLE]),GC=new Set([xC.TR,xC.TEMPLATE,xC.HTML]),YC=new Set([xC.TBODY,xC.TFOOT,xC.THEAD,xC.TEMPLATE,xC.HTML]),KC=new Set([xC.TABLE,xC.TEMPLATE,xC.HTML]),JC=new Set([xC.TD,xC.TH]);class eP{get currentTmplContentOrNode(){return this._isInTemplate()?this.treeAdapter.getTemplateContent(this.current):this.current}constructor(e,t,n){this.treeAdapter=t,this.handler=n,this.items=[],this.tagIDs=[],this.stackTop=-1,this.tmplCount=0,this.currentTagId=xC.UNKNOWN,this.current=e}_indexOf(e){return this.items.lastIndexOf(e,this.stackTop)}_isInTemplate(){return this.currentTagId===xC.TEMPLATE&&this.treeAdapter.getNamespaceURI(this.current)===fC.HTML}_updateCurrentElement(){this.current=this.items[this.stackTop],this.currentTagId=this.tagIDs[this.stackTop]}push(e,t){this.stackTop++,this.items[this.stackTop]=e,this.current=e,this.tagIDs[this.stackTop]=t,this.currentTagId=t,this._isInTemplate()&&this.tmplCount++,this.handler.onItemPush(e,t,!0)}pop(){const e=this.current;this.tmplCount>0&&this._isInTemplate()&&this.tmplCount--,this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!0)}replace(e,t){const n=this._indexOf(e);this.items[n]=t,n===this.stackTop&&(this.current=t)}insertAfter(e,t,n){const r=this._indexOf(e)+1;this.items.splice(r,0,t),this.tagIDs.splice(r,0,n),this.stackTop++,r===this.stackTop&&this._updateCurrentElement(),this.current&&void 0!==this.currentTagId&&this.handler.onItemPush(this.current,this.currentTagId,r===this.stackTop)}popUntilTagNamePopped(e){let t=this.stackTop+1;do{t=this.tagIDs.lastIndexOf(e,t-1)}while(t>0&&this.treeAdapter.getNamespaceURI(this.items[t])!==fC.HTML);this.shortenToLength(Math.max(t,0))}shortenToLength(e){for(;this.stackTop>=e;){const t=this.current;this.tmplCount>0&&this._isInTemplate()&&(this.tmplCount-=1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(t,this.stackTop=0;n--)if(e.has(this.tagIDs[n])&&this.treeAdapter.getNamespaceURI(this.items[n])===t)return n;return-1}clearBackTo(e,t){const n=this._indexOfTagNames(e,t);this.shortenToLength(n+1)}clearBackToTableContext(){this.clearBackTo(KC,fC.HTML)}clearBackToTableBodyContext(){this.clearBackTo(YC,fC.HTML)}clearBackToTableRowContext(){this.clearBackTo(GC,fC.HTML)}remove(e){const t=this._indexOf(e);t>=0&&(t===this.stackTop?this.pop():(this.items.splice(t,1),this.tagIDs.splice(t,1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!1)))}tryPeekProperlyNestedBodyElement(){return this.stackTop>=1&&this.tagIDs[1]===xC.BODY?this.items[1]:null}contains(e){return this._indexOf(e)>-1}getCommonAncestor(e){const t=this._indexOf(e)-1;return t>=0?this.items[t]:null}isRootHtmlElementCurrent(){return 0===this.stackTop&&this.tagIDs[0]===xC.HTML}hasInDynamicScope(e,t){for(let n=this.stackTop;n>=0;n--){const r=this.tagIDs[n];switch(this.treeAdapter.getNamespaceURI(this.items[n])){case fC.HTML:if(r===e)return!0;if(t.has(r))return!1;break;case fC.SVG:if(XC.has(r))return!1;break;case fC.MATHML:if(WC.has(r))return!1}}return!0}hasInScope(e){return this.hasInDynamicScope(e,HC)}hasInListItemScope(e){return this.hasInDynamicScope(e,VC)}hasInButtonScope(e){return this.hasInDynamicScope(e,qC)}hasNumberedHeaderInScope(){for(let e=this.stackTop;e>=0;e--){const t=this.tagIDs[e];switch(this.treeAdapter.getNamespaceURI(this.items[e])){case fC.HTML:if(DC.has(t))return!0;if(HC.has(t))return!1;break;case fC.SVG:if(XC.has(t))return!1;break;case fC.MATHML:if(WC.has(t))return!1}}return!0}hasInTableScope(e){for(let t=this.stackTop;t>=0;t--)if(this.treeAdapter.getNamespaceURI(this.items[t])===fC.HTML)switch(this.tagIDs[t]){case e:return!0;case xC.TABLE:case xC.HTML:return!1}return!0}hasTableBodyContextInTableScope(){for(let e=this.stackTop;e>=0;e--)if(this.treeAdapter.getNamespaceURI(this.items[e])===fC.HTML)switch(this.tagIDs[e]){case xC.TBODY:case xC.THEAD:case xC.TFOOT:return!0;case xC.TABLE:case xC.HTML:return!1}return!0}hasInSelectScope(e){for(let t=this.stackTop;t>=0;t--)if(this.treeAdapter.getNamespaceURI(this.items[t])===fC.HTML)switch(this.tagIDs[t]){case e:return!0;case xC.OPTION:case xC.OPTGROUP:break;default:return!1}return!0}generateImpliedEndTags(){for(;void 0!==this.currentTagId&&QC.has(this.currentTagId);)this.pop()}generateImpliedEndTagsThoroughly(){for(;void 0!==this.currentTagId&&ZC.has(this.currentTagId);)this.pop()}generateImpliedEndTagsWithExclusion(e){for(;void 0!==this.currentTagId&&this.currentTagId!==e&&ZC.has(this.currentTagId);)this.pop()}}var tP,nP;(nP=tP||(tP={}))[nP.Marker=0]=\"Marker\",nP[nP.Element=1]=\"Element\";const rP={type:tP.Marker};class aP{constructor(e){this.treeAdapter=e,this.entries=[],this.bookmark=null}_getNoahArkConditionCandidates(e,t){const n=[],r=t.length,a=this.treeAdapter.getTagName(e),o=this.treeAdapter.getNamespaceURI(e);for(let e=0;e[e.name,e.value])));let a=0;for(let e=0;er.get(e.name)===e.value))&&(a+=1,a>=3&&this.entries.splice(t.idx,1))}}insertMarker(){this.entries.unshift(rP)}pushElement(e,t){this._ensureNoahArkCondition(e),this.entries.unshift({type:tP.Element,element:e,token:t})}insertElementAfterBookmark(e,t){const n=this.entries.indexOf(this.bookmark);this.entries.splice(n,0,{type:tP.Element,element:e,token:t})}removeEntry(e){const t=this.entries.indexOf(e);-1!==t&&this.entries.splice(t,1)}clearToLastMarker(){const e=this.entries.indexOf(rP);-1===e?this.entries.length=0:this.entries.splice(0,e+1)}getElementEntryInScopeWithTagName(e){const t=this.entries.find((t=>t.type===tP.Marker||this.treeAdapter.getTagName(t.element)===e));return t&&t.type===tP.Element?t:null}getElementEntry(e){return this.entries.find((t=>t.type===tP.Element&&t.element===e))}}const oP={createDocument:()=>({nodeName:\"#document\",mode:bC.NO_QUIRKS,childNodes:[]}),createDocumentFragment:()=>({nodeName:\"#document-fragment\",childNodes:[]}),createElement:(e,t,n)=>({nodeName:e,tagName:e,attrs:n,namespaceURI:t,childNodes:[],parentNode:null}),createCommentNode:e=>({nodeName:\"#comment\",data:e,parentNode:null}),createTextNode:e=>({nodeName:\"#text\",value:e,parentNode:null}),appendChild(e,t){e.childNodes.push(t),t.parentNode=e},insertBefore(e,t,n){const r=e.childNodes.indexOf(n);e.childNodes.splice(r,0,t),t.parentNode=e},setTemplateContent(e,t){e.content=t},getTemplateContent:e=>e.content,setDocumentType(e,t,n,r){const a=e.childNodes.find((e=>\"#documentType\"===e.nodeName));if(a)a.name=t,a.publicId=n,a.systemId=r;else{const a={nodeName:\"#documentType\",name:t,publicId:n,systemId:r,parentNode:null};oP.appendChild(e,a)}},setDocumentMode(e,t){e.mode=t},getDocumentMode:e=>e.mode,detachNode(e){if(e.parentNode){const t=e.parentNode.childNodes.indexOf(e);e.parentNode.childNodes.splice(t,1),e.parentNode=null}},insertText(e,t){if(e.childNodes.length>0){const n=e.childNodes[e.childNodes.length-1];if(oP.isTextNode(n))return void(n.value+=t)}oP.appendChild(e,oP.createTextNode(t))},insertTextBefore(e,t,n){const r=e.childNodes[e.childNodes.indexOf(n)-1];r&&oP.isTextNode(r)?r.value+=t:oP.insertBefore(e,oP.createTextNode(t),n)},adoptAttributes(e,t){const n=new Set(e.attrs.map((e=>e.name)));for(let r=0;re.childNodes[0],getChildNodes:e=>e.childNodes,getParentNode:e=>e.parentNode,getAttrList:e=>e.attrs,getTagName:e=>e.tagName,getNamespaceURI:e=>e.namespaceURI,getTextNodeContent:e=>e.value,getCommentNodeContent:e=>e.data,getDocumentTypeNodeName:e=>e.name,getDocumentTypeNodePublicId:e=>e.publicId,getDocumentTypeNodeSystemId:e=>e.systemId,isTextNode:e=>\"#text\"===e.nodeName,isCommentNode:e=>\"#comment\"===e.nodeName,isDocumentTypeNode:e=>\"#documentType\"===e.nodeName,isElementNode:e=>Object.prototype.hasOwnProperty.call(e,\"tagName\"),setNodeSourceCodeLocation(e,t){e.sourceCodeLocation=t},getNodeSourceCodeLocation:e=>e.sourceCodeLocation,updateNodeSourceCodeLocation(e,t){e.sourceCodeLocation={...e.sourceCodeLocation,...t}}},iP=\"html\",sP=[\"+//silmaril//dtd html pro v0r11 19970101//\",\"-//as//dtd html 3.0 aswedit + extensions//\",\"-//advasoft ltd//dtd html 3.0 aswedit + extensions//\",\"-//ietf//dtd html 2.0 level 1//\",\"-//ietf//dtd html 2.0 level 2//\",\"-//ietf//dtd html 2.0 strict level 1//\",\"-//ietf//dtd html 2.0 strict level 2//\",\"-//ietf//dtd html 2.0 strict//\",\"-//ietf//dtd html 2.0//\",\"-//ietf//dtd html 2.1e//\",\"-//ietf//dtd html 3.0//\",\"-//ietf//dtd html 3.2 final//\",\"-//ietf//dtd html 3.2//\",\"-//ietf//dtd html 3//\",\"-//ietf//dtd html level 0//\",\"-//ietf//dtd html level 1//\",\"-//ietf//dtd html level 2//\",\"-//ietf//dtd html level 3//\",\"-//ietf//dtd html strict level 0//\",\"-//ietf//dtd html strict level 1//\",\"-//ietf//dtd html strict level 2//\",\"-//ietf//dtd html strict level 3//\",\"-//ietf//dtd html strict//\",\"-//ietf//dtd html//\",\"-//metrius//dtd metrius presentational//\",\"-//microsoft//dtd internet explorer 2.0 html strict//\",\"-//microsoft//dtd internet explorer 2.0 html//\",\"-//microsoft//dtd internet explorer 2.0 tables//\",\"-//microsoft//dtd internet explorer 3.0 html strict//\",\"-//microsoft//dtd internet explorer 3.0 html//\",\"-//microsoft//dtd internet explorer 3.0 tables//\",\"-//netscape comm. corp.//dtd html//\",\"-//netscape comm. corp.//dtd strict html//\",\"-//o'reilly and associates//dtd html 2.0//\",\"-//o'reilly and associates//dtd html extended 1.0//\",\"-//o'reilly and associates//dtd html extended relaxed 1.0//\",\"-//sq//dtd html 2.0 hotmetal + extensions//\",\"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//\",\"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//\",\"-//spyglass//dtd html 2.0 extended//\",\"-//sun microsystems corp.//dtd hotjava html//\",\"-//sun microsystems corp.//dtd hotjava strict html//\",\"-//w3c//dtd html 3 1995-03-24//\",\"-//w3c//dtd html 3.2 draft//\",\"-//w3c//dtd html 3.2 final//\",\"-//w3c//dtd html 3.2//\",\"-//w3c//dtd html 3.2s draft//\",\"-//w3c//dtd html 4.0 frameset//\",\"-//w3c//dtd html 4.0 transitional//\",\"-//w3c//dtd html experimental 19960712//\",\"-//w3c//dtd html experimental 970421//\",\"-//w3c//dtd w3 html//\",\"-//w3o//dtd w3 html 3.0//\",\"-//webtechs//dtd mozilla html 2.0//\",\"-//webtechs//dtd mozilla html//\"],lP=[...sP,\"-//w3c//dtd html 4.01 frameset//\",\"-//w3c//dtd html 4.01 transitional//\"],cP=new Set([\"-//w3o//dtd w3 html strict 3.0//en//\",\"-/w3c/dtd html 4.0 transitional/en\",\"html\"]),uP=[\"-//w3c//dtd xhtml 1.0 frameset//\",\"-//w3c//dtd xhtml 1.0 transitional//\"],dP=[...uP,\"-//w3c//dtd html 4.01 frameset//\",\"-//w3c//dtd html 4.01 transitional//\"];function pP(e,t){return t.some((t=>e.startsWith(t)))}const hP=\"text/html\",fP=\"application/xhtml+xml\",mP=new Map([\"attributeName\",\"attributeType\",\"baseFrequency\",\"baseProfile\",\"calcMode\",\"clipPathUnits\",\"diffuseConstant\",\"edgeMode\",\"filterUnits\",\"glyphRef\",\"gradientTransform\",\"gradientUnits\",\"kernelMatrix\",\"kernelUnitLength\",\"keyPoints\",\"keySplines\",\"keyTimes\",\"lengthAdjust\",\"limitingConeAngle\",\"markerHeight\",\"markerUnits\",\"markerWidth\",\"maskContentUnits\",\"maskUnits\",\"numOctaves\",\"pathLength\",\"patternContentUnits\",\"patternTransform\",\"patternUnits\",\"pointsAtX\",\"pointsAtY\",\"pointsAtZ\",\"preserveAlpha\",\"preserveAspectRatio\",\"primitiveUnits\",\"refX\",\"refY\",\"repeatCount\",\"repeatDur\",\"requiredExtensions\",\"requiredFeatures\",\"specularConstant\",\"specularExponent\",\"spreadMethod\",\"startOffset\",\"stdDeviation\",\"stitchTiles\",\"surfaceScale\",\"systemLanguage\",\"tableValues\",\"targetX\",\"targetY\",\"textLength\",\"viewBox\",\"viewTarget\",\"xChannelSelector\",\"yChannelSelector\",\"zoomAndPan\"].map((e=>[e.toLowerCase(),e]))),gP=new Map([[\"xlink:actuate\",{prefix:\"xlink\",name:\"actuate\",namespace:fC.XLINK}],[\"xlink:arcrole\",{prefix:\"xlink\",name:\"arcrole\",namespace:fC.XLINK}],[\"xlink:href\",{prefix:\"xlink\",name:\"href\",namespace:fC.XLINK}],[\"xlink:role\",{prefix:\"xlink\",name:\"role\",namespace:fC.XLINK}],[\"xlink:show\",{prefix:\"xlink\",name:\"show\",namespace:fC.XLINK}],[\"xlink:title\",{prefix:\"xlink\",name:\"title\",namespace:fC.XLINK}],[\"xlink:type\",{prefix:\"xlink\",name:\"type\",namespace:fC.XLINK}],[\"xml:lang\",{prefix:\"xml\",name:\"lang\",namespace:fC.XML}],[\"xml:space\",{prefix:\"xml\",name:\"space\",namespace:fC.XML}],[\"xmlns\",{prefix:\"\",name:\"xmlns\",namespace:fC.XMLNS}],[\"xmlns:xlink\",{prefix:\"xmlns\",name:\"xlink\",namespace:fC.XMLNS}]]),vP=new Map([\"altGlyph\",\"altGlyphDef\",\"altGlyphItem\",\"animateColor\",\"animateMotion\",\"animateTransform\",\"clipPath\",\"feBlend\",\"feColorMatrix\",\"feComponentTransfer\",\"feComposite\",\"feConvolveMatrix\",\"feDiffuseLighting\",\"feDisplacementMap\",\"feDistantLight\",\"feFlood\",\"feFuncA\",\"feFuncB\",\"feFuncG\",\"feFuncR\",\"feGaussianBlur\",\"feImage\",\"feMerge\",\"feMergeNode\",\"feMorphology\",\"feOffset\",\"fePointLight\",\"feSpecularLighting\",\"feSpotLight\",\"feTile\",\"feTurbulence\",\"foreignObject\",\"glyphRef\",\"linearGradient\",\"radialGradient\",\"textPath\"].map((e=>[e.toLowerCase(),e]))),bP=new Set([xC.B,xC.BIG,xC.BLOCKQUOTE,xC.BODY,xC.BR,xC.CENTER,xC.CODE,xC.DD,xC.DIV,xC.DL,xC.DT,xC.EM,xC.EMBED,xC.H1,xC.H2,xC.H3,xC.H4,xC.H5,xC.H6,xC.HEAD,xC.HR,xC.I,xC.IMG,xC.LI,xC.LISTING,xC.MENU,xC.META,xC.NOBR,xC.OL,xC.P,xC.PRE,xC.RUBY,xC.S,xC.SMALL,xC.SPAN,xC.STRONG,xC.STRIKE,xC.SUB,xC.SUP,xC.TABLE,xC.TT,xC.U,xC.UL,xC.VAR]);function yP(e){for(let t=0;t0&&this._setContextModes(e,t)}onItemPop(e,t){var n,r;if(this.options.sourceCodeLocationInfo&&this._setEndLocation(e,this.currentToken),null===(r=(n=this.treeAdapter).onItemPop)||void 0===r||r.call(n,e,this.openElements.current),t){let e,t;0===this.openElements.stackTop&&this.fragmentContext?(e=this.fragmentContext,t=this.fragmentContextID):({current:e,currentTagId:t}=this.openElements),this._setContextModes(e,t)}}_setContextModes(e,t){const n=e===this.document||e&&this.treeAdapter.getNamespaceURI(e)===fC.HTML;this.currentNotInHTML=!n,this.tokenizer.inForeignNode=!n&&void 0!==e&&void 0!==t&&!this._isIntegrationPoint(t,e)}_switchToTextParsing(e,t){this._insertElement(e,fC.HTML),this.tokenizer.state=t,this.originalInsertionMode=this.insertionMode,this.insertionMode=xP.TEXT}switchToPlaintextParsing(){this.insertionMode=xP.TEXT,this.originalInsertionMode=xP.IN_BODY,this.tokenizer.state=NC.PLAINTEXT}_getAdjustedCurrentElement(){return 0===this.openElements.stackTop&&this.fragmentContext?this.fragmentContext:this.openElements.current}_findFormInFragmentContext(){let e=this.fragmentContext;for(;e;){if(this.treeAdapter.getTagName(e)===OC.FORM){this.formElement=e;break}e=this.treeAdapter.getParentNode(e)}}_initTokenizerForFragmentParsing(){if(this.fragmentContext&&this.treeAdapter.getNamespaceURI(this.fragmentContext)===fC.HTML)switch(this.fragmentContextID){case xC.TITLE:case xC.TEXTAREA:this.tokenizer.state=NC.RCDATA;break;case xC.STYLE:case xC.XMP:case xC.IFRAME:case xC.NOEMBED:case xC.NOFRAMES:case xC.NOSCRIPT:this.tokenizer.state=NC.RAWTEXT;break;case xC.SCRIPT:this.tokenizer.state=NC.SCRIPT_DATA;break;case xC.PLAINTEXT:this.tokenizer.state=NC.PLAINTEXT}}_setDocumentType(e){const t=e.name||\"\",n=e.publicId||\"\",r=e.systemId||\"\";if(this.treeAdapter.setDocumentType(this.document,t,n,r),e.location){const t=this.treeAdapter.getChildNodes(this.document).find((e=>this.treeAdapter.isDocumentTypeNode(e)));t&&this.treeAdapter.setNodeSourceCodeLocation(t,e.location)}}_attachElementToTree(e,t){if(this.options.sourceCodeLocationInfo){const n=t&&{...t,startTag:t};this.treeAdapter.setNodeSourceCodeLocation(e,n)}if(this._shouldFosterParentOnInsertion())this._fosterParentElement(e);else{const t=this.openElements.currentTmplContentOrNode;this.treeAdapter.appendChild(null!=t?t:this.document,e)}}_appendElement(e,t){const n=this.treeAdapter.createElement(e.tagName,t,e.attrs);this._attachElementToTree(n,e.location)}_insertElement(e,t){const n=this.treeAdapter.createElement(e.tagName,t,e.attrs);this._attachElementToTree(n,e.location),this.openElements.push(n,e.tagID)}_insertFakeElement(e,t){const n=this.treeAdapter.createElement(e,fC.HTML,[]);this._attachElementToTree(n,null),this.openElements.push(n,t)}_insertTemplate(e){const t=this.treeAdapter.createElement(e.tagName,fC.HTML,e.attrs),n=this.treeAdapter.createDocumentFragment();this.treeAdapter.setTemplateContent(t,n),this._attachElementToTree(t,e.location),this.openElements.push(t,e.tagID),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(n,null)}_insertFakeRootElement(){const e=this.treeAdapter.createElement(OC.HTML,fC.HTML,[]);this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(e,null),this.treeAdapter.appendChild(this.openElements.current,e),this.openElements.push(e,xC.HTML)}_appendCommentNode(e,t){const n=this.treeAdapter.createCommentNode(e.data);this.treeAdapter.appendChild(t,n),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(n,e.location)}_insertCharacters(e){let t,n;if(this._shouldFosterParentOnInsertion()?(({parent:t,beforeElement:n}=this._findFosterParentingLocation()),n?this.treeAdapter.insertTextBefore(t,e.chars,n):this.treeAdapter.insertText(t,e.chars)):(t=this.openElements.currentTmplContentOrNode,this.treeAdapter.insertText(t,e.chars)),!e.location)return;const r=this.treeAdapter.getChildNodes(t),a=n?r.lastIndexOf(n):r.length,o=r[a-1];if(this.treeAdapter.getNodeSourceCodeLocation(o)){const{endLine:t,endCol:n,endOffset:r}=e.location;this.treeAdapter.updateNodeSourceCodeLocation(o,{endLine:t,endCol:n,endOffset:r})}else this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(o,e.location)}_adoptNodes(e,t){for(let n=this.treeAdapter.getFirstChild(e);n;n=this.treeAdapter.getFirstChild(e))this.treeAdapter.detachNode(n),this.treeAdapter.appendChild(t,n)}_setEndLocation(e,t){if(this.treeAdapter.getNodeSourceCodeLocation(e)&&t.location){const n=t.location,r=this.treeAdapter.getTagName(e),a=t.type===eC.END_TAG&&r===t.tagName?{endTag:{...n},endLine:n.endLine,endCol:n.endCol,endOffset:n.endOffset}:{endLine:n.startLine,endCol:n.startCol,endOffset:n.startOffset};this.treeAdapter.updateNodeSourceCodeLocation(e,a)}}shouldProcessStartTagTokenInForeignContent(e){if(!this.currentNotInHTML)return!1;let t,n;return 0===this.openElements.stackTop&&this.fragmentContext?(t=this.fragmentContext,n=this.fragmentContextID):({current:t,currentTagId:n}=this.openElements),(e.tagID!==xC.SVG||this.treeAdapter.getTagName(t)!==OC.ANNOTATION_XML||this.treeAdapter.getNamespaceURI(t)!==fC.MATHML)&&(this.tokenizer.inForeignNode||(e.tagID===xC.MGLYPH||e.tagID===xC.MALIGNMARK)&&void 0!==n&&!this._isIntegrationPoint(n,t,fC.HTML))}_processToken(e){switch(e.type){case eC.CHARACTER:this.onCharacter(e);break;case eC.NULL_CHARACTER:this.onNullCharacter(e);break;case eC.COMMENT:this.onComment(e);break;case eC.DOCTYPE:this.onDoctype(e);break;case eC.START_TAG:this._processStartTag(e);break;case eC.END_TAG:this.onEndTag(e);break;case eC.EOF:this.onEof(e);break;case eC.WHITESPACE_CHARACTER:this.onWhitespaceCharacter(e)}}_isIntegrationPoint(e,t,n){return function(e,t,n,r){return(!r||r===fC.HTML)&&function(e,t,n){if(t===fC.MATHML&&e===xC.ANNOTATION_XML)for(let e=0;ee.type===tP.Marker||this.openElements.contains(e.element)));for(let n=-1===t?e-1:t-1;n>=0;n--){const e=this.activeFormattingElements.entries[n];this._insertElement(e.token,this.treeAdapter.getNamespaceURI(e.element)),e.element=this.openElements.current}}}_closeTableCell(){this.openElements.generateImpliedEndTags(),this.openElements.popUntilTableCellPopped(),this.activeFormattingElements.clearToLastMarker(),this.insertionMode=xP.IN_ROW}_closePElement(){this.openElements.generateImpliedEndTagsWithExclusion(xC.P),this.openElements.popUntilTagNamePopped(xC.P)}_resetInsertionMode(){for(let e=this.openElements.stackTop;e>=0;e--)switch(0===e&&this.fragmentContext?this.fragmentContextID:this.openElements.tagIDs[e]){case xC.TR:return void(this.insertionMode=xP.IN_ROW);case xC.TBODY:case xC.THEAD:case xC.TFOOT:return void(this.insertionMode=xP.IN_TABLE_BODY);case xC.CAPTION:return void(this.insertionMode=xP.IN_CAPTION);case xC.COLGROUP:return void(this.insertionMode=xP.IN_COLUMN_GROUP);case xC.TABLE:return void(this.insertionMode=xP.IN_TABLE);case xC.BODY:return void(this.insertionMode=xP.IN_BODY);case xC.FRAMESET:return void(this.insertionMode=xP.IN_FRAMESET);case xC.SELECT:return void this._resetInsertionModeForSelect(e);case xC.TEMPLATE:return void(this.insertionMode=this.tmplInsertionModeStack[0]);case xC.HTML:return void(this.insertionMode=this.headElement?xP.AFTER_HEAD:xP.BEFORE_HEAD);case xC.TD:case xC.TH:if(e>0)return void(this.insertionMode=xP.IN_CELL);break;case xC.HEAD:if(e>0)return void(this.insertionMode=xP.IN_HEAD)}this.insertionMode=xP.IN_BODY}_resetInsertionModeForSelect(e){if(e>0)for(let t=e-1;t>0;t--){const e=this.openElements.tagIDs[t];if(e===xC.TEMPLATE)break;if(e===xC.TABLE)return void(this.insertionMode=xP.IN_SELECT_IN_TABLE)}this.insertionMode=xP.IN_SELECT}_isElementCausesFosterParenting(e){return _P.has(e)}_shouldFosterParentOnInsertion(){return this.fosterParentingEnabled&&void 0!==this.openElements.currentTagId&&this._isElementCausesFosterParenting(this.openElements.currentTagId)}_findFosterParentingLocation(){for(let e=this.openElements.stackTop;e>=0;e--){const t=this.openElements.items[e];switch(this.openElements.tagIDs[e]){case xC.TEMPLATE:if(this.treeAdapter.getNamespaceURI(t)===fC.HTML)return{parent:this.treeAdapter.getTemplateContent(t),beforeElement:null};break;case xC.TABLE:{const n=this.treeAdapter.getParentNode(t);return n?{parent:n,beforeElement:t}:{parent:this.openElements.items[e-1],beforeElement:null}}}}return{parent:this.openElements.items[0],beforeElement:null}}_fosterParentElement(e){const t=this._findFosterParentingLocation();t.beforeElement?this.treeAdapter.insertBefore(t.parent,e,t.beforeElement):this.treeAdapter.appendChild(t.parent,e)}_isSpecialElement(e,t){const n=this.treeAdapter.getNamespaceURI(e);return $C[n].has(t)}onCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode)!function(e,t){e._insertCharacters(t),e.framesetOk=!1}(this,e);else switch(this.insertionMode){case xP.INITIAL:LP(this,e);break;case xP.BEFORE_HTML:BP(this,e);break;case xP.BEFORE_HEAD:jP(this,e);break;case xP.IN_HEAD:FP(this,e);break;case xP.IN_HEAD_NO_SCRIPT:QP(this,e);break;case xP.AFTER_HEAD:ZP(this,e);break;case xP.IN_BODY:case xP.IN_CAPTION:case xP.IN_CELL:case xP.IN_TEMPLATE:qP(this,e);break;case xP.TEXT:case xP.IN_SELECT:case xP.IN_SELECT_IN_TABLE:this._insertCharacters(e);break;case xP.IN_TABLE:case xP.IN_TABLE_BODY:case xP.IN_ROW:n$(this,e);break;case xP.IN_TABLE_TEXT:s$(this,e);break;case xP.IN_COLUMN_GROUP:d$(this,e);break;case xP.AFTER_BODY:O$(this,e);break;case xP.AFTER_AFTER_BODY:w$(this,e)}}onNullCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode)!function(e,t){t.chars=QA,e._insertCharacters(t)}(this,e);else switch(this.insertionMode){case xP.INITIAL:LP(this,e);break;case xP.BEFORE_HTML:BP(this,e);break;case xP.BEFORE_HEAD:jP(this,e);break;case xP.IN_HEAD:FP(this,e);break;case xP.IN_HEAD_NO_SCRIPT:QP(this,e);break;case xP.AFTER_HEAD:ZP(this,e);break;case xP.TEXT:this._insertCharacters(e);break;case xP.IN_TABLE:case xP.IN_TABLE_BODY:case xP.IN_ROW:n$(this,e);break;case xP.IN_COLUMN_GROUP:d$(this,e);break;case xP.AFTER_BODY:O$(this,e);break;case xP.AFTER_AFTER_BODY:w$(this,e)}}onComment(e){if(this.skipNextNewLine=!1,this.currentNotInHTML)NP(this,e);else switch(this.insertionMode){case xP.INITIAL:case xP.BEFORE_HTML:case xP.BEFORE_HEAD:case xP.IN_HEAD:case xP.IN_HEAD_NO_SCRIPT:case xP.AFTER_HEAD:case xP.IN_BODY:case xP.IN_TABLE:case xP.IN_CAPTION:case xP.IN_COLUMN_GROUP:case xP.IN_TABLE_BODY:case xP.IN_ROW:case xP.IN_CELL:case xP.IN_SELECT:case xP.IN_SELECT_IN_TABLE:case xP.IN_TEMPLATE:case xP.IN_FRAMESET:case xP.AFTER_FRAMESET:NP(this,e);break;case xP.IN_TABLE_TEXT:l$(this,e);break;case xP.AFTER_BODY:!function(e,t){e._appendCommentNode(t,e.openElements.items[0])}(this,e);break;case xP.AFTER_AFTER_BODY:case xP.AFTER_AFTER_FRAMESET:!function(e,t){e._appendCommentNode(t,e.document)}(this,e)}}onDoctype(e){switch(this.skipNextNewLine=!1,this.insertionMode){case xP.INITIAL:!function(e,t){e._setDocumentType(t);const n=t.forceQuirks?bC.QUIRKS:function(e){if(e.name!==iP)return bC.QUIRKS;const{systemId:t}=e;if(t&&\"http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd\"===t.toLowerCase())return bC.QUIRKS;let{publicId:n}=e;if(null!==n){if(n=n.toLowerCase(),cP.has(n))return bC.QUIRKS;let e=null===t?lP:sP;if(pP(n,e))return bC.QUIRKS;if(e=null===t?uP:dP,pP(n,e))return bC.LIMITED_QUIRKS}return bC.NO_QUIRKS}(t);(function(e){return e.name===iP&&null===e.publicId&&(null===e.systemId||\"about:legacy-compat\"===e.systemId)})(t)||e._err(t,KA.nonConformingDoctype),e.treeAdapter.setDocumentMode(e.document,n),e.insertionMode=xP.BEFORE_HTML}(this,e);break;case xP.BEFORE_HEAD:case xP.IN_HEAD:case xP.IN_HEAD_NO_SCRIPT:case xP.AFTER_HEAD:this._err(e,KA.misplacedDoctype);break;case xP.IN_TABLE_TEXT:l$(this,e)}}onStartTag(e){this.skipNextNewLine=!1,this.currentToken=e,this._processStartTag(e),e.selfClosing&&!e.ackSelfClosing&&this._err(e,KA.nonVoidHtmlElementStartTagWithTrailingSolidus)}_processStartTag(e){this.shouldProcessStartTagTokenInForeignContent(e)?function(e,t){if(function(e){const t=e.tagID;return t===xC.FONT&&e.attrs.some((({name:e})=>e===gC.COLOR||e===gC.SIZE||e===gC.FACE))||bP.has(t)}(t))x$(e),e._startTagOutsideForeignContent(t);else{const n=e._getAdjustedCurrentElement(),r=e.treeAdapter.getNamespaceURI(n);r===fC.MATHML?yP(t):r===fC.SVG&&(function(e){const t=vP.get(e.tagName);null!=t&&(e.tagName=t,e.tagID=CC(e.tagName))}(t),OP(t)),wP(t),t.selfClosing?e._appendElement(t,r):e._insertElement(t,r),t.ackSelfClosing=!0}}(this,e):this._startTagOutsideForeignContent(e)}_startTagOutsideForeignContent(e){switch(this.insertionMode){case xP.INITIAL:LP(this,e);break;case xP.BEFORE_HTML:!function(e,t){t.tagID===xC.HTML?(e._insertElement(t,fC.HTML),e.insertionMode=xP.BEFORE_HEAD):BP(e,t)}(this,e);break;case xP.BEFORE_HEAD:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.HEAD:e._insertElement(t,fC.HTML),e.headElement=e.openElements.current,e.insertionMode=xP.IN_HEAD;break;default:jP(e,t)}}(this,e);break;case xP.IN_HEAD:UP(this,e);break;case xP.IN_HEAD_NO_SCRIPT:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.BASEFONT:case xC.BGSOUND:case xC.HEAD:case xC.LINK:case xC.META:case xC.NOFRAMES:case xC.STYLE:UP(e,t);break;case xC.NOSCRIPT:e._err(t,KA.nestedNoscriptInHead);break;default:QP(e,t)}}(this,e);break;case xP.AFTER_HEAD:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.BODY:e._insertElement(t,fC.HTML),e.framesetOk=!1,e.insertionMode=xP.IN_BODY;break;case xC.FRAMESET:e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_FRAMESET;break;case xC.BASE:case xC.BASEFONT:case xC.BGSOUND:case xC.LINK:case xC.META:case xC.NOFRAMES:case xC.SCRIPT:case xC.STYLE:case xC.TEMPLATE:case xC.TITLE:e._err(t,KA.abandonedHeadElementChild),e.openElements.push(e.headElement,xC.HEAD),UP(e,t),e.openElements.remove(e.headElement);break;case xC.HEAD:e._err(t,KA.misplacedStartTagForHeadElement);break;default:ZP(e,t)}}(this,e);break;case xP.IN_BODY:KP(this,e);break;case xP.IN_TABLE:r$(this,e);break;case xP.IN_TABLE_TEXT:l$(this,e);break;case xP.IN_CAPTION:!function(e,t){const n=t.tagID;c$.has(n)?e.openElements.hasInTableScope(xC.CAPTION)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(xC.CAPTION),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=xP.IN_TABLE,r$(e,t)):KP(e,t)}(this,e);break;case xP.IN_COLUMN_GROUP:u$(this,e);break;case xP.IN_TABLE_BODY:p$(this,e);break;case xP.IN_ROW:f$(this,e);break;case xP.IN_CELL:!function(e,t){const n=t.tagID;c$.has(n)?(e.openElements.hasInTableScope(xC.TD)||e.openElements.hasInTableScope(xC.TH))&&(e._closeTableCell(),f$(e,t)):KP(e,t)}(this,e);break;case xP.IN_SELECT:g$(this,e);break;case xP.IN_SELECT_IN_TABLE:!function(e,t){const n=t.tagID;n===xC.CAPTION||n===xC.TABLE||n===xC.TBODY||n===xC.TFOOT||n===xC.THEAD||n===xC.TR||n===xC.TD||n===xC.TH?(e.openElements.popUntilTagNamePopped(xC.SELECT),e._resetInsertionMode(),e._processStartTag(t)):g$(e,t)}(this,e);break;case xP.IN_TEMPLATE:!function(e,t){switch(t.tagID){case xC.BASE:case xC.BASEFONT:case xC.BGSOUND:case xC.LINK:case xC.META:case xC.NOFRAMES:case xC.SCRIPT:case xC.STYLE:case xC.TEMPLATE:case xC.TITLE:UP(e,t);break;case xC.CAPTION:case xC.COLGROUP:case xC.TBODY:case xC.TFOOT:case xC.THEAD:e.tmplInsertionModeStack[0]=xP.IN_TABLE,e.insertionMode=xP.IN_TABLE,r$(e,t);break;case xC.COL:e.tmplInsertionModeStack[0]=xP.IN_COLUMN_GROUP,e.insertionMode=xP.IN_COLUMN_GROUP,u$(e,t);break;case xC.TR:e.tmplInsertionModeStack[0]=xP.IN_TABLE_BODY,e.insertionMode=xP.IN_TABLE_BODY,p$(e,t);break;case xC.TD:case xC.TH:e.tmplInsertionModeStack[0]=xP.IN_ROW,e.insertionMode=xP.IN_ROW,f$(e,t);break;default:e.tmplInsertionModeStack[0]=xP.IN_BODY,e.insertionMode=xP.IN_BODY,KP(e,t)}}(this,e);break;case xP.AFTER_BODY:!function(e,t){t.tagID===xC.HTML?KP(e,t):O$(e,t)}(this,e);break;case xP.IN_FRAMESET:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.FRAMESET:e._insertElement(t,fC.HTML);break;case xC.FRAME:e._appendElement(t,fC.HTML),t.ackSelfClosing=!0;break;case xC.NOFRAMES:UP(e,t)}}(this,e);break;case xP.AFTER_FRAMESET:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.NOFRAMES:UP(e,t)}}(this,e);break;case xP.AFTER_AFTER_BODY:!function(e,t){t.tagID===xC.HTML?KP(e,t):w$(e,t)}(this,e);break;case xP.AFTER_AFTER_FRAMESET:!function(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.NOFRAMES:UP(e,t)}}(this,e)}}onEndTag(e){this.skipNextNewLine=!1,this.currentToken=e,this.currentNotInHTML?function(e,t){if(t.tagID===xC.P||t.tagID===xC.BR)return x$(e),void e._endTagOutsideForeignContent(t);for(let n=e.openElements.stackTop;n>0;n--){const r=e.openElements.items[n];if(e.treeAdapter.getNamespaceURI(r)===fC.HTML){e._endTagOutsideForeignContent(t);break}const a=e.treeAdapter.getTagName(r);if(a.toLowerCase()===t.tagName){t.tagName=a,e.openElements.shortenToLength(n);break}}}(this,e):this._endTagOutsideForeignContent(e)}_endTagOutsideForeignContent(e){switch(this.insertionMode){case xP.INITIAL:LP(this,e);break;case xP.BEFORE_HTML:!function(e,t){const n=t.tagID;n!==xC.HTML&&n!==xC.HEAD&&n!==xC.BODY&&n!==xC.BR||BP(e,t)}(this,e);break;case xP.BEFORE_HEAD:!function(e,t){const n=t.tagID;n===xC.HEAD||n===xC.BODY||n===xC.HTML||n===xC.BR?jP(e,t):e._err(t,KA.endTagWithoutMatchingOpenElement)}(this,e);break;case xP.IN_HEAD:!function(e,t){switch(t.tagID){case xC.HEAD:e.openElements.pop(),e.insertionMode=xP.AFTER_HEAD;break;case xC.BODY:case xC.BR:case xC.HTML:FP(e,t);break;case xC.TEMPLATE:zP(e,t);break;default:e._err(t,KA.endTagWithoutMatchingOpenElement)}}(this,e);break;case xP.IN_HEAD_NO_SCRIPT:!function(e,t){switch(t.tagID){case xC.NOSCRIPT:e.openElements.pop(),e.insertionMode=xP.IN_HEAD;break;case xC.BR:QP(e,t);break;default:e._err(t,KA.endTagWithoutMatchingOpenElement)}}(this,e);break;case xP.AFTER_HEAD:!function(e,t){switch(t.tagID){case xC.BODY:case xC.HTML:case xC.BR:ZP(e,t);break;case xC.TEMPLATE:zP(e,t);break;default:e._err(t,KA.endTagWithoutMatchingOpenElement)}}(this,e);break;case xP.IN_BODY:e$(this,e);break;case xP.TEXT:!function(e,t){var n;t.tagID===xC.SCRIPT&&(null===(n=e.scriptHandler)||void 0===n||n.call(e,e.openElements.current)),e.openElements.pop(),e.insertionMode=e.originalInsertionMode}(this,e);break;case xP.IN_TABLE:a$(this,e);break;case xP.IN_TABLE_TEXT:l$(this,e);break;case xP.IN_CAPTION:!function(e,t){const n=t.tagID;switch(n){case xC.CAPTION:case xC.TABLE:e.openElements.hasInTableScope(xC.CAPTION)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(xC.CAPTION),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=xP.IN_TABLE,n===xC.TABLE&&a$(e,t));break;case xC.BODY:case xC.COL:case xC.COLGROUP:case xC.HTML:case xC.TBODY:case xC.TD:case xC.TFOOT:case xC.TH:case xC.THEAD:case xC.TR:break;default:e$(e,t)}}(this,e);break;case xP.IN_COLUMN_GROUP:!function(e,t){switch(t.tagID){case xC.COLGROUP:e.openElements.currentTagId===xC.COLGROUP&&(e.openElements.pop(),e.insertionMode=xP.IN_TABLE);break;case xC.TEMPLATE:zP(e,t);break;case xC.COL:break;default:d$(e,t)}}(this,e);break;case xP.IN_TABLE_BODY:h$(this,e);break;case xP.IN_ROW:m$(this,e);break;case xP.IN_CELL:!function(e,t){const n=t.tagID;switch(n){case xC.TD:case xC.TH:e.openElements.hasInTableScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=xP.IN_ROW);break;case xC.TABLE:case xC.TBODY:case xC.TFOOT:case xC.THEAD:case xC.TR:e.openElements.hasInTableScope(n)&&(e._closeTableCell(),m$(e,t));break;case xC.BODY:case xC.CAPTION:case xC.COL:case xC.COLGROUP:case xC.HTML:break;default:e$(e,t)}}(this,e);break;case xP.IN_SELECT:v$(this,e);break;case xP.IN_SELECT_IN_TABLE:!function(e,t){const n=t.tagID;n===xC.CAPTION||n===xC.TABLE||n===xC.TBODY||n===xC.TFOOT||n===xC.THEAD||n===xC.TR||n===xC.TD||n===xC.TH?e.openElements.hasInTableScope(n)&&(e.openElements.popUntilTagNamePopped(xC.SELECT),e._resetInsertionMode(),e.onEndTag(t)):v$(e,t)}(this,e);break;case xP.IN_TEMPLATE:!function(e,t){t.tagID===xC.TEMPLATE&&zP(e,t)}(this,e);break;case xP.AFTER_BODY:y$(this,e);break;case xP.IN_FRAMESET:!function(e,t){t.tagID!==xC.FRAMESET||e.openElements.isRootHtmlElementCurrent()||(e.openElements.pop(),e.fragmentContext||e.openElements.currentTagId===xC.FRAMESET||(e.insertionMode=xP.AFTER_FRAMESET))}(this,e);break;case xP.AFTER_FRAMESET:!function(e,t){t.tagID===xC.HTML&&(e.insertionMode=xP.AFTER_AFTER_FRAMESET)}(this,e);break;case xP.AFTER_AFTER_BODY:w$(this,e)}}onEof(e){switch(this.insertionMode){case xP.INITIAL:LP(this,e);break;case xP.BEFORE_HTML:BP(this,e);break;case xP.BEFORE_HEAD:jP(this,e);break;case xP.IN_HEAD:FP(this,e);break;case xP.IN_HEAD_NO_SCRIPT:QP(this,e);break;case xP.AFTER_HEAD:ZP(this,e);break;case xP.IN_BODY:case xP.IN_TABLE:case xP.IN_CAPTION:case xP.IN_COLUMN_GROUP:case xP.IN_TABLE_BODY:case xP.IN_ROW:case xP.IN_CELL:case xP.IN_SELECT:case xP.IN_SELECT_IN_TABLE:t$(this,e);break;case xP.TEXT:!function(e,t){e._err(t,KA.eofInElementThatCanContainOnlyText),e.openElements.pop(),e.insertionMode=e.originalInsertionMode,e.onEof(t)}(this,e);break;case xP.IN_TABLE_TEXT:l$(this,e);break;case xP.IN_TEMPLATE:b$(this,e);break;case xP.AFTER_BODY:case xP.IN_FRAMESET:case xP.AFTER_FRAMESET:case xP.AFTER_AFTER_BODY:case xP.AFTER_AFTER_FRAMESET:RP(this,e)}}onWhitespaceCharacter(e){if(this.skipNextNewLine&&(this.skipNextNewLine=!1,e.chars.charCodeAt(0)===ZA.LINE_FEED)){if(1===e.chars.length)return;e.chars=e.chars.substr(1)}if(this.tokenizer.inForeignNode)this._insertCharacters(e);else switch(this.insertionMode){case xP.IN_HEAD:case xP.IN_HEAD_NO_SCRIPT:case xP.AFTER_HEAD:case xP.TEXT:case xP.IN_COLUMN_GROUP:case xP.IN_SELECT:case xP.IN_SELECT_IN_TABLE:case xP.IN_FRAMESET:case xP.AFTER_FRAMESET:this._insertCharacters(e);break;case xP.IN_BODY:case xP.IN_CAPTION:case xP.IN_CELL:case xP.IN_TEMPLATE:case xP.AFTER_BODY:case xP.AFTER_AFTER_BODY:case xP.AFTER_AFTER_FRAMESET:VP(this,e);break;case xP.IN_TABLE:case xP.IN_TABLE_BODY:case xP.IN_ROW:n$(this,e);break;case xP.IN_TABLE_TEXT:i$(this,e)}}};function AP(e,t){let n=e.activeFormattingElements.getElementEntryInScopeWithTagName(t.tagName);return n?e.openElements.contains(n.element)?e.openElements.hasInScope(t.tagID)||(n=null):(e.activeFormattingElements.removeEntry(n),n=null):JP(e,t),n}function CP(e,t){let n=null,r=e.openElements.stackTop;for(;r>=0;r--){const a=e.openElements.items[r];if(a===t.element)break;e._isSpecialElement(a,e.openElements.tagIDs[r])&&(n=a)}return n||(e.openElements.shortenToLength(Math.max(r,0)),e.activeFormattingElements.removeEntry(t)),n}function PP(e,t,n){let r=t,a=e.openElements.getCommonAncestor(t);for(let o=0,i=a;i!==n;o++,i=a){a=e.openElements.getCommonAncestor(i);const n=e.activeFormattingElements.getElementEntry(i),s=n&&o>=3;!n||s?(s&&e.activeFormattingElements.removeEntry(n),e.openElements.remove(i)):(i=$P(e,n),r===t&&(e.activeFormattingElements.bookmark=n),e.treeAdapter.detachNode(r),e.treeAdapter.appendChild(i,r),r=i)}return r}function $P(e,t){const n=e.treeAdapter.getNamespaceURI(t.element),r=e.treeAdapter.createElement(t.token.tagName,n,t.token.attrs);return e.openElements.replace(t.element,r),t.element=r,r}function DP(e,t,n){const r=CC(e.treeAdapter.getTagName(t));if(e._isElementCausesFosterParenting(r))e._fosterParentElement(n);else{const a=e.treeAdapter.getNamespaceURI(t);r===xC.TEMPLATE&&a===fC.HTML&&(t=e.treeAdapter.getTemplateContent(t)),e.treeAdapter.appendChild(t,n)}}function IP(e,t,n){const r=e.treeAdapter.getNamespaceURI(n.element),{token:a}=n,o=e.treeAdapter.createElement(a.tagName,r,a.attrs);e._adoptNodes(t,o),e.treeAdapter.appendChild(t,o),e.activeFormattingElements.insertElementAfterBookmark(o,a),e.activeFormattingElements.removeEntry(n),e.openElements.remove(n.element),e.openElements.insertAfter(t,o,a.tagID)}function MP(e,t){for(let n=0;n<8;n++){const n=AP(e,t);if(!n)break;const r=CP(e,n);if(!r)break;e.activeFormattingElements.bookmark=n;const a=PP(e,r,n.element),o=e.openElements.getCommonAncestor(n.element);e.treeAdapter.detachNode(a),o&&DP(e,o,a),IP(e,r,n)}}function NP(e,t){e._appendCommentNode(t,e.openElements.currentTmplContentOrNode)}function RP(e,t){if(e.stopped=!0,t.location){const n=e.fragmentContext?0:2;for(let r=e.openElements.stackTop;r>=n;r--)e._setEndLocation(e.openElements.items[r],t);if(!e.fragmentContext&&e.openElements.stackTop>=0){const n=e.openElements.items[0],r=e.treeAdapter.getNodeSourceCodeLocation(n);if(r&&!r.endTag&&(e._setEndLocation(n,t),e.openElements.stackTop>=1)){const n=e.openElements.items[1],r=e.treeAdapter.getNodeSourceCodeLocation(n);r&&!r.endTag&&e._setEndLocation(n,t)}}}}function LP(e,t){e._err(t,KA.missingDoctype,!0),e.treeAdapter.setDocumentMode(e.document,bC.QUIRKS),e.insertionMode=xP.BEFORE_HTML,e._processToken(t)}function BP(e,t){e._insertFakeRootElement(),e.insertionMode=xP.BEFORE_HEAD,e._processToken(t)}function jP(e,t){e._insertFakeElement(OC.HEAD,xC.HEAD),e.headElement=e.openElements.current,e.insertionMode=xP.IN_HEAD,e._processToken(t)}function UP(e,t){switch(t.tagID){case xC.HTML:KP(e,t);break;case xC.BASE:case xC.BASEFONT:case xC.BGSOUND:case xC.LINK:case xC.META:e._appendElement(t,fC.HTML),t.ackSelfClosing=!0;break;case xC.TITLE:e._switchToTextParsing(t,NC.RCDATA);break;case xC.NOSCRIPT:e.options.scriptingEnabled?e._switchToTextParsing(t,NC.RAWTEXT):(e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_HEAD_NO_SCRIPT);break;case xC.NOFRAMES:case xC.STYLE:e._switchToTextParsing(t,NC.RAWTEXT);break;case xC.SCRIPT:e._switchToTextParsing(t,NC.SCRIPT_DATA);break;case xC.TEMPLATE:e._insertTemplate(t),e.activeFormattingElements.insertMarker(),e.framesetOk=!1,e.insertionMode=xP.IN_TEMPLATE,e.tmplInsertionModeStack.unshift(xP.IN_TEMPLATE);break;case xC.HEAD:e._err(t,KA.misplacedStartTagForHeadElement);break;default:FP(e,t)}}function zP(e,t){e.openElements.tmplCount>0?(e.openElements.generateImpliedEndTagsThoroughly(),e.openElements.currentTagId!==xC.TEMPLATE&&e._err(t,KA.closingOfElementWithOpenChildElements),e.openElements.popUntilTagNamePopped(xC.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e.tmplInsertionModeStack.shift(),e._resetInsertionMode()):e._err(t,KA.endTagWithoutMatchingOpenElement)}function FP(e,t){e.openElements.pop(),e.insertionMode=xP.AFTER_HEAD,e._processToken(t)}function QP(e,t){const n=t.type===eC.EOF?KA.openElementsLeftAfterEof:KA.disallowedContentInNoscriptInHead;e._err(t,n),e.openElements.pop(),e.insertionMode=xP.IN_HEAD,e._processToken(t)}function ZP(e,t){e._insertFakeElement(OC.BODY,xC.BODY),e.insertionMode=xP.IN_BODY,HP(e,t)}function HP(e,t){switch(t.type){case eC.CHARACTER:qP(e,t);break;case eC.WHITESPACE_CHARACTER:VP(e,t);break;case eC.COMMENT:NP(e,t);break;case eC.START_TAG:KP(e,t);break;case eC.END_TAG:e$(e,t);break;case eC.EOF:t$(e,t)}}function VP(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t)}function qP(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t),e.framesetOk=!1}function WP(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,fC.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}function XP(e){const t=rC(e,gC.TYPE);return null!=t&&\"hidden\"===t.toLowerCase()}function GP(e,t){e._switchToTextParsing(t,NC.RAWTEXT)}function YP(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML)}function KP(e,t){switch(t.tagID){case xC.I:case xC.S:case xC.B:case xC.U:case xC.EM:case xC.TT:case xC.BIG:case xC.CODE:case xC.FONT:case xC.SMALL:case xC.STRIKE:case xC.STRONG:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case xC.A:!function(e,t){const n=e.activeFormattingElements.getElementEntryInScopeWithTagName(OC.A);n&&(MP(e,t),e.openElements.remove(n.element),e.activeFormattingElements.removeEntry(n)),e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case xC.H1:case xC.H2:case xC.H3:case xC.H4:case xC.H5:case xC.H6:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),void 0!==e.openElements.currentTagId&&DC.has(e.openElements.currentTagId)&&e.openElements.pop(),e._insertElement(t,fC.HTML)}(e,t);break;case xC.P:case xC.DL:case xC.OL:case xC.UL:case xC.DIV:case xC.DIR:case xC.NAV:case xC.MAIN:case xC.MENU:case xC.ASIDE:case xC.CENTER:case xC.FIGURE:case xC.FOOTER:case xC.HEADER:case xC.HGROUP:case xC.DIALOG:case xC.DETAILS:case xC.ADDRESS:case xC.ARTICLE:case xC.SEARCH:case xC.SECTION:case xC.SUMMARY:case xC.FIELDSET:case xC.BLOCKQUOTE:case xC.FIGCAPTION:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML)}(e,t);break;case xC.LI:case xC.DD:case xC.DT:!function(e,t){e.framesetOk=!1;const n=t.tagID;for(let t=e.openElements.stackTop;t>=0;t--){const r=e.openElements.tagIDs[t];if(n===xC.LI&&r===xC.LI||(n===xC.DD||n===xC.DT)&&(r===xC.DD||r===xC.DT)){e.openElements.generateImpliedEndTagsWithExclusion(r),e.openElements.popUntilTagNamePopped(r);break}if(r!==xC.ADDRESS&&r!==xC.DIV&&r!==xC.P&&e._isSpecialElement(e.openElements.items[t],r))break}e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML)}(e,t);break;case xC.BR:case xC.IMG:case xC.WBR:case xC.AREA:case xC.EMBED:case xC.KEYGEN:WP(e,t);break;case xC.HR:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._appendElement(t,fC.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}(e,t);break;case xC.RB:case xC.RTC:!function(e,t){e.openElements.hasInScope(xC.RUBY)&&e.openElements.generateImpliedEndTags(),e._insertElement(t,fC.HTML)}(e,t);break;case xC.RT:case xC.RP:!function(e,t){e.openElements.hasInScope(xC.RUBY)&&e.openElements.generateImpliedEndTagsWithExclusion(xC.RTC),e._insertElement(t,fC.HTML)}(e,t);break;case xC.PRE:case xC.LISTING:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML),e.skipNextNewLine=!0,e.framesetOk=!1}(e,t);break;case xC.XMP:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._reconstructActiveFormattingElements(),e.framesetOk=!1,e._switchToTextParsing(t,NC.RAWTEXT)}(e,t);break;case xC.SVG:!function(e,t){e._reconstructActiveFormattingElements(),OP(t),wP(t),t.selfClosing?e._appendElement(t,fC.SVG):e._insertElement(t,fC.SVG),t.ackSelfClosing=!0}(e,t);break;case xC.HTML:!function(e,t){0===e.openElements.tmplCount&&e.treeAdapter.adoptAttributes(e.openElements.items[0],t.attrs)}(e,t);break;case xC.BASE:case xC.LINK:case xC.META:case xC.STYLE:case xC.TITLE:case xC.SCRIPT:case xC.BGSOUND:case xC.BASEFONT:case xC.TEMPLATE:UP(e,t);break;case xC.BODY:!function(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();n&&0===e.openElements.tmplCount&&(e.framesetOk=!1,e.treeAdapter.adoptAttributes(n,t.attrs))}(e,t);break;case xC.FORM:!function(e,t){const n=e.openElements.tmplCount>0;e.formElement&&!n||(e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML),n||(e.formElement=e.openElements.current))}(e,t);break;case xC.NOBR:!function(e,t){e._reconstructActiveFormattingElements(),e.openElements.hasInScope(xC.NOBR)&&(MP(e,t),e._reconstructActiveFormattingElements()),e._insertElement(t,fC.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case xC.MATH:!function(e,t){e._reconstructActiveFormattingElements(),yP(t),wP(t),t.selfClosing?e._appendElement(t,fC.MATHML):e._insertElement(t,fC.MATHML),t.ackSelfClosing=!0}(e,t);break;case xC.TABLE:!function(e,t){e.treeAdapter.getDocumentMode(e.document)!==bC.QUIRKS&&e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML),e.framesetOk=!1,e.insertionMode=xP.IN_TABLE}(e,t);break;case xC.INPUT:!function(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,fC.HTML),XP(t)||(e.framesetOk=!1),t.ackSelfClosing=!0}(e,t);break;case xC.PARAM:case xC.TRACK:case xC.SOURCE:!function(e,t){e._appendElement(t,fC.HTML),t.ackSelfClosing=!0}(e,t);break;case xC.IMAGE:!function(e,t){t.tagName=OC.IMG,t.tagID=xC.IMG,WP(e,t)}(e,t);break;case xC.BUTTON:!function(e,t){e.openElements.hasInScope(xC.BUTTON)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(xC.BUTTON)),e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML),e.framesetOk=!1}(e,t);break;case xC.APPLET:case xC.OBJECT:case xC.MARQUEE:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML),e.activeFormattingElements.insertMarker(),e.framesetOk=!1}(e,t);break;case xC.IFRAME:!function(e,t){e.framesetOk=!1,e._switchToTextParsing(t,NC.RAWTEXT)}(e,t);break;case xC.SELECT:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML),e.framesetOk=!1,e.insertionMode=e.insertionMode===xP.IN_TABLE||e.insertionMode===xP.IN_CAPTION||e.insertionMode===xP.IN_TABLE_BODY||e.insertionMode===xP.IN_ROW||e.insertionMode===xP.IN_CELL?xP.IN_SELECT_IN_TABLE:xP.IN_SELECT}(e,t);break;case xC.OPTION:case xC.OPTGROUP:!function(e,t){e.openElements.currentTagId===xC.OPTION&&e.openElements.pop(),e._reconstructActiveFormattingElements(),e._insertElement(t,fC.HTML)}(e,t);break;case xC.NOEMBED:case xC.NOFRAMES:GP(e,t);break;case xC.FRAMESET:!function(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();e.framesetOk&&n&&(e.treeAdapter.detachNode(n),e.openElements.popAllUpToHtmlElement(),e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_FRAMESET)}(e,t);break;case xC.TEXTAREA:!function(e,t){e._insertElement(t,fC.HTML),e.skipNextNewLine=!0,e.tokenizer.state=NC.RCDATA,e.originalInsertionMode=e.insertionMode,e.framesetOk=!1,e.insertionMode=xP.TEXT}(e,t);break;case xC.NOSCRIPT:e.options.scriptingEnabled?GP(e,t):YP(e,t);break;case xC.PLAINTEXT:!function(e,t){e.openElements.hasInButtonScope(xC.P)&&e._closePElement(),e._insertElement(t,fC.HTML),e.tokenizer.state=NC.PLAINTEXT}(e,t);break;case xC.COL:case xC.TH:case xC.TD:case xC.TR:case xC.HEAD:case xC.FRAME:case xC.TBODY:case xC.TFOOT:case xC.THEAD:case xC.CAPTION:case xC.COLGROUP:break;default:YP(e,t)}}function JP(e,t){const n=t.tagName,r=t.tagID;for(let t=e.openElements.stackTop;t>0;t--){const a=e.openElements.items[t],o=e.openElements.tagIDs[t];if(r===o&&(r!==xC.UNKNOWN||e.treeAdapter.getTagName(a)===n)){e.openElements.generateImpliedEndTagsWithExclusion(r),e.openElements.stackTop>=t&&e.openElements.shortenToLength(t);break}if(e._isSpecialElement(a,o))break}}function e$(e,t){switch(t.tagID){case xC.A:case xC.B:case xC.I:case xC.S:case xC.U:case xC.EM:case xC.TT:case xC.BIG:case xC.CODE:case xC.FONT:case xC.NOBR:case xC.SMALL:case xC.STRIKE:case xC.STRONG:MP(e,t);break;case xC.P:!function(e){e.openElements.hasInButtonScope(xC.P)||e._insertFakeElement(OC.P,xC.P),e._closePElement()}(e);break;case xC.DL:case xC.UL:case xC.OL:case xC.DIR:case xC.DIV:case xC.NAV:case xC.PRE:case xC.MAIN:case xC.MENU:case xC.ASIDE:case xC.BUTTON:case xC.CENTER:case xC.FIGURE:case xC.FOOTER:case xC.HEADER:case xC.HGROUP:case xC.DIALOG:case xC.ADDRESS:case xC.ARTICLE:case xC.DETAILS:case xC.SEARCH:case xC.SECTION:case xC.SUMMARY:case xC.LISTING:case xC.FIELDSET:case xC.BLOCKQUOTE:case xC.FIGCAPTION:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n))}(e,t);break;case xC.LI:!function(e){e.openElements.hasInListItemScope(xC.LI)&&(e.openElements.generateImpliedEndTagsWithExclusion(xC.LI),e.openElements.popUntilTagNamePopped(xC.LI))}(e);break;case xC.DD:case xC.DT:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTagsWithExclusion(n),e.openElements.popUntilTagNamePopped(n))}(e,t);break;case xC.H1:case xC.H2:case xC.H3:case xC.H4:case xC.H5:case xC.H6:!function(e){e.openElements.hasNumberedHeaderInScope()&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilNumberedHeaderPopped())}(e);break;case xC.BR:!function(e){e._reconstructActiveFormattingElements(),e._insertFakeElement(OC.BR,xC.BR),e.openElements.pop(),e.framesetOk=!1}(e);break;case xC.BODY:!function(e,t){if(e.openElements.hasInScope(xC.BODY)&&(e.insertionMode=xP.AFTER_BODY,e.options.sourceCodeLocationInfo)){const n=e.openElements.tryPeekProperlyNestedBodyElement();n&&e._setEndLocation(n,t)}}(e,t);break;case xC.HTML:!function(e,t){e.openElements.hasInScope(xC.BODY)&&(e.insertionMode=xP.AFTER_BODY,y$(e,t))}(e,t);break;case xC.FORM:!function(e){const t=e.openElements.tmplCount>0,{formElement:n}=e;t||(e.formElement=null),(n||t)&&e.openElements.hasInScope(xC.FORM)&&(e.openElements.generateImpliedEndTags(),t?e.openElements.popUntilTagNamePopped(xC.FORM):n&&e.openElements.remove(n))}(e);break;case xC.APPLET:case xC.OBJECT:case xC.MARQUEE:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n),e.activeFormattingElements.clearToLastMarker())}(e,t);break;case xC.TEMPLATE:zP(e,t);break;default:JP(e,t)}}function t$(e,t){e.tmplInsertionModeStack.length>0?b$(e,t):RP(e,t)}function n$(e,t){if(void 0!==e.openElements.currentTagId&&_P.has(e.openElements.currentTagId))switch(e.pendingCharacterTokens.length=0,e.hasNonWhitespacePendingCharacterToken=!1,e.originalInsertionMode=e.insertionMode,e.insertionMode=xP.IN_TABLE_TEXT,t.type){case eC.CHARACTER:s$(e,t);break;case eC.WHITESPACE_CHARACTER:i$(e,t)}else o$(e,t)}function r$(e,t){switch(t.tagID){case xC.TD:case xC.TH:case xC.TR:!function(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(OC.TBODY,xC.TBODY),e.insertionMode=xP.IN_TABLE_BODY,p$(e,t)}(e,t);break;case xC.STYLE:case xC.SCRIPT:case xC.TEMPLATE:UP(e,t);break;case xC.COL:!function(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(OC.COLGROUP,xC.COLGROUP),e.insertionMode=xP.IN_COLUMN_GROUP,u$(e,t)}(e,t);break;case xC.FORM:!function(e,t){e.formElement||0!==e.openElements.tmplCount||(e._insertElement(t,fC.HTML),e.formElement=e.openElements.current,e.openElements.pop())}(e,t);break;case xC.TABLE:!function(e,t){e.openElements.hasInTableScope(xC.TABLE)&&(e.openElements.popUntilTagNamePopped(xC.TABLE),e._resetInsertionMode(),e._processStartTag(t))}(e,t);break;case xC.TBODY:case xC.TFOOT:case xC.THEAD:!function(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_TABLE_BODY}(e,t);break;case xC.INPUT:!function(e,t){XP(t)?e._appendElement(t,fC.HTML):o$(e,t),t.ackSelfClosing=!0}(e,t);break;case xC.CAPTION:!function(e,t){e.openElements.clearBackToTableContext(),e.activeFormattingElements.insertMarker(),e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_CAPTION}(e,t);break;case xC.COLGROUP:!function(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,fC.HTML),e.insertionMode=xP.IN_COLUMN_GROUP}(e,t);break;default:o$(e,t)}}function a$(e,t){switch(t.tagID){case xC.TABLE:e.openElements.hasInTableScope(xC.TABLE)&&(e.openElements.popUntilTagNamePopped(xC.TABLE),e._resetInsertionMode());break;case xC.TEMPLATE:zP(e,t);break;case xC.BODY:case xC.CAPTION:case xC.COL:case xC.COLGROUP:case xC.HTML:case xC.TBODY:case xC.TD:case xC.TFOOT:case xC.TH:case xC.THEAD:case xC.TR:break;default:o$(e,t)}}function o$(e,t){const n=e.fosterParentingEnabled;e.fosterParentingEnabled=!0,HP(e,t),e.fosterParentingEnabled=n}function i$(e,t){e.pendingCharacterTokens.push(t)}function s$(e,t){e.pendingCharacterTokens.push(t),e.hasNonWhitespacePendingCharacterToken=!0}function l$(e,t){let n=0;if(e.hasNonWhitespacePendingCharacterToken)for(;n0&&e.openElements.currentTagId===xC.OPTION&&e.openElements.tagIDs[e.openElements.stackTop-1]===xC.OPTGROUP&&e.openElements.pop(),e.openElements.currentTagId===xC.OPTGROUP&&e.openElements.pop();break;case xC.OPTION:e.openElements.currentTagId===xC.OPTION&&e.openElements.pop();break;case xC.SELECT:e.openElements.hasInSelectScope(xC.SELECT)&&(e.openElements.popUntilTagNamePopped(xC.SELECT),e._resetInsertionMode());break;case xC.TEMPLATE:zP(e,t)}}function b$(e,t){e.openElements.tmplCount>0?(e.openElements.popUntilTagNamePopped(xC.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e.tmplInsertionModeStack.shift(),e._resetInsertionMode(),e.onEof(t)):RP(e,t)}function y$(e,t){var n;if(t.tagID===xC.HTML){if(e.fragmentContext||(e.insertionMode=xP.AFTER_AFTER_BODY),e.options.sourceCodeLocationInfo&&e.openElements.tagIDs[0]===xC.HTML){e._setEndLocation(e.openElements.items[0],t);const r=e.openElements.items[1];r&&!(null===(n=e.treeAdapter.getNodeSourceCodeLocation(r))||void 0===n?void 0:n.endTag)&&e._setEndLocation(r,t)}}else O$(e,t)}function O$(e,t){e.insertionMode=xP.IN_BODY,HP(e,t)}function w$(e,t){e.insertionMode=xP.IN_BODY,HP(e,t)}function x$(e){for(;e.treeAdapter.getNamespaceURI(e.openElements.current)!==fC.HTML&&void 0!==e.openElements.currentTagId&&!e._isIntegrationPoint(e.openElements.currentTagId,e.openElements.current);)e.openElements.pop()}function k$(e,t){return EP.parse(e,t)}function S$(e,t,n){\"string\"==typeof e&&(n=t,t=e,e=null);const r=EP.getFragmentParser(e,n);return r.tokenizer.write(t,!0),r.getFragment()}function _$(e){return e&&\"object\"==typeof e?\"position\"in e||\"type\"in e?E$(e.position):\"start\"in e||\"end\"in e?E$(e):\"line\"in e||\"column\"in e?T$(e):\"\":\"\"}function T$(e){return A$(e&&e.line)+\":\"+A$(e&&e.column)}function E$(e){return T$(e&&e.start)+\"-\"+T$(e&&e.end)}function A$(e){return e&&\"number\"==typeof e?e:1}OC.AREA,OC.BASE,OC.BASEFONT,OC.BGSOUND,OC.BR,OC.COL,OC.EMBED,OC.FRAME,OC.HR,OC.IMG,OC.INPUT,OC.KEYGEN,OC.LINK,OC.META,OC.PARAM,OC.SOURCE,OC.TRACK,OC.WBR;class C$ extends Error{constructor(e,t,n){super(),\"string\"==typeof t&&(n=t,t=void 0);let r=\"\",a={},o=!1;if(t&&(a=\"line\"in t&&\"column\"in t||\"start\"in t&&\"end\"in t?{place:t}:\"type\"in t?{ancestors:[t],place:t.position}:{...t}),\"string\"==typeof e?r=e:!a.cause&&e&&(o=!0,r=e.message,a.cause=e),!a.ruleId&&!a.source&&\"string\"==typeof n){const e=n.indexOf(\":\");-1===e?a.ruleId=n:(a.source=n.slice(0,e),a.ruleId=n.slice(e+1))}if(!a.place&&a.ancestors&&a.ancestors){const e=a.ancestors[a.ancestors.length-1];e&&(a.place=e.position)}const i=a.place&&\"start\"in a.place?a.place.start:a.place;this.ancestors=a.ancestors||void 0,this.cause=a.cause||void 0,this.column=i?i.column:void 0,this.fatal=void 0,this.file,this.message=r,this.line=i?i.line:void 0,this.name=_$(a.place)||\"1:1\",this.place=a.place||void 0,this.reason=this.message,this.ruleId=a.ruleId||void 0,this.source=a.source||void 0,this.stack=o&&a.cause&&\"string\"==typeof a.cause.stack?a.cause.stack:\"\",this.actual,this.expected,this.note,this.url}}C$.prototype.file=\"\",C$.prototype.name=\"\",C$.prototype.reason=\"\",C$.prototype.message=\"\",C$.prototype.stack=\"\",C$.prototype.column=void 0,C$.prototype.line=void 0,C$.prototype.ancestors=void 0,C$.prototype.cause=void 0,C$.prototype.fatal=void 0,C$.prototype.place=void 0,C$.prototype.ruleId=void 0,C$.prototype.source=void 0;const P$=function(e,t){if(void 0!==t&&\"string\"!=typeof t)throw new TypeError('\"ext\" argument must be a string');N$(e);let n,r=0,a=-1,o=e.length;if(void 0===t||0===t.length||t.length>e.length){for(;o--;)if(47===e.codePointAt(o)){if(n){r=o+1;break}}else a<0&&(n=!0,a=o+1);return a<0?\"\":e.slice(r,a)}if(t===e)return\"\";let i=-1,s=t.length-1;for(;o--;)if(47===e.codePointAt(o)){if(n){r=o+1;break}}else i<0&&(n=!0,i=o+1),s>-1&&(e.codePointAt(o)===t.codePointAt(s--)?s<0&&(a=o):(s=-1,a=i));return r===a?a=i:a<0&&(a=e.length),e.slice(r,a)},$$=function(e){if(N$(e),0===e.length)return\".\";let t,n=-1,r=e.length;for(;--r;)if(47===e.codePointAt(r)){if(t){n=r;break}}else t||(t=!0);return n<0?47===e.codePointAt(0)?\"/\":\".\":1===n&&47===e.codePointAt(0)?\"//\":e.slice(0,n)},D$=function(e){N$(e);let t,n=e.length,r=-1,a=0,o=-1,i=0;for(;n--;){const s=e.codePointAt(n);if(47!==s)r<0&&(t=!0,r=n+1),46===s?o<0?o=n:1!==i&&(i=1):o>-1&&(i=-1);else if(t){a=n+1;break}}return o<0||r<0||0===i||1===i&&o===r-1&&o===a+1?\"\":e.slice(o,r)},I$=function(...e){let t,n=-1;for(;++n2){if(r=a.lastIndexOf(\"/\"),r!==a.length-1){r<0?(a=\"\",o=0):(a=a.slice(0,r),o=a.length-1-a.lastIndexOf(\"/\")),i=l,s=0;continue}}else if(a.length>0){a=\"\",o=0,i=l,s=0;continue}t&&(a=a.length>0?a+\"/..\":\"..\",o=2)}else a.length>0?a+=\"/\"+e.slice(i+1,l):a=e.slice(i+1,l),o=l-i-1;i=l,s=0}else 46===n&&s>-1?s++:s=-1}return a}(e,!t);return 0!==n.length||t||(n=\".\"),n.length>0&&47===e.codePointAt(e.length-1)&&(n+=\"/\"),t?\"/\"+n:n}(t)},M$=\"/\";function N$(e){if(\"string\"!=typeof e)throw new TypeError(\"Path must be a string. Received \"+JSON.stringify(e))}const R$=function(){return\"/\"};function L$(e){return Boolean(null!==e&&\"object\"==typeof e&&\"href\"in e&&e.href&&\"protocol\"in e&&e.protocol&&void 0===e.auth)}const B$=[\"history\",\"path\",\"basename\",\"stem\",\"extname\",\"dirname\"];class j${constructor(e){let t;t=e?L$(e)?{path:e}:\"string\"==typeof e||function(e){return Boolean(e&&\"object\"==typeof e&&\"byteLength\"in e&&\"byteOffset\"in e)}(e)?{value:e}:e:{},this.cwd=\"cwd\"in t?\"\":R$(),this.data={},this.history=[],this.messages=[],this.value,this.map,this.result,this.stored;let n,r=-1;for(;++r`\",url:!1},abruptClosingOfEmptyComment:{reason:\"Unexpected abruptly closed empty comment\",description:\"Unexpected `>` or `->`. Expected `--\\x3e` to close comments\"},abruptDoctypePublicIdentifier:{reason:\"Unexpected abruptly closed public identifier\",description:\"Unexpected `>`. Expected a closing `\\\"` or `'` after the public identifier\"},abruptDoctypeSystemIdentifier:{reason:\"Unexpected abruptly closed system identifier\",description:\"Unexpected `>`. Expected a closing `\\\"` or `'` after the identifier identifier\"},absenceOfDigitsInNumericCharacterReference:{reason:\"Unexpected non-digit at start of numeric character reference\",description:\"Unexpected `%c`. Expected `[0-9]` for decimal references or `[0-9a-fA-F]` for hexadecimal references\"},cdataInHtmlContent:{reason:\"Unexpected CDATA section in HTML\",description:\"Unexpected `` in ``\",description:\"Unexpected text character `%c`. Only use text in `