Compare commits

...

1 Commits

Author SHA1 Message Date
Corentin Thomasset
e2f0b83863 refactor(client): switched from ModularForms to Formisch lib 2025-07-31 00:15:47 +02:00
19 changed files with 258 additions and 254 deletions

View File

@@ -31,9 +31,9 @@
},
"dependencies": {
"@corentinth/chisels": "^1.3.1",
"@formisch/solid": "^0.2.0",
"@kobalte/core": "^0.13.10",
"@kobalte/utils": "^0.9.1",
"@modular-forms/solid": "^0.25.1",
"@pdfslick/solid": "^2.3.0",
"@solid-primitives/storage": "^4.3.2",
"@solidjs/router": "^0.14.10",

View File

@@ -1,5 +1,5 @@
import type { Component } from 'solid-js';
import { setValue } from '@modular-forms/solid';
import { setInput } from '@formisch/solid';
import { A } from '@solidjs/router';
import { createSignal, Show } from 'solid-js';
import * as v from 'valibot';
@@ -79,35 +79,35 @@ export const CreateApiKeyPage: Component = () => {
<Show when={!getToken()}>
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="name">{t('api-keys.create.form.name.label')}</TextFieldLabel>
<TextField type="text" id="name" placeholder={t('api-keys.create.form.name.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="text" id="name" placeholder={t('api-keys.create.form.name.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="permissions" type="string[]">
<Field path={['permissions']}>
{field => (
<div>
<p class="text-sm font-bold">{t('api-keys.create.form.permissions.label')}</p>
<div class="p-6 pb-8 border rounded-md mt-2">
<ApiKeyPermissionsPicker permissions={field.value ?? []} onChange={permissions => setValue(form, 'permissions', permissions)} />
<ApiKeyPermissionsPicker permissions={(field.input as string[]) ?? []} onChange={permissions => setInput(form, { path: ['permissions'], input: permissions })} />
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</div>
)}
</Field>
<div class="flex justify-end mt-6">
<Button type="submit" isLoading={form.submitting}>
<Button type="submit" isLoading={form.isSubmitting}>
{t('api-keys.create.form.submit')}
</Button>
</div>

View File

@@ -31,7 +31,7 @@ export const EmailLoginForm: Component = () => {
}
if (error) {
throw error;
throw new Error(error.message);
}
},
schema: v.object({
@@ -54,32 +54,32 @@ export const EmailLoginForm: Component = () => {
return (
<Form>
<Field name="email">
{(field, inputProps) => (
<Field path={['email']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="email">{t('auth.login.form.email.label')}</TextFieldLabel>
<TextField type="email" id="email" placeholder={t('auth.login.form.email.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="email" id="email" placeholder={t('auth.login.form.email.placeholder')} {...field.props} value={field.input} autoFocus aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="password">
{(field, inputProps) => (
<Field path={['password']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="password">{t('auth.login.form.password.label')}</TextFieldLabel>
<TextField type="password" id="password" placeholder={t('auth.login.form.password.placeholder')} {...inputProps} value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="password" id="password" placeholder={t('auth.login.form.password.placeholder')} {...field.props} value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="flex justify-between items-center mb-4">
<Field name="rememberMe" type="boolean">
{(field, inputProps) => (
<Checkbox class="flex items-center gap-2" defaultChecked={field.value}>
<CheckboxControl inputProps={inputProps} />
<Field path={['rememberMe']}>
{field => (
<Checkbox class="flex items-center gap-2" defaultChecked={field.input as boolean}>
<CheckboxControl inputProps={field.props} />
<CheckboxLabel class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{t('auth.login.form.remember-me.label')}
</CheckboxLabel>
@@ -94,9 +94,9 @@ export const EmailLoginForm: Component = () => {
</Show>
</div>
<Button type="submit" class="w-full">{t('auth.login.form.submit')}</Button>
<Button type="submit" class="w-full" isLoading={form.isSubmitting}>{t('auth.login.form.submit')}</Button>
<div class="text-red-500 text-sm mt-4">{form.response.message}</div>
<div class="text-red-500 text-sm mt-4">{form.errors?.[0]}</div>
</Form>
);

View File

@@ -30,7 +30,7 @@ export const EmailRegisterForm: Component = () => {
});
if (error) {
throw error;
throw new Error(error.message);
}
if (config.auth.isEmailVerificationRequired) {
@@ -63,41 +63,42 @@ export const EmailRegisterForm: Component = () => {
return (
<Form>
<Field name="email">
{(field, inputProps) => (
<Field path={['email']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="email">{t('auth.register.form.email.label')}</TextFieldLabel>
<TextField type="email" id="email" placeholder={t('auth.register.form.email.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="email" id="email" placeholder={t('auth.register.form.email.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="name">{t('auth.register.form.name.label')}</TextFieldLabel>
<TextField type="text" id="name" placeholder={t('auth.register.form.name.placeholder')} {...inputProps} value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="text" id="name" placeholder={t('auth.register.form.name.placeholder')} {...field.props} value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="password">
{(field, inputProps) => (
<Field path={['password']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="password">{t('auth.register.form.password.label')}</TextFieldLabel>
<TextField type="password" id="password" placeholder={t('auth.register.form.password.placeholder')} {...inputProps} value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="password" id="password" placeholder={t('auth.register.form.password.placeholder')} {...field.props} value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Button type="submit" class="w-full">{t('auth.register.form.submit')}</Button>
<div class="text-red-500 text-sm mt-4">{form.response.message}</div>
<Button type="submit" class="w-full" isLoading={form.isSubmitting}>
{t('auth.register.form.submit')}
</Button>
<div class="text-red-500 text-sm mt-4">{form.errors?.[0]}</div>
</Form>
);
};

View File

@@ -29,21 +29,21 @@ export const ResetPasswordForm: Component<{ onSubmit: (args: { email: string })
return (
<Form>
<Field name="email">
{(field, inputProps) => (
<Field path={['email']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="email">{t('auth.request-password-reset.form.email.label')}</TextFieldLabel>
<TextField type="email" id="email" placeholder={t('auth.request-password-reset.form.email.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="email" id="email" placeholder={t('auth.request-password-reset.form.email.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Button type="submit" class="w-full">
<Button type="submit" class="w-full" isLoading={form.isSubmitting}>
{t('auth.request-password-reset.form.submit')}
</Button>
<div class="text-red-500 text-sm mt-2">{form.response.message}</div>
<div class="text-red-500 text-sm mt-2">{form.errors?.[0]}</div>
</Form>
);

View File

@@ -27,21 +27,21 @@ export const ResetPasswordForm: Component<{ onSubmit: (args: { newPassword: stri
return (
<Form>
<Field name="newPassword">
{(field, inputProps) => (
<Field path={['newPassword']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="newPassword">{t('auth.reset-password.form.new-password.label')}</TextFieldLabel>
<TextField type="password" id="newPassword" placeholder={t('auth.reset-password.form.new-password.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="password" id="newPassword" placeholder={t('auth.reset-password.form.new-password.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Button type="submit" class="w-full">
<Button type="submit" class="w-full" isLoading={form.isSubmitting}>
{t('auth.reset-password.form.submit')}
</Button>
<div class="text-red-500 text-sm mt-2">{form.response.message}</div>
<div class="text-red-500 text-sm mt-2">{form.errors?.[0]}</div>
</Form>
);

View File

@@ -1,5 +1,5 @@
import type { Component, ParentComponent } from 'solid-js';
import { setValue } from '@modular-forms/solid';
import { setInput } from '@formisch/solid';
import { useMutation } from '@tanstack/solid-query';
import { createContext, createEffect, createSignal, useContext } from 'solid-js';
import * as v from 'valibot';
@@ -58,7 +58,7 @@ export const RenameDocumentDialog: Component<{
});
createEffect(() => {
setValue(form, 'name', getDocumentNameWithoutExtension({ name: props.documentName }));
setInput(form, { path: ['name'], input: getDocumentNameWithoutExtension({ name: props.documentName }) });
});
return (
@@ -69,21 +69,21 @@ export const RenameDocumentDialog: Component<{
</DialogHeader>
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot>
<TextFieldLabel class="sr-only" for="name">{t('documents.rename.form.name.label')}</TextFieldLabel>
<TextField {...inputProps} value={field.value} id="name" placeholder={t('documents.rename.form.name.placeholder')} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField {...field.props} value={field.input} id="name" placeholder={t('documents.rename.form.name.placeholder')} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="flex justify-end mt-4 gap-2">
<Button type="button" variant="secondary" onClick={() => props.setIsOpen(false)}>
<Button type="button" variant="secondary" onClick={() => props.setIsOpen(false)} disabled={form.isSubmitting}>
{t('documents.rename.cancel')}
</Button>
<Button type="submit">{t('documents.rename.form.submit')}</Button>
<Button type="submit" isLoading={form.isSubmitting}>{t('documents.rename.form.submit')}</Button>
</div>
</Form>
</DialogContent>

View File

@@ -102,21 +102,21 @@ const AllowedOriginsDialog: Component<{
</DialogHeader>
<Form>
<Field name="email">
{(field, inputProps) => (
<Field path={['email']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4 mt-4">
<TextFieldLabel for="email">{t('intake-emails.allowed-origins.add.label')}</TextFieldLabel>
<div class="flex items-center gap-2">
<TextField type="email" id="email" placeholder={t('intake-emails.allowed-origins.add.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
<Button type="submit">
<TextField type="email" id="email" placeholder={t('intake-emails.allowed-origins.add.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
<Button type="submit" isLoading={form.isSubmitting}>
<div class="i-tabler-plus size-4 mr-2" />
{t('intake-emails.allowed-origins.add.button')}
</Button>
</div>
<div class="text-red-500 text-sm mt-4">{form.response.message}</div>
{field.error && <div class="text-red-500 text-sm">{field.error }</div>}
<div class="text-red-500 text-sm mt-4">{form.errors?.[0]}</div>
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>

View File

@@ -37,23 +37,23 @@ export const CreateOrganizationForm: Component<{
return (
<div>
<Form>
<Field name="organizationName">
{(field, inputProps) => (
<Field path={['organizationName']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-6">
<TextFieldLabel for="organizationName">{t('organizations.create.form.name.label')}</TextFieldLabel>
<TextField type="text" id="organizationName" placeholder={t('organizations.create.form.name.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="text" id="organizationName" placeholder={t('organizations.create.form.name.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="flex justify-end">
<Button type="submit" isLoading={form.submitting} class="w-full">
<Button type="submit" isLoading={form.isSubmitting} class="w-full">
{t('organizations.create.form.submit')}
</Button>
</div>
<div class="text-red-500 text-sm mt-4">{form.response.message}</div>
<div class="text-red-500 text-sm mt-4">{form.errors?.[0]}</div>
</Form>
</div>
);

View File

@@ -1,5 +1,5 @@
import type { Component } from 'solid-js';
import { setValue } from '@modular-forms/solid';
import { setInput } from '@formisch/solid';
import { useNavigate, useParams } from '@solidjs/router';
import { useMutation } from '@tanstack/solid-query';
import { onMount, Show } from 'solid-js';
@@ -101,8 +101,8 @@ export const InviteMemberPage: Component = () => {
<div class="mt-10 max-w-xs mx-auto">
<Form>
<Field name="email">
{(field, inputProps) => (
<Field path={['email']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="email">
{t('organizations.invite-member.form.email.label')}
@@ -113,16 +113,16 @@ export const InviteMemberPage: Component = () => {
placeholder={t(
'organizations.invite-member.form.email.placeholder',
)}
{...inputProps}
{...field.props}
/>
{field.error && (
<div class="text-red-500 text-sm">{field.error}</div>
{field.errors && (
<div class="text-red-500 text-sm">{field.errors[0]}</div>
)}
</TextFieldRoot>
)}
</Field>
<Field name="role">
<Field path={['role']}>
{field => (
<div>
<label for="role" class="text-sm font-medium mb-1 block">
@@ -139,9 +139,9 @@ export const InviteMemberPage: Component = () => {
{tRole(props.item.rawValue)}
</SelectItem>
)}
value={field.value}
value={field.input as InvitableRole}
onChange={value =>
setValue(form, 'role', value as InvitableRole)}
setInput(form, { path: ['role'], input: value as InvitableRole })}
>
<SelectTrigger>
<SelectValue<string>>

View File

@@ -138,25 +138,25 @@ const UpdateOrganizationNameCard: Component<{ organization: Organization }> = (p
<Form>
<CardContent class="pt-6 ">
<Field name="organizationName">
{(field, inputProps) => (
<Field path={['organizationName']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1">
<TextFieldLabel for="organizationName" class="sr-only">
{t('organization.settings.name.title')}
</TextFieldLabel>
<div class="flex gap-2 flex-col sm:flex-row">
<TextField type="text" id="organizationName" placeholder={t('organization.settings.name.placeholder')} {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} />
<TextField type="text" id="organizationName" placeholder={t('organization.settings.name.placeholder')} {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} />
<Button type="submit" isLoading={form.submitting} class="flex-shrink-0" disabled={field.value?.trim() === props.organization.name}>
<Button type="submit" isLoading={form.isSubmitting} class="flex-shrink-0" disabled={(field.input as string)?.trim() === props.organization.name}>
{t('organization.settings.name.update')}
</Button>
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="text-red-500 text-sm">{form.response.message}</div>
<div class="text-red-500 text-sm">{form.errors?.[0]}</div>
</CardContent>
</Form>
</Card>

View File

@@ -1,15 +1,18 @@
import type { FormErrors, FormProps, PartialValues } from '@modular-forms/solid';
import type { FieldArrayProps, FieldProps, FormProps } from '@formisch/solid';
import type * as v from 'valibot';
import { createForm as createModularForm, FormError, valiForm } from '@modular-forms/solid';
import { createForm as createFormishForm, Field, FieldArray, Form } from '@formisch/solid';
import { createHook } from '../hooks/hooks';
// Extracted from the library to avoid type errors
type FormishDeepPartial<TValue> = TValue extends readonly unknown[] ? number extends TValue['length'] ? TValue : { [Key in keyof TValue]?: FormishDeepPartial<TValue[Key]> | undefined } : TValue extends Record<PropertyKey, unknown> ? { [Key in keyof TValue]?: FormishDeepPartial<TValue[Key]> | undefined } : TValue | undefined;
export function createForm<Schema extends v.ObjectSchema<any, any>>({
schema,
initialValues,
onSubmit,
}: {
schema: Schema;
initialValues?: PartialValues<v.InferInput<Schema>>;
initialValues?: FormishDeepPartial<v.InferInput<Schema>>;
onSubmit?: (values: v.InferInput<Schema>) => Promise<void>;
}) {
const submitHook = createHook<v.InferInput<Schema>>();
@@ -18,18 +21,18 @@ export function createForm<Schema extends v.ObjectSchema<any, any>>({
submitHook.on(onSubmit);
}
const [form, { Form, Field, FieldArray }] = createModularForm<v.InferInput<Schema>>({
validate: valiForm(schema),
initialValues,
const form = createFormishForm({
schema,
initialInput: initialValues,
});
return {
form,
Form: (props: Omit<FormProps<v.InferInput<Schema>, undefined>, 'of'>) => Form({ ...props, onSubmit: submitHook.trigger }),
Field,
FieldArray,
onSubmit: submitHook.on,
Form: (props: Omit<FormProps<Schema>, 'of' | 'onSubmit'>) => Form({ of: form, ...props, onSubmit: async (args) => {
await submitHook.trigger(args);
} }),
Field: (props: Omit<FieldProps<Schema>, 'of'>) => Field({ of: form, ...props }),
FieldArray: (props: Omit<FieldArrayProps<Schema>, 'of'>) => FieldArray({ of: form, ...props }),
submit: submitHook.trigger,
createFormError: ({ message, fields }: { message: string; fields?: FormErrors<v.InferInput<Schema>> }) => new FormError<v.InferInput<Schema>>(message, fields),
};
}

View File

@@ -1,6 +1,6 @@
import type { Component } from 'solid-js';
import type { TaggingRule, TaggingRuleForCreation } from '../tagging-rules.types';
import { insert, remove, setValue } from '@modular-forms/solid';
import { insert, remove, setInput } from '@formisch/solid';
import { A } from '@solidjs/router';
import { For, Show } from 'solid-js';
import * as v from 'valibot';
@@ -45,24 +45,26 @@ export const TaggingRuleForm: Component<{
}
}
props.onSubmit({ taggingRule: { name, conditions, tagIds, description } });
props.onSubmit({ taggingRule: { name, conditions, tagIds, description: description ?? '' } });
},
schema: v.object({
name: v.pipe(
v.string(),
v.string(t('tagging-rules.form.name.min-length')),
v.minLength(1, t('tagging-rules.form.name.min-length')),
v.maxLength(64, t('tagging-rules.form.name.max-length')),
),
description: v.pipe(
v.string(),
v.maxLength(256, t('tagging-rules.form.description.max-length')),
description: v.optional(
v.pipe(
v.string(),
v.maxLength(256, t('tagging-rules.form.description.max-length')),
),
),
conditions: v.optional(
v.array(v.object({
field: v.picklist(Object.values(TAGGING_RULE_FIELDS)),
operator: v.picklist(Object.values(TAGGING_RULE_OPERATORS)),
value: v.pipe(
v.string(),
v.string(t('tagging-rules.form.conditions.value.min-length')),
v.minLength(1, t('tagging-rules.form.conditions.value.min-length')),
),
})),
@@ -90,33 +92,33 @@ export const TaggingRuleForm: Component<{
return (
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1">
<TextFieldLabel for="name">{t('tagging-rules.form.name.label')}</TextFieldLabel>
<TextField
type="text"
id="name"
placeholder={t('tagging-rules.form.name.placeholder')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="description">
{(field, inputProps) => (
<Field path={['description']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mt-6">
<TextFieldLabel for="description">{t('tagging-rules.form.description.label')}</TextFieldLabel>
<TextArea
id="description"
placeholder={t('tagging-rules.form.description.placeholder')}
{...inputProps}
value={field.value}
{...field.props}
value={field.input}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
@@ -126,7 +128,7 @@ export const TaggingRuleForm: Component<{
<p class="mb-1 font-medium">{t('tagging-rules.form.conditions.label')}</p>
<p class="mb-2 text-sm text-muted-foreground">{t('tagging-rules.form.conditions.description')}</p>
<FieldArray name="conditions">
<FieldArray path={['conditions']}>
{fieldArray => (
<div>
<For each={fieldArray.items}>
@@ -134,12 +136,12 @@ export const TaggingRuleForm: Component<{
<div class="px-4 py-4 mb-1 flex gap-2 items-center bg-card border rounded-md">
<div>When</div>
<Field name={`conditions.${index()}.field`}>
<Field path={['conditions', index(), 'field']}>
{field => (
<Select
id="field"
defaultValue={field.value}
onChange={value => value && setValue(form, `conditions.${index()}.field`, value)}
defaultValue={field.input as string}
onChange={value => value && setInput(form, { path: ['conditions', index(), 'field'], input: value })}
options={Object.values(TAGGING_RULE_FIELDS)}
itemComponent={props => (
<SelectItem item={props.item}>{getFieldLabel(props.item.rawValue)}</SelectItem>
@@ -153,12 +155,12 @@ export const TaggingRuleForm: Component<{
)}
</Field>
<Field name={`conditions.${index()}.operator`}>
<Field path={['conditions', index(), 'operator']}>
{field => (
<Select
id="operator"
defaultValue={field.value}
onChange={value => value && setValue(form, `conditions.${index()}.operator`, value)}
defaultValue={field.input as string}
onChange={value => value && setInput(form, { path: ['conditions', index(), 'operator'], input: value })}
options={Object.values(TAGGING_RULE_OPERATORS)}
itemComponent={props => (
<SelectItem item={props.item}>{getOperatorLabel(props.item.rawValue)}</SelectItem>
@@ -172,36 +174,36 @@ export const TaggingRuleForm: Component<{
)}
</Field>
<Field name={`conditions.${index()}.value`}>
{(field, inputProps) => (
<Field path={['conditions', index(), 'value']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 flex-1">
<TextField
id="value"
{...inputProps}
value={field.value}
{...field.props}
value={field.input}
placeholder={t('tagging-rules.form.conditions.value.placeholder')}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Button variant="outline" size="icon" onClick={() => remove(form, 'conditions', { at: index() })}>
<Button variant="outline" size="icon" onClick={() => remove(form, { path: ['conditions'], at: index() })}>
<div class="i-tabler-x size-4"></div>
</Button>
</div>
)}
</For>
{fieldArray.error && <div class="text-red-500 text-sm">{fieldArray.error}</div>}
{fieldArray.errors && <div class="text-red-500 text-sm">{fieldArray.errors[0]}</div>}
</div>
)}
</FieldArray>
<Button
variant="outline"
onClick={() => insert(form, 'conditions', { value: { field: 'name', operator: 'contains', value: '' } })}
onClick={() => insert(form, { path: ['conditions'], input: { field: 'name', operator: 'contains', value: '' } })}
class="gap-2 mt-2"
>
<div class="i-tabler-plus size-4"></div>
@@ -213,7 +215,7 @@ export const TaggingRuleForm: Component<{
<p class="mb-1 font-medium">{t('tagging-rules.form.tags.label')}</p>
<p class="mb-2 text-sm text-muted-foreground">{t('tagging-rules.form.tags.description')}</p>
<Field name="tagIds" type="string[]">
<Field path={['tagIds']}>
{field => (
<>
<div class="flex gap-2 sm:items-center sm:flex-row flex-col">
@@ -221,8 +223,8 @@ export const TaggingRuleForm: Component<{
<DocumentTagPicker
organizationId={props.organizationId}
tagIds={field.value ?? []}
onTagsChange={({ tags }) => setValue(form, 'tagIds', tags.map(tag => tag.id))}
tagIds={(field.input as string[]) ?? []}
onTagsChange={({ tags }) => setInput(form, { path: ['tagIds'], input: tags.map(tag => tag.id) })}
/>
</div>
@@ -235,7 +237,7 @@ export const TaggingRuleForm: Component<{
)}
</CreateTagModal>
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</>
)}
</Field>

View File

@@ -2,7 +2,7 @@ import type { DialogTriggerProps } from '@kobalte/core/dialog';
import type { Component, JSX } from 'solid-js';
import type { Tag as TagType } from '../tags.types';
import { safely } from '@corentinth/chisels';
import { getValues, setValue } from '@modular-forms/solid';
import { getInput, setInput } from '@formisch/solid';
import { A, useParams } from '@solidjs/router';
import { useQuery } from '@tanstack/solid-query';
import { createSignal, For, Show, Suspense } from 'solid-js';
@@ -45,7 +45,7 @@ const TagColorPicker: Component<{
};
const TagForm: Component<{
onSubmit: (values: { name: string; color: string; description: string }) => Promise<void>;
onSubmit: (values: { name: string; color: string; description?: string }) => Promise<void>;
initialValues?: { name?: string; color?: string; description?: string | null };
submitLabel?: string;
}> = (props) => {
@@ -54,21 +54,23 @@ const TagForm: Component<{
onSubmit: props.onSubmit,
schema: v.object({
name: v.pipe(
v.string(),
v.string(t('tags.form.name.required')),
v.trim(),
v.nonEmpty(t('tags.form.name.required')),
v.maxLength(64, t('tags.form.name.max-length')),
),
color: v.pipe(
v.string(),
v.string(t('tags.form.color.required')),
v.trim(),
v.nonEmpty(t('tags.form.color.required')),
v.hexColor(t('tags.form.color.invalid')),
),
description: v.pipe(
v.string(),
v.trim(),
v.maxLength(256, t('tags.form.description.max-length')),
description: v.optional(
v.pipe(
v.string(),
v.trim(),
v.maxLength(256, t('tags.form.description.max-length')),
),
'',
),
}),
initialValues: {
@@ -77,39 +79,39 @@ const TagForm: Component<{
},
});
const getFormValues = () => getValues(form);
const getFormValues = () => getInput(form);
return (
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="name">{t('tags.form.name.label')}</TextFieldLabel>
<TextField type="text" id="name" {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} placeholder={t('tags.form.name.placeholder')} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextField type="text" id="name" {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} placeholder={t('tags.form.name.placeholder')} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="color">
<Field path={['color']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="color">{t('tags.form.color.label')}</TextFieldLabel>
<TagColorPicker color={field.value ?? ''} onChange={color => setValue(form, 'color', color)} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TagColorPicker color={(field.input as string) ?? ''} onChange={color => setInput(form, { path: ['color'], input: color })} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="description">
{(field, inputProps) => (
<Field path={['description']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1 mb-4">
<TextFieldLabel for="description">
{t('tags.form.description.label')}
<span class="font-normal ml-1 text-muted-foreground">{t('tags.form.description.optional')}</span>
</TextFieldLabel>
<TextArea id="description" {...inputProps} autoFocus value={field.value} aria-invalid={Boolean(field.error)} placeholder={t('tags.form.description.placeholder')} />
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
<TextArea id="description" {...field.props} autoFocus value={field.input} aria-invalid={Boolean(field.errors)} placeholder={t('tags.form.description.placeholder')} />
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
@@ -137,7 +139,7 @@ export const CreateTagModal: Component<{
const { t } = useI18n();
const { getErrorMessage } = useI18nApiErrors({ t });
const onSubmit = async ({ name, color, description }: { name: string; color: string; description: string }) => {
const onSubmit = async ({ name, color, description }: { name: string; color: string; description?: string }) => {
const [,error] = await safely(createTag({
name,
color: color.toLowerCase(),
@@ -188,7 +190,7 @@ const UpdateTagModal: Component<{
const [getIsModalOpen, setIsModalOpen] = createSignal(false);
const { t } = useI18n();
const onSubmit = async ({ name, color, description }: { name: string; color: string; description: string }) => {
const onSubmit = async ({ name, color, description }: { name: string; color: string; description?: string }) => {
await updateTag({
name,
color: color.toLowerCase(),

View File

@@ -14,7 +14,7 @@ export async function fetchTags({ organizationId }: { organizationId: string })
};
}
export async function createTag({ organizationId, name, color, description }: { organizationId: string; name: string; color: string; description: string }) {
export async function createTag({ organizationId, name, color, description = '' }: { organizationId: string; name: string; color: string; description?: string }) {
const { tag } = await apiClient<{ tag: AsDto<Tag> }>({
path: `/api/organizations/${organizationId}/tags`,
method: 'POST',
@@ -26,7 +26,7 @@ export async function createTag({ organizationId, name, color, description }: {
};
}
export async function updateTag({ organizationId, tagId, name, color, description }: { organizationId: string; tagId: string; name: string; color: string; description: string }) {
export async function updateTag({ organizationId, tagId, name, color, description = '' }: { organizationId: string; tagId: string; name: string; color: string; description?: string }) {
const { tag } = await apiClient<{ tag: AsDto<Tag> }>({
path: `/api/organizations/${organizationId}/tags/${tagId}`,
method: 'PUT',

View File

@@ -90,8 +90,8 @@ const UpdateFullNameCard: Component<{ name: string }> = (props) => {
<Form>
<CardContent class="pt-6">
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col gap-1">
<TextFieldLabel for="name" class="sr-only">
{t('user.settings.name.label')}
@@ -101,25 +101,25 @@ const UpdateFullNameCard: Component<{ name: string }> = (props) => {
type="text"
id="name"
placeholder={t('user.settings.name.placeholder')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
<Button
type="submit"
isLoading={form.submitting}
isLoading={form.isSubmitting}
class="flex-shrink-0"
disabled={field.value?.trim() === props.name}
disabled={(field.input as string)?.trim() === props.name}
>
{t('user.settings.name.update')}
</Button>
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="text-red-500 text-sm">{form.response.message}</div>
<div class="text-red-500 text-sm">{form.errors?.[0]}</div>
</CardContent>
</Form>
</Card>

View File

@@ -1,5 +1,6 @@
import type { Component } from 'solid-js';
import { setValue } from '@modular-forms/solid';
import type { WebhookEvent } from '../webhooks.types';
import { setInput } from '@formisch/solid';
import { A, useNavigate, useParams } from '@solidjs/router';
import * as v from 'valibot';
import { useI18n } from '@/modules/i18n/i18n.provider';
@@ -39,11 +40,11 @@ export const CreateWebhookPage: Component = () => {
},
schema: v.object({
name: v.pipe(
v.string(),
v.string(t('webhooks.create.form.name.required')),
v.nonEmpty(t('webhooks.create.form.name.required')),
),
url: v.pipe(
v.string(),
v.string(t('webhooks.create.form.url.required')),
v.nonEmpty(t('webhooks.create.form.url.required')),
v.url(t('webhooks.create.form.url.invalid')),
),
@@ -71,68 +72,68 @@ export const CreateWebhookPage: Component = () => {
</div>
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="name">{t('webhooks.create.form.name.label')}</TextFieldLabel>
<TextField
type="text"
id="name"
placeholder={t('webhooks.create.form.name.placeholder')}
{...inputProps}
{...field.props}
autoFocus
value={field.value}
aria-invalid={Boolean(field.error)}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="url">
{(field, inputProps) => (
<Field path={['url']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="url">{t('webhooks.create.form.url.label')}</TextFieldLabel>
<TextField
type="url"
id="url"
placeholder={t('webhooks.create.form.url.placeholder')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="secret">
{(field, inputProps) => (
<Field path={['secret']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="secret">{t('webhooks.create.form.secret.label')}</TextFieldLabel>
<TextField
type="password"
id="secret"
placeholder={t('webhooks.create.form.secret.placeholder')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="events" type="string[]">
<Field path={['events']}>
{field => (
<div>
<p class="text-sm font-bold">{t('webhooks.create.form.events.label')}</p>
<div class="p-6 pb-8 border rounded-md mt-2">
<WebhookEventsPicker events={field.value ?? []} onChange={events => setValue(form, 'events', events)} />
<WebhookEventsPicker events={(field.input as WebhookEvent[]) ?? []} onChange={events => setInput(form, { path: ['events'], input: events })} />
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</div>
)}
</Field>
@@ -141,10 +142,12 @@ export const CreateWebhookPage: Component = () => {
<Button type="button" variant="secondary" as={A} href={`/organizations/${params.organizationId}/settings/webhooks`}>
{t('webhooks.create.back')}
</Button>
<Button type="submit" class="ml-2" isLoading={form.submitting}>
<Button type="submit" class="ml-2" isLoading={form.isSubmitting}>
{t('webhooks.create.form.submit')}
</Button>
</div>
<div class="text-red-500 text-sm">{form.errors?.[0]}</div>
</Form>
</div>
);

View File

@@ -1,6 +1,6 @@
import type { Component } from 'solid-js';
import type { Webhook } from '../webhooks.types';
import { setValue } from '@modular-forms/solid';
import type { Webhook, WebhookEvent } from '../webhooks.types';
import { setInput } from '@formisch/solid';
import { A, useNavigate, useParams } from '@solidjs/router';
import { useQuery } from '@tanstack/solid-query';
import { createSignal, Show, Suspense } from 'solid-js';
@@ -53,11 +53,11 @@ export const EditWebhookForm: Component<{ webhook: Webhook }> = (props) => {
},
schema: v.object({
name: v.pipe(
v.string(),
v.string(t('webhooks.create.form.name.required')),
v.nonEmpty(t('webhooks.create.form.name.required')),
),
url: v.pipe(
v.string(),
v.string(t('webhooks.create.form.url.required')),
v.nonEmpty(t('webhooks.create.form.url.required')),
v.url(t('webhooks.create.form.url.invalid')),
),
@@ -79,44 +79,44 @@ export const EditWebhookForm: Component<{ webhook: Webhook }> = (props) => {
return (
<Form>
<Field name="name">
{(field, inputProps) => (
<Field path={['name']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="name">{t('webhooks.create.form.name.label')}</TextFieldLabel>
<TextField
type="text"
id="name"
placeholder={t('webhooks.create.form.name.placeholder')}
{...inputProps}
{...field.props}
autoFocus
value={field.value}
aria-invalid={Boolean(field.error)}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<Field name="url">
{(field, inputProps) => (
<Field path={['url']}>
{field => (
<TextFieldRoot class="flex flex-col mb-6">
<TextFieldLabel for="url">{t('webhooks.create.form.url.label')}</TextFieldLabel>
<TextField
type="url"
id="url"
placeholder={t('webhooks.create.form.url.placeholder')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
/>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
<div class="mb-6">
<Field name="secret">
{(field, inputProps) => (
<Field path={['secret']}>
{field => (
<TextFieldRoot class="flex flex-col mt-4">
<TextFieldLabel for="secret">{t('webhooks.create.form.secret.label')}</TextFieldLabel>
<div class="flex items-center gap-2">
@@ -124,9 +124,9 @@ export const EditWebhookForm: Component<{ webhook: Webhook }> = (props) => {
type="password"
id="secret"
placeholder={rotateSecret() ? t('webhooks.update.form.secret.placeholder') : t('webhooks.update.form.secret.placeholder-redacted')}
{...inputProps}
value={field.value}
aria-invalid={Boolean(field.error)}
{...field.props}
value={field.input}
aria-invalid={Boolean(field.errors)}
disabled={!rotateSecret()}
/>
<Show when={!rotateSecret()}>
@@ -135,22 +135,22 @@ export const EditWebhookForm: Component<{ webhook: Webhook }> = (props) => {
</Button>
</Show>
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</TextFieldRoot>
)}
</Field>
</div>
<Field name="events" type="string[]">
<Field path={['events']}>
{field => (
<div>
<p class="text-sm font-bold">{t('webhooks.create.form.events.label')}</p>
<div class="p-6 pb-8 border rounded-md mt-2">
<WebhookEventsPicker events={field.value ?? []} onChange={events => setValue(form, 'events', events)} />
<WebhookEventsPicker events={(field.input as WebhookEvent[]) ?? []} onChange={events => setInput(form, { path: ['events'], input: events })} />
</div>
{field.error && <div class="text-red-500 text-sm">{field.error}</div>}
{field.errors && <div class="text-red-500 text-sm">{field.errors[0]}</div>}
</div>
)}
</Field>
@@ -159,7 +159,7 @@ export const EditWebhookForm: Component<{ webhook: Webhook }> = (props) => {
<Button type="button" variant="secondary" as={A} href={`/organizations/${params.organizationId}/settings/webhooks`}>
{t('webhooks.update.cancel')}
</Button>
<Button type="submit" class="ml-2" isLoading={form.submitting}>
<Button type="submit" class="ml-2" isLoading={form.isSubmitting}>
{t('webhooks.update.submit')}
</Button>
</div>

59
pnpm-lock.yaml generated
View File

@@ -12,9 +12,6 @@ catalogs:
'@types/node':
specifier: ^22.15.21
version: 22.16.0
'@vitest/coverage-v8':
specifier: ^3.0.2
version: 3.2.4
better-auth:
specifier: ^1.2.8
version: 1.2.8
@@ -24,9 +21,6 @@ catalogs:
typescript:
specifier: ^5.6.2
version: 5.8.3
unbuild:
specifier: ^3.5.0
version: 3.5.0
vitest:
specifier: ^3.0.5
version: 3.2.4
@@ -117,15 +111,15 @@ importers:
'@corentinth/chisels':
specifier: ^1.3.1
version: 1.3.1
'@formisch/solid':
specifier: ^0.2.0
version: 0.2.0(solid-js@1.9.7)(typescript@5.8.3)(valibot@1.0.0-beta.10(typescript@5.8.3))
'@kobalte/core':
specifier: ^0.13.10
version: 0.13.10(solid-js@1.9.7)
'@kobalte/utils':
specifier: ^0.9.1
version: 0.9.1(solid-js@1.9.7)
'@modular-forms/solid':
specifier: ^0.25.1
version: 0.25.1(solid-js@1.9.7)(typescript@5.8.3)
'@pdfslick/solid':
specifier: ^2.3.0
version: 2.3.0(react@18.3.1)(solid-js@1.9.7)(use-sync-external-store@1.2.2(react@18.3.1))
@@ -2233,6 +2227,16 @@ packages:
'@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
'@formisch/solid@0.2.0':
resolution: {integrity: sha512-9prUUijdbIO69sYA3noyXagSTD0TInF0A3KFbWQLmG9jzOnVD2P8TBghyUE1eapA/bfxMRT/hBJ3wOys+vHcQQ==}
peerDependencies:
solid-js: ^1.6.0
typescript: '>=5'
valibot: ^1.0.0
peerDependenciesMeta:
typescript:
optional: true
'@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
@@ -2517,11 +2521,6 @@ packages:
'@mdx-js/mdx@3.1.0':
resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==}
'@modular-forms/solid@0.25.1':
resolution: {integrity: sha512-issNZ3xl4tj+1K7KT4dNTQaRq5SmVUXgUPeGTMjtrAzCeTnwM/u6vUxSuTY2bcMw4GzTxreFXLx1xMMhrFkt0A==}
peerDependencies:
solid-js: ^1.3.1
'@napi-rs/canvas-android-arm64@0.1.68':
resolution: {integrity: sha512-h1KcSR4LKLfRfzeBH65xMxbWOGa1OtMFQbCMVlxPCkN1Zr+2gK+70pXO5ktojIYcUrP6KDcOwoc8clho5ccM/w==}
engines: {node: '>= 10'}
@@ -10159,12 +10158,6 @@ snapshots:
eslint: 9.27.0(jiti@2.4.2)
eslint-visitor-keys: 3.4.3
'@eslint-community/eslint-utils@4.5.1(eslint@9.30.1(jiti@2.4.2))':
dependencies:
eslint: 9.30.1(jiti@2.4.2)
eslint-visitor-keys: 3.4.3
optional: true
'@eslint-community/eslint-utils@4.7.0(eslint@9.27.0(jiti@2.4.2))':
dependencies:
eslint: 9.27.0(jiti@2.4.2)
@@ -10310,6 +10303,13 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
'@formisch/solid@0.2.0(solid-js@1.9.7)(typescript@5.8.3)(valibot@1.0.0-beta.10(typescript@5.8.3))':
dependencies:
solid-js: 1.9.7
valibot: 1.0.0-beta.10(typescript@5.8.3)
optionalDependencies:
typescript: 5.8.3
'@hexagon/base64@1.1.28': {}
'@hono/node-server@1.14.4(hono@4.8.2)':
@@ -10638,13 +10638,6 @@ snapshots:
- acorn
- supports-color
'@modular-forms/solid@0.25.1(solid-js@1.9.7)(typescript@5.8.3)':
dependencies:
solid-js: 1.9.7
valibot: 1.0.0-beta.10(typescript@5.8.3)
transitivePeerDependencies:
- typescript
'@napi-rs/canvas-android-arm64@0.1.68':
optional: true
@@ -13499,15 +13492,15 @@ snapshots:
eslint-plugin-astro@1.3.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3):
dependencies:
'@eslint-community/eslint-utils': 4.5.1(eslint@9.30.1(jiti@2.4.2))
'@jridgewell/sourcemap-codec': 1.5.0
'@typescript-eslint/types': 8.26.1
'@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2))
'@jridgewell/sourcemap-codec': 1.5.4
'@typescript-eslint/types': 8.35.1
astro-eslint-parser: 1.1.0(typescript@5.8.3)
eslint: 9.30.1(jiti@2.4.2)
eslint-compat-utils: 0.6.4(eslint@9.30.1(jiti@2.4.2))
eslint-compat-utils: 0.6.5(eslint@9.30.1(jiti@2.4.2))
globals: 15.15.0
postcss: 8.5.3
postcss-selector-parser: 7.0.0
postcss: 8.5.6
postcss-selector-parser: 7.1.0
transitivePeerDependencies:
- supports-color
- typescript