From 8057163ad0504db4942b2e912557a7f7f23fb28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 23 Aug 2025 03:16:07 +0200 Subject: [PATCH 01/67] Dependencies fix --- bun.lock | 5 ++++- packages/example/package.json | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bun.lock b/bun.lock index 04314be..661bc39 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,7 @@ }, "packages/effect-fc": { "name": "effect-fc", - "version": "0.1.2", + "version": "0.1.3", "devDependencies": { "@effect/language-service": "^0.35.2", }, @@ -51,6 +51,7 @@ "globals": "^16.3.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "type-fest": "^4.41.0", "typescript-eslint": "^8.40.0", "vite": "^7.1.3", }, @@ -799,6 +800,8 @@ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], "typescript-eslint": ["typescript-eslint@8.40.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.40.0", "@typescript-eslint/parser": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0", "@typescript-eslint/utils": "8.40.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q=="], diff --git a/packages/example/package.json b/packages/example/package.json index 80912f9..6c34e0e 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -25,6 +25,7 @@ "globals": "^16.3.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "type-fest": "^4.41.0", "typescript-eslint": "^8.40.0", "vite": "^7.1.3" }, -- 2.49.1 From 993e2c10389dca42bbb066e4d9acb72d3806f56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 24 Aug 2025 13:48:09 +0200 Subject: [PATCH 02/67] Form type --- packages/effect-fc/src/types/Form.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/effect-fc/src/types/Form.ts diff --git a/packages/effect-fc/src/types/Form.ts b/packages/effect-fc/src/types/Form.ts new file mode 100644 index 0000000..aa40de1 --- /dev/null +++ b/packages/effect-fc/src/types/Form.ts @@ -0,0 +1,15 @@ +import { Effectable, Schema, type Pipeable } from "effect" + + +export const TypeId: unique symbol = Symbol.for("effect-fc/types/Form") +export type TypeId = typeof TypeId + +export interface Form +extends Pipeable.Pipeable { + readonly schema: Schema.Schema +} + +class FormImpl +extends Effectable.Class<> implements Form<> { + readonly [TypeId]: TypeId = TypeId +} -- 2.49.1 From 3613d421f39e84e007f7f7a5be792fa020dac860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 24 Aug 2025 15:11:31 +0200 Subject: [PATCH 03/67] Form work --- packages/effect-fc/src/types/Form.ts | 16 ++++++++++++++-- packages/effect-fc/src/types/PropertyPath.ts | 14 +++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/effect-fc/src/types/Form.ts b/packages/effect-fc/src/types/Form.ts index aa40de1..23b14a0 100644 --- a/packages/effect-fc/src/types/Form.ts +++ b/packages/effect-fc/src/types/Form.ts @@ -1,4 +1,5 @@ -import { Effectable, Schema, type Pipeable } from "effect" +import { Effectable, Option, Schema, Subscribable, SubscriptionRef, type ParseResult, type Pipeable } from "effect" +import type * as PropertyPath from "./PropertyPath.js" export const TypeId: unique symbol = Symbol.for("effect-fc/types/Form") @@ -7,9 +8,20 @@ export type TypeId = typeof TypeId export interface Form extends Pipeable.Pipeable { readonly schema: Schema.Schema + readonly valueRef: SubscriptionRef.SubscriptionRef + readonly errorSubscribable: Subscribable.Subscribable> + + useRef

>(path: P): SubscriptionRef.SubscriptionRef> + useIssues(path: PropertyPath.Paths): readonly ParseResult.ParseIssue[] } class FormImpl -extends Effectable.Class<> implements Form<> { +extends Effectable.Class<> implements Form { readonly [TypeId]: TypeId = TypeId } + + +const TestSchema = Schema.Struct({ + name: Schema.String +}) +declare const form: Form diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/types/PropertyPath.ts index 6b59d37..a779e1e 100644 --- a/packages/effect-fc/src/types/PropertyPath.ts +++ b/packages/effect-fc/src/types/PropertyPath.ts @@ -3,9 +3,9 @@ import { Array, Function, Option, Predicate } from "effect" type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -export type Paths = [] | ( - D extends never ? [] : - T extends Seen ? [] : +export type Paths = readonly [] | ( + D extends never ? readonly [] : + T extends Seen ? readonly [] : T extends readonly any[] ? ArrayPaths : T extends object ? ObjectPaths : never @@ -13,8 +13,8 @@ export type Paths = [] | ( export type ArrayPaths = { [K in keyof T as K extends number ? K : never]: - | [K] - | [K, ...Paths] + | readonly [K] + | readonly [K, ...Paths] } extends infer O ? O[keyof O] : never @@ -22,13 +22,13 @@ export type ArrayPaths = { export type ObjectPaths = { [K in keyof T as K extends string | number | symbol ? K : never]-?: NonNullable extends infer V - ? [K] | [K, ...Paths] + ? readonly [K] | readonly [K, ...Paths] : never } extends infer O ? O[keyof O] : never -export type ValueFromPath = P extends [infer Head, ...infer Tail] +export type ValueFromPath = P extends [infer Head, ...infer Tail] ? Head extends keyof T ? ValueFromPath : T extends readonly any[] -- 2.49.1 From 4845e34b0aedb82c00dafd7981552e4db9c05493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 25 Aug 2025 05:24:42 +0200 Subject: [PATCH 04/67] Form work --- packages/effect-fc/src/types/Form.ts | 26 +++++- packages/effect-fc/src/types/Schema.ts | 118 +++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 packages/effect-fc/src/types/Schema.ts diff --git a/packages/effect-fc/src/types/Form.ts b/packages/effect-fc/src/types/Form.ts index 23b14a0..676af19 100644 --- a/packages/effect-fc/src/types/Form.ts +++ b/packages/effect-fc/src/types/Form.ts @@ -1,4 +1,4 @@ -import { Effectable, Option, Schema, Subscribable, SubscriptionRef, type ParseResult, type Pipeable } from "effect" +import { Effect, Option, Pipeable, Schema, Subscribable, SubscriptionRef, type ParseResult } from "effect" import type * as PropertyPath from "./PropertyPath.js" @@ -11,13 +11,31 @@ extends Pipeable.Pipeable { readonly valueRef: SubscriptionRef.SubscriptionRef readonly errorSubscribable: Subscribable.Subscribable> - useRef

>(path: P): SubscriptionRef.SubscriptionRef> - useIssues(path: PropertyPath.Paths): readonly ParseResult.ParseIssue[] + useRef

>(path: P): Effect.Effect>> + useIssues(path: PropertyPath.Paths): Effect.Effect } class FormImpl -extends Effectable.Class<> implements Form { +extends Pipeable.Class() implements Form { readonly [TypeId]: TypeId = TypeId + + constructor( + readonly schema: Schema.Schema, + readonly valueRef: SubscriptionRef.SubscriptionRef, + readonly errorSubscribable: Subscribable.Subscribable> + ) { + super() + } + + useRef

>( + path: P + ): Effect.Effect>, never, never> { + throw new Error("Method not implemented.") + } + + useIssues(path: PropertyPath.Paths): Effect.Effect { + throw new Error("Method not implemented.") + } } diff --git a/packages/effect-fc/src/types/Schema.ts b/packages/effect-fc/src/types/Schema.ts new file mode 100644 index 0000000..f073117 --- /dev/null +++ b/packages/effect-fc/src/types/Schema.ts @@ -0,0 +1,118 @@ +import { Array, Function, Option, Predicate, Schema } from "effect" + + +type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +export type Paths = readonly [] | ( + D extends never ? readonly [] : + S extends Seen ? readonly [] : + S extends Schema.Union ? UnionPaths : + S extends Schema.TupleType ? FormField.TupleFormField : + S extends Schema.Array$ ? FormField.ArrayFormField : + S extends Schema.Struct ? FormField.StructFormField : + S extends Schema.Schema.Any ? FormField.GenericFormField : + S extends Schema.PropertySignature< + infer TypeToken, + infer Type, + infer Key, + infer EncodedToken, + infer Encoded, + infer HasDefault, + infer R + > ? FormField.PropertySignatureFormField : + never +) + +export type UnionPaths, D extends number, Seen> = { + [K in keyof T as K extends number ? K : never]: + | readonly [K] + | readonly [K, ...Paths] +} extends infer O + ? O[keyof O] + : never + +export type ArrayPaths = { + [K in keyof T as K extends number ? K : never]: + | readonly [K] + | readonly [K, ...Paths] +} extends infer O + ? O[keyof O] + : never + +export type ObjectPaths = { + [K in keyof T as K extends string | number | symbol ? K : never]-?: + NonNullable extends infer V + ? readonly [K] | readonly [K, ...Paths] + : never +} extends infer O + ? O[keyof O] + : never + +export type ValueFromPath = P extends [infer Head, ...infer Tail] + ? Head extends keyof T + ? ValueFromPath + : T extends readonly any[] + ? Head extends number + ? ValueFromPath + : never + : never + : T + +export type AnyPath = readonly PropertyKey[] + + +export const unsafeGet: { + >(path: P): (self: T) => ValueFromPath + >(self: T, path: P): ValueFromPath +} = Function.dual(2, >(self: T, path: P): ValueFromPath => + path.reduce((acc: any, key: any) => acc?.[key], self) +) + +export const get: { + >(path: P): (self: T) => Option.Option> + >(self: T, path: P): Option.Option> +} = Function.dual(2, >(self: T, path: P): Option.Option> => + path.reduce( + (acc: Option.Option, key: any): Option.Option => Option.isSome(acc) + ? Predicate.hasProperty(acc.value, key) + ? Option.some(acc.value[key]) + : Option.none() + : acc, + + Option.some(self), + ) +) + +export const immutableSet: { + >(path: P, value: ValueFromPath): (self: T) => ValueFromPath + >(self: T, path: P, value: ValueFromPath): Option.Option +} = Function.dual(3, >(self: T, path: P, value: ValueFromPath): Option.Option => { + const key = Array.head(path as AnyPath) + if (Option.isNone(key)) + return Option.some(value as T) + if (!Predicate.hasProperty(self, key.value)) + return Option.none() + + const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path as AnyPath)), value) + if (Option.isNone(child)) + return child + + if (Array.isArray(self)) + return typeof key.value === "number" + ? Option.some([ + ...self.slice(0, key.value), + child.value, + ...self.slice(key.value + 1), + ] as T) + : Option.none() + + if (typeof self === "object") + return Option.some( + Object.assign( + Object.create(Object.getPrototypeOf(self)), + { ...self, [key.value]: child.value }, + ) + ) + + return Option.none() +}) -- 2.49.1 From ae3aa00a0661e548af359486ec032f7b2c78d734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 25 Aug 2025 18:23:20 +0200 Subject: [PATCH 05/67] Refactoring --- packages/effect-fc/src/types/PropertyPath.ts | 36 +++++++++----------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/types/PropertyPath.ts index a779e1e..0f26cf8 100644 --- a/packages/effect-fc/src/types/PropertyPath.ts +++ b/packages/effect-fc/src/types/PropertyPath.ts @@ -6,28 +6,26 @@ type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] export type Paths = readonly [] | ( D extends never ? readonly [] : T extends Seen ? readonly [] : - T extends readonly any[] ? ArrayPaths : - T extends object ? ObjectPaths : + T extends readonly any[] ? { + [K in keyof T as K extends number ? K : never]: + | readonly [K] + | readonly [K, ...Paths] + } extends infer O + ? O[keyof O] + : never + : + T extends object ? { + [K in keyof T as K extends string | number | symbol ? K : never]-?: + NonNullable extends infer V + ? readonly [K] | readonly [K, ...Paths] + : never + } extends infer O + ? O[keyof O] + : never + : never ) -export type ArrayPaths = { - [K in keyof T as K extends number ? K : never]: - | readonly [K] - | readonly [K, ...Paths] -} extends infer O - ? O[keyof O] - : never - -export type ObjectPaths = { - [K in keyof T as K extends string | number | symbol ? K : never]-?: - NonNullable extends infer V - ? readonly [K] | readonly [K, ...Paths] - : never -} extends infer O - ? O[keyof O] - : never - export type ValueFromPath = P extends [infer Head, ...infer Tail] ? Head extends keyof T ? ValueFromPath -- 2.49.1 From 30f3ef2353569dc25aa42ba9eddd66d51750793a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 25 Aug 2025 23:06:44 +0200 Subject: [PATCH 06/67] Tests --- packages/effect-fc/src/types/Schema.ts | 121 +++++++------------------ 1 file changed, 31 insertions(+), 90 deletions(-) diff --git a/packages/effect-fc/src/types/Schema.ts b/packages/effect-fc/src/types/Schema.ts index f073117..d2ee2bb 100644 --- a/packages/effect-fc/src/types/Schema.ts +++ b/packages/effect-fc/src/types/Schema.ts @@ -1,4 +1,5 @@ import { Array, Function, Option, Predicate, Schema } from "effect" +import type { Simplify } from "effect/Types" type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -23,96 +24,36 @@ export type Paths = readonly [] | ( never ) -export type UnionPaths, D extends number, Seen> = { - [K in keyof T as K extends number ? K : never]: - | readonly [K] - | readonly [K, ...Paths] -} extends infer O - ? O[keyof O] - : never - -export type ArrayPaths = { - [K in keyof T as K extends number ? K : never]: - | readonly [K] - | readonly [K, ...Paths] -} extends infer O - ? O[keyof O] - : never - -export type ObjectPaths = { - [K in keyof T as K extends string | number | symbol ? K : never]-?: - NonNullable extends infer V - ? readonly [K] | readonly [K, ...Paths] - : never -} extends infer O - ? O[keyof O] - : never - -export type ValueFromPath = P extends [infer Head, ...infer Tail] - ? Head extends keyof T - ? ValueFromPath - : T extends readonly any[] - ? Head extends number - ? ValueFromPath - : never - : never - : T - -export type AnyPath = readonly PropertyKey[] - - -export const unsafeGet: { - >(path: P): (self: T) => ValueFromPath - >(self: T, path: P): ValueFromPath -} = Function.dual(2, >(self: T, path: P): ValueFromPath => - path.reduce((acc: any, key: any) => acc?.[key], self) -) - -export const get: { - >(path: P): (self: T) => Option.Option> - >(self: T, path: P): Option.Option> -} = Function.dual(2, >(self: T, path: P): Option.Option> => - path.reduce( - (acc: Option.Option, key: any): Option.Option => Option.isSome(acc) - ? Predicate.hasProperty(acc.value, key) - ? Option.some(acc.value[key]) - : Option.none() - : acc, - - Option.some(self), - ) -) - -export const immutableSet: { - >(path: P, value: ValueFromPath): (self: T) => ValueFromPath - >(self: T, path: P, value: ValueFromPath): Option.Option -} = Function.dual(3, >(self: T, path: P, value: ValueFromPath): Option.Option => { - const key = Array.head(path as AnyPath) - if (Option.isNone(key)) - return Option.some(value as T) - if (!Predicate.hasProperty(self, key.value)) - return Option.none() - - const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path as AnyPath)), value) - if (Option.isNone(child)) - return child - - if (Array.isArray(self)) - return typeof key.value === "number" - ? Option.some([ - ...self.slice(0, key.value), - child.value, - ...self.slice(key.value + 1), - ] as T) - : Option.none() - - if (typeof self === "object") - return Option.some( - Object.assign( - Object.create(Object.getPrototypeOf(self)), - { ...self, [key.value]: child.value }, +export type SchemaFromPath = S extends Schema.Schema.Any + ? P extends [infer Head, ...infer Tail] + ? Head extends keyof S["Type"] + ? ( + S extends Schema.TupleType ? ( + Head extends keyof Elements ? SchemaFromPath : + Head extends keyof Rest ? SchemaFromPath : + never + ) : + S extends Schema.Array$ ? SchemaFromPath : + S extends Schema.Struct ? SchemaFromPath : + never ) - ) + : never + : S + : never - return Option.none() +const TestSchema = Schema.Struct({ + allUsers: Schema.Array(Schema.Struct({ + name: Schema.String + })), + + admins: Schema.Tuple( + Schema.Struct({ + name: Schema.Literal("Gneugneu") + }), + Schema.Struct({ + name: Schema.Literal("AAAAYA") + }), + ), }) +type S = SchemaFromPath +type T = number extends keyof typeof TestSchema.fields.admins.elements ? true : false -- 2.49.1 From edb4b7ccd8e0f0aefe497240df4750266bad692c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 26 Aug 2025 04:13:22 +0200 Subject: [PATCH 07/67] Form work --- packages/effect-fc/src/types/Form.ts | 37 ++++++++++++++------ packages/effect-fc/src/types/PropertyPath.ts | 10 +++--- packages/effect-fc/src/types/Schema.ts | 22 ------------ 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/packages/effect-fc/src/types/Form.ts b/packages/effect-fc/src/types/Form.ts index 676af19..e3af1d8 100644 --- a/packages/effect-fc/src/types/Form.ts +++ b/packages/effect-fc/src/types/Form.ts @@ -1,8 +1,11 @@ -import { Effect, Option, Pipeable, Schema, Subscribable, SubscriptionRef, type ParseResult } from "effect" -import type * as PropertyPath from "./PropertyPath.js" +import { Array, Effect, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" +import * as React from "react" +import { SubscriptionSubRef } from "./index.js" +import * as PropertyPath from "./PropertyPath.js" +import * as InternalSubscribable from "./Subscribable.js" -export const TypeId: unique symbol = Symbol.for("effect-fc/types/Form") +export const TypeId: unique symbol = Symbol.for("effect-fc/Form") export type TypeId = typeof TypeId export interface Form @@ -11,8 +14,8 @@ extends Pipeable.Pipeable { readonly valueRef: SubscriptionRef.SubscriptionRef readonly errorSubscribable: Subscribable.Subscribable> - useRef

>(path: P): Effect.Effect>> - useIssues(path: PropertyPath.Paths): Effect.Effect + useRef

>(path: P): SubscriptionRef.SubscriptionRef> + useIssuesSubscribable(path: PropertyPath.Paths): Subscribable.Subscribable } class FormImpl @@ -27,14 +30,26 @@ extends Pipeable.Class() implements Form { super() } - useRef

>( - path: P - ): Effect.Effect>, never, never> { - throw new Error("Method not implemented.") + useRef

>(path: P) { + return React.useMemo(() => SubscriptionSubRef.makeFromPath(this.valueRef, path), [this.valueRef, ...path]) } - useIssues(path: PropertyPath.Paths): Effect.Effect { - throw new Error("Method not implemented.") + useIssuesSubscribable(path: PropertyPath.Paths) { + return React.useMemo(() => { + const filter = Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }) + + const errorSubscribable = this.errorSubscribable + return InternalSubscribable.make({ + get: Effect.andThen(errorSubscribable.get, filter), + get changes() { return Stream.flatMap(errorSubscribable.changes, filter) }, + }) + }, [this.errorSubscribable, ...path]) } } diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/types/PropertyPath.ts index 0f26cf8..08c00ac 100644 --- a/packages/effect-fc/src/types/PropertyPath.ts +++ b/packages/effect-fc/src/types/PropertyPath.ts @@ -1,6 +1,8 @@ -import { Array, Function, Option, Predicate } from "effect" +import { Array, Equivalence, Function, Option, Predicate } from "effect" +export type PropertyPath = readonly PropertyKey[] + type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] export type Paths = readonly [] | ( @@ -36,8 +38,8 @@ export type ValueFromPath = P extends [infer Head, : never : T -export type AnyPath = readonly PropertyKey[] +export const equivalence: Equivalence.Equivalence = Equivalence.array(Equivalence.strict()) export const unsafeGet: { >(path: P): (self: T) => ValueFromPath @@ -65,13 +67,13 @@ export const immutableSet: { >(path: P, value: ValueFromPath): (self: T) => ValueFromPath >(self: T, path: P, value: ValueFromPath): Option.Option } = Function.dual(3, >(self: T, path: P, value: ValueFromPath): Option.Option => { - const key = Array.head(path as AnyPath) + const key = Array.head(path as PropertyPath) if (Option.isNone(key)) return Option.some(value as T) if (!Predicate.hasProperty(self, key.value)) return Option.none() - const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path as AnyPath)), value) + const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path as PropertyPath)), value) if (Option.isNone(child)) return child diff --git a/packages/effect-fc/src/types/Schema.ts b/packages/effect-fc/src/types/Schema.ts index d2ee2bb..65ad399 100644 --- a/packages/effect-fc/src/types/Schema.ts +++ b/packages/effect-fc/src/types/Schema.ts @@ -2,28 +2,6 @@ import { Array, Function, Option, Predicate, Schema } from "effect" import type { Simplify } from "effect/Types" -type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - -export type Paths = readonly [] | ( - D extends never ? readonly [] : - S extends Seen ? readonly [] : - S extends Schema.Union ? UnionPaths : - S extends Schema.TupleType ? FormField.TupleFormField : - S extends Schema.Array$ ? FormField.ArrayFormField : - S extends Schema.Struct ? FormField.StructFormField : - S extends Schema.Schema.Any ? FormField.GenericFormField : - S extends Schema.PropertySignature< - infer TypeToken, - infer Type, - infer Key, - infer EncodedToken, - infer Encoded, - infer HasDefault, - infer R - > ? FormField.PropertySignatureFormField : - never -) - export type SchemaFromPath = S extends Schema.Schema.Any ? P extends [infer Head, ...infer Tail] ? Head extends keyof S["Type"] -- 2.49.1 From 7c5a1187174e619914dfe2865e13505a7d254202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 26 Aug 2025 04:15:23 +0200 Subject: [PATCH 08/67] Moved Form --- packages/effect-fc/src/{types => }/Form.ts | 6 ++---- packages/effect-fc/src/index.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) rename packages/effect-fc/src/{types => }/Form.ts (91%) diff --git a/packages/effect-fc/src/types/Form.ts b/packages/effect-fc/src/Form.ts similarity index 91% rename from packages/effect-fc/src/types/Form.ts rename to packages/effect-fc/src/Form.ts index e3af1d8..9111990 100644 --- a/packages/effect-fc/src/types/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,8 +1,6 @@ import { Array, Effect, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" import * as React from "react" -import { SubscriptionSubRef } from "./index.js" -import * as PropertyPath from "./PropertyPath.js" -import * as InternalSubscribable from "./Subscribable.js" +import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Form") @@ -45,7 +43,7 @@ extends Pipeable.Class() implements Form { }) const errorSubscribable = this.errorSubscribable - return InternalSubscribable.make({ + return SubscribableInternal.make({ get: Effect.andThen(errorSubscribable.get, filter), get changes() { return Stream.flatMap(errorSubscribable.changes, filter) }, }) diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 2545011..1595021 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,4 +1,5 @@ export * as Component from "./Component.js" +export * as Form from "./Form.js" export * as Memo from "./Memo.js" export * as ReactRuntime from "./ReactRuntime.js" export * as Suspense from "./Suspense.js" -- 2.49.1 From e61d8ecae12e5689cc4d406df93bad0e2cfa866c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 26 Aug 2025 05:35:53 +0200 Subject: [PATCH 09/67] Form work --- packages/effect-fc/src/Form.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 9111990..70af85e 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -56,3 +56,4 @@ const TestSchema = Schema.Struct({ name: Schema.String }) declare const form: Form +const t = form.useRef(["name"]) -- 2.49.1 From fa6dc2a92197ee0f6c0a1f45bd8178c4478b2f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:08:37 +0200 Subject: [PATCH 10/67] Form work --- packages/effect-fc/src/Form.ts | 141 +++++++++++++++---- packages/effect-fc/src/types/PropertyPath.ts | 2 +- 2 files changed, 117 insertions(+), 26 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 70af85e..127a54b 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,7 @@ -import { Array, Effect, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Effect, Either, Equivalence, flow, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" +import type { NoSuchElementException } from "effect/Cause" import * as React from "react" +import { Hooks } from "./hooks/index.js" import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" @@ -9,45 +11,134 @@ export type TypeId = typeof TypeId export interface Form extends Pipeable.Pipeable { readonly schema: Schema.Schema - readonly valueRef: SubscriptionRef.SubscriptionRef - readonly errorSubscribable: Subscribable.Subscribable> + readonly latestValueSubscribable: Subscribable.Subscribable> - useRef

>(path: P): SubscriptionRef.SubscriptionRef> - useIssuesSubscribable(path: PropertyPath.Paths): Subscribable.Subscribable + readonly fieldLatestValueSubscribable:

>>( + path: P + ) => Effect.Effect, P>, NoSuchElementException>> + readonly fieldIssuesSubscribable: ( + path: PropertyPath.Paths> + ) => Effect.Effect> + + useInput

>>( + path: P + ): Effect.Effect< + Form.useInput.Result, P>>, + ParseResult.ParseError | NoSuchElementException, + R + > } +export namespace Form { + export namespace useInput { + export interface Result { + readonly value: I + readonly setValue: React.Dispatch> + } + } +} + + class FormImpl extends Pipeable.Class() implements Form { readonly [TypeId]: TypeId = TypeId + readonly latestValueSubscribable: Subscribable.Subscribable> constructor( readonly schema: Schema.Schema, - readonly valueRef: SubscriptionRef.SubscriptionRef, - readonly errorSubscribable: Subscribable.Subscribable> + readonly latestValueRef: SubscriptionRef.SubscriptionRef>, + readonly errorRef: SubscriptionRef.SubscriptionRef>, + + readonly fieldLatestValueSubscribable:

>>( + path: P + ) => Effect.Effect, P>, NoSuchElementException>>, + readonly fieldIssuesSubscribable: ( + path: PropertyPath.Paths> + ) => Effect.Effect>, ) { super() + this.latestValueSubscribable = latestValueRef } - useRef

>(path: P) { - return React.useMemo(() => SubscriptionSubRef.makeFromPath(this.valueRef, path), [this.valueRef, ...path]) - } + useInput

>(path: P) { + const self = this + return Effect.gen(function*() { + const issuesSubscribable = yield* self.fieldIssuesSubscribable(path) + const internalValueRef = yield* Hooks.useMemo(() => self.latestValueRef.pipe( + Effect.andThen(Schema.encode(self.schema)), + Effect.andThen(PropertyPath.get(path)), + Effect.andThen(SubscriptionRef.make>), + ), [self.latestValueRef, ...path]) - useIssuesSubscribable(path: PropertyPath.Paths) { - return React.useMemo(() => { - const filter = Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + const [value, setValue] = yield* Hooks.useRefState(internalValueRef) + const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) + + yield* Hooks.useFork(() => Stream.runForEach( + internalValueRef.changes.pipe( + Stream.changesWith(Equivalence.strict()), + // options.debounce ? Stream.debounce(options.debounce) : identity, + Stream.drop(1), ), - onNone: () => Effect.succeed([]), - }) + internalValue => self.latestValueRef.pipe( + Effect.andThen(Schema.encode(self.schema)), + Effect.andThen(PropertyPath.immutableSet(path, internalValue)), + Effect.andThen(flow( + Schema.decode(self.schema), + Effect.andThen(v => SubscriptionRef.set(self.latestValueRef, v)), + Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) + )), + ), + ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...path]) - const errorSubscribable = this.errorSubscribable - return SubscribableInternal.make({ - get: Effect.andThen(errorSubscribable.get, filter), - get changes() { return Stream.flatMap(errorSubscribable.changes, filter) }, - }) - }, [this.errorSubscribable, ...path]) + return { value, setValue, issues } + }) + } +} + + +export const make = ( + options: make.Options +): Effect.Effect> => Effect.gen(function*() { + const latestValueRef = yield* SubscriptionRef.make(options.initialValue) + const errorRef = yield* SubscriptionRef.make(Option.none()) + + return new FormImpl( + options.schema, + latestValueRef, + errorRef, + + yield* Effect.cachedFunction( + (path: PropertyPath.PropertyPath) => Effect.succeed(SubscribableInternal.make({ + get: Effect.flatMap(latestValueRef.get, PropertyPath.get(path as PropertyPath.Paths)), + get changes() { return Stream.flatMap(latestValueRef.changes, PropertyPath.get(path as PropertyPath.Paths)) }, + })), + PropertyPath.equivalence, + ), + yield* Effect.cachedFunction( + (path: PropertyPath.PropertyPath) => Effect.gen(function*() { + const filter = Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }) + + return SubscribableInternal.make({ + get: Effect.flatMap(errorRef.get, filter), + get changes() { return Stream.flatMap(errorRef.changes, filter) }, + }) + }), + PropertyPath.equivalence, + ), + ) +}) + +export namespace make { + export interface Options { + readonly schema: Schema.Schema + readonly initialValue: NoInfer } } @@ -56,4 +147,4 @@ const TestSchema = Schema.Struct({ name: Schema.String }) declare const form: Form -const t = form.useRef(["name"]) +const t = form.useInput(["name"]) diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/types/PropertyPath.ts index 08c00ac..af47a8c 100644 --- a/packages/effect-fc/src/types/PropertyPath.ts +++ b/packages/effect-fc/src/types/PropertyPath.ts @@ -64,7 +64,7 @@ export const get: { ) export const immutableSet: { - >(path: P, value: ValueFromPath): (self: T) => ValueFromPath + >(path: P, value: ValueFromPath): (self: T) => Option.Option >(self: T, path: P, value: ValueFromPath): Option.Option } = Function.dual(3, >(self: T, path: P, value: ValueFromPath): Option.Option => { const key = Array.head(path as PropertyPath) -- 2.49.1 From 483e90dc67de1149eea9bfef357832b8ad0b7a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:25:22 +0200 Subject: [PATCH 11/67] Form done --- packages/effect-fc/src/Form.ts | 85 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 127a54b..1237c9e 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,8 +1,8 @@ -import { Array, Effect, Either, Equivalence, flow, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Effect, Equivalence, flow, Option, ParseResult, Pipeable, Schema, Stream, type Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" -import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" +import { PropertyPath, Subscribable as SubscribableInternal } from "./types/index.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Form") @@ -13,12 +13,12 @@ extends Pipeable.Pipeable { readonly schema: Schema.Schema readonly latestValueSubscribable: Subscribable.Subscribable> - readonly fieldLatestValueSubscribable:

>>( + useFieldLatestValueSubscribable

>>( path: P - ) => Effect.Effect, P>, NoSuchElementException>> - readonly fieldIssuesSubscribable: ( + ): Subscribable.Subscribable, P>, NoSuchElementException> + useFieldIssuesSubscribable( path: PropertyPath.Paths> - ) => Effect.Effect> + ): Subscribable.Subscribable useInput

>>( path: P @@ -48,27 +48,52 @@ extends Pipeable.Class() implements Form { readonly schema: Schema.Schema, readonly latestValueRef: SubscriptionRef.SubscriptionRef>, readonly errorRef: SubscriptionRef.SubscriptionRef>, - - readonly fieldLatestValueSubscribable:

>>( - path: P - ) => Effect.Effect, P>, NoSuchElementException>>, - readonly fieldIssuesSubscribable: ( - path: PropertyPath.Paths> - ) => Effect.Effect>, ) { super() this.latestValueSubscribable = latestValueRef } + useFieldLatestValueSubscribable

>>( + path: P + ) { + return React.useMemo(() => { + const latestValueRef = this.latestValueRef + return SubscribableInternal.make({ + get: Effect.flatMap(latestValueRef.get, PropertyPath.get(path)), + get changes() { return Stream.flatMap(latestValueRef.changes, PropertyPath.get(path)) }, + }) + }, [this.latestValueRef, ...path]) + } + + useFieldIssuesSubscribable( + path: PropertyPath.Paths> + ) { + return React.useMemo(() => { + const filter = Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }) + + const errorRef = this.errorRef + return SubscribableInternal.make({ + get: Effect.flatMap(errorRef.get, filter), + get changes() { return Stream.flatMap(errorRef.changes, filter) }, + }) + }, [this.latestValueRef, ...path]) + } + useInput

>(path: P) { const self = this return Effect.gen(function*() { - const issuesSubscribable = yield* self.fieldIssuesSubscribable(path) const internalValueRef = yield* Hooks.useMemo(() => self.latestValueRef.pipe( Effect.andThen(Schema.encode(self.schema)), Effect.andThen(PropertyPath.get(path)), Effect.andThen(SubscriptionRef.make>), ), [self.latestValueRef, ...path]) + const issuesSubscribable = self.useFieldIssuesSubscribable(path) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) @@ -107,31 +132,6 @@ export const make = ( options.schema, latestValueRef, errorRef, - - yield* Effect.cachedFunction( - (path: PropertyPath.PropertyPath) => Effect.succeed(SubscribableInternal.make({ - get: Effect.flatMap(latestValueRef.get, PropertyPath.get(path as PropertyPath.Paths)), - get changes() { return Stream.flatMap(latestValueRef.changes, PropertyPath.get(path as PropertyPath.Paths)) }, - })), - PropertyPath.equivalence, - ), - yield* Effect.cachedFunction( - (path: PropertyPath.PropertyPath) => Effect.gen(function*() { - const filter = Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), - ), - onNone: () => Effect.succeed([]), - }) - - return SubscribableInternal.make({ - get: Effect.flatMap(errorRef.get, filter), - get changes() { return Stream.flatMap(errorRef.changes, filter) }, - }) - }), - PropertyPath.equivalence, - ), ) }) @@ -141,10 +141,3 @@ export namespace make { readonly initialValue: NoInfer } } - - -const TestSchema = Schema.Struct({ - name: Schema.String -}) -declare const form: Form -const t = form.useInput(["name"]) -- 2.49.1 From ecc37515b87b8e8199226affb8af159b5046dc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:27:55 +0200 Subject: [PATCH 12/67] Fix --- packages/effect-fc/src/Form.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 1237c9e..52542cc 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -122,16 +122,13 @@ extends Pipeable.Class() implements Form { } -export const make = ( - options: make.Options -): Effect.Effect> => Effect.gen(function*() { - const latestValueRef = yield* SubscriptionRef.make(options.initialValue) - const errorRef = yield* SubscriptionRef.make(Option.none()) - +export const make: { + (options: make.Options): Effect.Effect> +} = Effect.fnUntraced(function* (options: make.Options) { return new FormImpl( options.schema, - latestValueRef, - errorRef, + yield* SubscriptionRef.make(options.initialValue), + yield* SubscriptionRef.make(Option.none()), ) }) -- 2.49.1 From 64114f02087c2342ba25f839c1643b43d7bb4fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:40:39 +0200 Subject: [PATCH 13/67] Form tests --- packages/example/src/routeTree.gen.ts | 28 ++++++++++++- packages/example/src/routes/form.tsx | 57 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 packages/example/src/routes/form.tsx diff --git a/packages/example/src/routeTree.gen.ts b/packages/example/src/routeTree.gen.ts index 7f917e3..d29c115 100644 --- a/packages/example/src/routeTree.gen.ts +++ b/packages/example/src/routeTree.gen.ts @@ -9,12 +9,18 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as FormRouteImport } from './routes/form' import { Route as BlankRouteImport } from './routes/blank' import { Route as IndexRouteImport } from './routes/index' import { Route as DevMemoRouteImport } from './routes/dev/memo' import { Route as DevInputRouteImport } from './routes/dev/input' import { Route as DevAsyncRenderingRouteImport } from './routes/dev/async-rendering' +const FormRoute = FormRouteImport.update({ + id: '/form', + path: '/form', + getParentRoute: () => rootRouteImport, +} as any) const BlankRoute = BlankRouteImport.update({ id: '/blank', path: '/blank', @@ -44,6 +50,7 @@ const DevAsyncRenderingRoute = DevAsyncRenderingRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -51,6 +58,7 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -59,6 +67,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -68,15 +77,23 @@ export interface FileRouteTypes { fullPaths: | '/' | '/blank' + | '/form' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' fileRoutesByTo: FileRoutesByTo - to: '/' | '/blank' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' + to: + | '/' + | '/blank' + | '/form' + | '/dev/async-rendering' + | '/dev/input' + | '/dev/memo' id: | '__root__' | '/' | '/blank' + | '/form' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' @@ -85,6 +102,7 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlankRoute: typeof BlankRoute + FormRoute: typeof FormRoute DevAsyncRenderingRoute: typeof DevAsyncRenderingRoute DevInputRoute: typeof DevInputRoute DevMemoRoute: typeof DevMemoRoute @@ -92,6 +110,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/form': { + id: '/form' + path: '/form' + fullPath: '/form' + preLoaderRoute: typeof FormRouteImport + parentRoute: typeof rootRouteImport + } '/blank': { id: '/blank' path: '/blank' @@ -133,6 +158,7 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlankRoute: BlankRoute, + FormRoute: FormRoute, DevAsyncRenderingRoute: DevAsyncRenderingRoute, DevInputRoute: DevInputRoute, DevMemoRoute: DevMemoRoute, diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx new file mode 100644 index 0000000..e1cd935 --- /dev/null +++ b/packages/example/src/routes/form.tsx @@ -0,0 +1,57 @@ +import { runtime } from "@/runtime" +import { Container, Flex, TextField } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect, Schema, Stream } from "effect" +import { Component, Form } from "effect-fc" +import { useContext, useFork } from "effect-fc/hooks" + + +const LoginFormSchema = Schema.Struct({ + email: Schema.String, + password: Schema.String, +}) + +class LoginForm extends Effect.Service()("LoginForm", { + scoped: Form.make({ + schema: LoginFormSchema, + initialValue: { email: "", password: "" }, + }) +}) {} + +class LoginFormComponent extends Component.makeUntraced(function* LoginFormComponent() { + const form = yield* LoginForm + const emailInput = yield* form.useInput(["email"]) + const passwordInput = yield* form.useInput(["password"]) + + yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log)) + + return ( + + + emailInput.setValue(e.target.value)} + /> + + passwordInput.setValue(e.target.value)} + /> + + + ) +}) {} + + +const FormRoute = Component.makeUntraced(function* FormRoute() { + const context = yield* useContext(LoginForm.Default) + const LoginFormComponentFC = yield* Effect.provide(LoginFormComponent, context) + + return +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/form")({ + component: FormRoute +}) -- 2.49.1 From 8ba0189472233fee3a9d892b6b700b1a569dd7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:41:31 +0200 Subject: [PATCH 14/67] Fix --- packages/example/src/routes/form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index e1cd935..5a2880a 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -23,7 +23,7 @@ class LoginFormComponent extends Component.makeUntraced(function* LoginFormCompo const emailInput = yield* form.useInput(["email"]) const passwordInput = yield* form.useInput(["password"]) - yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log)) + yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log), []) return ( -- 2.49.1 From a11180d03eb288c69c3976dd303a4d4ebed6b804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 06:11:15 +0200 Subject: [PATCH 15/67] Form tests --- packages/effect-fc/src/Form.ts | 1 + packages/example/src/routes/form.tsx | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 52542cc..e0ab185 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -34,6 +34,7 @@ export namespace Form { export interface Result { readonly value: I readonly setValue: React.Dispatch> + readonly issues: readonly ParseResult.ArrayFormatterIssue[] } } } diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 5a2880a..2cfb956 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,20 +1,20 @@ import { runtime } from "@/runtime" -import { Container, Flex, TextField } from "@radix-ui/themes" +import { Callout, Container, Flex, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Console, Effect, Schema, Stream } from "effect" +import { Array, Console, Effect, Option, Schema, Stream } from "effect" import { Component, Form } from "effect-fc" import { useContext, useFork } from "effect-fc/hooks" const LoginFormSchema = Schema.Struct({ email: Schema.String, - password: Schema.String, + password: Schema.String.pipe(Schema.minLength(3)), }) class LoginForm extends Effect.Service()("LoginForm", { scoped: Form.make({ schema: LoginFormSchema, - initialValue: { email: "", password: "" }, + initialValue: { email: "", password: "xxx" }, }) }) {} @@ -33,10 +33,30 @@ class LoginFormComponent extends Component.makeUntraced(function* LoginFormCompo onChange={e => emailInput.setValue(e.target.value)} /> + {Option.match(Array.head(emailInput.issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + passwordInput.setValue(e.target.value)} /> + + {Option.match(Array.head(passwordInput.issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} ) -- 2.49.1 From f6e69a05fdf08c2003a503d117b18f8a7d42900a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 19:58:57 +0200 Subject: [PATCH 16/67] Fix --- packages/effect-fc/src/Form.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index e0ab185..580477b 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -11,19 +11,19 @@ export type TypeId = typeof TypeId export interface Form extends Pipeable.Pipeable { readonly schema: Schema.Schema - readonly latestValueSubscribable: Subscribable.Subscribable> + readonly latestValueSubscribable: Subscribable.Subscribable - useFieldLatestValueSubscribable

>>( + useFieldLatestValueSubscribable

>( path: P - ): Subscribable.Subscribable, P>, NoSuchElementException> + ): Subscribable.Subscribable, NoSuchElementException> useFieldIssuesSubscribable( - path: PropertyPath.Paths> + path: PropertyPath.Paths ): Subscribable.Subscribable - useInput

>>( + useInput

>( path: P ): Effect.Effect< - Form.useInput.Result, P>>, + Form.useInput.Result>, ParseResult.ParseError | NoSuchElementException, R > @@ -43,18 +43,18 @@ export namespace Form { class FormImpl extends Pipeable.Class() implements Form { readonly [TypeId]: TypeId = TypeId - readonly latestValueSubscribable: Subscribable.Subscribable> + readonly latestValueSubscribable: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, - readonly latestValueRef: SubscriptionRef.SubscriptionRef>, + readonly latestValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, ) { super() this.latestValueSubscribable = latestValueRef } - useFieldLatestValueSubscribable

>>( + useFieldLatestValueSubscribable

>( path: P ) { return React.useMemo(() => { @@ -67,7 +67,7 @@ extends Pipeable.Class() implements Form { } useFieldIssuesSubscribable( - path: PropertyPath.Paths> + path: PropertyPath.Paths ) { return React.useMemo(() => { const filter = Option.match({ -- 2.49.1 From 450d11cc3e9a58fa434fc79bd25e2efc6902af3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Aug 2025 04:42:23 +0200 Subject: [PATCH 17/67] Fix --- packages/effect-fc/src/Form.ts | 36 +++++++++++++++++++--------- packages/example/src/routes/form.tsx | 26 ++++++++++---------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 580477b..e9c44ea 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -21,7 +21,7 @@ extends Pipeable.Pipeable { ): Subscribable.Subscribable useInput

>( - path: P + options: Form.useInput.Options ): Effect.Effect< Form.useInput.Result>, ParseResult.ParseError | NoSuchElementException, @@ -31,9 +31,14 @@ extends Pipeable.Pipeable { export namespace Form { export namespace useInput { - export interface Result { - readonly value: I - readonly setValue: React.Dispatch> + export interface Options> { + readonly path: P + readonly defaultValue?: PropertyPath.ValueFromPath, NoInfer

> + } + + export interface Result { + readonly value: T + readonly setValue: React.Dispatch> readonly issues: readonly ParseResult.ArrayFormatterIssue[] } } @@ -86,15 +91,24 @@ extends Pipeable.Class() implements Form { }, [this.latestValueRef, ...path]) } - useInput

>(path: P) { + useInput

>( + options: Form.useInput.Options + ) { const self = this return Effect.gen(function*() { const internalValueRef = yield* Hooks.useMemo(() => self.latestValueRef.pipe( - Effect.andThen(Schema.encode(self.schema)), - Effect.andThen(PropertyPath.get(path)), + Effect.andThen(flow( + Schema.encode(self.schema), + Effect.andThen(PropertyPath.get(options.path)), + Effect.catchTag("ParseError", e => options.defaultValue !== undefined && options.defaultValue !== null + ? Effect.succeed(options.defaultValue) + : Effect.fail(e) + ), + )), Effect.andThen(SubscriptionRef.make>), - ), [self.latestValueRef, ...path]) - const issuesSubscribable = self.useFieldIssuesSubscribable(path) + ), [self.latestValueRef, ...options.path]) + + const issuesSubscribable = self.useFieldIssuesSubscribable(options.path) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) @@ -107,7 +121,7 @@ extends Pipeable.Class() implements Form { ), internalValue => self.latestValueRef.pipe( Effect.andThen(Schema.encode(self.schema)), - Effect.andThen(PropertyPath.immutableSet(path, internalValue)), + Effect.andThen(PropertyPath.immutableSet(options.path, internalValue)), Effect.andThen(flow( Schema.decode(self.schema), Effect.andThen(v => SubscriptionRef.set(self.latestValueRef, v)), @@ -115,7 +129,7 @@ extends Pipeable.Class() implements Form { Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) )), ), - ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...path]) + ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...options.path]) return { value, setValue, issues } }) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 2cfb956..a25a09d 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -6,22 +6,22 @@ import { Component, Form } from "effect-fc" import { useContext, useFork } from "effect-fc/hooks" -const LoginFormSchema = Schema.Struct({ +const RegisterFormSchema = Schema.Struct({ email: Schema.String, password: Schema.String.pipe(Schema.minLength(3)), }) -class LoginForm extends Effect.Service()("LoginForm", { +class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.make({ - schema: LoginFormSchema, - initialValue: { email: "", password: "xxx" }, + schema: RegisterFormSchema, + initialValue: { email: "", password: "" }, }) }) {} -class LoginFormComponent extends Component.makeUntraced(function* LoginFormComponent() { - const form = yield* LoginForm - const emailInput = yield* form.useInput(["email"]) - const passwordInput = yield* form.useInput(["password"]) +class RegisterPage extends Component.makeUntraced(function* RegisterPage() { + const form = yield* RegisterForm + const emailInput = yield* form.useInput({ path: ["email"], defaultValue: "" }) + const passwordInput = yield* form.useInput({ path: ["password"], defaultValue: "" }) yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log), []) @@ -63,15 +63,15 @@ class LoginFormComponent extends Component.makeUntraced(function* LoginFormCompo }) {} -const FormRoute = Component.makeUntraced(function* FormRoute() { - const context = yield* useContext(LoginForm.Default) - const LoginFormComponentFC = yield* Effect.provide(LoginFormComponent, context) +const RegisterRoute = Component.makeUntraced(function* RegisterRoute() { + const context = yield* useContext(RegisterForm.Default) + const RegisterRouteFC = yield* Effect.provide(RegisterPage, context) - return + return }).pipe( Component.withRuntime(runtime.context) ) export const Route = createFileRoute("/form")({ - component: FormRoute + component: RegisterRoute }) -- 2.49.1 From 8b5074bd56bdfd240add5ab5aeea93f8caed848e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 Aug 2025 05:52:39 +0200 Subject: [PATCH 18/67] Form work --- packages/effect-fc/src/Form.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index e9c44ea..be66cb7 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -53,6 +53,7 @@ extends Pipeable.Class() implements Form { constructor( readonly schema: Schema.Schema, readonly latestValueRef: SubscriptionRef.SubscriptionRef, + readonly latestCandidateRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, ) { super() @@ -150,6 +151,6 @@ export const make: { export namespace make { export interface Options { readonly schema: Schema.Schema - readonly initialValue: NoInfer + readonly initialEncodedValue: NoInfer } } -- 2.49.1 From 7545b4bb305d91bbecce3c540e7a3ad799b1422e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Sep 2025 05:28:23 +0200 Subject: [PATCH 19/67] Form V2 --- packages/effect-fc/src/Form.ts | 205 ++++++++++++--------------- packages/example/src/routes/form.tsx | 8 +- 2 files changed, 91 insertions(+), 122 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index be66cb7..a019282 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,4 +1,4 @@ -import { Array, Effect, Equivalence, flow, Option, ParseResult, Pipeable, Schema, Stream, type Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equivalence, flow, identity, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -10,147 +10,116 @@ export type TypeId = typeof TypeId export interface Form extends Pipeable.Pipeable { - readonly schema: Schema.Schema - readonly latestValueSubscribable: Subscribable.Subscribable + readonly schema: Schema.Schema, + readonly valueRef: SubscriptionRef.SubscriptionRef>, + readonly encodedValueRef: SubscriptionRef.SubscriptionRef, + readonly errorRef: SubscriptionRef.SubscriptionRef>, - useFieldLatestValueSubscribable

>( - path: P - ): Subscribable.Subscribable, NoSuchElementException> - useFieldIssuesSubscribable( - path: PropertyPath.Paths - ): Subscribable.Subscribable - - useInput

>( - options: Form.useInput.Options - ): Effect.Effect< - Form.useInput.Result>, - ParseResult.ParseError | NoSuchElementException, - R - > -} - -export namespace Form { - export namespace useInput { - export interface Options> { - readonly path: P - readonly defaultValue?: PropertyPath.ValueFromPath, NoInfer

> - } - - export interface Result { - readonly value: T - readonly setValue: React.Dispatch> - readonly issues: readonly ParseResult.ArrayFormatterIssue[] - } - } + makeFieldIssuesSubscribable

>(path: P): Subscribable.Subscribable } class FormImpl extends Pipeable.Class() implements Form { readonly [TypeId]: TypeId = TypeId - readonly latestValueSubscribable: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, - readonly latestValueRef: SubscriptionRef.SubscriptionRef, - readonly latestCandidateRef: SubscriptionRef.SubscriptionRef, + readonly valueRef: SubscriptionRef.SubscriptionRef>, + readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, ) { super() - this.latestValueSubscribable = latestValueRef } - useFieldLatestValueSubscribable

>( - path: P - ) { - return React.useMemo(() => { - const latestValueRef = this.latestValueRef - return SubscribableInternal.make({ - get: Effect.flatMap(latestValueRef.get, PropertyPath.get(path)), - get changes() { return Stream.flatMap(latestValueRef.changes, PropertyPath.get(path)) }, - }) - }, [this.latestValueRef, ...path]) - } + makeFieldIssuesSubscribable

>(path: P) { + const filter = Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }) - useFieldIssuesSubscribable( - path: PropertyPath.Paths - ) { - return React.useMemo(() => { - const filter = Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), - ), - onNone: () => Effect.succeed([]), - }) - - const errorRef = this.errorRef - return SubscribableInternal.make({ - get: Effect.flatMap(errorRef.get, filter), - get changes() { return Stream.flatMap(errorRef.changes, filter) }, - }) - }, [this.latestValueRef, ...path]) - } - - useInput

>( - options: Form.useInput.Options - ) { - const self = this - return Effect.gen(function*() { - const internalValueRef = yield* Hooks.useMemo(() => self.latestValueRef.pipe( - Effect.andThen(flow( - Schema.encode(self.schema), - Effect.andThen(PropertyPath.get(options.path)), - Effect.catchTag("ParseError", e => options.defaultValue !== undefined && options.defaultValue !== null - ? Effect.succeed(options.defaultValue) - : Effect.fail(e) - ), - )), - Effect.andThen(SubscriptionRef.make>), - ), [self.latestValueRef, ...options.path]) - - const issuesSubscribable = self.useFieldIssuesSubscribable(options.path) - - const [value, setValue] = yield* Hooks.useRefState(internalValueRef) - const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) - - yield* Hooks.useFork(() => Stream.runForEach( - internalValueRef.changes.pipe( - Stream.changesWith(Equivalence.strict()), - // options.debounce ? Stream.debounce(options.debounce) : identity, - Stream.drop(1), - ), - internalValue => self.latestValueRef.pipe( - Effect.andThen(Schema.encode(self.schema)), - Effect.andThen(PropertyPath.immutableSet(options.path, internalValue)), - Effect.andThen(flow( - Schema.decode(self.schema), - Effect.andThen(v => SubscriptionRef.set(self.latestValueRef, v)), - Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) - )), - ), - ), [internalValueRef, self.latestValueRef, self.schema, self.errorRef, ...options.path]) - - return { value, setValue, issues } + const errorRef = this.errorRef + return SubscribableInternal.make({ + get: Effect.flatMap(errorRef.get, filter), + get changes() { return Stream.flatMap(errorRef.changes, filter) }, }) } } -export const make: { - (options: make.Options): Effect.Effect> -} = Effect.fnUntraced(function* (options: make.Options) { - return new FormImpl( - options.schema, - yield* SubscriptionRef.make(options.initialValue), - yield* SubscriptionRef.make(Option.none()), - ) -}) - export namespace make { export interface Options { readonly schema: Schema.Schema readonly initialEncodedValue: NoInfer } } + +export const make: { + (options: make.Options): Effect.Effect> +} = Effect.fnUntraced(function* (options: make.Options) { + return new FormImpl( + options.schema, + yield* SubscriptionRef.make(Option.none()), + yield* SubscriptionRef.make(options.initialEncodedValue), + yield* SubscriptionRef.make(Option.none()), + ) +}) + + +export namespace useInput { + export interface Options> { + readonly path: P + readonly debounce?: Duration.DurationInput + } + + export interface Result { + readonly value: T + readonly setValue: React.Dispatch> + readonly issues: readonly ParseResult.ArrayFormatterIssue[] + } +} + +export const useInput: { + >( + self: Form, + options: useInput.Options, P>, + ): Effect.Effect>, NoSuchElementException, R> +} = Effect.fnUntraced(function* >( + self: Form, + options: useInput.Options, P>, +) { + const [internalValueRef, issuesSubscribable] = yield* Hooks.useMemo(() => Effect.all([ + self.encodedValueRef.pipe( + Effect.andThen(PropertyPath.get(options.path)), + Effect.andThen(SubscriptionRef.make>), + ), + Effect.succeed(self.makeFieldIssuesSubscribable(options.path)), + ]), [self, ...options.path]) + + const [value, setValue] = yield* Hooks.useRefState(internalValueRef) + const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) + + yield* Hooks.useFork(() => Stream.runForEach( + internalValueRef.changes.pipe( + Stream.changesWith(Equivalence.strict()), + options.debounce ? Stream.debounce(options.debounce) : identity, + Stream.drop(1), + ), + + internalValue => self.encodedValueRef.pipe( + Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, options.path, internalValue)), + Effect.tap(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), + Effect.andThen(flow( + Schema.decode(self.schema), + Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), + Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) + )), + ), + ), [internalValueRef, self, ...options.path]) + + return { value, setValue, issues } +}) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index a25a09d..f4bc4e0 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -14,16 +14,16 @@ const RegisterFormSchema = Schema.Struct({ class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.make({ schema: RegisterFormSchema, - initialValue: { email: "", password: "" }, + initialEncodedValue: { email: "", password: "" }, }) }) {} class RegisterPage extends Component.makeUntraced(function* RegisterPage() { const form = yield* RegisterForm - const emailInput = yield* form.useInput({ path: ["email"], defaultValue: "" }) - const passwordInput = yield* form.useInput({ path: ["password"], defaultValue: "" }) + const emailInput = yield* Form.useInput(form, { path: ["email"] }) + const passwordInput = yield* Form.useInput(form, { path: ["password"] }) - yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log), []) + yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), []) return ( -- 2.49.1 From 46ef35040a8dd04b9d7387d6d96c4c5491688186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Sep 2025 06:02:01 +0200 Subject: [PATCH 20/67] Fix? --- packages/effect-fc/src/Form.ts | 25 +++++++++++++------------ packages/example/src/routes/form.tsx | 4 ++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index a019282..cebe80a 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -70,8 +70,7 @@ export const make: { export namespace useInput { - export interface Options> { - readonly path: P + export interface Options { readonly debounce?: Duration.DurationInput } @@ -83,21 +82,23 @@ export namespace useInput { } export const useInput: { - >( + >( self: Form, - options: useInput.Options, P>, + path: P, + options?: useInput.Options, ): Effect.Effect>, NoSuchElementException, R> -} = Effect.fnUntraced(function* >( +} = Effect.fnUntraced(function* >( self: Form, - options: useInput.Options, P>, + path: P, + options?: useInput.Options, ) { const [internalValueRef, issuesSubscribable] = yield* Hooks.useMemo(() => Effect.all([ self.encodedValueRef.pipe( - Effect.andThen(PropertyPath.get(options.path)), + Effect.andThen(PropertyPath.get(path)), Effect.andThen(SubscriptionRef.make>), ), - Effect.succeed(self.makeFieldIssuesSubscribable(options.path)), - ]), [self, ...options.path]) + Effect.succeed(self.makeFieldIssuesSubscribable(path)), + ]), [self, ...path]) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) @@ -105,12 +106,12 @@ export const useInput: { yield* Hooks.useFork(() => Stream.runForEach( internalValueRef.changes.pipe( Stream.changesWith(Equivalence.strict()), - options.debounce ? Stream.debounce(options.debounce) : identity, + options?.debounce ? Stream.debounce(options.debounce) : identity, Stream.drop(1), ), internalValue => self.encodedValueRef.pipe( - Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, options.path, internalValue)), + Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, path, internalValue)), Effect.tap(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), Effect.andThen(flow( Schema.decode(self.schema), @@ -119,7 +120,7 @@ export const useInput: { Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) )), ), - ), [internalValueRef, self, ...options.path]) + ), [internalValueRef, self, ...path]) return { value, setValue, issues } }) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index f4bc4e0..d2307b3 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -20,8 +20,8 @@ class RegisterForm extends Effect.Service()("RegisterForm", { class RegisterPage extends Component.makeUntraced(function* RegisterPage() { const form = yield* RegisterForm - const emailInput = yield* Form.useInput(form, { path: ["email"] }) - const passwordInput = yield* Form.useInput(form, { path: ["password"] }) + const emailInput = yield* Form.useInput(form, ["email"]) + const passwordInput = yield* Form.useInput(form, ["password"]) yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), []) -- 2.49.1 From ed1efb41cdc9b285e6001eb432be6807798be6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Sep 2025 18:15:11 +0200 Subject: [PATCH 21/67] Fix --- packages/effect-fc/src/Form.ts | 8 +++++--- packages/effect-fc/src/types/PropertyPath.ts | 2 +- packages/example/src/routes/form.tsx | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index cebe80a..c5a02d0 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -15,7 +15,9 @@ extends Pipeable.Pipeable { readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, - makeFieldIssuesSubscribable

>(path: P): Subscribable.Subscribable + makeFieldIssuesSubscribable>( + path: P + ): Subscribable.Subscribable } @@ -32,7 +34,7 @@ extends Pipeable.Class() implements Form { super() } - makeFieldIssuesSubscribable

>(path: P) { + makeFieldIssuesSubscribable>(path: P) { const filter = Option.match({ onSome: (v: ParseResult.ParseError) => Effect.andThen( ParseResult.ArrayFormatter.formatError(v), @@ -82,7 +84,7 @@ export namespace useInput { } export const useInput: { - >( + >>( self: Form, path: P, options?: useInput.Options, diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/types/PropertyPath.ts index af47a8c..b73d24d 100644 --- a/packages/effect-fc/src/types/PropertyPath.ts +++ b/packages/effect-fc/src/types/PropertyPath.ts @@ -28,7 +28,7 @@ export type Paths = readonly [] | ( never ) -export type ValueFromPath = P extends [infer Head, ...infer Tail] +export type ValueFromPath = P extends readonly [infer Head, ...infer Tail] ? Head extends keyof T ? ValueFromPath : T extends readonly any[] diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index d2307b3..4e65251 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -20,8 +20,8 @@ class RegisterForm extends Effect.Service()("RegisterForm", { class RegisterPage extends Component.makeUntraced(function* RegisterPage() { const form = yield* RegisterForm - const emailInput = yield* Form.useInput(form, ["email"]) - const passwordInput = yield* Form.useInput(form, ["password"]) + const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" }) + const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), []) -- 2.49.1 From 9033fcbb321c81ad2a3679cc0cb0988adf45fb33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Sep 2025 19:38:30 +0200 Subject: [PATCH 22/67] Form refactoring --- packages/effect-fc/src/Form.ts | 45 ++++++++++++++++++++++------ packages/example/src/routes/form.tsx | 4 +-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index c5a02d0..1b982e7 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,4 +1,4 @@ -import { Array, Duration, Effect, Equivalence, flow, identity, Option, ParseResult, Pipeable, Schema, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equivalence, flow, identity, Option, ParseResult, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -53,7 +53,7 @@ extends Pipeable.Class() implements Form { export namespace make { - export interface Options { + export interface Options { readonly schema: Schema.Schema readonly initialEncodedValue: NoInfer } @@ -70,6 +70,39 @@ export const make: { ) }) +const run = (self: Form) => Stream.runForEach( + self.encodedValueRef.changes, + flow( + Schema.decode(self.schema), + Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), + Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) + ), +) + +export namespace service { + export interface Options extends make.Options {} +} + +export const service = ( + options: service.Options +): Effect.Effect, never, R | Scope.Scope> => Effect.tap( + make(options), + form => Effect.forkScoped(run(form)), +) + +export namespace useForm { + export interface Options extends make.Options {} +} + +export const useForm: { + (options: service.Options): Effect.Effect, never, R | Scope.Scope> +} = Effect.fnUntraced(function* (options: service.Options) { + const form = yield* Hooks.useOnce(() => make(options)) + yield* Hooks.useFork(() => run(form), [form]) + return form +}) + export namespace useInput { export interface Options { @@ -114,13 +147,7 @@ export const useInput: { internalValue => self.encodedValueRef.pipe( Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, path, internalValue)), - Effect.tap(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), - Effect.andThen(flow( - Schema.decode(self.schema), - Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), - Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) - )), + Effect.andThen(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), ), ), [internalValueRef, self, ...path]) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 4e65251..c977569 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -12,7 +12,7 @@ const RegisterFormSchema = Schema.Struct({ }) class RegisterForm extends Effect.Service()("RegisterForm", { - scoped: Form.make({ + scoped: Form.service({ schema: RegisterFormSchema, initialEncodedValue: { email: "", password: "" }, }) @@ -64,7 +64,7 @@ class RegisterPage extends Component.makeUntraced(function* RegisterPage() { const RegisterRoute = Component.makeUntraced(function* RegisterRoute() { - const context = yield* useContext(RegisterForm.Default) + const context = yield* useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }) const RegisterRouteFC = yield* Effect.provide(RegisterPage, context) return -- 2.49.1 From e2cd7bb671ee3c55f7d475e6ce3aeda850c9ad64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Sep 2025 22:13:00 +0200 Subject: [PATCH 23/67] Fix --- packages/effect-fc/src/Form.ts | 3 ++- packages/example/src/routes/form.tsx | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 1b982e7..aea44e6 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -11,6 +11,7 @@ export type TypeId = typeof TypeId export interface Form extends Pipeable.Pipeable { readonly schema: Schema.Schema, + /** A reference to the latest valid value produced by the form, if any. */ readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, @@ -73,7 +74,7 @@ export const make: { const run = (self: Form) => Stream.runForEach( self.encodedValueRef.changes, flow( - Schema.decode(self.schema), + Schema.decode(self.schema, { errors: "all" }), Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index c977569..f0d452e 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -6,8 +6,18 @@ import { Component, Form } from "effect-fc" import { useContext, useFork } from "effect-fc/hooks" +const email = Schema.pattern( + /^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, + + { + identifier: "email", + title: "email", + message: () => "Not an email address", + }, +) + const RegisterFormSchema = Schema.Struct({ - email: Schema.String, + email: Schema.String.pipe(email), password: Schema.String.pipe(Schema.minLength(3)), }) @@ -24,6 +34,7 @@ class RegisterPage extends Component.makeUntraced(function* RegisterPage() { const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), []) + // yield* useFork(() => Stream.runForEach(form.errorRef.changes, Console.log), []) return ( -- 2.49.1 From 8c1fed78003caa55e0ae6467524d65af5c978557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 24 Sep 2025 00:41:42 +0200 Subject: [PATCH 24/67] Component refactoring --- packages/effect-fc/src/Component.ts | 40 ++++++++++--------- .../example/src/lib/input/TextAreaInput.tsx | 2 +- .../example/src/lib/input/TextFieldInput.tsx | 2 +- .../src/routes/dev/async-rendering.tsx | 4 +- packages/example/src/routes/dev/input.tsx | 10 ++--- packages/example/src/routes/dev/memo.tsx | 4 +- packages/example/src/routes/form.tsx | 2 +- packages/example/src/routes/index.tsx | 2 +- packages/example/src/todo/Todo.tsx | 2 +- packages/example/src/todo/Todos.tsx | 2 +- 10 files changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 6c36f5e..c0ddc6f 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,4 +1,4 @@ -import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect" +import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, Tracer, type Types, type Utils } from "effect" import * as React from "react" import { Hooks } from "./hooks/index.js" import * as Memo from "./Memo.js" @@ -331,13 +331,9 @@ export const make: ( ) => make.Gen & make.NonGen) ) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => { if (typeof spanNameOrBody !== "string") { - const displayName = displayNameFromBody(spanNameOrBody) return Object.setPrototypeOf( Object.assign(function() {}, defaultOptions, { - body: displayName - ? Effect.fn(displayName)(spanNameOrBody as any, ...pipeables as []) - : Effect.fn(spanNameOrBody as any, ...pipeables), - displayName, + body: Effect.fn(spanNameOrBody as any, ...pipeables), }), ComponentProto, ) @@ -347,26 +343,34 @@ export const make: ( return (body: any, ...pipeables: any[]) => Object.setPrototypeOf( Object.assign(function() {}, defaultOptions, { body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), - displayName: displayNameFromBody(body) ?? spanNameOrBody, + displayName: spanNameOrBody, }), ComponentProto, ) } } -export const makeUntraced: make.Gen & make.NonGen = ( - body: Function, - ...pipeables: any[] -) => Object.setPrototypeOf( - Object.assign(function() {}, defaultOptions, { - body: Effect.fnUntraced(body as any, ...pipeables as []), - displayName: displayNameFromBody(body), - }), - ComponentProto, +export const makeUntraced: ( + & make.Gen + & make.NonGen + & ((name: string) => make.Gen & make.NonGen) +) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => ( + typeof spanNameOrBody !== "string" + ? Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fnUntraced(spanNameOrBody as any, ...pipeables as []), + }), + ComponentProto, + ) + : (body: any, ...pipeables: any[]) => Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fnUntraced(body, ...pipeables as []), + displayName: spanNameOrBody, + }), + ComponentProto, + ) ) -const displayNameFromBody = (body: Function) => !String.isEmpty(body.name) ? body.name : undefined - export const withOptions: { >( options: Partial diff --git a/packages/example/src/lib/input/TextAreaInput.tsx b/packages/example/src/lib/input/TextAreaInput.tsx index 9bfeb37..d4413fd 100644 --- a/packages/example/src/lib/input/TextAreaInput.tsx +++ b/packages/example/src/lib/input/TextAreaInput.tsx @@ -15,7 +15,7 @@ export const TextAreaInput = (options: { React.JSX.Element, ParseResult.ParseError, R -> => Component.makeUntraced(function* TextFieldInput(props) { +> => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props) { const input = yield* useInput({ ...options, ...props }) const issue = React.useMemo(() => input.error.pipe( Option.map(ParseResult.ArrayFormatter.formatErrorSync), diff --git a/packages/example/src/lib/input/TextFieldInput.tsx b/packages/example/src/lib/input/TextFieldInput.tsx index b3c475e..d5f8432 100644 --- a/packages/example/src/lib/input/TextFieldInput.tsx +++ b/packages/example/src/lib/input/TextFieldInput.tsx @@ -18,7 +18,7 @@ export const TextFieldInput = (options: { readonly optional?: O readonly schema: Schema.Schema readonly equivalence?: Equivalence.Equivalence -}) => Component.makeUntraced(function* TextFieldInput(props: O extends true +}) => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props: O extends true ? TextFieldOptionalInputProps : TextFieldInputProps ) { diff --git a/packages/example/src/routes/dev/async-rendering.tsx b/packages/example/src/routes/dev/async-rendering.tsx index 3728739..fadd316 100644 --- a/packages/example/src/routes/dev/async-rendering.tsx +++ b/packages/example/src/routes/dev/async-rendering.tsx @@ -51,7 +51,7 @@ const RouteComponent = Component.makeUntraced(function* AsyncRendering() { // ) -class AsyncComponent extends Component.makeUntraced(function* AsyncComponent() { +class AsyncComponent extends Component.makeUntraced("AsyncComponent")(function*() { const SubComponentFC = yield* SubComponent yield* Effect.sleep("500 millis") // Async operation @@ -69,7 +69,7 @@ class AsyncComponent extends Component.makeUntraced(function* AsyncComponent() { ) {} class MemoizedAsyncComponent extends Memo.memo(AsyncComponent) {} -class SubComponent extends Component.makeUntraced(function* SubComponent() { +class SubComponent extends Component.makeUntraced("SubComponent")(function*() { const [state] = React.useState(yield* Hooks.useOnce(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom))) return {state} }) {} diff --git a/packages/example/src/routes/dev/input.tsx b/packages/example/src/routes/dev/input.tsx index 2514963..9a8a53f 100644 --- a/packages/example/src/routes/dev/input.tsx +++ b/packages/example/src/routes/dev/input.tsx @@ -4,7 +4,7 @@ import { Container } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Schema, SubscriptionRef } from "effect" import { Component, Memo } from "effect-fc" -import { useInput, useOnce, useRefState } from "effect-fc/hooks" +import { useOnce } from "effect-fc/hooks" const IntFromString = Schema.NumberFromString.pipe(Schema.int()) @@ -12,18 +12,18 @@ const IntFromString = Schema.NumberFromString.pipe(Schema.int()) const IntTextFieldInput = TextFieldInput({ schema: IntFromString }) const StringTextFieldInput = TextFieldInput({ schema: Schema.String }) -const Input = Component.makeUntraced(function* Input() { +const Input = Component.makeUntraced("Input")(function*() { const IntTextFieldInputFC = yield* IntTextFieldInput const StringTextFieldInputFC = yield* StringTextFieldInput const intRef1 = yield* useOnce(() => SubscriptionRef.make(0)) - const intRef2 = yield* useOnce(() => SubscriptionRef.make(0)) + // const intRef2 = yield* useOnce(() => SubscriptionRef.make(0)) const stringRef = yield* useOnce(() => SubscriptionRef.make("")) // yield* useFork(() => Stream.runForEach(intRef1.changes, Console.log), [intRef1]) - const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 }) + // const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 }) - const [str, setStr] = yield* useRefState(stringRef) + // const [str, setStr] = yield* useRefState(stringRef) return ( diff --git a/packages/example/src/routes/dev/memo.tsx b/packages/example/src/routes/dev/memo.tsx index 4413107..b81f132 100644 --- a/packages/example/src/routes/dev/memo.tsx +++ b/packages/example/src/routes/dev/memo.tsx @@ -7,7 +7,7 @@ import { Component, Memo } from "effect-fc" import * as React from "react" -const RouteComponent = Component.makeUntraced(function* RouteComponent() { +const RouteComponent = Component.makeUntraced("RouteComponent")(function*() { const [value, setValue] = React.useState("") return ( @@ -25,7 +25,7 @@ const RouteComponent = Component.makeUntraced(function* RouteComponent() { Component.withRuntime(runtime.context) ) -class SubComponent extends Component.makeUntraced(function* SubComponent() { +class SubComponent extends Component.makeUntraced("SubComponent")(function*() { const id = yield* makeUuid4.pipe(Effect.provide(GetRandomValues.CryptoRandom)) return {id} }) {} diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index f0d452e..850f56d 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -28,7 +28,7 @@ class RegisterForm extends Effect.Service()("RegisterForm", { }) }) {} -class RegisterPage extends Component.makeUntraced(function* RegisterPage() { +class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const form = yield* RegisterForm const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" }) const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) diff --git a/packages/example/src/routes/index.tsx b/packages/example/src/routes/index.tsx index ea8bc2e..3bf279f 100644 --- a/packages/example/src/routes/index.tsx +++ b/packages/example/src/routes/index.tsx @@ -9,7 +9,7 @@ import { useContext } from "effect-fc/hooks" const TodosStateLive = TodosState.Default("todos") -const Index = Component.makeUntraced(function* Index() { +const Index = Component.makeUntraced("Index")(function*() { const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" }) const TodosFC = yield* Effect.provide(Todos, context) diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index cc8a6cc..62ed92c 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -31,7 +31,7 @@ export type TodoProps = ( | { readonly _tag: "edit", readonly id: string } ) -export class Todo extends Component.makeUntraced(function* Todo(props: TodoProps) { +export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoProps) { const runtime = yield* Effect.runtime() const state = yield* TodosState diff --git a/packages/example/src/todo/Todos.tsx b/packages/example/src/todo/Todos.tsx index e533f24..25aa06d 100644 --- a/packages/example/src/todo/Todos.tsx +++ b/packages/example/src/todo/Todos.tsx @@ -6,7 +6,7 @@ import { Todo } from "./Todo" import { TodosState } from "./TodosState.service" -export class Todos extends Component.makeUntraced(function* Todos() { +export class Todos extends Component.makeUntraced("Todos")(function*() { const state = yield* TodosState const [todos] = yield* useSubscribe(state.ref) -- 2.49.1 From bfd4b7f07343c3c3fb5d150e992dc73a687155fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 24 Sep 2025 01:20:21 +0200 Subject: [PATCH 25/67] Change useSubscribe to useSubscribables --- packages/effect-fc/src/Form.ts | 2 +- packages/effect-fc/src/hooks/Hooks/index.ts | 2 +- packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts | 4 ++-- .../src/hooks/Hooks/{useSubscribe.ts => useSubscribables.ts} | 2 +- packages/example/src/todo/Todo.tsx | 4 ++-- packages/example/src/todo/Todos.tsx | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) rename packages/effect-fc/src/hooks/Hooks/{useSubscribe.ts => useSubscribables.ts} (97%) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index aea44e6..eea0c07 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -137,7 +137,7 @@ export const useInput: { ]), [self, ...path]) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) - const [issues] = yield* Hooks.useSubscribe(issuesSubscribable) + const [issues] = yield* Hooks.useSubscribables(issuesSubscribable) yield* Hooks.useFork(() => Stream.runForEach( internalValueRef.changes.pipe( diff --git a/packages/effect-fc/src/hooks/Hooks/index.ts b/packages/effect-fc/src/hooks/Hooks/index.ts index 7d9443f..0f99a97 100644 --- a/packages/effect-fc/src/hooks/Hooks/index.ts +++ b/packages/effect-fc/src/hooks/Hooks/index.ts @@ -12,5 +12,5 @@ export * from "./useRefFromState.js" export * from "./useRefState.js" export * from "./useScope.js" export * from "./useStreamFromReactiveValues.js" -export * from "./useSubscribe.js" +export * from "./useSubscribables.js" export * from "./useSubscribeStream.js" diff --git a/packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts b/packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts index c104952..cae2e23 100644 --- a/packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts +++ b/packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts @@ -5,7 +5,7 @@ import { useCallbackSync } from "../useCallbackSync.js" import { useFork } from "../useFork.js" import { useOnce } from "../useOnce.js" import { useRefState } from "../useRefState.js" -import { useSubscribe } from "../useSubscribe.js" +import { useSubscribables } from "../useSubscribables.js" export namespace useOptionalInput { @@ -101,7 +101,7 @@ export const useOptionalInput: { [options.schema, options.ref, internalRef, enabledRef], ) - const [enabled] = yield* useSubscribe(enabledRef) + const [enabled] = yield* useSubscribables(enabledRef) const [value, setValue] = yield* useRefState(internalRef) return { value, setValue, enabled, setEnabled, error } }) diff --git a/packages/effect-fc/src/hooks/Hooks/useSubscribe.ts b/packages/effect-fc/src/hooks/Hooks/useSubscribables.ts similarity index 97% rename from packages/effect-fc/src/hooks/Hooks/useSubscribe.ts rename to packages/effect-fc/src/hooks/Hooks/useSubscribables.ts index 29290cd..9d96fee 100644 --- a/packages/effect-fc/src/hooks/Hooks/useSubscribe.ts +++ b/packages/effect-fc/src/hooks/Hooks/useSubscribables.ts @@ -4,7 +4,7 @@ import { useFork } from "./useFork.js" import { useOnce } from "./useOnce.js" -export const useSubscribe: { +export const useSubscribables: { []>( ...elements: T ): Effect.Effect< diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index 62ed92c..469be99 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -6,7 +6,7 @@ import { Box, Button, Flex, IconButton } from "@radix-ui/themes" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Chunk, DateTime, Effect, Match, Option, Ref, Runtime, Schema, Stream, SubscriptionRef } from "effect" import { Component, Memo } from "effect-fc" -import { useMemo, useOnce, useSubscribe } from "effect-fc/hooks" +import { useMemo, useOnce, useSubscribables } from "effect-fc/hooks" import { Subscribable, SubscriptionSubRef } from "effect-fc/types" import { FaArrowDown, FaArrowUp } from "react-icons/fa" import { FaDeleteLeft } from "react-icons/fa6" @@ -51,7 +51,7 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr // eslint-disable-next-line react-hooks/exhaustive-deps ), [props._tag, props._tag === "edit" ? props.id : undefined]) - const [index, size] = yield* useSubscribe(indexRef, state.sizeSubscribable) + const [index, size] = yield* useSubscribables(indexRef, state.sizeSubscribable) const StringTextAreaInputFC = yield* StringTextAreaInput const OptionalDateTimeInputFC = yield* OptionalDateTimeInput diff --git a/packages/example/src/todo/Todos.tsx b/packages/example/src/todo/Todos.tsx index 25aa06d..9b84082 100644 --- a/packages/example/src/todo/Todos.tsx +++ b/packages/example/src/todo/Todos.tsx @@ -1,14 +1,14 @@ import { Container, Flex, Heading } from "@radix-ui/themes" import { Chunk, Console, Effect } from "effect" import { Component } from "effect-fc" -import { useOnce, useSubscribe } from "effect-fc/hooks" +import { useOnce, useSubscribables } from "effect-fc/hooks" import { Todo } from "./Todo" import { TodosState } from "./TodosState.service" export class Todos extends Component.makeUntraced("Todos")(function*() { const state = yield* TodosState - const [todos] = yield* useSubscribe(state.ref) + const [todos] = yield* useSubscribables(state.ref) yield* useOnce(() => Effect.andThen( Console.log("Todos mounted"), -- 2.49.1 From 2df12d7f404b0d1e5c9169917d9d2b88e4169225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 24 Sep 2025 06:08:02 +0200 Subject: [PATCH 26/67] Form work --- packages/effect-fc/src/Form.ts | 51 +++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index eea0c07..5ccf511 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -71,16 +71,6 @@ export const make: { ) }) -const run = (self: Form) => Stream.runForEach( - self.encodedValueRef.changes, - flow( - Schema.decode(self.schema, { errors: "all" }), - Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), - Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) - ), -) - export namespace service { export interface Options extends make.Options {} } @@ -104,6 +94,16 @@ export const useForm: { return form }) +const run = (self: Form) => Stream.runForEach( + self.encodedValueRef.changes, + flow( + Schema.decode(self.schema, { errors: "all" }), + Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), + Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) + ), +) + export namespace useInput { export interface Options { @@ -139,18 +139,31 @@ export const useInput: { const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribables(issuesSubscribable) - yield* Hooks.useFork(() => Stream.runForEach( - internalValueRef.changes.pipe( - Stream.changesWith(Equivalence.strict()), - options?.debounce ? Stream.debounce(options.debounce) : identity, - Stream.drop(1), + yield* Hooks.useFork(() => Effect.all([ + Stream.runForEach( + self.encodedValueRef.changes.pipe( + Stream.flatMap(PropertyPath.get(path)), + Stream.drop(1), + ), + + upstreamEncodedValue => internalValueRef.pipe( + + ), ), - internalValue => self.encodedValueRef.pipe( - Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, path, internalValue)), - Effect.andThen(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), + Stream.runForEach( + internalValueRef.changes.pipe( + Stream.changesWith(Equivalence.strict()), + options?.debounce ? Stream.debounce(options.debounce) : identity, + Stream.drop(1), + ), + + internalValue => self.encodedValueRef.pipe( + Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, path, internalValue)), + Effect.andThen(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), + ), ), - ), [internalValueRef, self, ...path]) + ], { concurrency: "unbounded" }), [internalValueRef, self, ...path]) return { value, setValue, issues } }) -- 2.49.1 From a7471c0d49dc282d05641c411bdce2d22b5b1c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 25 Sep 2025 02:31:46 +0200 Subject: [PATCH 27/67] Form work --- packages/effect-fc/src/Form.ts | 22 +++++++++++++++++++--- packages/example/src/routes/form.tsx | 11 ++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 5ccf511..94391b8 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,4 +1,4 @@ -import { Array, Duration, Effect, Equivalence, flow, identity, Option, ParseResult, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equal, Equivalence, flow, identity, Option, ParseResult, pipe, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -16,6 +16,8 @@ extends Pipeable.Pipeable { readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, + readonly canSubmitSubscribable: Subscribable.Subscribable + makeFieldIssuesSubscribable>( path: P ): Subscribable.Subscribable @@ -25,6 +27,7 @@ extends Pipeable.Pipeable { class FormImpl extends Pipeable.Class() implements Form { readonly [TypeId]: TypeId = TypeId + readonly canSubmitSubscribable: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, @@ -33,6 +36,18 @@ extends Pipeable.Class() implements Form { readonly errorRef: SubscriptionRef.SubscriptionRef>, ) { super() + + this.canSubmitSubscribable = pipe( + ([value, error]: readonly [ + Option.Option, + Option.Option, + ]) => Option.isSome(value) && Option.isNone(error), + + filter => SubscribableInternal.make({ + get: Effect.map(Effect.all([valueRef, errorRef]), filter), + get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes), filter)}, + }), + ) } makeFieldIssuesSubscribable>(path: P) { @@ -146,8 +161,9 @@ export const useInput: { Stream.drop(1), ), - upstreamEncodedValue => internalValueRef.pipe( - + upstreamEncodedValue => Effect.whenEffect( + SubscriptionRef.set(internalValueRef, upstreamEncodedValue), + Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), ), ), diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 850f56d..569d26c 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,9 +1,9 @@ import { runtime } from "@/runtime" -import { Callout, Container, Flex, TextField } from "@radix-ui/themes" +import { Button, Callout, Container, Flex, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Array, Console, Effect, Option, Schema, Stream } from "effect" +import { Array, Effect, Option, Schema } from "effect" import { Component, Form } from "effect-fc" -import { useContext, useFork } from "effect-fc/hooks" +import { useContext, useSubscribables } from "effect-fc/hooks" const email = Schema.pattern( @@ -33,8 +33,7 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" }) const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) - yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), []) - // yield* useFork(() => Stream.runForEach(form.errorRef.changes, Console.log), []) + const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) return ( @@ -68,6 +67,8 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { onNone: () => <>, })} + + ) -- 2.49.1 From 5f531c9b2ecb5bf5be0480f47764a293f5e65ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 25 Sep 2025 02:51:23 +0200 Subject: [PATCH 28/67] Fix --- packages/effect-fc/src/Form.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 94391b8..0a4b3bc 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -51,19 +51,21 @@ extends Pipeable.Class() implements Form { } makeFieldIssuesSubscribable>(path: P) { - const filter = Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), - ), - onNone: () => Effect.succeed([]), - }) - const errorRef = this.errorRef - return SubscribableInternal.make({ - get: Effect.flatMap(errorRef.get, filter), - get changes() { return Stream.flatMap(errorRef.changes, filter) }, - }) + return pipe( + Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }), + + filter => SubscribableInternal.make({ + get: Effect.flatMap(errorRef.get, filter), + get changes() { return Stream.flatMap(errorRef.changes, filter) }, + }), + ) } } -- 2.49.1 From b3d6cc6764eddc804d2ccc010947f200972c595c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 25 Sep 2025 12:52:44 +0200 Subject: [PATCH 29/67] Form refactoring --- bun.lock | 3 + packages/effect-fc/package.json | 3 + packages/effect-fc/src/Form.ts | 150 ++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/bun.lock b/bun.lock index 661bc39..a9d6aef 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,9 @@ "packages/effect-fc": { "name": "effect-fc", "version": "0.1.3", + "dependencies": { + "@typed/async-data": "^0.13.1", + }, "devDependencies": { "@effect/language-service": "^0.35.2", }, diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 8ca1343..92572b8 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -45,5 +45,8 @@ }, "devDependencies": { "@effect/language-service": "^0.35.2" + }, + "dependencies": { + "@typed/async-data": "^0.13.1" } } diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 0a4b3bc..ad20e04 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,51 +1,57 @@ -import { Array, Duration, Effect, Equal, Equivalence, flow, identity, Option, ParseResult, pipe, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import * as AsyncData from "@typed/async-data" +import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseResult, pipe, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" import { PropertyPath, Subscribable as SubscribableInternal } from "./types/index.js" -export const TypeId: unique symbol = Symbol.for("effect-fc/Form") -export type TypeId = typeof TypeId +export const FormTypeId: unique symbol = Symbol.for("effect-fc/Form") +export type FormTypeId = typeof FormTypeId -export interface Form +export interface Form extends Pipeable.Pipeable { + readonly [FormTypeId]: FormTypeId + readonly schema: Schema.Schema, - /** A reference to the latest valid value produced by the form, if any. */ + readonly submit: (value: NoInfer) => Effect.Effect, + readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, + /** Whether or not a validation is currently being executed */ + readonly isValidatingRef: SubscriptionRef.SubscriptionRef + readonly submitStateRef: SubscriptionRef.SubscriptionRef>, readonly canSubmitSubscribable: Subscribable.Subscribable - - makeFieldIssuesSubscribable>( - path: P - ): Subscribable.Subscribable } - -class FormImpl -extends Pipeable.Class() implements Form { - readonly [TypeId]: TypeId = TypeId +class FormImpl +extends Pipeable.Class() implements Form { + readonly [FormTypeId]: FormTypeId = FormTypeId readonly canSubmitSubscribable: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, + readonly submit: (value: NoInfer) => Effect.Effect, readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, + readonly isValidatingRef: SubscriptionRef.SubscriptionRef, + readonly submitStateRef: SubscriptionRef.SubscriptionRef>, ) { super() this.canSubmitSubscribable = pipe( - ([value, error]: readonly [ + ([value, error, isValidating]: readonly [ Option.Option, Option.Option, - ]) => Option.isSome(value) && Option.isNone(error), + boolean, + ]) => Option.isSome(value) && Option.isNone(error) && !isValidating, filter => SubscribableInternal.make({ - get: Effect.map(Effect.all([valueRef, errorRef]), filter), - get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes), filter)}, + get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef]), filter), + get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes, isValidatingRef.changes), filter)}, }), ) } @@ -70,54 +76,99 @@ extends Pipeable.Class() implements Form { } +export const FormFieldTypeId: unique symbol = Symbol.for("effect-fc/FormField") +export type FormFieldTypeId = typeof FormFieldTypeId + +export interface FormField +extends Pipeable.Pipeable { + readonly [FormFieldTypeId]: FormFieldTypeId + + readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException> + readonly encodedValueRef: SubscriptionRef.SubscriptionRef + readonly issuesSubscribable: Subscribable.Subscribable + readonly isValidatingSubscribable: Subscribable.Subscribable + readonly isSubmittingSubscribable: Subscribable.Subscribable +} + +class FormFieldImpl +extends Pipeable.Class() implements FormField { + readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId + + constructor( + readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException>, + readonly encodedValueRef: SubscriptionRef.SubscriptionRef, + readonly issuesSubscribable: Subscribable.Subscribable, + readonly isValidatingSubscribable: Subscribable.Subscribable, + readonly isSubmittingSubscribable: Subscribable.Subscribable, + ) { + super() + } +} + + export namespace make { - export interface Options { + export interface Options { readonly schema: Schema.Schema readonly initialEncodedValue: NoInfer + readonly submit: (value: NoInfer) => Effect.Effect } } export const make: { - (options: make.Options): Effect.Effect> -} = Effect.fnUntraced(function* (options: make.Options) { + ( + options: make.Options + ): Effect.Effect> +} = Effect.fnUntraced(function* ( + options: make.Options +) { return new FormImpl( options.schema, + options.submit, yield* SubscriptionRef.make(Option.none()), yield* SubscriptionRef.make(options.initialEncodedValue), yield* SubscriptionRef.make(Option.none()), + yield* SubscriptionRef.make(false), + yield* SubscriptionRef.make(AsyncData.noData()), ) }) export namespace service { - export interface Options extends make.Options {} + export interface Options + extends make.Options {} } -export const service = ( - options: service.Options -): Effect.Effect, never, R | Scope.Scope> => Effect.tap( +export const service = ( + options: service.Options +): Effect.Effect, never, R | Scope.Scope> => Effect.tap( make(options), form => Effect.forkScoped(run(form)), ) export namespace useForm { - export interface Options extends make.Options {} + export interface Options + extends make.Options {} } export const useForm: { - (options: service.Options): Effect.Effect, never, R | Scope.Scope> -} = Effect.fnUntraced(function* (options: service.Options) { + ( + options: service.Options + ): Effect.Effect, never, R> +} = Effect.fnUntraced(function* ( + options: service.Options +) { const form = yield* Hooks.useOnce(() => make(options)) yield* Hooks.useFork(() => run(form), [form]) return form }) -const run = (self: Form) => Stream.runForEach( +const run = (self: Form) => Stream.runForEach( self.encodedValueRef.changes, - flow( - Schema.decode(self.schema, { errors: "all" }), + encodedValue => SubscriptionRef.set(self.isValidatingRef, true).pipe( + Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)), Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))) + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))), + Effect.andThen(SubscriptionRef.set(self.isValidatingRef, false)), ), ) @@ -135,34 +186,21 @@ export namespace useInput { } export const useInput: { - >>( - self: Form, - path: P, + ( + field: FormField, options?: useInput.Options, - ): Effect.Effect>, NoSuchElementException, R> -} = Effect.fnUntraced(function* >( - self: Form, - path: P, + ): Effect.Effect, NoSuchElementException> +} = Effect.fnUntraced(function* ( + field: FormField, options?: useInput.Options, ) { - const [internalValueRef, issuesSubscribable] = yield* Hooks.useMemo(() => Effect.all([ - self.encodedValueRef.pipe( - Effect.andThen(PropertyPath.get(path)), - Effect.andThen(SubscriptionRef.make>), - ), - Effect.succeed(self.makeFieldIssuesSubscribable(path)), - ]), [self, ...path]) - + const internalValueRef = yield* Hooks.useMemo(() => Effect.andThen(field.encodedValueRef, SubscriptionRef.make), [field]) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) - const [issues] = yield* Hooks.useSubscribables(issuesSubscribable) + const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable) yield* Hooks.useFork(() => Effect.all([ Stream.runForEach( - self.encodedValueRef.changes.pipe( - Stream.flatMap(PropertyPath.get(path)), - Stream.drop(1), - ), - + Stream.drop(field.encodedValueRef, 1), upstreamEncodedValue => Effect.whenEffect( SubscriptionRef.set(internalValueRef, upstreamEncodedValue), Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), @@ -175,13 +213,9 @@ export const useInput: { options?.debounce ? Stream.debounce(options.debounce) : identity, Stream.drop(1), ), - - internalValue => self.encodedValueRef.pipe( - Effect.andThen(encodedValue => PropertyPath.immutableSet(encodedValue, path, internalValue)), - Effect.andThen(encodedValue => SubscriptionRef.set(self.encodedValueRef, encodedValue)), - ), + internalValue => SubscriptionRef.set(field.encodedValueRef, internalValue), ), - ], { concurrency: "unbounded" }), [internalValueRef, self, ...path]) + ], { concurrency: "unbounded" }), [field, internalValueRef]) return { value, setValue, issues } }) -- 2.49.1 From 5c748d7daced7a562c0ea4593f9c8e8b44bc230d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 25 Sep 2025 22:25:42 +0200 Subject: [PATCH 30/67] Form work --- packages/effect-fc/src/Form.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index ad20e04..d58b12c 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -173,6 +173,23 @@ const run = (self: Form) => Stream.run ) +export const field: { + >>( + self: Form, + path: P, + ): Effect.Effect, PropertyPath.ValueFromPath>> +} = Effect.fnUntraced(function* >>( + self: Form, + path: P, +) { + return new FormFieldImpl( + SubscribableInternal.make({ + + }) + ) +}) + + export namespace useInput { export interface Options { readonly debounce?: Duration.DurationInput -- 2.49.1 From 7e57cadd9cd01bca097f3fffba01474a4e01c8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 27 Sep 2025 02:35:15 +0200 Subject: [PATCH 31/67] Form work --- packages/effect-fc/src/Form.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index d58b12c..1febeaa 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -183,9 +183,13 @@ export const field: { path: P, ) { return new FormFieldImpl( - SubscribableInternal.make({ - - }) + pipe( + (value: Option.Option) => Option.map(value, v => PropertyPath.get(v, path)), + filter => SubscribableInternal.make({ + get: Effect.flatMap(self.valueRef, filter), + get changes() { return Stream.flatMap(self.valueRef.changes, filter) }, + }), + ) ) }) -- 2.49.1 From 70555943c130fae4ac0921ef88905669de30ba2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 27 Sep 2025 03:47:21 +0200 Subject: [PATCH 32/67] Form work --- packages/effect-fc/src/Form.ts | 35 +++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 1febeaa..1cd7773 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -3,7 +3,7 @@ import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseRes import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" -import { PropertyPath, Subscribable as SubscribableInternal } from "./types/index.js" +import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" export const FormTypeId: unique symbol = Symbol.for("effect-fc/Form") @@ -184,12 +184,41 @@ export const field: { ) { return new FormFieldImpl( pipe( - (value: Option.Option) => Option.map(value, v => PropertyPath.get(v, path)), + (v: Option.Option) => Option.match(v, { + onSome: PropertyPath.get(path), + onNone: () => Option.none(), + }), filter => SubscribableInternal.make({ get: Effect.flatMap(self.valueRef, filter), get changes() { return Stream.flatMap(self.valueRef.changes, filter) }, }), - ) + ), + + SubscriptionSubRef.makeFromPath(self.encodedValueRef, path), + + pipe( + Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }), + filter => SubscribableInternal.make({ + get: Effect.flatMap(self.errorRef.get, filter), + get changes() { return Stream.flatMap(self.errorRef.changes, filter) }, + }), + ), + + self.isValidatingRef, + + pipe( + AsyncData.isLoading, + filter => SubscribableInternal.make({ + get: Effect.map(self.submitStateRef, filter), + get changes() { return Stream.map(self.submitStateRef.changes, filter) }, + }), + ), ) }) -- 2.49.1 From 3eaa250c28a45b20bf69ab16ddb87f6568045ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 28 Sep 2025 18:01:47 +0200 Subject: [PATCH 33/67] Fix --- packages/effect-fc/src/Form.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 1cd7773..ccd02dc 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -55,24 +55,6 @@ extends Pipeable.Class() implements Form { }), ) } - - makeFieldIssuesSubscribable>(path: P) { - const errorRef = this.errorRef - return pipe( - Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), - ), - onNone: () => Effect.succeed([]), - }), - - filter => SubscribableInternal.make({ - get: Effect.flatMap(errorRef.get, filter), - get changes() { return Stream.flatMap(errorRef.changes, filter) }, - }), - ) - } } -- 2.49.1 From ef042ed4b174822aa7ba1a1dda88726c541aa597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 28 Sep 2025 19:16:33 +0200 Subject: [PATCH 34/67] Fix --- packages/effect-fc/src/Form.ts | 85 ++++++++++++++-------------- packages/example/src/routes/form.tsx | 7 ++- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index ccd02dc..e490bce 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -155,54 +155,55 @@ const run = (self: Form) => Stream.run ) -export const field: { - >>( - self: Form, - path: P, - ): Effect.Effect, PropertyPath.ValueFromPath>> -} = Effect.fnUntraced(function* >>( +export const field = >>( self: Form, path: P, -) { - return new FormFieldImpl( - pipe( - (v: Option.Option) => Option.match(v, { - onSome: PropertyPath.get(path), - onNone: () => Option.none(), - }), - filter => SubscribableInternal.make({ - get: Effect.flatMap(self.valueRef, filter), - get changes() { return Stream.flatMap(self.valueRef.changes, filter) }, - }), - ), +): FormField, PropertyPath.ValueFromPath> => new FormFieldImpl( + pipe( + Option.match({ + onSome: (v: A) => Option.map(PropertyPath.get(v, path), Option.some), + onNone: () => Option.some(Option.none()), + }), + filter => SubscribableInternal.make({ + get: Effect.flatMap(self.valueRef, filter), + get changes() { return Stream.flatMap(self.valueRef.changes, filter) }, + }), + ), - SubscriptionSubRef.makeFromPath(self.encodedValueRef, path), + SubscriptionSubRef.makeFromPath(self.encodedValueRef, path), - pipe( - Option.match({ - onSome: (v: ParseResult.ParseError) => Effect.andThen( - ParseResult.ArrayFormatter.formatError(v), - Array.filter(issue => PropertyPath.equivalence(issue.path, path)), - ), - onNone: () => Effect.succeed([]), - }), - filter => SubscribableInternal.make({ - get: Effect.flatMap(self.errorRef.get, filter), - get changes() { return Stream.flatMap(self.errorRef.changes, filter) }, - }), - ), + pipe( + Option.match({ + onSome: (v: ParseResult.ParseError) => Effect.andThen( + ParseResult.ArrayFormatter.formatError(v), + Array.filter(issue => PropertyPath.equivalence(issue.path, path)), + ), + onNone: () => Effect.succeed([]), + }), + filter => SubscribableInternal.make({ + get: Effect.flatMap(self.errorRef.get, filter), + get changes() { return Stream.flatMap(self.errorRef.changes, filter) }, + }), + ), - self.isValidatingRef, + self.isValidatingRef, - pipe( - AsyncData.isLoading, - filter => SubscribableInternal.make({ - get: Effect.map(self.submitStateRef, filter), - get changes() { return Stream.map(self.submitStateRef.changes, filter) }, - }), - ), - ) -}) + pipe( + AsyncData.isLoading, + filter => SubscribableInternal.make({ + get: Effect.map(self.submitStateRef, filter), + get changes() { return Stream.map(self.submitStateRef.changes, filter) }, + }), + ), +) + +export const useField = >>( + self: Form, + path: P, +): FormField, PropertyPath.ValueFromPath> => React.useMemo( + () => field(self, path), + [self, ...path], +) export namespace useInput { diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 569d26c..127019f 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -25,13 +25,16 @@ class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ schema: RegisterFormSchema, initialEncodedValue: { email: "", password: "" }, + submit: () => Effect.void, }) }) {} class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const form = yield* RegisterForm - const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" }) - const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) + const emailField = Form.useField(form, ["email"]) + const passwordField = Form.useField(form, ["password"]) + const emailInput = yield* Form.useInput(emailField, { debounce: "200 millis" }) + const passwordInput = yield* Form.useInput(passwordField, { debounce: "200 millis" }) const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) -- 2.49.1 From b9210885a7ae66677bff99410829241b6b0a4bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 28 Sep 2025 20:54:41 +0200 Subject: [PATCH 35/67] Fix --- packages/effect-fc/src/Form.ts | 8 ++++---- packages/effect-fc/src/types/SubscriptionSubRef.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index e490bce..d473efb 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseResult, pipe, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -235,18 +235,18 @@ export const useInput: { Stream.runForEach( Stream.drop(field.encodedValueRef, 1), upstreamEncodedValue => Effect.whenEffect( - SubscriptionRef.set(internalValueRef, upstreamEncodedValue), + Ref.set(internalValueRef, upstreamEncodedValue), Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), ), ), Stream.runForEach( internalValueRef.changes.pipe( + Stream.drop(1), Stream.changesWith(Equivalence.strict()), options?.debounce ? Stream.debounce(options.debounce) : identity, - Stream.drop(1), ), - internalValue => SubscriptionRef.set(field.encodedValueRef, internalValue), + internalValue => Ref.set(field.encodedValueRef, internalValue), ), ], { concurrency: "unbounded" }), [field, internalValueRef]) diff --git a/packages/effect-fc/src/types/SubscriptionSubRef.ts b/packages/effect-fc/src/types/SubscriptionSubRef.ts index 1abeaac..c007397 100644 --- a/packages/effect-fc/src/types/SubscriptionSubRef.ts +++ b/packages/effect-fc/src/types/SubscriptionSubRef.ts @@ -78,7 +78,7 @@ extends Effectable.Class implements SubscriptionSubRef { return Effect.Do.pipe( Effect.bind("b", (): Effect.Effect> => this.parent), Effect.bind("ca", ({ b }) => f(this.getter(b))), - Effect.tap(({ b, ca: [, a] }) => Ref.set(this.parent, this.setter(b, a))), + Effect.tap(({ b, ca: [, a] }) => SubscriptionRef.set(this.parent, this.setter(b, a))), Effect.map(({ ca: [c] }) => c), ) } -- 2.49.1 From 71d3c77e1ae1a0b2f618e38bdf45f5233c983004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 28 Sep 2025 21:04:00 +0200 Subject: [PATCH 36/67] Fix --- packages/effect-fc/src/Form.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index d473efb..aca094a 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -200,10 +200,10 @@ export const field = >>( self: Form, path: P, -): FormField, PropertyPath.ValueFromPath> => React.useMemo( - () => field(self, path), - [self, ...path], -) +): FormField< + PropertyPath.ValueFromPath, + PropertyPath.ValueFromPath +> => React.useMemo(() => field(self, path), [self, ...path]) export namespace useInput { -- 2.49.1 From 73134478ed9187b9181eba00a77bd2d9a6308cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 29 Sep 2025 14:52:51 +0200 Subject: [PATCH 37/67] Working form --- packages/effect-fc/src/Form.ts | 171 ++++++++++++++++----------- packages/example/src/routes/form.tsx | 65 +++++----- 2 files changed, 142 insertions(+), 94 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index aca094a..eccc270 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equal, Equivalence, Exit, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -43,51 +43,31 @@ extends Pipeable.Class() implements Form { super() this.canSubmitSubscribable = pipe( - ([value, error, isValidating]: readonly [ + ([value, error, isValidating, submitState]: readonly [ Option.Option, Option.Option, boolean, - ]) => Option.isSome(value) && Option.isNone(error) && !isValidating, + AsyncData.AsyncData, + ]) => Option.isSome(value) && Option.isNone(error) && !isValidating && !AsyncData.isLoading(submitState), filter => SubscribableInternal.make({ - get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef]), filter), - get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes, isValidatingRef.changes), filter)}, + get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef, submitStateRef]), filter), + get changes() { + return Stream.map( + Stream.zipLatestAll( + valueRef.changes, + errorRef.changes, + isValidatingRef.changes, + submitStateRef.changes, + ), + filter, + ) + }, }), ) } } - -export const FormFieldTypeId: unique symbol = Symbol.for("effect-fc/FormField") -export type FormFieldTypeId = typeof FormFieldTypeId - -export interface FormField -extends Pipeable.Pipeable { - readonly [FormFieldTypeId]: FormFieldTypeId - - readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException> - readonly encodedValueRef: SubscriptionRef.SubscriptionRef - readonly issuesSubscribable: Subscribable.Subscribable - readonly isValidatingSubscribable: Subscribable.Subscribable - readonly isSubmittingSubscribable: Subscribable.Subscribable -} - -class FormFieldImpl -extends Pipeable.Class() implements FormField { - readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId - - constructor( - readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException>, - readonly encodedValueRef: SubscriptionRef.SubscriptionRef, - readonly issuesSubscribable: Subscribable.Subscribable, - readonly isValidatingSubscribable: Subscribable.Subscribable, - readonly isSubmittingSubscribable: Subscribable.Subscribable, - ) { - super() - } -} - - export namespace make { export interface Options { readonly schema: Schema.Schema @@ -114,6 +94,39 @@ export const make: { ) }) +export const run = ( + self: Form +): Effect.Effect => Stream.runForEach( + self.encodedValueRef.changes, + encodedValue => SubscriptionRef.set(self.isValidatingRef, true).pipe( + Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)), + Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), + Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), + Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))), + Effect.andThen(SubscriptionRef.set(self.isValidatingRef, false)), + ), +) + +export const submit = ( + self: Form +): Effect.Effect>, NoSuchElementException, SR> => Effect.whenEffect( + self.valueRef.pipe( + Effect.andThen(identity), + Effect.tap(Ref.set(self.submitStateRef, AsyncData.loading())), + Effect.andThen(flow( + self.submit, + Effect.exit, + Effect.map(Exit.match({ + onSuccess: a => AsyncData.success(a), + onFailure: e => AsyncData.failure(e), + })), + Effect.tap(v => Ref.set(self.submitStateRef, v)) + )), + ), + + self.canSubmitSubscribable.get, +) + export namespace service { export interface Options extends make.Options {} @@ -126,35 +139,6 @@ export const service = ( form => Effect.forkScoped(run(form)), ) -export namespace useForm { - export interface Options - extends make.Options {} -} - -export const useForm: { - ( - options: service.Options - ): Effect.Effect, never, R> -} = Effect.fnUntraced(function* ( - options: service.Options -) { - const form = yield* Hooks.useOnce(() => make(options)) - yield* Hooks.useFork(() => run(form), [form]) - return form -}) - -const run = (self: Form) => Stream.runForEach( - self.encodedValueRef.changes, - encodedValue => SubscriptionRef.set(self.isValidatingRef, true).pipe( - Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)), - Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), - Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))), - Effect.andThen(SubscriptionRef.set(self.isValidatingRef, false)), - ), -) - - export const field = >>( self: Form, path: P, @@ -197,6 +181,62 @@ export const field = +extends Pipeable.Pipeable { + readonly [FormFieldTypeId]: FormFieldTypeId + + readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException> + readonly encodedValueRef: SubscriptionRef.SubscriptionRef + readonly issuesSubscribable: Subscribable.Subscribable + readonly isValidatingSubscribable: Subscribable.Subscribable + readonly isSubmittingSubscribable: Subscribable.Subscribable +} + +class FormFieldImpl +extends Pipeable.Class() implements FormField { + readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId + + constructor( + readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException>, + readonly encodedValueRef: SubscriptionRef.SubscriptionRef, + readonly issuesSubscribable: Subscribable.Subscribable, + readonly isValidatingSubscribable: Subscribable.Subscribable, + readonly isSubmittingSubscribable: Subscribable.Subscribable, + ) { + super() + } +} + + +export namespace useForm { + export interface Options + extends make.Options {} +} + +export const useForm: { + ( + options: service.Options + ): Effect.Effect, never, R> +} = Effect.fnUntraced(function* ( + options: service.Options +) { + const form = yield* Hooks.useOnce(() => make(options)) + yield* Hooks.useFork(() => run(form), [form]) + return form +}) + +export const useSubmit = ( + self: Form +): Effect.Effect< + () => Promise>>, + never, + SR +> => Hooks.useCallbackPromise(() => submit(self), [self]) + export const useField = >>( self: Form, path: P, @@ -205,7 +245,6 @@ export const useField = > => React.useMemo(() => field(self, path), [self, ...path]) - export namespace useInput { export interface Options { readonly debounce?: Duration.DurationInput diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 127019f..ddb6942 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -25,7 +25,10 @@ class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ schema: RegisterFormSchema, initialEncodedValue: { email: "", password: "" }, - submit: () => Effect.void, + submit: () => Effect.andThen( + Effect.sleep("500 millis"), + Effect.sync(() => alert("Done!")), + ), }) }) {} @@ -36,43 +39,49 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const emailInput = yield* Form.useInput(emailField, { debounce: "200 millis" }) const passwordInput = yield* Form.useInput(passwordField, { debounce: "200 millis" }) + const submit = yield* Form.useSubmit(form) const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) return ( - - emailInput.setValue(e.target.value)} - /> +

{ + e.preventDefault() + void submit() + }}> + + emailInput.setValue(e.target.value)} + /> - {Option.match(Array.head(emailInput.issues), { - onSome: issue => ( - - {issue.message} - - ), + {Option.match(Array.head(emailInput.issues), { + onSome: issue => ( + + {issue.message} + + ), - onNone: () => <>, - })} + onNone: () => <>, + })} - passwordInput.setValue(e.target.value)} - /> + passwordInput.setValue(e.target.value)} + /> - {Option.match(Array.head(passwordInput.issues), { - onSome: issue => ( - - {issue.message} - - ), + {Option.match(Array.head(passwordInput.issues), { + onSome: issue => ( + + {issue.message} + + ), - onNone: () => <>, - })} + onNone: () => <>, + })} - - + + + ) }) {} -- 2.49.1 From 833cfa503a6bc3db8ed394374b5e1e9c7a9585e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 29 Sep 2025 15:28:59 +0200 Subject: [PATCH 38/67] Fix --- packages/effect-fc/src/Form.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index eccc270..5ab6848 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -19,7 +19,6 @@ extends Pipeable.Pipeable { readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, - /** Whether or not a validation is currently being executed */ readonly isValidatingRef: SubscriptionRef.SubscriptionRef readonly submitStateRef: SubscriptionRef.SubscriptionRef>, @@ -34,6 +33,7 @@ extends Pipeable.Class() implements Form { constructor( readonly schema: Schema.Schema, readonly submit: (value: NoInfer) => Effect.Effect, + readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, @@ -86,6 +86,7 @@ export const make: { return new FormImpl( options.schema, options.submit, + yield* SubscriptionRef.make(Option.none()), yield* SubscriptionRef.make(options.initialEncodedValue), yield* SubscriptionRef.make(Option.none()), -- 2.49.1 From 74a8714acb816479691917d88b1e4b6c5425c13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 14:22:21 +0200 Subject: [PATCH 39/67] Form work --- packages/effect-fc/src/Form.ts | 115 ++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 30 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 5ab6848..472a45b 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -28,7 +28,6 @@ extends Pipeable.Pipeable { class FormImpl extends Pipeable.Class() implements Form { readonly [FormTypeId]: FormTypeId = FormTypeId - readonly canSubmitSubscribable: Subscribable.Subscribable constructor( readonly schema: Schema.Schema, @@ -39,10 +38,44 @@ extends Pipeable.Class() implements Form { readonly errorRef: SubscriptionRef.SubscriptionRef>, readonly isValidatingRef: SubscriptionRef.SubscriptionRef, readonly submitStateRef: SubscriptionRef.SubscriptionRef>, + + readonly canSubmitSubscribable: Subscribable.Subscribable, ) { super() + } +} - this.canSubmitSubscribable = pipe( +export namespace make { + export interface Options { + readonly schema: Schema.Schema + readonly initialEncodedValue: NoInfer + readonly submit: (value: NoInfer) => Effect.Effect + } +} + +export const make: { + ( + options: make.Options + ): Effect.Effect> +} = Effect.fnUntraced(function* ( + options: make.Options +) { + const valueRef = yield* SubscriptionRef.make(Option.none()) + const errorRef = yield* SubscriptionRef.make(Option.none()) + const isValidatingRef = yield* SubscriptionRef.make(false) + const submitStateRef = yield* SubscriptionRef.make(AsyncData.noData()) + + return new FormImpl( + options.schema, + options.submit, + + valueRef, + yield* SubscriptionRef.make(options.initialEncodedValue), + errorRef, + isValidatingRef, + submitStateRef, + + pipe( ([value, error, isValidating, submitState]: readonly [ Option.Option, Option.Option, @@ -64,34 +97,7 @@ extends Pipeable.Class() implements Form { ) }, }), - ) - } -} - -export namespace make { - export interface Options { - readonly schema: Schema.Schema - readonly initialEncodedValue: NoInfer - readonly submit: (value: NoInfer) => Effect.Effect - } -} - -export const make: { - ( - options: make.Options - ): Effect.Effect> -} = Effect.fnUntraced(function* ( - options: make.Options -) { - return new FormImpl( - options.schema, - options.submit, - - yield* SubscriptionRef.make(Option.none()), - yield* SubscriptionRef.make(options.initialEncodedValue), - yield* SubscriptionRef.make(Option.none()), - yield* SubscriptionRef.make(false), - yield* SubscriptionRef.make(AsyncData.noData()), + ), ) }) @@ -292,3 +298,52 @@ export const useInput: { return { value, setValue, issues } }) + +export namespace useOptionalInput { + export interface Options> extends useInput.Options { + readonly defaultValue: Option.Option.Value + } + + export interface Result extends useInput.Result {} +} + +export const useOptionalInput: { + >( + field: FormField, + options: useOptionalInput.Options, + ): Effect.Effect>, NoSuchElementException> +} = Effect.fnUntraced(function* >( + field: FormField, + options: useOptionalInput.Options, +) { + const internalValueRef = yield* Hooks.useMemo(() => field.encodedValueRef.pipe( + Effect.map(Option.match({ + onSome: identity, + onNone: () => options.defaultValue, + })), + Effect.andThen(SubscriptionRef.make), + ), [field]) + const [value, setValue] = yield* Hooks.useRefState(internalValueRef) + const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable) + + yield* Hooks.useFork(() => Effect.all([ + Stream.runForEach( + Stream.drop(field.encodedValueRef, 1), + upstreamEncodedValue => Effect.whenEffect( + Ref.set(internalValueRef, upstreamEncodedValue), + Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), + ), + ), + + Stream.runForEach( + internalValueRef.changes.pipe( + Stream.drop(1), + Stream.changesWith(Equivalence.strict()), + options?.debounce ? Stream.debounce(options.debounce) : identity, + ), + internalValue => Ref.set(field.encodedValueRef, internalValue), + ), + ], { concurrency: "unbounded" }), [field, internalValueRef]) + + return { value, setValue, issues } +}) -- 2.49.1 From 58bb84ac2b02cb0455758945a69e82a688f1e1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 14:56:08 +0200 Subject: [PATCH 40/67] Form work --- packages/effect-fc/src/Form.ts | 50 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 472a45b..9054291 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Duration, Effect, Equal, Equivalence, Exit, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Duration, Effect, Equal, Exit, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -289,40 +289,45 @@ export const useInput: { Stream.runForEach( internalValueRef.changes.pipe( Stream.drop(1), - Stream.changesWith(Equivalence.strict()), + Stream.changesWith(Equal.equivalence()), options?.debounce ? Stream.debounce(options.debounce) : identity, ), internalValue => Ref.set(field.encodedValueRef, internalValue), ), - ], { concurrency: "unbounded" }), [field, internalValueRef]) + ], { concurrency: "unbounded" }), [field, internalValueRef, options?.debounce]) return { value, setValue, issues } }) export namespace useOptionalInput { - export interface Options> extends useInput.Options { - readonly defaultValue: Option.Option.Value + export interface Options extends useInput.Options { + readonly defaultValue: I } - export interface Result extends useInput.Result {} + export interface Result extends useInput.Result { + readonly enabled: boolean + readonly setEnabled: React.Dispatch> + } } export const useOptionalInput: { - >( - field: FormField, + ( + field: FormField>, options: useOptionalInput.Options, - ): Effect.Effect>, NoSuchElementException> -} = Effect.fnUntraced(function* >( - field: FormField, + ): Effect.Effect, NoSuchElementException> +} = Effect.fnUntraced(function* ( + field: FormField>, options: useOptionalInput.Options, ) { - const internalValueRef = yield* Hooks.useMemo(() => field.encodedValueRef.pipe( - Effect.map(Option.match({ - onSome: identity, - onNone: () => options.defaultValue, - })), - Effect.andThen(SubscriptionRef.make), + const [enabledRef, internalValueRef] = yield* Hooks.useMemo(() => Effect.andThen( + field.encodedValueRef, + Option.match({ + onSome: v => Effect.all([SubscriptionRef.make(true), SubscriptionRef.make(v)]), + onNone: () => Effect.all([SubscriptionRef.make(false), SubscriptionRef.make(options.defaultValue)]), + }), ), [field]) + + const [enabled, setEnabled] = yield* Hooks.useRefState(enabledRef) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable) @@ -336,14 +341,15 @@ export const useOptionalInput: { ), Stream.runForEach( - internalValueRef.changes.pipe( + enabledRef.changes.pipe( + Stream.zipLatest(internalValueRef.changes), Stream.drop(1), - Stream.changesWith(Equivalence.strict()), + Stream.changesWith(Equal.equivalence()), options?.debounce ? Stream.debounce(options.debounce) : identity, ), - internalValue => Ref.set(field.encodedValueRef, internalValue), + ([enabled, internalValue]) => Ref.set(field.encodedValueRef, enabled ? Option.some(internalValue) : Option.none()), ), - ], { concurrency: "unbounded" }), [field, internalValueRef]) + ], { concurrency: "unbounded" }), [field, enabledRef, internalValueRef, options.debounce]) - return { value, setValue, issues } + return { enabled, setEnabled, value, setValue, issues } }) -- 2.49.1 From 47e21f6340ab8816b7187f43c9c2efe30d8190d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 15:12:53 +0200 Subject: [PATCH 41/67] Finished form --- packages/effect-fc/src/Form.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 9054291..10ff4f6 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -334,9 +334,23 @@ export const useOptionalInput: { yield* Hooks.useFork(() => Effect.all([ Stream.runForEach( Stream.drop(field.encodedValueRef, 1), + upstreamEncodedValue => Effect.whenEffect( - Ref.set(internalValueRef, upstreamEncodedValue), - Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), + Option.match(upstreamEncodedValue, { + onSome: v => Effect.andThen( + Ref.set(enabledRef, true), + Ref.set(internalValueRef, v), + ), + onNone: () => Effect.andThen( + Ref.set(enabledRef, false), + Ref.set(internalValueRef, options.defaultValue), + ), + }), + + Effect.andThen( + Effect.all([enabledRef, internalValueRef]), + ([enabled, internalValue]) => !Equal.equals(upstreamEncodedValue, enabled ? Option.some(internalValue) : Option.none()), + ), ), ), -- 2.49.1 From 040e671fd3d82f23c096f22f1a081ffae0dbc223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 15:54:59 +0200 Subject: [PATCH 42/67] Working field --- packages/effect-fc/src/Form.ts | 7 +-- .../src/lib/form/TextFieldFormInput.tsx | 40 +++++++++++++++++ packages/example/src/routes/form.tsx | 44 +++++-------------- 3 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 packages/example/src/lib/form/TextFieldFormInput.tsx diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 10ff4f6..e108139 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -260,7 +260,6 @@ export namespace useInput { export interface Result { readonly value: T readonly setValue: React.Dispatch> - readonly issues: readonly ParseResult.ArrayFormatterIssue[] } } @@ -275,7 +274,6 @@ export const useInput: { ) { const internalValueRef = yield* Hooks.useMemo(() => Effect.andThen(field.encodedValueRef, SubscriptionRef.make), [field]) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) - const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable) yield* Hooks.useFork(() => Effect.all([ Stream.runForEach( @@ -296,7 +294,7 @@ export const useInput: { ), ], { concurrency: "unbounded" }), [field, internalValueRef, options?.debounce]) - return { value, setValue, issues } + return { value, setValue } }) export namespace useOptionalInput { @@ -329,7 +327,6 @@ export const useOptionalInput: { const [enabled, setEnabled] = yield* Hooks.useRefState(enabledRef) const [value, setValue] = yield* Hooks.useRefState(internalValueRef) - const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable) yield* Hooks.useFork(() => Effect.all([ Stream.runForEach( @@ -365,5 +362,5 @@ export const useOptionalInput: { ), ], { concurrency: "unbounded" }), [field, enabledRef, internalValueRef, options.debounce]) - return { enabled, setEnabled, value, setValue, issues } + return { enabled, setEnabled, value, setValue } }) diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx new file mode 100644 index 0000000..ea596b0 --- /dev/null +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -0,0 +1,40 @@ +import { Callout, Flex, TextField } from "@radix-ui/themes" +import { Array, Option } from "effect" +import { Component, Form } from "effect-fc" +import { useSubscribables } from "effect-fc/hooks" + + +export interface TextFieldFormInputProps +extends TextField.RootProps, Form.useInput.Options { + readonly field: Form.FormField +} + +export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")(function*(props: TextFieldFormInputProps) { + const { value, setValue } = yield* Form.useInput(props.field, props) + const [issues, isValidating, isSubmitting] = yield* useSubscribables( + props.field.issuesSubscribable, + props.field.isValidatingSubscribable, + props.field.isSubmittingSubscribable, + ) + + return ( + + setValue(e.target.value)} + disabled={isSubmitting} + {...props} + /> + + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) +}) {} diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index ddb6942..4f7f831 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,7 +1,8 @@ +import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" import { runtime } from "@/runtime" -import { Button, Callout, Container, Flex, TextField } from "@radix-ui/themes" +import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Array, Effect, Option, Schema } from "effect" +import { Effect, Schema } from "effect" import { Component, Form } from "effect-fc" import { useContext, useSubscribables } from "effect-fc/hooks" @@ -34,14 +35,11 @@ class RegisterForm extends Effect.Service()("RegisterForm", { class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const form = yield* RegisterForm - const emailField = Form.useField(form, ["email"]) - const passwordField = Form.useField(form, ["password"]) - const emailInput = yield* Form.useInput(emailField, { debounce: "200 millis" }) - const passwordInput = yield* Form.useInput(passwordField, { debounce: "200 millis" }) - const submit = yield* Form.useSubmit(form) const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) + const TextFieldFormInputFC = yield* TextFieldFormInput + return (
{ @@ -49,36 +47,16 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { void submit() }}> - emailInput.setValue(e.target.value)} + - {Option.match(Array.head(emailInput.issues), { - onSome: issue => ( - - {issue.message} - - ), - - onNone: () => <>, - })} - - passwordInput.setValue(e.target.value)} + - {Option.match(Array.head(passwordInput.issues), { - onSome: issue => ( - - {issue.message} - - ), - - onNone: () => <>, - })} -
-- 2.49.1 From 9d978e709feb09656147da88f1e6232ad30e38e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 16:03:00 +0200 Subject: [PATCH 43/67] Fix --- packages/effect-fc/src/Form.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index e108139..d17b673 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -226,12 +226,14 @@ export namespace useForm { export const useForm: { ( - options: service.Options + options: make.Options, + deps: React.DependencyList, ): Effect.Effect, never, R> } = Effect.fnUntraced(function* ( - options: service.Options + options: make.Options, + deps: React.DependencyList, ) { - const form = yield* Hooks.useOnce(() => make(options)) + const form = yield* Hooks.useMemo(() => make(options), deps) yield* Hooks.useFork(() => run(form), [form]) return form }) -- 2.49.1 From a1dc98aa04ba2b659e67d4186084e491cb8e207f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 19:43:39 +0200 Subject: [PATCH 44/67] Refactoring --- packages/effect-fc/src/Form.ts | 66 ++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index d17b673..4f27696 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Duration, Effect, Equal, Exit, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -19,7 +19,7 @@ extends Pipeable.Pipeable { readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, - readonly isValidatingRef: SubscriptionRef.SubscriptionRef + readonly validationFiberRef: SubscriptionRef.SubscriptionRef>> readonly submitStateRef: SubscriptionRef.SubscriptionRef>, readonly canSubmitSubscribable: Subscribable.Subscribable @@ -36,7 +36,7 @@ extends Pipeable.Class() implements Form { readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, readonly errorRef: SubscriptionRef.SubscriptionRef>, - readonly isValidatingRef: SubscriptionRef.SubscriptionRef, + readonly validationFiberRef: SubscriptionRef.SubscriptionRef>>, readonly submitStateRef: SubscriptionRef.SubscriptionRef>, readonly canSubmitSubscribable: Subscribable.Subscribable, @@ -62,7 +62,7 @@ export const make: { ) { const valueRef = yield* SubscriptionRef.make(Option.none
()) const errorRef = yield* SubscriptionRef.make(Option.none()) - const isValidatingRef = yield* SubscriptionRef.make(false) + const validationFiberRef = yield* SubscriptionRef.make(Option.none>()) const submitStateRef = yield* SubscriptionRef.make(AsyncData.noData()) return new FormImpl( @@ -72,25 +72,25 @@ export const make: { valueRef, yield* SubscriptionRef.make(options.initialEncodedValue), errorRef, - isValidatingRef, + validationFiberRef, submitStateRef, pipe( - ([value, error, isValidating, submitState]: readonly [ + ([value, error, validationFiber, submitState]: readonly [ Option.Option, Option.Option, - boolean, + Option.Option>, AsyncData.AsyncData, - ]) => Option.isSome(value) && Option.isNone(error) && !isValidating && !AsyncData.isLoading(submitState), + ]) => Option.isSome(value) && Option.isNone(error) && Option.isNone(validationFiber) && !AsyncData.isLoading(submitState), filter => SubscribableInternal.make({ - get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef, submitStateRef]), filter), + get: Effect.map(Effect.all([valueRef, errorRef, validationFiberRef, submitStateRef]), filter), get changes() { return Stream.map( Stream.zipLatestAll( valueRef.changes, errorRef.changes, - isValidatingRef.changes, + validationFiberRef.changes, submitStateRef.changes, ), filter, @@ -103,14 +103,38 @@ export const make: { export const run = ( self: Form -): Effect.Effect => Stream.runForEach( +): Effect.Effect => Stream.runForEach( self.encodedValueRef.changes, - encodedValue => SubscriptionRef.set(self.isValidatingRef, true).pipe( - Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)), - Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))), - Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())), - Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))), - Effect.andThen(SubscriptionRef.set(self.isValidatingRef, false)), + encodedValue => self.validationFiberRef.pipe( + Effect.andThen(Option.match({ + onSome: Fiber.interrupt, + onNone: () => Effect.void, + })), + Effect.andThen( + Effect.addFinalizer(() => SubscriptionRef.set(self.validationFiberRef, Option.none())).pipe( + Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)), + Effect.exit, + Effect.andThen(flow( + Exit.matchEffect({ + onSuccess: v => Effect.andThen( + SubscriptionRef.set(self.valueRef, Option.some(v)), + SubscriptionRef.set(self.errorRef, Option.none()), + ), + onFailure: c => Option.match( + Chunk.findFirst(Cause.failures(c), e => e._tag === "ParseError"), + { + onSome: e => SubscriptionRef.set(self.errorRef, Option.some(e)), + onNone: () => Effect.void, + }, + ), + }), + Effect.uninterruptible, + )), + Effect.scoped, + Effect.forkScoped, + ) + ), + Effect.andThen(fiber => SubscriptionRef.set(self.validationFiberRef, Option.some(fiber))) ), ) @@ -177,7 +201,13 @@ export const field = SubscribableInternal.make({ + get: Effect.map(self.validationFiberRef.get, filter), + get changes() { return Stream.map(self.validationFiberRef.changes, filter) }, + }), + ), pipe( AsyncData.isLoading, -- 2.49.1 From 917a9590de73b3bfadd012f514ed7200943abcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 19:52:44 +0200 Subject: [PATCH 45/67] Fix --- packages/example/src/lib/form/TextFieldFormInput.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index ea596b0..907fbde 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,4 +1,4 @@ -import { Callout, Flex, TextField } from "@radix-ui/themes" +import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" import { Component, Form } from "effect-fc" import { useSubscribables } from "effect-fc/hooks" @@ -24,7 +24,13 @@ export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInp onChange={e => setValue(e.target.value)} disabled={isSubmitting} {...props} - /> + > + {isValidating && + + + + } + {Option.match(Array.head(issues), { onSome: issue => ( -- 2.49.1 From ef137d07f79e9964aff04c514d72e975a794d63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 20:38:46 +0200 Subject: [PATCH 46/67] Tests --- packages/example/src/routes/form.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 4f7f831..4653e1f 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -2,7 +2,7 @@ import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" import { runtime } from "@/runtime" import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect, Schema } from "effect" +import { Effect, ParseResult, Schema } from "effect" import { Component, Form } from "effect-fc" import { useContext, useSubscribables } from "effect-fc/hooks" @@ -24,7 +24,14 @@ const RegisterFormSchema = Schema.Struct({ class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ - schema: RegisterFormSchema, + schema: Schema.transformOrFail( + Schema.encodedSchema(RegisterFormSchema), + Schema.typeSchema(RegisterFormSchema), + { + decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + encode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + }, + ), initialEncodedValue: { email: "", password: "" }, submit: () => Effect.andThen( Effect.sleep("500 millis"), @@ -49,12 +56,12 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { -- 2.49.1 From e2187077b344bccb8122bcaac9ef72d2634d41f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 30 Sep 2025 22:02:11 +0200 Subject: [PATCH 47/67] Cleanup --- packages/effect-fc/src/types/Schema.ts | 37 -------------------------- 1 file changed, 37 deletions(-) delete mode 100644 packages/effect-fc/src/types/Schema.ts diff --git a/packages/effect-fc/src/types/Schema.ts b/packages/effect-fc/src/types/Schema.ts deleted file mode 100644 index 65ad399..0000000 --- a/packages/effect-fc/src/types/Schema.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Array, Function, Option, Predicate, Schema } from "effect" -import type { Simplify } from "effect/Types" - - -export type SchemaFromPath = S extends Schema.Schema.Any - ? P extends [infer Head, ...infer Tail] - ? Head extends keyof S["Type"] - ? ( - S extends Schema.TupleType ? ( - Head extends keyof Elements ? SchemaFromPath : - Head extends keyof Rest ? SchemaFromPath : - never - ) : - S extends Schema.Array$ ? SchemaFromPath : - S extends Schema.Struct ? SchemaFromPath : - never - ) - : never - : S - : never - -const TestSchema = Schema.Struct({ - allUsers: Schema.Array(Schema.Struct({ - name: Schema.String - })), - - admins: Schema.Tuple( - Schema.Struct({ - name: Schema.Literal("Gneugneu") - }), - Schema.Struct({ - name: Schema.Literal("AAAAYA") - }), - ), -}) -type S = SchemaFromPath -type T = number extends keyof typeof TestSchema.fields.admins.elements ? true : false -- 2.49.1 From 57230116421abc57d8c44da8f4bf55ae2fa5e4dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 00:49:24 +0200 Subject: [PATCH 48/67] Memo -> Memoized --- packages/effect-fc/src/Component.ts | 4 +-- packages/effect-fc/src/Memo.ts | 50 ----------------------------- packages/effect-fc/src/Memoized.ts | 50 +++++++++++++++++++++++++++++ packages/effect-fc/src/index.ts | 2 +- 4 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 packages/effect-fc/src/Memo.ts create mode 100644 packages/effect-fc/src/Memoized.ts diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index c0ddc6f..7383963 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,7 +1,7 @@ import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, Tracer, type Types, type Utils } from "effect" import * as React from "react" import { Hooks } from "./hooks/index.js" -import * as Memo from "./Memo.js" +import { Memoized } from "./index.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Component") @@ -67,7 +67,7 @@ const ComponentProto = Object.freeze({ const FC = React.useMemo(() => { const f: React.FC

= self.makeFunctionComponent(runtimeRef, scope) f.displayName = self.displayName ?? "Anonymous" - return Memo.isMemo(self) + return Memoized.isMemoized(self) ? React.memo(f, self.propsAreEqual) : f }, [scope]) diff --git a/packages/effect-fc/src/Memo.ts b/packages/effect-fc/src/Memo.ts deleted file mode 100644 index 7abf319..0000000 --- a/packages/effect-fc/src/Memo.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { type Equivalence, Function, Predicate } from "effect" -import type * as Component from "./Component.js" - - -export const TypeId: unique symbol = Symbol.for("effect-fc/Memo") -export type TypeId = typeof TypeId - -export interface Memo

extends Memo.Options

{ - readonly [TypeId]: TypeId -} - -export namespace Memo { - export interface Options

{ - readonly propsAreEqual?: Equivalence.Equivalence

- } -} - - -const MemoProto = Object.freeze({ - [TypeId]: TypeId -} as const) - - -export const isMemo = (u: unknown): u is Memo => Predicate.hasProperty(u, TypeId) - -export const memo = >( - self: T -): T & Memo> => Object.setPrototypeOf( - Object.assign(function() {}, self), - Object.freeze(Object.setPrototypeOf( - Object.assign({}, MemoProto), - Object.getPrototypeOf(self), - )), -) - -export const withOptions: { - & Memo>( - options: Partial>> - ): (self: T) => T - & Memo>( - self: T, - options: Partial>>, - ): T -} = Function.dual(2, & Memo>( - self: T, - options: Partial>>, -): T => Object.setPrototypeOf( - Object.assign(function() {}, self, options), - Object.getPrototypeOf(self), -)) diff --git a/packages/effect-fc/src/Memoized.ts b/packages/effect-fc/src/Memoized.ts new file mode 100644 index 0000000..c80a3e0 --- /dev/null +++ b/packages/effect-fc/src/Memoized.ts @@ -0,0 +1,50 @@ +import { type Equivalence, Function, Predicate } from "effect" +import type * as Component from "./Component.js" + + +export const TypeId: unique symbol = Symbol.for("effect-fc/Memoized") +export type TypeId = typeof TypeId + +export interface Memoized

extends Memoized.Options

{ + readonly [TypeId]: TypeId +} + +export namespace Memoized { + export interface Options

{ + readonly propsAreEqual?: Equivalence.Equivalence

+ } +} + + +const MemoizedProto = Object.freeze({ + [TypeId]: TypeId +} as const) + + +export const isMemoized = (u: unknown): u is Memoized => Predicate.hasProperty(u, TypeId) + +export const memoized = >( + self: T +): T & Memoized> => Object.setPrototypeOf( + Object.assign(function() {}, self), + Object.freeze(Object.setPrototypeOf( + Object.assign({}, MemoizedProto), + Object.getPrototypeOf(self), + )), +) + +export const withOptions: { + & Memoized>( + options: Partial>> + ): (self: T) => T + & Memoized>( + self: T, + options: Partial>>, + ): T +} = Function.dual(2, & Memoized>( + self: T, + options: Partial>>, +): T => Object.setPrototypeOf( + Object.assign(function() {}, self, options), + Object.getPrototypeOf(self), +)) diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 1595021..513ddd1 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,5 +1,5 @@ export * as Component from "./Component.js" export * as Form from "./Form.js" -export * as Memo from "./Memo.js" +export * as Memoized from "./Memoized.js" export * as ReactRuntime from "./ReactRuntime.js" export * as Suspense from "./Suspense.js" -- 2.49.1 From bad869015652d6e303fcb45a4a422716ccf7bd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 00:59:35 +0200 Subject: [PATCH 49/67] Suspense -> Async --- .../effect-fc/src/{Suspense.ts => Async.ts} | 30 +++++++++---------- packages/effect-fc/src/index.ts | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) rename packages/effect-fc/src/{Suspense.ts => Async.ts} (69%) diff --git a/packages/effect-fc/src/Suspense.ts b/packages/effect-fc/src/Async.ts similarity index 69% rename from packages/effect-fc/src/Suspense.ts rename to packages/effect-fc/src/Async.ts index bd9cb57..22fe2f1 100644 --- a/packages/effect-fc/src/Suspense.ts +++ b/packages/effect-fc/src/Async.ts @@ -3,14 +3,14 @@ import * as React from "react" import type * as Component from "./Component.js" -export const TypeId: unique symbol = Symbol.for("effect-fc/Suspense") +export const TypeId: unique symbol = Symbol.for("effect-fc/Async") export type TypeId = typeof TypeId -export interface Suspense extends Suspense.Options { +export interface Async extends Async.Options { readonly [TypeId]: TypeId } -export namespace Suspense { +export namespace Async { export interface Options { readonly defaultFallback?: React.ReactNode } @@ -23,13 +23,13 @@ const SuspenseProto = Object.freeze({ [TypeId]: TypeId, makeFunctionComponent

( - this: Component.Component & Suspense, + this: Component.Component & Async, runtimeRef: React.RefObject>>, scope: Scope.Scope, ) { const SuspenseInner = (props: { readonly promise: Promise }) => React.use(props.promise) - return ({ fallback, name, ...props }: Suspense.Props) => { + return ({ fallback, name, ...props }: Async.Props) => { const promise = Runtime.runPromise(runtimeRef.current)( Effect.provideService(this.body(props as P), Scope.Scope, scope) ) @@ -44,19 +44,19 @@ const SuspenseProto = Object.freeze({ } as const) -export const isSuspense = (u: unknown): u is Suspense => Predicate.hasProperty(u, TypeId) +export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, TypeId) -export const suspense = >( +export const async = >( self: T ): ( & Omit> & Component.Component< - Component.Component.Props & Suspense.Props, + Component.Component.Props & Async.Props, Component.Component.Success, Component.Component.Error, Component.Component.Context > - & Suspense + & Async ) => Object.setPrototypeOf( Object.assign(function() {}, self), Object.freeze(Object.setPrototypeOf( @@ -66,16 +66,16 @@ export const suspense = >( ) export const withOptions: { - & Suspense>( - options: Partial + & Async>( + options: Partial ): (self: T) => T - & Suspense>( + & Async>( self: T, - options: Partial, + options: Partial, ): T -} = Function.dual(2, & Suspense>( +} = Function.dual(2, & Async>( self: T, - options: Partial, + options: Partial, ): T => Object.setPrototypeOf( Object.assign(function() {}, self, options), Object.getPrototypeOf(self), diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 513ddd1..e3fe33f 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,5 +1,5 @@ +export * as Async from "./Async.js" export * as Component from "./Component.js" export * as Form from "./Form.js" export * as Memoized from "./Memoized.js" export * as ReactRuntime from "./ReactRuntime.js" -export * as Suspense from "./Suspense.js" -- 2.49.1 From 8e0e6b40f6a1e3dd4aca0bb43b2586948b5e929e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 01:06:07 +0200 Subject: [PATCH 50/67] Fix --- packages/effect-fc/src/Form.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 4f27696..5d775bf 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, pipe, Pipeable, Predicate, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import { Hooks } from "./hooks/index.js" @@ -45,6 +45,8 @@ extends Pipeable.Class() implements Form { } } +export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) + export namespace make { export interface Options { readonly schema: Schema.Schema @@ -248,6 +250,7 @@ extends Pipeable.Class() implements FormField { } } +export const isFormField = (u: unknown): u is FormField => Predicate.hasProperty(u, FormFieldTypeId) export namespace useForm { export interface Options -- 2.49.1 From 62a57b1b289e12ad0ef6c877cc3c86a10aa2cdd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 01:14:45 +0200 Subject: [PATCH 51/67] Move Hooks --- packages/effect-fc/package.json | 8 -------- packages/effect-fc/src/Form.ts | 2 +- packages/effect-fc/src/{hooks => }/Hooks/ScopeOptions.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/index.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/input/index.ts | 0 .../effect-fc/src/{hooks => }/Hooks/input/useInput.ts | 0 .../src/{hooks => }/Hooks/input/useOptionalInput.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/internal.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useCallbackPromise.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useCallbackSync.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useContext.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useEffect.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useFork.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useLayoutEffect.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useMemo.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useOnce.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useRefFromState.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useRefState.ts | 0 packages/effect-fc/src/{hooks => }/Hooks/useScope.ts | 0 .../src/{hooks => }/Hooks/useStreamFromReactiveValues.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useSubscribables.ts | 0 .../effect-fc/src/{hooks => }/Hooks/useSubscribeStream.ts | 0 packages/effect-fc/src/hooks/index.ts | 2 -- packages/effect-fc/src/index.ts | 1 + 24 files changed, 2 insertions(+), 11 deletions(-) rename packages/effect-fc/src/{hooks => }/Hooks/ScopeOptions.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/index.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/input/index.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/input/useInput.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/input/useOptionalInput.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/internal.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useCallbackPromise.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useCallbackSync.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useContext.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useEffect.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useFork.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useLayoutEffect.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useMemo.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useOnce.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useRefFromState.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useRefState.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useScope.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useStreamFromReactiveValues.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useSubscribables.ts (100%) rename packages/effect-fc/src/{hooks => }/Hooks/useSubscribeStream.ts (100%) delete mode 100644 packages/effect-fc/src/hooks/index.ts diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 92572b8..6ae5a99 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -17,14 +17,6 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, - "./hooks": { - "types": "./dist/hooks/index.d.ts", - "default": "./dist/hooks/index.js" - }, - "./types": { - "types": "./dist/types/index.d.ts", - "default": "./dist/types/index.js" - }, "./*": { "types": "./dist/*.d.ts", "default": "./dist/*.js" diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 5d775bf..2ff3909 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -2,7 +2,7 @@ import * as AsyncData from "@typed/async-data" import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, pipe, Pipeable, Predicate, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" -import { Hooks } from "./hooks/index.js" +import * as Hooks from "./Hooks/index.js" import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" diff --git a/packages/effect-fc/src/hooks/Hooks/ScopeOptions.ts b/packages/effect-fc/src/Hooks/ScopeOptions.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/ScopeOptions.ts rename to packages/effect-fc/src/Hooks/ScopeOptions.ts diff --git a/packages/effect-fc/src/hooks/Hooks/index.ts b/packages/effect-fc/src/Hooks/index.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/index.ts rename to packages/effect-fc/src/Hooks/index.ts diff --git a/packages/effect-fc/src/hooks/Hooks/input/index.ts b/packages/effect-fc/src/Hooks/input/index.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/input/index.ts rename to packages/effect-fc/src/Hooks/input/index.ts diff --git a/packages/effect-fc/src/hooks/Hooks/input/useInput.ts b/packages/effect-fc/src/Hooks/input/useInput.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/input/useInput.ts rename to packages/effect-fc/src/Hooks/input/useInput.ts diff --git a/packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts b/packages/effect-fc/src/Hooks/input/useOptionalInput.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/input/useOptionalInput.ts rename to packages/effect-fc/src/Hooks/input/useOptionalInput.ts diff --git a/packages/effect-fc/src/hooks/Hooks/internal.ts b/packages/effect-fc/src/Hooks/internal.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/internal.ts rename to packages/effect-fc/src/Hooks/internal.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useCallbackPromise.ts b/packages/effect-fc/src/Hooks/useCallbackPromise.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useCallbackPromise.ts rename to packages/effect-fc/src/Hooks/useCallbackPromise.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useCallbackSync.ts b/packages/effect-fc/src/Hooks/useCallbackSync.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useCallbackSync.ts rename to packages/effect-fc/src/Hooks/useCallbackSync.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useContext.ts b/packages/effect-fc/src/Hooks/useContext.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useContext.ts rename to packages/effect-fc/src/Hooks/useContext.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useEffect.ts b/packages/effect-fc/src/Hooks/useEffect.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useEffect.ts rename to packages/effect-fc/src/Hooks/useEffect.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useFork.ts b/packages/effect-fc/src/Hooks/useFork.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useFork.ts rename to packages/effect-fc/src/Hooks/useFork.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useLayoutEffect.ts b/packages/effect-fc/src/Hooks/useLayoutEffect.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useLayoutEffect.ts rename to packages/effect-fc/src/Hooks/useLayoutEffect.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useMemo.ts b/packages/effect-fc/src/Hooks/useMemo.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useMemo.ts rename to packages/effect-fc/src/Hooks/useMemo.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useOnce.ts b/packages/effect-fc/src/Hooks/useOnce.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useOnce.ts rename to packages/effect-fc/src/Hooks/useOnce.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useRefFromState.ts b/packages/effect-fc/src/Hooks/useRefFromState.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useRefFromState.ts rename to packages/effect-fc/src/Hooks/useRefFromState.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useRefState.ts b/packages/effect-fc/src/Hooks/useRefState.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useRefState.ts rename to packages/effect-fc/src/Hooks/useRefState.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useScope.ts b/packages/effect-fc/src/Hooks/useScope.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useScope.ts rename to packages/effect-fc/src/Hooks/useScope.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useStreamFromReactiveValues.ts b/packages/effect-fc/src/Hooks/useStreamFromReactiveValues.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useStreamFromReactiveValues.ts rename to packages/effect-fc/src/Hooks/useStreamFromReactiveValues.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useSubscribables.ts b/packages/effect-fc/src/Hooks/useSubscribables.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useSubscribables.ts rename to packages/effect-fc/src/Hooks/useSubscribables.ts diff --git a/packages/effect-fc/src/hooks/Hooks/useSubscribeStream.ts b/packages/effect-fc/src/Hooks/useSubscribeStream.ts similarity index 100% rename from packages/effect-fc/src/hooks/Hooks/useSubscribeStream.ts rename to packages/effect-fc/src/Hooks/useSubscribeStream.ts diff --git a/packages/effect-fc/src/hooks/index.ts b/packages/effect-fc/src/hooks/index.ts deleted file mode 100644 index 6192316..0000000 --- a/packages/effect-fc/src/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./Hooks/index.js" -export * as Hooks from "./Hooks/index.js" diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index e3fe33f..0a7e9af 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,5 +1,6 @@ export * as Async from "./Async.js" export * as Component from "./Component.js" export * as Form from "./Form.js" +export * as Hooks from "./Hooks/index.js" export * as Memoized from "./Memoized.js" export * as ReactRuntime from "./ReactRuntime.js" -- 2.49.1 From 32ba35e4a9f0ba3088680a304b28c774af8cd133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 12:24:45 +0200 Subject: [PATCH 52/67] Refactor --- packages/effect-fc/src/Component.ts | 2 +- packages/effect-fc/src/Form.ts | 4 +++- packages/effect-fc/src/Hooks/input/useOptionalInput.ts | 2 +- packages/effect-fc/src/Hooks/useRefState.ts | 2 +- packages/effect-fc/src/{types => }/PropertyPath.ts | 0 packages/effect-fc/src/{types => }/SetStateAction.ts | 0 packages/effect-fc/src/{types => }/Subscribable.ts | 0 packages/effect-fc/src/{types => }/SubscriptionSubRef.ts | 2 +- packages/effect-fc/src/index.ts | 4 ++++ packages/effect-fc/src/types/index.ts | 4 ---- 10 files changed, 11 insertions(+), 9 deletions(-) rename packages/effect-fc/src/{types => }/PropertyPath.ts (100%) rename packages/effect-fc/src/{types => }/SetStateAction.ts (100%) rename packages/effect-fc/src/{types => }/Subscribable.ts (100%) rename packages/effect-fc/src/{types => }/SubscriptionSubRef.ts (99%) delete mode 100644 packages/effect-fc/src/types/index.ts diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 7383963..061acad 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,6 +1,6 @@ import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, Tracer, type Types, type Utils } from "effect" import * as React from "react" -import { Hooks } from "./hooks/index.js" +import * as Hooks from "./Hooks/index.js" import { Memoized } from "./index.js" diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 2ff3909..94d91b4 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -3,7 +3,9 @@ import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identi import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import * as Hooks from "./Hooks/index.js" -import { PropertyPath, Subscribable as SubscribableInternal, SubscriptionSubRef } from "./types/index.js" +import * as PropertyPath from "./PropertyPath.js" +import * as SubscribableInternal from "./Subscribable.js" +import * as SubscriptionSubRef from "./SubscriptionSubRef.js" export const FormTypeId: unique symbol = Symbol.for("effect-fc/Form") diff --git a/packages/effect-fc/src/Hooks/input/useOptionalInput.ts b/packages/effect-fc/src/Hooks/input/useOptionalInput.ts index cae2e23..0f519aa 100644 --- a/packages/effect-fc/src/Hooks/input/useOptionalInput.ts +++ b/packages/effect-fc/src/Hooks/input/useOptionalInput.ts @@ -1,6 +1,6 @@ import { type Duration, Effect, Equal, Equivalence, flow, identity, Option, type ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect" import * as React from "react" -import { SetStateAction } from "../../../types/index.js" +import * as SetStateAction from "../../SetStateAction.js" import { useCallbackSync } from "../useCallbackSync.js" import { useFork } from "../useFork.js" import { useOnce } from "../useOnce.js" diff --git a/packages/effect-fc/src/Hooks/useRefState.ts b/packages/effect-fc/src/Hooks/useRefState.ts index 6cc3980..e6bae8b 100644 --- a/packages/effect-fc/src/Hooks/useRefState.ts +++ b/packages/effect-fc/src/Hooks/useRefState.ts @@ -1,6 +1,6 @@ import { Effect, Equivalence, Ref, Stream, SubscriptionRef } from "effect" import * as React from "react" -import { SetStateAction } from "../../types/index.js" +import * as SetStateAction from "../SetStateAction.js" import { useCallbackSync } from "./useCallbackSync.js" import { useFork } from "./useFork.js" import { useOnce } from "./useOnce.js" diff --git a/packages/effect-fc/src/types/PropertyPath.ts b/packages/effect-fc/src/PropertyPath.ts similarity index 100% rename from packages/effect-fc/src/types/PropertyPath.ts rename to packages/effect-fc/src/PropertyPath.ts diff --git a/packages/effect-fc/src/types/SetStateAction.ts b/packages/effect-fc/src/SetStateAction.ts similarity index 100% rename from packages/effect-fc/src/types/SetStateAction.ts rename to packages/effect-fc/src/SetStateAction.ts diff --git a/packages/effect-fc/src/types/Subscribable.ts b/packages/effect-fc/src/Subscribable.ts similarity index 100% rename from packages/effect-fc/src/types/Subscribable.ts rename to packages/effect-fc/src/Subscribable.ts diff --git a/packages/effect-fc/src/types/SubscriptionSubRef.ts b/packages/effect-fc/src/SubscriptionSubRef.ts similarity index 99% rename from packages/effect-fc/src/types/SubscriptionSubRef.ts rename to packages/effect-fc/src/SubscriptionSubRef.ts index c007397..8533999 100644 --- a/packages/effect-fc/src/types/SubscriptionSubRef.ts +++ b/packages/effect-fc/src/SubscriptionSubRef.ts @@ -2,7 +2,7 @@ import { Chunk, Effect, Effectable, Option, Predicate, Readable, Ref, Stream, Su import * as PropertyPath from "./PropertyPath.js" -export const SubscriptionSubRefTypeId: unique symbol = Symbol.for("effect-fc/types/SubscriptionSubRef") +export const SubscriptionSubRefTypeId: unique symbol = Symbol.for("effect-fc/SubscriptionSubRef") export type SubscriptionSubRefTypeId = typeof SubscriptionSubRefTypeId export interface SubscriptionSubRef> diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 0a7e9af..b2227e8 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -3,4 +3,8 @@ export * as Component from "./Component.js" export * as Form from "./Form.js" export * as Hooks from "./Hooks/index.js" export * as Memoized from "./Memoized.js" +export * as PropertyPath from "./PropertyPath.js" export * as ReactRuntime from "./ReactRuntime.js" +export * as SetStateAction from "./SetStateAction.js" +export * as Subscribable from "./Subscribable.js" +export * as SubscriptionSubRef from "./SubscriptionSubRef.js" diff --git a/packages/effect-fc/src/types/index.ts b/packages/effect-fc/src/types/index.ts deleted file mode 100644 index 6273110..0000000 --- a/packages/effect-fc/src/types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * as PropertyPath from "./PropertyPath.js" -export * as SetStateAction from "./SetStateAction.js" -export * as Subscribable from "./Subscribable.js" -export * as SubscriptionSubRef from "./SubscriptionSubRef.js" -- 2.49.1 From 520ee8ac1f0fcd0cd4fdc691b888bf7ac660dbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 13:23:11 +0200 Subject: [PATCH 53/67] Change project setup --- .gitea/workflows/lint.yaml | 6 +- .gitea/workflows/publish.yaml | 4 + .gitea/workflows/test-build.yaml | 8 +- .npmrc | 1 - biome.json | 34 ++++ bun.lock | 255 +++------------------------- bunfig.toml | 2 - package.json | 10 +- packages/effect-fc/biome.json | 8 + packages/effect-fc/package.json | 8 +- packages/effect-fc/tsconfig.json | 7 +- packages/example/biome.json | 8 + packages/example/eslint.config.js | 28 --- packages/example/package.json | 14 +- packages/example/tsconfig.app.json | 6 +- packages/example/tsconfig.node.json | 1 + turbo.json | 17 +- 17 files changed, 126 insertions(+), 291 deletions(-) delete mode 100644 .npmrc create mode 100644 biome.json delete mode 100644 bunfig.toml create mode 100644 packages/effect-fc/biome.json create mode 100644 packages/example/biome.json delete mode 100644 packages/example/eslint.config.js diff --git a/.gitea/workflows/lint.yaml b/.gitea/workflows/lint.yaml index d96555a..d6771de 100644 --- a/.gitea/workflows/lint.yaml +++ b/.gitea/workflows/lint.yaml @@ -12,5 +12,7 @@ jobs: uses: actions/checkout@v4 - name: Install dependencies run: bun install --frozen-lockfile - - name: Build - run: bun run build + - name: Lint TypeScript + run: bun lint:tsc + - name: Lint Biome + run: bun lint:biome diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index 5ef690e..7f9ed56 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -15,6 +15,10 @@ jobs: uses: actions/checkout@v4 - name: Install dependencies run: bun install --frozen-lockfile + - name: Lint TypeScript + run: bun lint:tsc + - name: Lint Biome + run: bun lint:biome - name: Build run: bun run build - name: Publish effect-fc diff --git a/.gitea/workflows/test-build.yaml b/.gitea/workflows/test-build.yaml index bf2f832..e1a7e23 100644 --- a/.gitea/workflows/test-build.yaml +++ b/.gitea/workflows/test-build.yaml @@ -12,12 +12,16 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "22" - name: Clone repo uses: actions/checkout@v4 - name: Install dependencies run: bun install --frozen-lockfile + - name: Lint TypeScript + run: bun lint:tsc + - name: Lint Biome + run: bun lint:biome - name: Build run: bun run build - name: Pack - run: bun run pack + run: bun pack diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 7bd7ffe..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -@thilawyn:registry=https://git.valverde.cloud/api/packages/thilawyn/npm/ diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..c59f6d7 --- /dev/null +++ b/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false + }, + "formatter": { + "enabled": false, + "indentStyle": "tab" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/bun.lock b/bun.lock index a9d6aef..7a5667b 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,8 @@ "": { "name": "@effect-fc/monorepo", "devDependencies": { + "@biomejs/biome": "^2.2.4", + "@effect/language-service": "^0.41.1", "npm-check-updates": "^18.0.2", "npm-sort": "^0.0.4", "turbo": "^2.5.6", @@ -16,9 +18,6 @@ "dependencies": { "@typed/async-data": "^0.13.1", }, - "devDependencies": { - "@effect/language-service": "^0.35.2", - }, "peerDependencies": { "@types/react": "^19.0.0", "effect": "^3.15.0", @@ -40,22 +39,16 @@ "react-icons": "^5.5.0", }, "devDependencies": { - "@effect/language-service": "^0.35.2", - "@eslint/js": "^9.34.0", "@tanstack/react-router": "^1.131.27", "@tanstack/react-router-devtools": "^1.131.27", "@tanstack/router-plugin": "^1.131.27", "@types/react": "^19.1.11", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.1", - "eslint": "^9.34.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "react": "^19.1.1", "react-dom": "^19.1.1", "type-fest": "^4.41.0", - "typescript-eslint": "^8.40.0", "vite": "^7.1.3", }, }, @@ -123,9 +116,27 @@ "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + "@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="], + "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], - "@effect/language-service": ["@effect/language-service@0.35.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-J7GbtthuYeruD4kYUHn3QEZtbl9v7OX9+ElD20mDBGBMA+Q6W4KnVMxZc+yDvKQBBYvfXImVUSzBbXzbrZJpyg=="], + "@effect/language-service": ["@effect/language-service@0.41.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-BZaFkF1JdoTIJuzT7mNfUnfQG2MLxkQtn5b9y1z0BZUIcNfs5p7yR0ekYfTkPkt+TVD+9HE12VC1SsG5DReKww=="], "@effect/platform": ["@effect/platform@0.90.6", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.17.8" } }, "sha512-aT7aLJR1+rYrSLdw5af2UZzwnWoAy8WmkTxTUD3pFY6vjFmh+8137RhbwKiWjIJBTm2DVyPXl1dx1kGg28xt6Q=="], @@ -183,24 +194,6 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], - - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], - - "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], - - "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], - - "@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="], - - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], - - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], - "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], @@ -209,14 +202,6 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], - - "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -239,12 +224,6 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], @@ -451,94 +430,46 @@ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/react": ["@types/react@19.1.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ=="], "@types/react-dom": ["@types/react-dom@19.1.7", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.40.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.40.0", "@typescript-eslint/type-utils": "8.40.0", "@typescript-eslint/utils": "8.40.0", "@typescript-eslint/visitor-keys": "8.40.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.40.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.40.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.40.0", "@typescript-eslint/types": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0", "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw=="], - - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.40.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.40.0", "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.40.0", "", { "dependencies": { "@typescript-eslint/types": "8.40.0", "@typescript-eslint/visitor-keys": "8.40.0" } }, "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w=="], - - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.40.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw=="], - - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.40.0", "", { "dependencies": { "@typescript-eslint/types": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0", "@typescript-eslint/utils": "8.40.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@8.40.0", "", {}, "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.40.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.40.0", "@typescript-eslint/tsconfig-utils": "8.40.0", "@typescript-eslint/types": "8.40.0", "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.40.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.40.0", "@typescript-eslint/types": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg=="], - - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.40.0", "", { "dependencies": { "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.1", "", { "dependencies": { "@babel/core": "^7.28.3", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.32", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-DE4UNaBXwtVoDJ0ccBdLVjFTWL70NRuWNCxEieTI3lrq9ORB9aOCQEKstwDXBl87NvFdbqh/p7eINGyj0BthJA=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "ansis": ["ansis@4.1.0", "", {}, "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "browserslist": ["browserslist@4.25.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ=="], - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001737", "", {}, "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], - "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], @@ -555,56 +486,16 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@9.34.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="], - - "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], - - "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.20", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA=="], - - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - - "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="], - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], @@ -613,22 +504,12 @@ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], "goober": ["goober@2.1.16", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g=="], - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -639,38 +520,14 @@ "isbot": ["isbot@5.1.30", "", {}, "sha512-3wVJEonAns1OETX83uWsk5IAne2S5zfDcntD2hbtU23LelSqNXzXs9zKjMPOLMzroCgIjCfjYAEHrd2D6FOkiA=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], - - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="], @@ -681,8 +538,6 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], @@ -693,34 +548,16 @@ "npm-sort": ["npm-sort@0.0.4", "", { "bin": { "npm-sort": "./index.js" } }, "sha512-S5Id/3Jvr7Cf/QnWjRteprngERCBhhEFOM+wMhUrAYP060/HUBC1aL5GoXS3xITlgacJCWaSmP4HQaAt91nNYQ=="], - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], @@ -741,16 +578,10 @@ "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rollup": ["rollup@4.47.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.47.1", "@rollup/rollup-android-arm64": "4.47.1", "@rollup/rollup-darwin-arm64": "4.47.1", "@rollup/rollup-darwin-x64": "4.47.1", "@rollup/rollup-freebsd-arm64": "4.47.1", "@rollup/rollup-freebsd-x64": "4.47.1", "@rollup/rollup-linux-arm-gnueabihf": "4.47.1", "@rollup/rollup-linux-arm-musleabihf": "4.47.1", "@rollup/rollup-linux-arm64-gnu": "4.47.1", "@rollup/rollup-linux-arm64-musl": "4.47.1", "@rollup/rollup-linux-loongarch64-gnu": "4.47.1", "@rollup/rollup-linux-ppc64-gnu": "4.47.1", "@rollup/rollup-linux-riscv64-gnu": "4.47.1", "@rollup/rollup-linux-riscv64-musl": "4.47.1", "@rollup/rollup-linux-s390x-gnu": "4.47.1", "@rollup/rollup-linux-x64-gnu": "4.47.1", "@rollup/rollup-linux-x64-musl": "4.47.1", "@rollup/rollup-win32-arm64-msvc": "4.47.1", "@rollup/rollup-win32-ia32-msvc": "4.47.1", "@rollup/rollup-win32-x64-msvc": "4.47.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -759,20 +590,12 @@ "seroval-plugins": ["seroval-plugins@1.3.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="], "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], @@ -781,8 +604,6 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tsx": ["tsx@4.20.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg=="], @@ -801,20 +622,14 @@ "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="], - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], - "typescript-eslint": ["typescript-eslint@8.40.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.40.0", "@typescript-eslint/parser": "8.40.0", "@typescript-eslint/typescript-estree": "8.40.0", "@typescript-eslint/utils": "8.40.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q=="], - "unplugin": ["unplugin@2.3.8", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-lkaSIlxceytPyt9yfb1h7L9jDFqwMqvUZeGsKB7Z8QrvAO3xZv2S+xMQQYzxk0AGJHcQhbcvhKEstrMy99jnuQ=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], @@ -825,40 +640,14 @@ "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - - "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], - - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], } } diff --git a/bunfig.toml b/bunfig.toml deleted file mode 100644 index 4fe1e6e..0000000 --- a/bunfig.toml +++ /dev/null @@ -1,2 +0,0 @@ -[install.scopes] -"@thilawyn" = "https://git.valverde.cloud/api/packages/thilawyn/npm/" diff --git a/package.json b/package.json index 133cd06..84517e2 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,15 @@ "scripts": { "build": "turbo build --filter=!@effect-fc/example", "lint:tsc": "turbo lint:tsc", + "lint:biome": "turbo lint:biome", "pack": "turbo pack --filter=!@effect-fc/example", - "publish": "turbo publish --filter=!@effect-fc/example", - "clean:cache": "rm -f tsconfig.tsbuildinfo", - "clean:dist": "rm -rf dist", - "clean:node": "rm -rf node_modules" + "clean:cache": "turbo clean:cache", + "clean:dist": "turbo clean:dist", + "clean:node": "turbo clean:modules rm -rf node_modules" }, "devDependencies": { + "@biomejs/biome": "^2.2.4", + "@effect/language-service": "^0.41.1", "npm-check-updates": "^18.0.2", "npm-sort": "^0.0.4", "turbo": "^2.5.6", diff --git a/packages/effect-fc/biome.json b/packages/effect-fc/biome.json new file mode 100644 index 0000000..401b454 --- /dev/null +++ b/packages/effect-fc/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "root": false, + "extends": "//", + "files": { + "includes": ["./src/**"] + } +} diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 6ae5a99..48c8af8 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -25,19 +25,17 @@ "scripts": { "build": "tsc", "lint:tsc": "tsc --noEmit", + "lint:biome": "biome lint", "pack": "npm pack", - "clean:cache": "rm -f tsconfig.tsbuildinfo", + "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", - "clean:node": "rm -rf node_modules" + "clean:modules": "rm -rf node_modules" }, "peerDependencies": { "@types/react": "^19.0.0", "effect": "^3.15.0", "react": "^19.0.0" }, - "devDependencies": { - "@effect/language-service": "^0.35.2" - }, "dependencies": { "@typed/async-data": "^0.13.1" } diff --git a/packages/effect-fc/tsconfig.json b/packages/effect-fc/tsconfig.json index eea16a8..3a9e67c 100644 --- a/packages/effect-fc/tsconfig.json +++ b/packages/effect-fc/tsconfig.json @@ -26,7 +26,12 @@ // Build "outDir": "./dist", - "declaration": true + "declaration": true, + "sourceMap": true, + + "plugins": [ + { "name": "@effect/language-service" } + ] }, "include": ["./src"] diff --git a/packages/example/biome.json b/packages/example/biome.json new file mode 100644 index 0000000..0e84ec8 --- /dev/null +++ b/packages/example/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "root": false, + "extends": "//", + "files": { + "includes": ["./src/**", "!src/routeTree.gen.ts"] + } +} diff --git a/packages/example/eslint.config.js b/packages/example/eslint.config.js deleted file mode 100644 index 092408a..0000000 --- a/packages/example/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' - -export default tseslint.config( - { ignores: ['dist'] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - }, -) diff --git a/packages/example/package.json b/packages/example/package.json index 6c34e0e..8eb577a 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -5,28 +5,24 @@ "private": true, "scripts": { "dev": "vite", - "build": "tsc -b && vite build", "lint:tsc": "tsc --noEmit", - "lint:eslint": "eslint .", - "preview": "vite preview" + "lint:biome": "biome lint", + "preview": "vite preview", + "clean:cache": "rm -rf .turbo node_modules/.tmp node_modules/.vite* .tanstack", + "clean:dist": "rm -rf dist", + "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@effect/language-service": "^0.35.2", - "@eslint/js": "^9.34.0", "@tanstack/react-router": "^1.131.27", "@tanstack/react-router-devtools": "^1.131.27", "@tanstack/router-plugin": "^1.131.27", "@types/react": "^19.1.11", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.1", - "eslint": "^9.34.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "react": "^19.1.1", "react-dom": "^19.1.1", "type-fest": "^4.41.0", - "typescript-eslint": "^8.40.0", "vite": "^7.1.3" }, "dependencies": { diff --git a/packages/example/tsconfig.app.json b/packages/example/tsconfig.app.json index 61b3094..835427f 100644 --- a/packages/example/tsconfig.app.json +++ b/packages/example/tsconfig.app.json @@ -22,15 +22,15 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, + "baseUrl": ".", "paths": { "@/*": ["./src/*"] }, "plugins": [ - { - "name": "@effect/language-service" - } + { "name": "@effect/language-service" } ] }, + "include": ["src"] } diff --git a/packages/example/tsconfig.node.json b/packages/example/tsconfig.node.json index db0becc..0d067e8 100644 --- a/packages/example/tsconfig.node.json +++ b/packages/example/tsconfig.node.json @@ -20,5 +20,6 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, + "include": ["vite.config.ts"] } diff --git a/turbo.json b/turbo.json index 2ce9ea7..55b67b2 100644 --- a/turbo.json +++ b/turbo.json @@ -1,11 +1,26 @@ { "$schema": "https://turbo.build/schema.json", + "tasks": { + "lint:tsc": { + "cache": false + }, + "lint:biome": { + "cache": false + }, "build": { "dependsOn": ["^build"], "inputs": ["./src/**"], "outputs": ["./dist/**"] }, - "pack": {} + "clean:cache": { + "cache": false + }, + "clean:dist": { + "cache": false + }, + "clean:modules": { + "cache": false + } } } -- 2.49.1 From d172682f553fb9b3d8a263b73d37558f39cd7116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 13:24:56 +0200 Subject: [PATCH 54/67] Project fix --- bun.lock | 224 +++++++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 127 insertions(+), 99 deletions(-) diff --git a/bun.lock b/bun.lock index 7a5667b..e5b07ee 100644 --- a/bun.lock +++ b/bun.lock @@ -54,13 +54,11 @@ }, }, "packages": { - "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], - "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@babel/compat-data": ["@babel/compat-data@7.28.0", "", {}, "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="], + "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], - "@babel/core": ["@babel/core@7.28.3", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ=="], + "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], @@ -92,9 +90,9 @@ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - "@babel/helpers": ["@babel/helpers@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.2" } }, "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw=="], + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], - "@babel/parser": ["@babel/parser@7.28.3", "", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="], + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="], @@ -112,9 +110,9 @@ "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], - "@babel/traverse": ["@babel/traverse@7.28.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/types": "^7.28.2", "debug": "^4.3.1" } }, "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ=="], + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], - "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], "@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="], @@ -138,61 +136,61 @@ "@effect/language-service": ["@effect/language-service@0.41.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-BZaFkF1JdoTIJuzT7mNfUnfQG2MLxkQtn5b9y1z0BZUIcNfs5p7yR0ekYfTkPkt+TVD+9HE12VC1SsG5DReKww=="], - "@effect/platform": ["@effect/platform@0.90.6", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.17.8" } }, "sha512-aT7aLJR1+rYrSLdw5af2UZzwnWoAy8WmkTxTUD3pFY6vjFmh+8137RhbwKiWjIJBTm2DVyPXl1dx1kGg28xt6Q=="], + "@effect/platform": ["@effect/platform@0.90.10", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.17.13" } }, "sha512-QhDPgCaLfIMQKOCoCPQvRUS+Y34iYJ07jdZ/CBAvYFvg/iUBebsmFuHL63RCD/YZH9BuK/kqqLYAA3M0fmUEgg=="], "@effect/platform-browser": ["@effect/platform-browser@0.70.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.90.0", "effect": "^3.17.0" } }, "sha512-dq2ukUralavQIipSsQVuIOLLxBlFWL5Mkg1Fnr8esYPuPv0BXAI39nwNNi75X2tPAgak8OCSD0qh9qhmNj2gPA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], @@ -210,7 +208,7 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], @@ -224,6 +222,12 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], @@ -348,71 +352,75 @@ "@radix-ui/themes": ["@radix-ui/themes@3.2.1", "", { "dependencies": { "@radix-ui/colors": "^3.0.0", "classnames": "^2.3.2", "radix-ui": "^1.1.3", "react-remove-scroll-bar": "^2.3.8" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WJL2YKAGItkunwm3O4cLTFKCGJTfAfF6Hmq7f5bCo1ggqC9qJQ/wfg/25AAN72aoEM1yqXZQ+pslsw48AFR0Xg=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.32", "", {}, "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.38", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.47.1", "", { "os": "android", "cpu": "arm" }, "sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.3", "", { "os": "android", "cpu": "arm" }, "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.47.1", "", { "os": "android", "cpu": "arm64" }, "sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.3", "", { "os": "android", "cpu": "arm64" }, "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.47.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.47.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.47.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.47.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.47.1", "", { "os": "linux", "cpu": "arm" }, "sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.3", "", { "os": "linux", "cpu": "arm" }, "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.47.1", "", { "os": "linux", "cpu": "arm" }, "sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.3", "", { "os": "linux", "cpu": "arm" }, "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.47.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.47.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.47.1", "", { "os": "linux", "cpu": "none" }, "sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.47.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.47.1", "", { "os": "linux", "cpu": "none" }, "sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.47.1", "", { "os": "linux", "cpu": "none" }, "sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.47.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.47.1", "", { "os": "linux", "cpu": "x64" }, "sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.47.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.3", "", { "os": "linux", "cpu": "x64" }, "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.47.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.3", "", { "os": "none", "cpu": "arm64" }, "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.47.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.47.1", "", { "os": "win32", "cpu": "x64" }, "sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.3", "", { "os": "win32", "cpu": "x64" }, "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.3", "", { "os": "win32", "cpu": "x64" }, "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA=="], "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], - "@tanstack/history": ["@tanstack/history@1.131.2", "", {}, "sha512-cs1WKawpXIe+vSTeiZUuSBy8JFjEuDgdMKZFRLKwQysKo8y2q6Q1HvS74Yw+m5IhOW1nTZooa6rlgdfXcgFAaw=="], + "@tanstack/history": ["@tanstack/history@1.132.21", "", {}, "sha512-5ziPz3YarKU5cBJoEJ4muV8cy+5W4oWdJMqW7qosMrK5fb9Qfm+QWX+kO3emKJMu4YOUofVu3toEuuD3x1zXKw=="], - "@tanstack/react-router": ["@tanstack/react-router@1.131.27", "", { "dependencies": { "@tanstack/history": "1.131.2", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.131.27", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-JLUsmlarNxMz7VDhFscZCqoc2quhocQZKhia/7YXWf8Jbc8rANk6lukK4ecYn92m/ytoHAAy77JeaB6n0HvqwQ=="], + "@tanstack/react-router": ["@tanstack/react-router@1.132.27", "", { "dependencies": { "@tanstack/history": "1.132.21", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.132.27", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-fxSm1kxrtl3dQslqqKgYnIbQf7qW4fyWXKQhZYCWBFHO2GT11DWPZTNkTXw5YifOpw2RTmYJBH3C44t1HF95Cw=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.131.27", "", { "dependencies": { "@tanstack/router-devtools-core": "1.131.27" }, "peerDependencies": { "@tanstack/react-router": "^1.131.27", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-SHulN0a7hZvyl3fXi+VLHxdMKdsg1lhPOZeKd5xs6bu/x+N5FaXEA5bUPGB2sbiSYXw/XFcjUqR5dkw8T1dkXg=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.132.27", "", { "dependencies": { "@tanstack/router-devtools-core": "1.132.27", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.132.27", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-qLSRWw281HWg+QZtv3MCx3jIoKzY4t4XciWoLOcHZy9W1BKnFu3epI/jXMMvQ/VW9p0OzF69fQXtTFDx2ODCgA=="], - "@tanstack/react-store": ["@tanstack/react-store@0.7.4", "", { "dependencies": { "@tanstack/store": "0.7.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-DyG1e5Qz/c1cNLt/NdFbCA7K1QGuFXQYT6EfUltYMJoQ4LzBOGnOl5IjuxepNcRtmIKkGpmdMzdFZEkevgU9bQ=="], + "@tanstack/react-store": ["@tanstack/react-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg=="], - "@tanstack/router-core": ["@tanstack/router-core@1.131.27", "", { "dependencies": { "@tanstack/history": "1.131.2", "@tanstack/store": "^0.7.0", "cookie-es": "^1.2.2", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-NEBNxZ/LIBIh6kvQntr6bKq57tDe55zecyTtjAmzPkYFsMy1LXEpRm5H3BPiteBMRApAjuaq+bS1qA664hLH6Q=="], + "@tanstack/router-core": ["@tanstack/router-core@1.132.27", "", { "dependencies": { "@tanstack/history": "1.132.21", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mNx+nba7mXc7sJdX+kYH4rSW8f7Jx/+0hPOkX4XAnqiq7I1ng3gGqmGuf4+2BYTG2aD+aTSPExUPczy9VNgRfQ=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.131.27", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/router-core": "^1.131.27", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-upoMv/uq1CQdrOyBO2h6CLXI1Ym7Rawoovt26fN1Wl+RMXqKGVpHAXYuKpugdFMFhFieccKVYcrj9NP4V5BIDw=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.132.27", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/router-core": "^1.132.27", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-TE+chd5aSx+tURjIFPhVYNh+hUuKWjoYcP0BYCkcKp3zLN9VfMZh+bKVn1b44jsFUr6gzkbbctBQRZ+Z6oUm6Q=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.131.27", "", { "dependencies": { "@tanstack/router-core": "1.131.27", "@tanstack/router-utils": "1.131.2", "@tanstack/virtual-file-routes": "1.131.2", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-PXBIVl45q2bBq9g0DDXLBGeKjO9eExcZd2JotLjLdIJ0I/wdxPQOBJHLPZfnmbf3vispToedRvG3b1YDWjL48g=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.132.27", "", { "dependencies": { "@tanstack/router-core": "1.132.27", "@tanstack/router-utils": "1.132.21", "@tanstack/virtual-file-routes": "1.132.21", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-dlW33RGxgG0mBF8vRqUs5CeId6wOf4qY6GmT2Pv6NgCVtrD7wsMSM1BasIRufNIxRT6FVnE7rztMy9wN7oCaKg=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.131.27", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.131.27", "@tanstack/router-generator": "1.131.27", "@tanstack/router-utils": "1.131.2", "@tanstack/virtual-file-routes": "1.131.2", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.131.27", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-0V611ehOE8nfCFT2tvrLfQMroyoYW/virDXPaaFe38hdDxslmfCW2miJxngxz4+QqgK/M3sX71ElrZDvkP2Ixw=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.132.27", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.132.27", "@tanstack/router-generator": "1.132.27", "@tanstack/router-utils": "1.132.21", "@tanstack/virtual-file-routes": "1.132.21", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.132.27", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.8", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-0mo+jdgO7L5hOPGyrmKtyy20ZUfX4LwN30IXS7mhsaSHbZub+XLS3Ta6mDxNLmOXAJLPI3Gc90yI8jVwhizv5Q=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.131.2", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2" } }, "sha512-sr3x0d2sx9YIJoVth0QnfEcAcl+39sQYaNQxThtHmRpyeFYNyM2TTH+Ud3TNEnI3bbzmLYEUD+7YqB987GzhDA=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.132.21", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "fast-glob": "^3.3.3", "pathe": "^2.0.3" } }, "sha512-d9MvyhdPaKN78hQOss89bayiv/HR/7FpVHuKTNGEzzYrBWDVIUBd0yMNaBxQBpTg6NuNxtZ01ZJS5VkbedbzJw=="], - "@tanstack/store": ["@tanstack/store@0.7.4", "", {}, "sha512-F1XqZQici1Aq6WigEfcxJSml92nW+85Om8ElBMokPNg5glCYVOmPkZGIQeieYFxcPiKTfwo0MTOQpUyJtwncrg=="], + "@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.131.2", "", {}, "sha512-VEEOxc4mvyu67O+Bl0APtYjwcNRcL9it9B4HKbNgcBTIOEalhk+ufBl4kiqc8WP1sx1+NAaiS+3CcJBhrqaSRg=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.132.21", "", {}, "sha512-+eT+vxZnf2/QPr9ci5aPn7i3MnVyWYNG2DUqiKJXGi7EvuFrXV9r8zDK40QfUTNGdCg9F880YOVe3cTwMCO0zw=="], "@typed/async-data": ["@typed/async-data@0.13.1", "", { "dependencies": { "@typed/lazy-ref": "^0.3.2", "effect": "^3.11.9" } }, "sha512-rKv3HQtoHeGJwZpEaTL0FAEKfqHcMr/x3GtgkE01p2tJiKjq1eVaPZYpweZEEF/zUutox7DQ14oH85x+ZpPA/Q=="], @@ -430,15 +438,15 @@ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/react": ["@types/react@19.1.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ=="], + "@types/react": ["@types/react@19.1.16", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog=="], - "@types/react-dom": ["@types/react-dom@19.1.7", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw=="], + "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.1", "", { "dependencies": { "@babel/core": "^7.28.3", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.32", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-DE4UNaBXwtVoDJ0ccBdLVjFTWL70NRuWNCxEieTI3lrq9ORB9aOCQEKstwDXBl87NvFdbqh/p7eINGyj0BthJA=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "ansis": ["ansis@4.1.0", "", {}, "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="], + "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -448,13 +456,15 @@ "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.9", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.25.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ=="], + "browserslist": ["browserslist@4.26.2", "", { "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", "electron-to-chromium": "^1.5.218", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A=="], - "caniuse-lite": ["caniuse-lite@1.0.30001737", "", {}, "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001746", "", {}, "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -464,25 +474,25 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], + "cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + "detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - "effect": ["effect@3.17.9", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-Nkkn9n1zhy30Dq0MpQatDCH7nfYnOIiebkOHNxmmvoVnEDKCto+2ZwDDWFGzcN/ojwfqjRXWGC9Lo91K5kwZCg=="], + "effect": ["effect@3.18.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-5aJ7yRlvvkBplMSnhPyol7WYvPenvau12asO3HJhG/126SySWV9D8bscGTbV52XxtC5bwO/VUd5ffjE6uep/1A=="], "effect-fc": ["effect-fc@workspace:packages/effect-fc"], - "electron-to-chromium": ["electron-to-chromium@1.5.208", "", {}, "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg=="], + "electron-to-chromium": ["electron-to-chromium@1.5.228", "", {}, "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA=="], - "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -490,6 +500,10 @@ "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -506,7 +520,7 @@ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], "goober": ["goober@2.1.16", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g=="], @@ -518,7 +532,7 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "isbot": ["isbot@5.1.30", "", {}, "sha512-3wVJEonAns1OETX83uWsk5IAne2S5zfDcntD2hbtU23LelSqNXzXs9zKjMPOLMzroCgIjCfjYAEHrd2D6FOkiA=="], + "isbot": ["isbot@5.1.31", "", {}, "sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -528,6 +542,10 @@ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="], @@ -540,14 +558,16 @@ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], - "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + "node-releases": ["node-releases@2.0.21", "", {}, "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "npm-check-updates": ["npm-check-updates@18.0.2", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-9uVFZUCg5oDOcbzdsrJ4BEvq2gikd23tXuF5mqpl4mxVl051lzB00Xmd7ZVjVWY3XNUF3BQKWlN/qmyD8/bwrA=="], + "npm-check-updates": ["npm-check-updates@18.3.1", "", { "bin": { "ncu": "build/cli.js", "npm-check-updates": "build/cli.js" } }, "sha512-5HwKPq7ybOOA1xB4FZg/1ToZZ5/i93U8m3co1mb3GYZAZPDkcxEFukQTTp/Abym+ZY6ShfrHl45Y0rCcwsNnQA=="], "npm-sort": ["npm-sort@0.0.4", "", { "bin": { "npm-sort": "./index.js" } }, "sha512-S5Id/3Jvr7Cf/QnWjRteprngERCBhhEFOM+wMhUrAYP060/HUBC1aL5GoXS3xITlgacJCWaSmP4HQaAt91nNYQ=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], @@ -558,6 +578,8 @@ "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], @@ -580,7 +602,11 @@ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "rollup": ["rollup@4.47.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.47.1", "@rollup/rollup-android-arm64": "4.47.1", "@rollup/rollup-darwin-arm64": "4.47.1", "@rollup/rollup-darwin-x64": "4.47.1", "@rollup/rollup-freebsd-arm64": "4.47.1", "@rollup/rollup-freebsd-x64": "4.47.1", "@rollup/rollup-linux-arm-gnueabihf": "4.47.1", "@rollup/rollup-linux-arm-musleabihf": "4.47.1", "@rollup/rollup-linux-arm64-gnu": "4.47.1", "@rollup/rollup-linux-arm64-musl": "4.47.1", "@rollup/rollup-linux-loongarch64-gnu": "4.47.1", "@rollup/rollup-linux-ppc64-gnu": "4.47.1", "@rollup/rollup-linux-riscv64-gnu": "4.47.1", "@rollup/rollup-linux-riscv64-musl": "4.47.1", "@rollup/rollup-linux-s390x-gnu": "4.47.1", "@rollup/rollup-linux-x64-gnu": "4.47.1", "@rollup/rollup-linux-x64-musl": "4.47.1", "@rollup/rollup-win32-arm64-msvc": "4.47.1", "@rollup/rollup-win32-ia32-msvc": "4.47.1", "@rollup/rollup-win32-x64-msvc": "4.47.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rollup": ["rollup@4.52.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.3", "@rollup/rollup-android-arm64": "4.52.3", "@rollup/rollup-darwin-arm64": "4.52.3", "@rollup/rollup-darwin-x64": "4.52.3", "@rollup/rollup-freebsd-arm64": "4.52.3", "@rollup/rollup-freebsd-x64": "4.52.3", "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", "@rollup/rollup-linux-arm-musleabihf": "4.52.3", "@rollup/rollup-linux-arm64-gnu": "4.52.3", "@rollup/rollup-linux-arm64-musl": "4.52.3", "@rollup/rollup-linux-loong64-gnu": "4.52.3", "@rollup/rollup-linux-ppc64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-musl": "4.52.3", "@rollup/rollup-linux-s390x-gnu": "4.52.3", "@rollup/rollup-linux-x64-gnu": "4.52.3", "@rollup/rollup-linux-x64-musl": "4.52.3", "@rollup/rollup-openharmony-arm64": "4.52.3", "@rollup/rollup-win32-arm64-msvc": "4.52.3", "@rollup/rollup-win32-ia32-msvc": "4.52.3", "@rollup/rollup-win32-x64-gnu": "4.52.3", "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], @@ -588,7 +614,7 @@ "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], - "seroval-plugins": ["seroval-plugins@1.3.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ=="], + "seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], "solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="], @@ -600,33 +626,33 @@ "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], - "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tsx": ["tsx@4.20.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg=="], + "tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="], - "turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="], + "turbo": ["turbo@2.5.8", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.8", "turbo-darwin-arm64": "2.5.8", "turbo-linux-64": "2.5.8", "turbo-linux-arm64": "2.5.8", "turbo-windows-64": "2.5.8", "turbo-windows-arm64": "2.5.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w=="], - "turbo-darwin-64": ["turbo-darwin-64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A=="], + "turbo-darwin-64": ["turbo-darwin-64@2.5.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ=="], - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA=="], + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ=="], - "turbo-linux-64": ["turbo-linux-64@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA=="], + "turbo-linux-64": ["turbo-linux-64@2.5.8", "", { "os": "linux", "cpu": "x64" }, "sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw=="], - "turbo-linux-arm64": ["turbo-linux-arm64@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ=="], + "turbo-linux-arm64": ["turbo-linux-arm64@2.5.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ=="], - "turbo-windows-64": ["turbo-windows-64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg=="], + "turbo-windows-64": ["turbo-windows-64@2.5.8", "", { "os": "win32", "cpu": "x64" }, "sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ=="], - "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="], + "turbo-windows-arm64": ["turbo-windows-arm64@2.5.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ=="], "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "unplugin": ["unplugin@2.3.8", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-lkaSIlxceytPyt9yfb1h7L9jDFqwMqvUZeGsKB7Z8QrvAO3xZv2S+xMQQYzxk0AGJHcQhbcvhKEstrMy99jnuQ=="], + "unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], @@ -636,7 +662,7 @@ "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], - "vite": ["vite@7.1.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw=="], + "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], @@ -646,6 +672,8 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], diff --git a/package.json b/package.json index 84517e2..650f203 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "pack": "turbo pack --filter=!@effect-fc/example", "clean:cache": "turbo clean:cache", "clean:dist": "turbo clean:dist", - "clean:node": "turbo clean:modules rm -rf node_modules" + "clean:modules": "turbo clean:modules && rm -rf node_modules" }, "devDependencies": { "@biomejs/biome": "^2.2.4", -- 2.49.1 From 42c8302705ae814f616070d675975b42517e9e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 13:26:04 +0200 Subject: [PATCH 55/67] Fix --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 650f203..c795a76 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "./packages/*" ], "scripts": { - "build": "turbo build --filter=!@effect-fc/example", + "build": "turbo build", "lint:tsc": "turbo lint:tsc", "lint:biome": "turbo lint:biome", - "pack": "turbo pack --filter=!@effect-fc/example", + "pack": "turbo pack", "clean:cache": "turbo clean:cache", "clean:dist": "turbo clean:dist", "clean:modules": "turbo clean:modules && rm -rf node_modules" -- 2.49.1 From a5df6d04c034bd19cf7cdb9c9a311f51bcbf9ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 18:58:25 +0200 Subject: [PATCH 56/67] Linter fixes --- .vscode/settings.json | 5 ++++- biome.json | 12 +++++++++--- packages/effect-fc/src/Async.ts | 1 + packages/effect-fc/src/Component.ts | 13 ++++++++----- packages/effect-fc/src/Form.ts | 3 ++- .../effect-fc/src/Hooks/useCallbackPromise.ts | 2 ++ packages/effect-fc/src/Hooks/useCallbackSync.ts | 2 ++ packages/effect-fc/src/Hooks/useEffect.ts | 3 ++- packages/effect-fc/src/Hooks/useFork.ts | 1 + packages/effect-fc/src/Hooks/useLayoutEffect.ts | 3 ++- packages/effect-fc/src/Hooks/useMemo.ts | 1 + packages/effect-fc/src/Hooks/useRefState.ts | 2 +- packages/effect-fc/src/Hooks/useScope.ts | 4 +++- .../effect-fc/src/Hooks/useSubscribeStream.ts | 1 + packages/effect-fc/src/Memoized.ts | 1 + packages/effect-fc/src/ReactRuntime.ts | 16 ++++++++-------- 16 files changed, 48 insertions(+), 22 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 72446f4..723e209 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "editor.codeActionsOnSave": { + "source.fixAll.biome": "explicit" + } } diff --git a/biome.json b/biome.json index c59f6d7..d76a607 100644 --- a/biome.json +++ b/biome.json @@ -9,13 +9,19 @@ "ignoreUnknown": false }, "formatter": { - "enabled": false, - "indentStyle": "tab" + "enabled": false }, "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "style": { + "useShorthandFunctionType": "off" + }, + "suspicious": { + "noExplicitAny": "off", + "noShadowRestrictedNames": "off" + } } }, "javascript": { diff --git a/packages/effect-fc/src/Async.ts b/packages/effect-fc/src/Async.ts index 22fe2f1..19c1912 100644 --- a/packages/effect-fc/src/Async.ts +++ b/packages/effect-fc/src/Async.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Effect, Function, Predicate, Runtime, Scope } from "effect" import * as React from "react" import type * as Component from "./Component.js" diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 061acad..297cd38 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,3 +1,5 @@ +/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, Tracer, type Types, type Utils } from "effect" import * as React from "react" import * as Hooks from "./Hooks/index.js" @@ -12,12 +14,12 @@ extends Effect.Effect<(props: P) => A, never, Exclude>, Component.Options { - new(_: never): {} + new(_: never): Record readonly [TypeId]: TypeId - readonly ["~Props"]: P - readonly ["~Success"]: A - readonly ["~Error"]: E - readonly ["~Context"]: R + readonly "~Props": P + readonly "~Success": A + readonly "~Error": E + readonly "~Context": R /** @internal */ readonly body: (props: P) => Effect.Effect @@ -53,6 +55,7 @@ const ComponentProto = Object.freeze({ this: Component ) { const self = this + // biome-ignore lint/style/noNonNullAssertion: context initialization const runtimeRef = React.useRef>>(null!) runtimeRef.current = yield* Effect.runtime>() diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 94d91b4..57ed027 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Array, Cause, Chunk, Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, pipe, Pipeable, Predicate, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, Pipeable, Predicate, pipe, Ref, Schema, type Scope, Stream, type Subscribable, SubscriptionRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as React from "react" import * as Hooks from "./Hooks/index.js" @@ -287,6 +287,7 @@ export const useField = , PropertyPath.ValueFromPath +// biome-ignore lint/correctness/useExhaustiveDependencies: individual path components need to be compared > => React.useMemo(() => field(self, path), [self, ...path]) export namespace useInput { diff --git a/packages/effect-fc/src/Hooks/useCallbackPromise.ts b/packages/effect-fc/src/Hooks/useCallbackPromise.ts index 51b99ec..0e2ce10 100644 --- a/packages/effect-fc/src/Hooks/useCallbackPromise.ts +++ b/packages/effect-fc/src/Hooks/useCallbackPromise.ts @@ -11,8 +11,10 @@ export const useCallbackPromise: { callback: (...args: Args) => Effect.Effect, deps: React.DependencyList, ) { + // biome-ignore lint/style/noNonNullAssertion: context initialization const runtimeRef = React.useRef>(null!) runtimeRef.current = yield* Effect.runtime() + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList return React.useCallback((...args: Args) => Runtime.runPromise(runtimeRef.current)(callback(...args)), deps) }) diff --git a/packages/effect-fc/src/Hooks/useCallbackSync.ts b/packages/effect-fc/src/Hooks/useCallbackSync.ts index d3ae332..3f96641 100644 --- a/packages/effect-fc/src/Hooks/useCallbackSync.ts +++ b/packages/effect-fc/src/Hooks/useCallbackSync.ts @@ -11,8 +11,10 @@ export const useCallbackSync: { callback: (...args: Args) => Effect.Effect, deps: React.DependencyList, ) { + // biome-ignore lint/style/noNonNullAssertion: context initialization const runtimeRef = React.useRef>(null!) runtimeRef.current = yield* Effect.runtime() + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList return React.useCallback((...args: Args) => Runtime.runSync(runtimeRef.current)(callback(...args)), deps) }) diff --git a/packages/effect-fc/src/Hooks/useEffect.ts b/packages/effect-fc/src/Hooks/useEffect.ts index b2de7e3..b325479 100644 --- a/packages/effect-fc/src/Hooks/useEffect.ts +++ b/packages/effect-fc/src/Hooks/useEffect.ts @@ -1,7 +1,7 @@ import { Effect, ExecutionStrategy, Runtime, Scope } from "effect" import * as React from "react" -import type { ScopeOptions } from "./ScopeOptions.js" import { closeScope } from "./internal.js" +import type { ScopeOptions } from "./ScopeOptions.js" export const useEffect: { @@ -24,5 +24,6 @@ export const useEffect: { () => closeScope(scope, runtime, options) ), Runtime.runSync(runtime), + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList ), deps) }) diff --git a/packages/effect-fc/src/Hooks/useFork.ts b/packages/effect-fc/src/Hooks/useFork.ts index 3365c0b..0ce8a5c 100644 --- a/packages/effect-fc/src/Hooks/useFork.ts +++ b/packages/effect-fc/src/Hooks/useFork.ts @@ -27,5 +27,6 @@ export const useFork: { ...options, finalizerExecutionMode: options?.finalizerExecutionMode ?? "fork", }) + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList }, deps) }) diff --git a/packages/effect-fc/src/Hooks/useLayoutEffect.ts b/packages/effect-fc/src/Hooks/useLayoutEffect.ts index 93f0302..6f8c438 100644 --- a/packages/effect-fc/src/Hooks/useLayoutEffect.ts +++ b/packages/effect-fc/src/Hooks/useLayoutEffect.ts @@ -1,7 +1,7 @@ import { Effect, ExecutionStrategy, Runtime, Scope } from "effect" import * as React from "react" -import type { ScopeOptions } from "./ScopeOptions.js" import { closeScope } from "./internal.js" +import type { ScopeOptions } from "./ScopeOptions.js" export const useLayoutEffect: { @@ -24,5 +24,6 @@ export const useLayoutEffect: { () => closeScope(scope, runtime, options) ), Runtime.runSync(runtime), + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList ), deps) }) diff --git a/packages/effect-fc/src/Hooks/useMemo.ts b/packages/effect-fc/src/Hooks/useMemo.ts index c12dd02..43d4449 100644 --- a/packages/effect-fc/src/Hooks/useMemo.ts +++ b/packages/effect-fc/src/Hooks/useMemo.ts @@ -12,5 +12,6 @@ export const useMemo: { deps: React.DependencyList, ) { const runtime = yield* Effect.runtime() + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList return yield* React.useMemo(() => Runtime.runSync(runtime)(Effect.cached(factory())), deps) }) diff --git a/packages/effect-fc/src/Hooks/useRefState.ts b/packages/effect-fc/src/Hooks/useRefState.ts index e6bae8b..9e55fbf 100644 --- a/packages/effect-fc/src/Hooks/useRefState.ts +++ b/packages/effect-fc/src/Hooks/useRefState.ts @@ -1,4 +1,4 @@ -import { Effect, Equivalence, Ref, Stream, SubscriptionRef } from "effect" +import { Effect, Equivalence, Ref, Stream, type SubscriptionRef } from "effect" import * as React from "react" import * as SetStateAction from "../SetStateAction.js" import { useCallbackSync } from "./useCallbackSync.js" diff --git a/packages/effect-fc/src/Hooks/useScope.ts b/packages/effect-fc/src/Hooks/useScope.ts index f475885..93212a0 100644 --- a/packages/effect-fc/src/Hooks/useScope.ts +++ b/packages/effect-fc/src/Hooks/useScope.ts @@ -1,7 +1,7 @@ import { Effect, ExecutionStrategy, Ref, Runtime, Scope } from "effect" import * as React from "react" -import type { ScopeOptions } from "./ScopeOptions.js" import { closeScope } from "./internal.js" +import type { ScopeOptions } from "./ScopeOptions.js" export const useScope: { @@ -12,6 +12,7 @@ export const useScope: { } = Effect.fnUntraced(function*(deps, options) { const runtime = yield* Effect.runtime() + // biome-ignore lint/correctness/useExhaustiveDependencies: no reactivity needed const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)(Effect.all([ Ref.make(true), Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential), @@ -30,6 +31,7 @@ export const useScope: { Effect.map(scope => () => closeScope(scope, runtime, options)), ), }) + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList ), deps) return scope diff --git a/packages/effect-fc/src/Hooks/useSubscribeStream.ts b/packages/effect-fc/src/Hooks/useSubscribeStream.ts index d84b4ab..252054f 100644 --- a/packages/effect-fc/src/Hooks/useSubscribeStream.ts +++ b/packages/effect-fc/src/Hooks/useSubscribeStream.ts @@ -16,6 +16,7 @@ export const useSubscribeStream: { initialValue?: A, ) { const [reactStateValue, setReactStateValue] = React.useState( + // biome-ignore lint/correctness/useExhaustiveDependencies: no reactivity needed React.useMemo(() => initialValue ? Option.some(initialValue) : Option.none(), diff --git a/packages/effect-fc/src/Memoized.ts b/packages/effect-fc/src/Memoized.ts index c80a3e0..7ed205f 100644 --- a/packages/effect-fc/src/Memoized.ts +++ b/packages/effect-fc/src/Memoized.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { type Equivalence, Function, Predicate } from "effect" import type * as Component from "./Component.js" diff --git a/packages/effect-fc/src/ReactRuntime.ts b/packages/effect-fc/src/ReactRuntime.ts index 7689275..64f1af9 100644 --- a/packages/effect-fc/src/ReactRuntime.ts +++ b/packages/effect-fc/src/ReactRuntime.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Effect, type Layer, ManagedRuntime, Predicate, type Runtime } from "effect" import * as React from "react" @@ -6,7 +7,7 @@ export const TypeId: unique symbol = Symbol.for("effect-fc/ReactRuntime") export type TypeId = typeof TypeId export interface ReactRuntime { - new(_: never): {} + new(_: never): Record readonly [TypeId]: TypeId readonly runtime: ManagedRuntime.ManagedRuntime readonly context: React.Context> @@ -23,6 +24,7 @@ export const make = ( ): ReactRuntime => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make(layer, memoMap), + // biome-ignore lint/style/noNonNullAssertion: context initialization context: React.createContext>(null!), }), ReactRuntimeProto, @@ -48,16 +50,14 @@ export const Provider = ( ) } -namespace ProviderInner { - export interface Props { - readonly runtime: ReactRuntime - readonly promise: Promise> - readonly children?: React.ReactNode - } +interface ProviderInnerProps { + readonly runtime: ReactRuntime + readonly promise: Promise> + readonly children?: React.ReactNode } const ProviderInner = ( - { runtime, promise, children }: ProviderInner.Props + { runtime, promise, children }: ProviderInnerProps ): React.ReactNode => React.createElement( runtime.context, { value: React.use(promise) }, -- 2.49.1 From 11dfcdaf855b2b9343887261129860f80eefca06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Oct 2025 22:55:16 +0200 Subject: [PATCH 57/67] Example fixes --- packages/effect-fc/package.json | 14 ++++++++++---- .../src/lib/form/TextFieldFormInput.tsx | 2 +- .../example/src/lib/input/TextAreaInput.tsx | 7 ++++--- .../example/src/lib/input/TextFieldInput.tsx | 9 +++++---- packages/example/src/routes/form.tsx | 6 +++--- packages/example/src/routes/index.tsx | 8 ++++---- packages/example/src/todo/Todo.tsx | 19 ++++++++----------- .../example/src/todo/TodosState.service.ts | 4 ++-- 8 files changed, 37 insertions(+), 32 deletions(-) diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 48c8af8..bc2b1a1 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -17,10 +17,16 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, - "./*": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - } + "./*": [ + { + "types": "./dist/*/index.d.ts", + "default": "./dist/*/index.js" + }, + { + "types": "./dist/*.d.ts", + "default": "./dist/*.js" + } + ] }, "scripts": { "build": "tsc", diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index 907fbde..040a3dc 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,7 +1,7 @@ import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" import { Component, Form } from "effect-fc" -import { useSubscribables } from "effect-fc/hooks" +import { useSubscribables } from "effect-fc/Hooks" export interface TextFieldFormInputProps diff --git a/packages/example/src/lib/input/TextAreaInput.tsx b/packages/example/src/lib/input/TextAreaInput.tsx index d4413fd..64fb1a0 100644 --- a/packages/example/src/lib/input/TextAreaInput.tsx +++ b/packages/example/src/lib/input/TextAreaInput.tsx @@ -1,7 +1,8 @@ -import { Callout, Flex, TextArea, TextAreaProps } from "@radix-ui/themes" -import { Array, Equivalence, Option, ParseResult, Schema, Struct } from "effect" +/** biome-ignore-all lint/correctness/useHookAtTopLevel: effect-fc HOC */ +import { Callout, Flex, TextArea, type TextAreaProps } from "@radix-ui/themes" +import { Array, type Equivalence, Option, ParseResult, type Schema, Struct } from "effect" import { Component } from "effect-fc" -import { useInput } from "effect-fc/hooks" +import { useInput } from "effect-fc/Hooks" import * as React from "react" diff --git a/packages/example/src/lib/input/TextFieldInput.tsx b/packages/example/src/lib/input/TextFieldInput.tsx index d5f8432..905c0f8 100644 --- a/packages/example/src/lib/input/TextFieldInput.tsx +++ b/packages/example/src/lib/input/TextFieldInput.tsx @@ -1,7 +1,8 @@ +/** biome-ignore-all lint/correctness/useHookAtTopLevel: effect-fc HOC */ import { Callout, Checkbox, Flex, TextField } from "@radix-ui/themes" -import { Array, Equivalence, Option, ParseResult, Schema, Struct } from "effect" -import { Component, Memo } from "effect-fc" -import { useInput, useOptionalInput } from "effect-fc/hooks" +import { Array, type Equivalence, Option, ParseResult, type Schema, Struct } from "effect" +import { Component } from "effect-fc" +import { useInput, useOptionalInput } from "effect-fc/Hooks" import * as React from "react" @@ -67,4 +68,4 @@ export const TextFieldInput = (options: { } ) -}).pipe(Memo.memo) +}) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 4653e1f..879f2f8 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,10 +1,10 @@ -import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" -import { runtime } from "@/runtime" import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Effect, ParseResult, Schema } from "effect" import { Component, Form } from "effect-fc" -import { useContext, useSubscribables } from "effect-fc/hooks" +import { useContext, useSubscribables } from "effect-fc/Hooks" +import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" +import { runtime } from "@/runtime" const email = Schema.pattern( diff --git a/packages/example/src/routes/index.tsx b/packages/example/src/routes/index.tsx index 3bf279f..3e0835c 100644 --- a/packages/example/src/routes/index.tsx +++ b/packages/example/src/routes/index.tsx @@ -1,10 +1,10 @@ -import { runtime } from "@/runtime" -import { Todos } from "@/todo/Todos" -import { TodosState } from "@/todo/TodosState.service" import { createFileRoute } from "@tanstack/react-router" import { Effect } from "effect" import { Component } from "effect-fc" -import { useContext } from "effect-fc/hooks" +import { useContext } from "effect-fc/Hooks" +import { runtime } from "@/runtime" +import { Todos } from "@/todo/Todos" +import { TodosState } from "@/todo/TodosState.service" const TodosStateLive = TodosState.Default("todos") diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index 469be99..e84c504 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -1,15 +1,13 @@ +import { Box, Button, Flex, IconButton } from "@radix-ui/themes" +import { GetRandomValues, makeUuid4 } from "@typed/id" +import { Chunk, DateTime, Effect, Match, Option, Ref, Runtime, Schema, Stream, SubscriptionRef } from "effect" +import { Component, Hooks } from "effect-fc" +import { FaArrowDown, FaArrowUp } from "react-icons/fa" +import { FaDeleteLeft } from "react-icons/fa6" import * as Domain from "@/domain" import { TextAreaInput } from "@/lib/input/TextAreaInput" import { TextFieldInput } from "@/lib/input/TextFieldInput" import { DateTimeUtcFromZonedInput } from "@/lib/schema" -import { Box, Button, Flex, IconButton } from "@radix-ui/themes" -import { GetRandomValues, makeUuid4 } from "@typed/id" -import { Chunk, DateTime, Effect, Match, Option, Ref, Runtime, Schema, Stream, SubscriptionRef } from "effect" -import { Component, Memo } from "effect-fc" -import { useMemo, useOnce, useSubscribables } from "effect-fc/hooks" -import { Subscribable, SubscriptionSubRef } from "effect-fc/types" -import { FaArrowDown, FaArrowUp } from "react-icons/fa" -import { FaDeleteLeft } from "react-icons/fa6" import { TodosState } from "./TodosState.service" @@ -35,7 +33,7 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr const runtime = yield* Effect.runtime() const state = yield* TodosState - const { ref, indexRef, contentRef, completedAtRef } = yield* useMemo(() => Match.value(props).pipe( + const { ref, indexRef, contentRef, completedAtRef } = yield* Hooks.useMemo(() => Match.value(props).pipe( Match.tag("new", () => Effect.Do.pipe( Effect.bind("ref", () => Effect.andThen(makeTodo, SubscriptionRef.make)), Effect.let("indexRef", () => Subscribable.make({ get: Effect.succeed(-1), changes: Stream.empty })), @@ -48,10 +46,9 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr Effect.let("contentRef", ({ ref }) => SubscriptionSubRef.makeFromPath(ref, ["content"])), Effect.let("completedAtRef", ({ ref }) => SubscriptionSubRef.makeFromPath(ref, ["completedAt"])), - // eslint-disable-next-line react-hooks/exhaustive-deps ), [props._tag, props._tag === "edit" ? props.id : undefined]) - const [index, size] = yield* useSubscribables(indexRef, state.sizeSubscribable) + const [index, size] = yield* Hooks.useSubscribables(indexRef, state.sizeSubscribable) const StringTextAreaInputFC = yield* StringTextAreaInput const OptionalDateTimeInputFC = yield* OptionalDateTimeInput diff --git a/packages/example/src/todo/TodosState.service.ts b/packages/example/src/todo/TodosState.service.ts index d024847..14595c0 100644 --- a/packages/example/src/todo/TodosState.service.ts +++ b/packages/example/src/todo/TodosState.service.ts @@ -1,8 +1,8 @@ -import { Todo } from "@/domain" import { KeyValueStore } from "@effect/platform" import { BrowserKeyValueStore } from "@effect/platform-browser" import { Chunk, Console, Effect, Option, Schema, Stream, SubscriptionRef } from "effect" -import { Subscribable, SubscriptionSubRef } from "effect-fc/types" +import { Subscribable, SubscriptionSubRef } from "effect-fc" +import { Todo } from "@/domain" export class TodosState extends Effect.Service()("TodosState", { -- 2.49.1 From 4c253fcfe848c566f9f57bc445b0b31f91da4459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 12:08:04 +0200 Subject: [PATCH 58/67] Fixes --- .../example/src/lib/input/TextAreaInput.tsx | 2 +- .../example/src/lib/input/TextFieldInput.tsx | 2 +- .../src/routes/dev/async-rendering.tsx | 11 ++++---- packages/example/src/routes/dev/input.tsx | 13 ++++----- packages/example/src/routes/dev/memo.tsx | 6 ++-- packages/example/src/routes/form.tsx | 28 +++++++++---------- packages/example/src/routes/index.tsx | 9 +++--- packages/example/src/todo/Todo.tsx | 8 ++++-- packages/example/src/todo/Todos.tsx | 7 ++--- 9 files changed, 43 insertions(+), 43 deletions(-) diff --git a/packages/example/src/lib/input/TextAreaInput.tsx b/packages/example/src/lib/input/TextAreaInput.tsx index 64fb1a0..03d4359 100644 --- a/packages/example/src/lib/input/TextAreaInput.tsx +++ b/packages/example/src/lib/input/TextAreaInput.tsx @@ -16,7 +16,7 @@ export const TextAreaInput = (options: { React.JSX.Element, ParseResult.ParseError, R -> => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props) { +> => Component.makeUntraced("TextFieldInput")(function*(props) { const input = yield* useInput({ ...options, ...props }) const issue = React.useMemo(() => input.error.pipe( Option.map(ParseResult.ArrayFormatter.formatErrorSync), diff --git a/packages/example/src/lib/input/TextFieldInput.tsx b/packages/example/src/lib/input/TextFieldInput.tsx index 905c0f8..e0bf339 100644 --- a/packages/example/src/lib/input/TextFieldInput.tsx +++ b/packages/example/src/lib/input/TextFieldInput.tsx @@ -19,7 +19,7 @@ export const TextFieldInput = (options: { readonly optional?: O readonly schema: Schema.Schema readonly equivalence?: Equivalence.Equivalence -}) => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props: O extends true +}) => Component.makeUntraced("TextFieldInput")(function*(props: O extends true ? TextFieldOptionalInputProps : TextFieldInputProps ) { diff --git a/packages/example/src/routes/dev/async-rendering.tsx b/packages/example/src/routes/dev/async-rendering.tsx index fadd316..e5a23e8 100644 --- a/packages/example/src/routes/dev/async-rendering.tsx +++ b/packages/example/src/routes/dev/async-rendering.tsx @@ -1,11 +1,10 @@ -import { runtime } from "@/runtime" import { Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Effect } from "effect" -import { Component, Memo, Suspense } from "effect-fc" -import { Hooks } from "effect-fc/hooks" +import { Async, Component, Hooks, Memoized } from "effect-fc" import * as React from "react" +import { runtime } from "@/runtime" // Generator version @@ -64,10 +63,10 @@ class AsyncComponent extends Component.makeUntraced("AsyncComponent")(function*( ) }).pipe( - Suspense.suspense, - Suspense.withOptions({ defaultFallback:

Loading...

}), + Async.async, + Async.withOptions({ defaultFallback:

Loading...

}), ) {} -class MemoizedAsyncComponent extends Memo.memo(AsyncComponent) {} +class MemoizedAsyncComponent extends Memoized.memoized(AsyncComponent) {} class SubComponent extends Component.makeUntraced("SubComponent")(function*() { const [state] = React.useState(yield* Hooks.useOnce(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom))) diff --git a/packages/example/src/routes/dev/input.tsx b/packages/example/src/routes/dev/input.tsx index 9a8a53f..ef20057 100644 --- a/packages/example/src/routes/dev/input.tsx +++ b/packages/example/src/routes/dev/input.tsx @@ -1,10 +1,9 @@ -import { TextFieldInput } from "@/lib/input/TextFieldInput" -import { runtime } from "@/runtime" import { Container } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Schema, SubscriptionRef } from "effect" -import { Component, Memo } from "effect-fc" -import { useOnce } from "effect-fc/hooks" +import { Component, Hooks, Memoized } from "effect-fc" +import { TextFieldInput } from "@/lib/input/TextFieldInput" +import { runtime } from "@/runtime" const IntFromString = Schema.NumberFromString.pipe(Schema.int()) @@ -16,9 +15,9 @@ const Input = Component.makeUntraced("Input")(function*() { const IntTextFieldInputFC = yield* IntTextFieldInput const StringTextFieldInputFC = yield* StringTextFieldInput - const intRef1 = yield* useOnce(() => SubscriptionRef.make(0)) + const intRef1 = yield* Hooks.useOnce(() => SubscriptionRef.make(0)) // const intRef2 = yield* useOnce(() => SubscriptionRef.make(0)) - const stringRef = yield* useOnce(() => SubscriptionRef.make("")) + const stringRef = yield* Hooks.useOnce(() => SubscriptionRef.make("")) // yield* useFork(() => Stream.runForEach(intRef1.changes, Console.log), [intRef1]) // const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 }) @@ -33,7 +32,7 @@ const Input = Component.makeUntraced("Input")(function*() { ) }).pipe( - Memo.memo, + Memoized.memoized, Component.withRuntime(runtime.context) ) diff --git a/packages/example/src/routes/dev/memo.tsx b/packages/example/src/routes/dev/memo.tsx index b81f132..87ece9c 100644 --- a/packages/example/src/routes/dev/memo.tsx +++ b/packages/example/src/routes/dev/memo.tsx @@ -1,10 +1,10 @@ -import { runtime } from "@/runtime" import { Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Effect } from "effect" -import { Component, Memo } from "effect-fc" +import { Component, Memoized } from "effect-fc" import * as React from "react" +import { runtime } from "@/runtime" const RouteComponent = Component.makeUntraced("RouteComponent")(function*() { @@ -30,7 +30,7 @@ class SubComponent extends Component.makeUntraced("SubComponent")(function*() { return {id} }) {} -class MemoizedSubComponent extends Memo.memo(SubComponent) {} +class MemoizedSubComponent extends Memoized.memoized(SubComponent) {} export const Route = createFileRoute("/dev/memo")({ component: RouteComponent, diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 879f2f8..83c74ee 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,8 +1,8 @@ import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Effect, ParseResult, Schema } from "effect" -import { Component, Form } from "effect-fc" -import { useContext, useSubscribables } from "effect-fc/Hooks" +import { Component, Form, Hooks } from "effect-fc" +import { useSubscribables } from "effect-fc/Hooks" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" import { runtime } from "@/runtime" @@ -29,7 +29,7 @@ class RegisterForm extends Effect.Service()("RegisterForm", { Schema.typeSchema(RegisterFormSchema), { decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), - encode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + encode: ParseResult.succeed, }, ), initialEncodedValue: { email: "", password: "" }, @@ -48,7 +48,7 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const TextFieldFormInputFC = yield* TextFieldFormInput return ( - +
{ e.preventDefault() void submit() @@ -72,15 +72,15 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { }) {} -const RegisterRoute = Component.makeUntraced(function* RegisterRoute() { - const context = yield* useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }) - const RegisterRouteFC = yield* Effect.provide(RegisterPage, context) - - return -}).pipe( - Component.withRuntime(runtime.context) -) - export const Route = createFileRoute("/form")({ - component: RegisterRoute + component: Component.makeUntraced("RegisterRoute")(function*() { + const RegisterRouteFC = yield* Effect.provide( + RegisterPage, + yield* Hooks.useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }), + ) + + return + }).pipe( + Component.withRuntime(runtime.context) + ) }) diff --git a/packages/example/src/routes/index.tsx b/packages/example/src/routes/index.tsx index 3e0835c..8da0367 100644 --- a/packages/example/src/routes/index.tsx +++ b/packages/example/src/routes/index.tsx @@ -1,7 +1,6 @@ import { createFileRoute } from "@tanstack/react-router" import { Effect } from "effect" -import { Component } from "effect-fc" -import { useContext } from "effect-fc/Hooks" +import { Component, Hooks } from "effect-fc" import { runtime } from "@/runtime" import { Todos } from "@/todo/Todos" import { TodosState } from "@/todo/TodosState.service" @@ -10,8 +9,10 @@ import { TodosState } from "@/todo/TodosState.service" const TodosStateLive = TodosState.Default("todos") const Index = Component.makeUntraced("Index")(function*() { - const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" }) - const TodosFC = yield* Effect.provide(Todos, context) + const TodosFC = yield* Effect.provide( + Todos, + yield* Hooks.useContext(TodosStateLive, { finalizerExecutionMode: "fork" }), + ) return }).pipe( diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index e84c504..9d575f1 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -1,7 +1,7 @@ import { Box, Button, Flex, IconButton } from "@radix-ui/themes" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Chunk, DateTime, Effect, Match, Option, Ref, Runtime, Schema, Stream, SubscriptionRef } from "effect" -import { Component, Hooks } from "effect-fc" +import { Component, Hooks, Memoized, Subscribable, SubscriptionSubRef } from "effect-fc" import { FaArrowDown, FaArrowUp } from "react-icons/fa" import { FaDeleteLeft } from "react-icons/fa6" import * as Domain from "@/domain" @@ -64,7 +64,7 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr DateTime.now)} + defaultValue={yield* Hooks.useOnce(() => DateTime.now)} /> {props._tag === "new" && @@ -106,4 +106,6 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr } ) -}).pipe(Memo.memo) {} +}).pipe( + Memoized.memoized +) {} diff --git a/packages/example/src/todo/Todos.tsx b/packages/example/src/todo/Todos.tsx index 9b84082..3fb4076 100644 --- a/packages/example/src/todo/Todos.tsx +++ b/packages/example/src/todo/Todos.tsx @@ -1,16 +1,15 @@ import { Container, Flex, Heading } from "@radix-ui/themes" import { Chunk, Console, Effect } from "effect" -import { Component } from "effect-fc" -import { useOnce, useSubscribables } from "effect-fc/hooks" +import { Component, Hooks } from "effect-fc" import { Todo } from "./Todo" import { TodosState } from "./TodosState.service" export class Todos extends Component.makeUntraced("Todos")(function*() { const state = yield* TodosState - const [todos] = yield* useSubscribables(state.ref) + const [todos] = yield* Hooks.useSubscribables(state.ref) - yield* useOnce(() => Effect.andThen( + yield* Hooks.useOnce(() => Effect.andThen( Console.log("Todos mounted"), Effect.addFinalizer(() => Console.log("Todos unmounted")), )) -- 2.49.1 From 3456440e1e5a0a9d9e0286b7b794e9a2ff6bd701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 15:43:40 +0200 Subject: [PATCH 59/67] Form examples --- packages/effect-fc/src/Form.ts | 4 +- .../src/lib/form/TextFieldFormInput.tsx | 71 ++++++++++--------- .../lib/form/TextFieldFormOptionalInput.tsx | 57 +++++++++++++++ .../example/src/lib/input/TextFieldInput.tsx | 2 - packages/example/src/routes/form.tsx | 40 +++++++---- 5 files changed, 122 insertions(+), 52 deletions(-) create mode 100644 packages/example/src/lib/form/TextFieldFormOptionalInput.tsx diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 57ed027..3df3bc0 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -336,8 +336,8 @@ export const useInput: { }) export namespace useOptionalInput { - export interface Options extends useInput.Options { - readonly defaultValue: I + export interface Options extends useInput.Options { + readonly defaultValue: T } export interface Result extends useInput.Result { diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index 040a3dc..d237096 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,7 +1,6 @@ import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" -import { Component, Form } from "effect-fc" -import { useSubscribables } from "effect-fc/Hooks" +import { Component, Form, Hooks } from "effect-fc" export interface TextFieldFormInputProps @@ -9,38 +8,42 @@ extends TextField.RootProps, Form.useInput.Options { readonly field: Form.FormField } -export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")(function*(props: TextFieldFormInputProps) { - const { value, setValue } = yield* Form.useInput(props.field, props) - const [issues, isValidating, isSubmitting] = yield* useSubscribables( - props.field.issuesSubscribable, - props.field.isValidatingSubscribable, - props.field.isSubmittingSubscribable, - ) +export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")( + function*(props: TextFieldFormInputProps) { + const { value, setValue } = yield* Form.useInput(props.field, props) + const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( + props.field.issuesSubscribable, + props.field.isValidatingSubscribable, + props.field.isSubmittingSubscribable, + ) - return ( - - setValue(e.target.value)} - disabled={isSubmitting} - {...props} - > - {isValidating && - - - - } - + return ( + + setValue(e.target.value)} + disabled={isSubmitting} + {...props} + > + {isValidating && + + + + } - {Option.match(Array.head(issues), { - onSome: issue => ( - - {issue.message} - - ), + {props.children} + - onNone: () => <>, - })} - - ) -}) {} + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) + } +) {} diff --git a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx new file mode 100644 index 0000000..4344f42 --- /dev/null +++ b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx @@ -0,0 +1,57 @@ +import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" +import { Array, Option } from "effect" +import { Component, Form, Hooks } from "effect-fc" + + +export interface TextFieldFormOptionalInputProps +extends Omit, Form.useOptionalInput.Options { + readonly field: Form.FormField> +} + +export class TextFieldFormOptionalInput extends Component.makeUntraced("TextFieldFormOptionalInput")( + function*(props: TextFieldFormOptionalInputProps) { + const { value, setValue, enabled, setEnabled } = yield* Form.useOptionalInput(props.field, props) + const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( + props.field.issuesSubscribable, + props.field.isValidatingSubscribable, + props.field.isSubmittingSubscribable, + ) + + return ( + + setValue(e.target.value)} + disabled={!enabled || isSubmitting} + {...props} + > + + + + + {isValidating && + + + + } + + {props.children} + + + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) + } +) {} diff --git a/packages/example/src/lib/input/TextFieldInput.tsx b/packages/example/src/lib/input/TextFieldInput.tsx index e0bf339..b06d1ff 100644 --- a/packages/example/src/lib/input/TextFieldInput.tsx +++ b/packages/example/src/lib/input/TextFieldInput.tsx @@ -29,12 +29,10 @@ export const TextFieldInput = (options: { ) = options.optional ? { optional: true, - // eslint-disable-next-line react-hooks/rules-of-hooks ...yield* useOptionalInput({ ...options, ...props as TextFieldOptionalInputProps }), } : { optional: false, - // eslint-disable-next-line react-hooks/rules-of-hooks ...yield* useInput({ ...options, ...props as TextFieldInputProps }), } diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 83c74ee..cef0a5a 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,9 +1,9 @@ import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect, ParseResult, Schema } from "effect" +import { Console, Effect, Option, ParseResult, Schema } from "effect" import { Component, Form, Hooks } from "effect-fc" -import { useSubscribables } from "effect-fc/Hooks" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" +import { TextFieldFormOptionalInput } from "@/lib/form/TextFieldFormOptionalInput" import { runtime } from "@/runtime" @@ -20,22 +20,28 @@ const email = Schema.pattern( const RegisterFormSchema = Schema.Struct({ email: Schema.String.pipe(email), password: Schema.String.pipe(Schema.minLength(3)), + iq: Schema.OptionFromSelf(Schema.NumberFromString), }) class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ - schema: Schema.transformOrFail( - Schema.encodedSchema(RegisterFormSchema), - Schema.typeSchema(RegisterFormSchema), - { - decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), - encode: ParseResult.succeed, - }, + schema: RegisterFormSchema.pipe( + Schema.compose( + Schema.transformOrFail( + Schema.typeSchema(RegisterFormSchema), + Schema.typeSchema(RegisterFormSchema), + { + decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + encode: ParseResult.succeed, + }, + ), + ), ), - initialEncodedValue: { email: "", password: "" }, - submit: () => Effect.andThen( - Effect.sleep("500 millis"), - Effect.sync(() => alert("Done!")), + + initialEncodedValue: { email: "", password: "", iq: Option.none() }, + submit: v => Effect.sleep("500 millis").pipe( + Effect.andThen(Console.log(v)), + Effect.andThen(Effect.sync(() => alert("Done!"))), ), }) }) {} @@ -43,9 +49,10 @@ class RegisterForm extends Effect.Service()("RegisterForm", { class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const form = yield* RegisterForm const submit = yield* Form.useSubmit(form) - const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) + const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable) const TextFieldFormInputFC = yield* TextFieldFormInput + const TextFieldFormOptionalInputFC = yield* TextFieldFormOptionalInput return ( @@ -64,6 +71,11 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { debounce="200 millis" /> + + -- 2.49.1 From fd6de8e621cc9a4e388ebe4de27ecc6462eb5b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 16:00:15 +0200 Subject: [PATCH 60/67] Form tests --- packages/example/src/routes/form.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index cef0a5a..803c7ec 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -4,6 +4,7 @@ import { Console, Effect, Option, ParseResult, Schema } from "effect" import { Component, Form, Hooks } from "effect-fc" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" import { TextFieldFormOptionalInput } from "@/lib/form/TextFieldFormOptionalInput" +import { DateTimeUtcFromZonedInput } from "@/lib/schema" import { runtime } from "@/runtime" @@ -20,7 +21,7 @@ const email = Schema.pattern( const RegisterFormSchema = Schema.Struct({ email: Schema.String.pipe(email), password: Schema.String.pipe(Schema.minLength(3)), - iq: Schema.OptionFromSelf(Schema.NumberFromString), + birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), }) class RegisterForm extends Effect.Service()("RegisterForm", { @@ -38,7 +39,7 @@ class RegisterForm extends Effect.Service()("RegisterForm", { ), ), - initialEncodedValue: { email: "", password: "", iq: Option.none() }, + initialEncodedValue: { email: "", password: "", birth: Option.none() }, submit: v => Effect.sleep("500 millis").pipe( Effect.andThen(Console.log(v)), Effect.andThen(Effect.sync(() => alert("Done!"))), @@ -72,8 +73,9 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { /> -- 2.49.1 From 5951a0bf160147fb0f735381f8f6e95380921f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 16:57:44 +0200 Subject: [PATCH 61/67] Form fix --- .../src/lib/form/TextFieldFormInput.tsx | 40 +++++++++++-- .../lib/form/TextFieldFormOptionalInput.tsx | 57 ------------------- packages/example/src/main.tsx | 1 + packages/example/src/routes/form.tsx | 6 +- 4 files changed, 38 insertions(+), 66 deletions(-) delete mode 100644 packages/example/src/lib/form/TextFieldFormOptionalInput.tsx diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index d237096..85db7fa 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,16 +1,34 @@ -import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" +import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" import { Component, Form, Hooks } from "effect-fc" -export interface TextFieldFormInputProps +interface Props extends TextField.RootProps, Form.useInput.Options { + readonly optional?: false readonly field: Form.FormField } +interface OptionalProps +extends Omit, Form.useOptionalInput.Options { + readonly optional: true + readonly field: Form.FormField> +} + +export type TextFieldFormInputProps = Props | OptionalProps + + export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")( function*(props: TextFieldFormInputProps) { - const { value, setValue } = yield* Form.useInput(props.field, props) + const input: ( + | { readonly optional: true } & Form.useOptionalInput.Result + | { readonly optional: false } & Form.useInput.Result + ) = props.optional + // biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported + ? { optional: true, ...yield* Form.useOptionalInput(props.field, props) } + // biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported + : { optional: false, ...yield* Form.useInput(props.field, props) } + const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( props.field.issuesSubscribable, props.field.isValidatingSubscribable, @@ -20,11 +38,21 @@ export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInp return ( setValue(e.target.value)} - disabled={isSubmitting} + value={input.value} + onChange={e => input.setValue(e.target.value)} + disabled={(input.optional && !input.enabled) || isSubmitting} {...props} > + {input.optional && + + + + } + {isValidating && diff --git a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx deleted file mode 100644 index 4344f42..0000000 --- a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" -import { Array, Option } from "effect" -import { Component, Form, Hooks } from "effect-fc" - - -export interface TextFieldFormOptionalInputProps -extends Omit, Form.useOptionalInput.Options { - readonly field: Form.FormField> -} - -export class TextFieldFormOptionalInput extends Component.makeUntraced("TextFieldFormOptionalInput")( - function*(props: TextFieldFormOptionalInputProps) { - const { value, setValue, enabled, setEnabled } = yield* Form.useOptionalInput(props.field, props) - const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( - props.field.issuesSubscribable, - props.field.isValidatingSubscribable, - props.field.isSubmittingSubscribable, - ) - - return ( - - setValue(e.target.value)} - disabled={!enabled || isSubmitting} - {...props} - > - - - - - {isValidating && - - - - } - - {props.children} - - - {Option.match(Array.head(issues), { - onSome: issue => ( - - {issue.message} - - ), - - onNone: () => <>, - })} - - ) - } -) {} diff --git a/packages/example/src/main.tsx b/packages/example/src/main.tsx index bcf65ab..7bdc0dd 100644 --- a/packages/example/src/main.tsx +++ b/packages/example/src/main.tsx @@ -14,6 +14,7 @@ declare module "@tanstack/react-router" { } } +// biome-ignore lint/style/noNonNullAssertion: React entrypoint createRoot(document.getElementById("root")!).render( diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 803c7ec..84ae904 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -3,7 +3,6 @@ import { createFileRoute } from "@tanstack/react-router" import { Console, Effect, Option, ParseResult, Schema } from "effect" import { Component, Form, Hooks } from "effect-fc" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" -import { TextFieldFormOptionalInput } from "@/lib/form/TextFieldFormOptionalInput" import { DateTimeUtcFromZonedInput } from "@/lib/schema" import { runtime } from "@/runtime" @@ -53,7 +52,7 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable) const TextFieldFormInputFC = yield* TextFieldFormInput - const TextFieldFormOptionalInputFC = yield* TextFieldFormOptionalInput + return ( @@ -72,7 +71,8 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { debounce="200 millis" /> - Date: Thu, 2 Oct 2025 17:37:59 +0200 Subject: [PATCH 62/67] Add Form-level debounce --- packages/effect-fc/src/Form.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 3df3bc0..26a1e6b 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -15,14 +15,15 @@ export interface Form, - readonly submit: (value: NoInfer
) => Effect.Effect, + readonly schema: Schema.Schema + readonly submit: (value: NoInfer) => Effect.Effect + readonly debounce: Option.Option - readonly valueRef: SubscriptionRef.SubscriptionRef>, - readonly encodedValueRef: SubscriptionRef.SubscriptionRef, - readonly errorRef: SubscriptionRef.SubscriptionRef>, + readonly valueRef: SubscriptionRef.SubscriptionRef> + readonly encodedValueRef: SubscriptionRef.SubscriptionRef + readonly errorRef: SubscriptionRef.SubscriptionRef> readonly validationFiberRef: SubscriptionRef.SubscriptionRef>> - readonly submitStateRef: SubscriptionRef.SubscriptionRef>, + readonly submitStateRef: SubscriptionRef.SubscriptionRef> readonly canSubmitSubscribable: Subscribable.Subscribable } @@ -34,6 +35,7 @@ extends Pipeable.Class() implements Form { constructor( readonly schema: Schema.Schema, readonly submit: (value: NoInfer) => Effect.Effect, + readonly debounce: Option.Option, readonly valueRef: SubscriptionRef.SubscriptionRef>, readonly encodedValueRef: SubscriptionRef.SubscriptionRef, @@ -53,7 +55,8 @@ export namespace make { export interface Options { readonly schema: Schema.Schema readonly initialEncodedValue: NoInfer - readonly submit: (value: NoInfer) => Effect.Effect + readonly submit: (value: NoInfer) => Effect.Effect, + readonly debounce?: Duration.DurationInput, } } @@ -72,6 +75,7 @@ export const make: { return new FormImpl( options.schema, options.submit, + Option.fromNullable(options.debounce), valueRef, yield* SubscriptionRef.make(options.initialEncodedValue), @@ -108,7 +112,10 @@ export const make: { export const run = ( self: Form ): Effect.Effect => Stream.runForEach( - self.encodedValueRef.changes, + self.encodedValueRef.changes.pipe( + Option.isSome(self.debounce) ? Stream.debounce(self.debounce.value) : identity + ), + encodedValue => self.validationFiberRef.pipe( Effect.andThen(Option.match({ onSome: Fiber.interrupt, @@ -268,7 +275,7 @@ export const useForm: { options: make.Options, deps: React.DependencyList, ) { - const form = yield* Hooks.useMemo(() => make(options), deps) + const form = yield* Hooks.useMemo(() => make(options), [options.debounce, ...deps]) yield* Hooks.useFork(() => run(form), [form]) return form }) -- 2.49.1 From e0ead5b36e60305286b68dba51af227674eb72bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 17:40:13 +0200 Subject: [PATCH 63/67] Fix --- packages/example/src/routes/form.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 84ae904..c00ff11 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -43,6 +43,7 @@ class RegisterForm extends Effect.Service()("RegisterForm", { Effect.andThen(Console.log(v)), Effect.andThen(Effect.sync(() => alert("Done!"))), ), + debounce: "500 millis", }) }) {} @@ -63,12 +64,10 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { Date: Thu, 2 Oct 2025 18:00:14 +0200 Subject: [PATCH 64/67] Add Renovate --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} -- 2.49.1 From 34fca5a726af2be2bf8027f0a4dbcbb4b84881ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 18:08:38 +0200 Subject: [PATCH 65/67] Upgrade dependencies --- bun.lock | 100 ++++++++++++++++---------------- package.json | 10 ++-- packages/effect-fc/package.json | 2 +- packages/example/package.json | 34 +++++------ 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/bun.lock b/bun.lock index e5b07ee..e6c7de8 100644 --- a/bun.lock +++ b/bun.lock @@ -4,12 +4,12 @@ "": { "name": "@effect-fc/monorepo", "devDependencies": { - "@biomejs/biome": "^2.2.4", + "@biomejs/biome": "^2.2.5", "@effect/language-service": "^0.41.1", - "npm-check-updates": "^18.0.2", + "npm-check-updates": "^19.0.0", "npm-sort": "^0.0.4", - "turbo": "^2.5.6", - "typescript": "^5.9.2", + "turbo": "^2.5.8", + "typescript": "^5.9.3", }, }, "packages/effect-fc": { @@ -28,28 +28,28 @@ "name": "@effect-fc/example", "version": "0.0.0", "dependencies": { - "@effect/platform": "^0.90.6", - "@effect/platform-browser": "^0.70.0", + "@effect/platform": "^0.92.1", + "@effect/platform-browser": "^0.72.0", "@radix-ui/themes": "^3.2.1", "@typed/async-data": "^0.13.1", "@typed/id": "^0.17.2", "@typed/lazy-ref": "^0.3.3", - "effect": "^3.17.9", + "effect": "^3.18.1", "effect-fc": "workspace:*", "react-icons": "^5.5.0", }, "devDependencies": { - "@tanstack/react-router": "^1.131.27", - "@tanstack/react-router-devtools": "^1.131.27", - "@tanstack/router-plugin": "^1.131.27", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", - "@vitejs/plugin-react": "^5.0.1", - "globals": "^16.3.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", - "type-fest": "^4.41.0", - "vite": "^7.1.3", + "@tanstack/react-router": "^1.132.31", + "@tanstack/react-router-devtools": "^1.132.31", + "@tanstack/router-plugin": "^1.132.31", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.4", + "globals": "^16.4.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "type-fest": "^5.0.1", + "vite": "^7.1.8", }, }, }, @@ -114,31 +114,31 @@ "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], - "@biomejs/biome": ["@biomejs/biome@2.2.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.4", "@biomejs/cli-darwin-x64": "2.2.4", "@biomejs/cli-linux-arm64": "2.2.4", "@biomejs/cli-linux-arm64-musl": "2.2.4", "@biomejs/cli-linux-x64": "2.2.4", "@biomejs/cli-linux-x64-musl": "2.2.4", "@biomejs/cli-win32-arm64": "2.2.4", "@biomejs/cli-win32-x64": "2.2.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg=="], + "@biomejs/biome": ["@biomejs/biome@2.2.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.5", "@biomejs/cli-darwin-x64": "2.2.5", "@biomejs/cli-linux-arm64": "2.2.5", "@biomejs/cli-linux-arm64-musl": "2.2.5", "@biomejs/cli-linux-x64": "2.2.5", "@biomejs/cli-linux-x64-musl": "2.2.5", "@biomejs/cli-win32-arm64": "2.2.5", "@biomejs/cli-win32-x64": "2.2.5" }, "bin": { "biome": "bin/biome" } }, "sha512-zcIi+163Rc3HtyHbEO7CjeHq8DjQRs40HsGbW6vx2WI0tg8mYQOPouhvHSyEnCBAorfYNnKdR64/IxO7xQ5faw=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MYT+nZ38wEIWVcL5xLyOhYQQ7nlWD0b/4mgATW2c8dvq7R4OQjt/XGXFkXrmtWmQofaIM14L7V8qIz/M+bx5QQ=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-FLIEl73fv0R7dI10EnEiZLw+IMz3mWLnF95ASDI0kbx6DDLJjWxE5JxxBfmG+udz1hIDd3fr5wsuP7nwuTRdAg=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5DjiiDfHqGgR2MS9D+AZ8kOfrzTGqLKywn8hoXpXXlJXIECGQ32t+gt/uiS2XyGBM2XQhR6ztUvbjZWeccFMoQ=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Ov2wgAFwqDvQiESnu7b9ufD1faRa+40uwrohgBopeY84El2TnBDoMNXx6iuQdreoFGjwW8vH6k68G21EpNERw=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-fq9meKm1AEXeAWan3uCg6XSP5ObA6F/Ovm89TwaMiy1DNIwdgxPkNwxlXJX8iM6oRbFysYeGnT0OG8diCWb9ew=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-AVqLCDb/6K7aPNIcxHaTQj01sl1m989CJIQFQEaiQkGr2EQwyOpaATJ473h+nXDUuAcREhccfRpe/tu+0wu0eQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-xaOIad4wBambwJa6mdp1FigYSIF9i7PCqRbvBqtIi9y29QtPVQ13sDGtUnsRoe6SjL10auMzQ6YAe+B3RpZXVg=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-F/jhuXCssPFAuciMhHKk00xnCAxJRS/pUzVfXYmOMUp//XW7mO6QeCjsjvnm8L4AO/dG2VOB0O+fJPiJ2uXtIw=="], "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], "@effect/language-service": ["@effect/language-service@0.41.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-BZaFkF1JdoTIJuzT7mNfUnfQG2MLxkQtn5b9y1z0BZUIcNfs5p7yR0ekYfTkPkt+TVD+9HE12VC1SsG5DReKww=="], - "@effect/platform": ["@effect/platform@0.90.10", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.17.13" } }, "sha512-QhDPgCaLfIMQKOCoCPQvRUS+Y34iYJ07jdZ/CBAvYFvg/iUBebsmFuHL63RCD/YZH9BuK/kqqLYAA3M0fmUEgg=="], + "@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="], - "@effect/platform-browser": ["@effect/platform-browser@0.70.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.90.0", "effect": "^3.17.0" } }, "sha512-dq2ukUralavQIipSsQVuIOLLxBlFWL5Mkg1Fnr8esYPuPv0BXAI39nwNNi75X2tPAgak8OCSD0qh9qhmNj2gPA=="], + "@effect/platform-browser": ["@effect/platform-browser@0.72.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.92.0", "effect": "^3.18.0" } }, "sha512-xLlhR2S5yGo7//i8rTOiu1wCyrmrotXk+lK7Y257odxmQ2+HhV4wA2E+xFa0bFbHnqFCE3Yza9r0BkA3y1tgag=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], @@ -400,27 +400,27 @@ "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], - "@tanstack/history": ["@tanstack/history@1.132.21", "", {}, "sha512-5ziPz3YarKU5cBJoEJ4muV8cy+5W4oWdJMqW7qosMrK5fb9Qfm+QWX+kO3emKJMu4YOUofVu3toEuuD3x1zXKw=="], + "@tanstack/history": ["@tanstack/history@1.132.31", "", {}, "sha512-UCHM2uS0t/uSszqPEo+SBSSoQVeQ+LlOWAVBl5SA7+AedeAbKafIPjFn8huZCXNLAYb0WKV2+wETr7lDK9uz7g=="], - "@tanstack/react-router": ["@tanstack/react-router@1.132.27", "", { "dependencies": { "@tanstack/history": "1.132.21", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.132.27", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-fxSm1kxrtl3dQslqqKgYnIbQf7qW4fyWXKQhZYCWBFHO2GT11DWPZTNkTXw5YifOpw2RTmYJBH3C44t1HF95Cw=="], + "@tanstack/react-router": ["@tanstack/react-router@1.132.31", "", { "dependencies": { "@tanstack/history": "1.132.31", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.132.31", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-bgYgffI9TQhi8Zc/I5DMQEO4WOcDNtSll66Eb3/+k3iuI59ovVB/CiVCGjqdT8+2YBBj2x0saRDjsF00vj5+Yg=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.132.27", "", { "dependencies": { "@tanstack/router-devtools-core": "1.132.27", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.132.27", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-qLSRWw281HWg+QZtv3MCx3jIoKzY4t4XciWoLOcHZy9W1BKnFu3epI/jXMMvQ/VW9p0OzF69fQXtTFDx2ODCgA=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.132.31", "", { "dependencies": { "@tanstack/router-devtools-core": "1.132.31", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.132.31", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-EiO+t6s1K8igqqtxCO0GLG6KoJgaIsv9JAZMcJV+z/BspElGQwGDBzTtWYcHd9NOP2Yw7OCkAhM8ihwMbzWJNQ=="], "@tanstack/react-store": ["@tanstack/react-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg=="], - "@tanstack/router-core": ["@tanstack/router-core@1.132.27", "", { "dependencies": { "@tanstack/history": "1.132.21", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mNx+nba7mXc7sJdX+kYH4rSW8f7Jx/+0hPOkX4XAnqiq7I1ng3gGqmGuf4+2BYTG2aD+aTSPExUPczy9VNgRfQ=="], + "@tanstack/router-core": ["@tanstack/router-core@1.132.31", "", { "dependencies": { "@tanstack/history": "1.132.31", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-74W+J5N1NuPcuWDwsBAjCgK4ahtIRaB51KdegYrD1AeSNqiV4u8KzOzHKAAZD01UipQApUbpJbzFrHq0XQ9BHw=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.132.27", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/router-core": "^1.132.27", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-TE+chd5aSx+tURjIFPhVYNh+hUuKWjoYcP0BYCkcKp3zLN9VfMZh+bKVn1b44jsFUr6gzkbbctBQRZ+Z6oUm6Q=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.132.31", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/router-core": "^1.132.31", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-GwymJRm21hkluQMjOkXn+mBNPMyWlpzQut8mqEObh1cnF3zUsYT5YkCFV8ePA0jb/YVdjK/AfCAgSlhyIa09IA=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.132.27", "", { "dependencies": { "@tanstack/router-core": "1.132.27", "@tanstack/router-utils": "1.132.21", "@tanstack/virtual-file-routes": "1.132.21", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-dlW33RGxgG0mBF8vRqUs5CeId6wOf4qY6GmT2Pv6NgCVtrD7wsMSM1BasIRufNIxRT6FVnE7rztMy9wN7oCaKg=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.132.31", "", { "dependencies": { "@tanstack/router-core": "1.132.31", "@tanstack/router-utils": "1.132.31", "@tanstack/virtual-file-routes": "1.132.31", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-6Ys47sBR3jxet3CaqnF/ykV44R8HLQoT5ZbDqi6f2At6TXYe/+VELRSApC+cq1yjVJwp6Ot5Hm6mYWewh69bdQ=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.132.27", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.132.27", "@tanstack/router-generator": "1.132.27", "@tanstack/router-utils": "1.132.21", "@tanstack/virtual-file-routes": "1.132.21", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.132.27", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.8", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-0mo+jdgO7L5hOPGyrmKtyy20ZUfX4LwN30IXS7mhsaSHbZub+XLS3Ta6mDxNLmOXAJLPI3Gc90yI8jVwhizv5Q=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.132.31", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.132.31", "@tanstack/router-generator": "1.132.31", "@tanstack/router-utils": "1.132.31", "@tanstack/virtual-file-routes": "1.132.31", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.132.31", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.8", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-5/n6VxA6tFLFyewjl1+Av0Qsxmr/WpnAR2UlccS7ZaYli3bvNPJSZd3dy9EphEAXeSbqvFT29nQ/ox8EmGTonQ=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.132.21", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "fast-glob": "^3.3.3", "pathe": "^2.0.3" } }, "sha512-d9MvyhdPaKN78hQOss89bayiv/HR/7FpVHuKTNGEzzYrBWDVIUBd0yMNaBxQBpTg6NuNxtZ01ZJS5VkbedbzJw=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.132.31", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "fast-glob": "^3.3.3", "pathe": "^2.0.3" } }, "sha512-uf8mQ3wV58K8TL5XXBoWhkYxmCV7LLWbbf6AvcxdhnCnBNmXBGlY+T8RdsRnXyI2Iyp2HfHaVZ+8H3CEQedXfw=="], "@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.132.21", "", {}, "sha512-+eT+vxZnf2/QPr9ci5aPn7i3MnVyWYNG2DUqiKJXGi7EvuFrXV9r8zDK40QfUTNGdCg9F880YOVe3cTwMCO0zw=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.132.31", "", {}, "sha512-rxS8Cm2nIXroLqkm9pE/8X2lFNuvcTIIiFi5VH4PwzvKscAuaW3YRMN1WmaGDI2mVEn+GLaoY6Kc3jOczL5i4w=="], "@typed/async-data": ["@typed/async-data@0.13.1", "", { "dependencies": { "@typed/lazy-ref": "^0.3.2", "effect": "^3.11.9" } }, "sha512-rKv3HQtoHeGJwZpEaTL0FAEKfqHcMr/x3GtgkE01p2tJiKjq1eVaPZYpweZEEF/zUutox7DQ14oH85x+ZpPA/Q=="], @@ -438,9 +438,9 @@ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/react": ["@types/react@19.1.16", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog=="], + "@types/react": ["@types/react@19.2.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA=="], - "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="], + "@types/react-dom": ["@types/react-dom@19.2.0", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="], @@ -456,13 +456,13 @@ "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.9", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.10", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.26.2", "", { "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", "electron-to-chromium": "^1.5.218", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A=="], + "browserslist": ["browserslist@4.26.3", "", { "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w=="], "caniuse-lite": ["caniuse-lite@1.0.30001746", "", {}, "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA=="], @@ -562,7 +562,7 @@ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "npm-check-updates": ["npm-check-updates@18.3.1", "", { "bin": { "ncu": "build/cli.js", "npm-check-updates": "build/cli.js" } }, "sha512-5HwKPq7ybOOA1xB4FZg/1ToZZ5/i93U8m3co1mb3GYZAZPDkcxEFukQTTp/Abym+ZY6ShfrHl45Y0rCcwsNnQA=="], + "npm-check-updates": ["npm-check-updates@19.0.0", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-qcfjZEv6xB+WvW24S8wU1MKISPPiTREraBg62XDo/7zmOLXH3Zj7ti2v/LRfks0qITU8SDZLTWwgIitflvursw=="], "npm-sort": ["npm-sort@0.0.4", "", { "bin": { "npm-sort": "./index.js" } }, "sha512-S5Id/3Jvr7Cf/QnWjRteprngERCBhhEFOM+wMhUrAYP060/HUBC1aL5GoXS3xITlgacJCWaSmP4HQaAt91nNYQ=="], @@ -582,9 +582,9 @@ "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], - "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], + "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], - "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], + "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], "react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="], @@ -608,7 +608,7 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -622,6 +622,8 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], @@ -648,7 +650,7 @@ "turbo-windows-arm64": ["turbo-windows-arm64@2.5.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ=="], - "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "type-fest": ["type-fest@5.0.1", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-9MpwAI52m8H6ssA542UxSLnSiSD2dsC3/L85g6hVubLSXd82wdI80eZwTWhdOfN67NlA+D+oipAs1MlcTcu3KA=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], @@ -660,9 +662,9 @@ "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], - "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], + "vite": ["vite@7.1.8", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], diff --git a/package.json b/package.json index c795a76..f6880eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@effect-fc/monorepo", - "packageManager": "bun@1.2.20", + "packageManager": "bun@1.2.23", "private": true, "workspaces": [ "./packages/*" @@ -15,11 +15,11 @@ "clean:modules": "turbo clean:modules && rm -rf node_modules" }, "devDependencies": { - "@biomejs/biome": "^2.2.4", + "@biomejs/biome": "^2.2.5", "@effect/language-service": "^0.41.1", - "npm-check-updates": "^18.0.2", + "npm-check-updates": "^19.0.0", "npm-sort": "^0.0.4", - "turbo": "^2.5.6", - "typescript": "^5.9.2" + "turbo": "^2.5.8", + "typescript": "^5.9.3" } } diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index bc2b1a1..e5f9d33 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -1,7 +1,7 @@ { "name": "effect-fc", "description": "Write React function components with Effect", - "version": "0.1.3", + "version": "0.1.4", "type": "module", "files": [ "./README.md", diff --git a/packages/example/package.json b/packages/example/package.json index 8eb577a..3d488b1 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -13,32 +13,32 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@tanstack/react-router": "^1.131.27", - "@tanstack/react-router-devtools": "^1.131.27", - "@tanstack/router-plugin": "^1.131.27", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", - "@vitejs/plugin-react": "^5.0.1", - "globals": "^16.3.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", - "type-fest": "^4.41.0", - "vite": "^7.1.3" + "@tanstack/react-router": "^1.132.31", + "@tanstack/react-router-devtools": "^1.132.31", + "@tanstack/router-plugin": "^1.132.31", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.4", + "globals": "^16.4.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "type-fest": "^5.0.1", + "vite": "^7.1.8" }, "dependencies": { - "@effect/platform": "^0.90.6", - "@effect/platform-browser": "^0.70.0", + "@effect/platform": "^0.92.1", + "@effect/platform-browser": "^0.72.0", "@radix-ui/themes": "^3.2.1", "@typed/async-data": "^0.13.1", "@typed/id": "^0.17.2", "@typed/lazy-ref": "^0.3.3", - "effect": "^3.17.9", + "effect": "^3.18.1", "effect-fc": "workspace:*", "react-icons": "^5.5.0" }, "overrides": { - "@types/react": "^19.1.11", - "effect": "^3.17.9", - "react": "^19.1.1" + "@types/react": "^19.2.0", + "effect": "^3.18.1", + "react": "^19.2.0" } } -- 2.49.1 From fa1edf1ef24ff31ce2399585031d9183c16d63a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 18:13:21 +0200 Subject: [PATCH 66/67] Fix README --- packages/effect-fc/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc/README.md b/packages/effect-fc/README.md index 1ecb7aa..c9c65c2 100644 --- a/packages/effect-fc/README.md +++ b/packages/effect-fc/README.md @@ -16,14 +16,14 @@ Documentation is currently being written. In the meantime, you can take a look a ## What writing components looks like ```typescript import { Component } from "effect-fc" -import { useOnce, useSubscribe } from "effect-fc/hooks" +import { useOnce, useSubscribables } from "effect-fc/Hooks" import { Todo } from "./Todo" import { TodosState } from "./TodosState.service" -export class Todos extends Component.makeUntraced(function* Todos() { +export class Todos extends Component.makeUntraced("Todos")(function*() { const state = yield* TodosState - const [todos] = yield* useSubscribe(state.ref) + const [todos] = yield* useSubscribables(state.ref) yield* useOnce(() => Effect.andThen( Console.log("Todos mounted"), @@ -49,7 +49,7 @@ export class Todos extends Component.makeUntraced(function* Todos() { const TodosStateLive = TodosState.Default("todos") -const Index = Component.makeUntraced(function* Index() { +const Index = Component.makeUntraced("Index")(function*() { const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" }) const TodosFC = yield* Effect.provide(Todos, context) -- 2.49.1 From d1253c4e5b4a2b96e1cad2e7baeb9667d52a39b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 18:17:02 +0200 Subject: [PATCH 67/67] Fix --- turbo.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/turbo.json b/turbo.json index 55b67b2..75a43f8 100644 --- a/turbo.json +++ b/turbo.json @@ -13,6 +13,10 @@ "inputs": ["./src/**"], "outputs": ["./dist/**"] }, + "pack": { + "dependsOn": ["^pack"], + "cache": false + }, "clean:cache": { "cache": false }, -- 2.49.1