@ilha/store 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -15
- package/dist/form.d.ts +102 -0
- package/dist/form.js +117 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A zustand-shaped reactive store for [Ilha](https://github.com/ilhajs/ilha) islands. Backed by [alien-signals](https://github.com/stackblitz/alien-signals) — the same engine that powers `ilha` core state — for shared global state that lives outside any single island.
|
|
4
4
|
|
|
5
|
+
Includes a `/form` subpath with unopinionated, type-safe form helpers built on [Standard Schema](https://standardschema.dev) — works with Zod, Valibot, ArkType, or any compatible library.
|
|
6
|
+
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## Installation
|
|
@@ -12,6 +14,19 @@ bun add @ilha/store
|
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
17
|
+
## When to Use
|
|
18
|
+
|
|
19
|
+
`ilha` state is **island-local** — signals are scoped to a single component instance. Use `@ilha/store` when you need state that is:
|
|
20
|
+
|
|
21
|
+
- **Shared across multiple islands** — e.g. a cart, auth session, or theme
|
|
22
|
+
- **Updated from outside an island** — e.g. from a WebSocket handler or a global event bus
|
|
23
|
+
- **Persisted or derived globally** — e.g. synced to `localStorage` via a `subscribe` listener
|
|
24
|
+
- **Form state** — pair `createStore` with `@ilha/store/form` helpers for typed validation, error mapping, and submission handling
|
|
25
|
+
|
|
26
|
+
For state that only one island reads and writes, prefer `ilha`'s built-in `.state()`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
15
30
|
## Quick Start
|
|
16
31
|
|
|
17
32
|
```ts
|
|
@@ -25,18 +40,6 @@ store.getState(); // → { count: 1 }
|
|
|
25
40
|
|
|
26
41
|
---
|
|
27
42
|
|
|
28
|
-
## When to Use
|
|
29
|
-
|
|
30
|
-
`ilha` state is **island-local** — signals are scoped to a single component instance. Use `@ilha/store` when you need state that is:
|
|
31
|
-
|
|
32
|
-
- **Shared across multiple islands** — e.g. a cart, auth session, or theme
|
|
33
|
-
- **Updated from outside an island** — e.g. from a WebSocket handler or a global event bus
|
|
34
|
-
- **Persisted or derived globally** — e.g. synced to `localStorage` via a `subscribe` listener
|
|
35
|
-
|
|
36
|
-
For state that only one island reads and writes, prefer `ilha`'s built-in `.state()`.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
43
|
## API
|
|
41
44
|
|
|
42
45
|
### `createStore(initialState, actions?)`
|
|
@@ -132,7 +135,7 @@ const unsub = store.subscribe(
|
|
|
132
135
|
Reactively renders a store-driven HTML string into a DOM element whenever state changes. The render function may return a plain string or an `html\`\`` tagged template.
|
|
133
136
|
|
|
134
137
|
```ts
|
|
135
|
-
import { html } from "
|
|
138
|
+
import { html } from "ilha";
|
|
136
139
|
|
|
137
140
|
const unsub = store.bind(
|
|
138
141
|
document.getElementById("counter")!,
|
|
@@ -161,8 +164,8 @@ store.bind(
|
|
|
161
164
|
The most common pattern is reading the store inside an island's `.effect()` and calling `store.subscribe()` to drive reactive re-renders:
|
|
162
165
|
|
|
163
166
|
```ts
|
|
164
|
-
import { createStore
|
|
165
|
-
import ilha from "ilha";
|
|
167
|
+
import { createStore } from "@ilha/store";
|
|
168
|
+
import ilha, { html } from "ilha";
|
|
166
169
|
|
|
167
170
|
export const cartStore = createStore({ items: [] as string[] }, (set, get) => ({
|
|
168
171
|
add(item: string) {
|
|
@@ -192,6 +195,116 @@ export const CartIsland = ilha
|
|
|
192
195
|
|
|
193
196
|
---
|
|
194
197
|
|
|
198
|
+
## Forms — `@ilha/store/form`
|
|
199
|
+
|
|
200
|
+
Three small helpers for building typed, validated forms with any [Standard Schema](https://standardschema.dev)-compatible library. They are **unopinionated** — you compose them with `createStore` however you like; nothing is imposed about your form's state shape.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { extractFormData, validateWithSchema, issuesToErrors } from "@ilha/store/form";
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `extractFormData(source)`
|
|
207
|
+
|
|
208
|
+
Turns an `HTMLFormElement` (or a `FormData` instance) into a plain object. Handles the `string` vs `string[]` dance correctly: single fields stay scalar, repeated keys (checkbox groups, multi-selects) collapse to arrays. File inputs pass through as `File` values.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const data = extractFormData(event.target as HTMLFormElement);
|
|
212
|
+
// → { email: "ada@example.com", role: ["admin", "editor"] }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### `validateWithSchema(schema, data)`
|
|
216
|
+
|
|
217
|
+
Runs a Standard Schema synchronously and returns a discriminated union — **never throws**.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const result = validateWithSchema(SignInSchema, data);
|
|
221
|
+
if (result.ok) {
|
|
222
|
+
result.data; // ← fully typed schema output
|
|
223
|
+
} else {
|
|
224
|
+
result.issues; // ← ReadonlyArray<StandardSchemaV1.Issue>
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
If your schema has async refinements (e.g. server-side uniqueness checks), use `validateWithSchemaAsync` instead — same return shape, always returns a `Promise`.
|
|
229
|
+
|
|
230
|
+
### `issuesToErrors(issues)`
|
|
231
|
+
|
|
232
|
+
Flattens Standard Schema issues into a per-field error map keyed by dot-separated path. Form-level errors (issues with no path) land under the `""` key.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
issuesToErrors([
|
|
236
|
+
{ message: "Required", path: ["email"] },
|
|
237
|
+
{ message: "Invalid", path: ["user", "email"] },
|
|
238
|
+
]);
|
|
239
|
+
// → { email: ["Required"], "user.email": ["Invalid"] }
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### Full example — contact form
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { createStore } from "@ilha/store";
|
|
248
|
+
import { extractFormData, validateWithSchema, issuesToErrors } from "@ilha/store/form";
|
|
249
|
+
import type { FormErrors } from "@ilha/store/form";
|
|
250
|
+
import ilha, { html } from "ilha";
|
|
251
|
+
import { z } from "zod";
|
|
252
|
+
|
|
253
|
+
const ContactSchema = z.object({
|
|
254
|
+
name: z.string().min(1, "Name is required"),
|
|
255
|
+
email: z.email("Invalid email"),
|
|
256
|
+
message: z.string().min(10, "Message must be at least 10 characters"),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const formStore = createStore({ errors: {} as FormErrors }, (set) => ({
|
|
260
|
+
submit(event: SubmitEvent) {
|
|
261
|
+
const result = validateWithSchema(
|
|
262
|
+
ContactSchema,
|
|
263
|
+
extractFormData(event.target as HTMLFormElement),
|
|
264
|
+
);
|
|
265
|
+
if (result.ok) {
|
|
266
|
+
console.log("submitting:", result.data);
|
|
267
|
+
set({ errors: {} });
|
|
268
|
+
} else {
|
|
269
|
+
set({ errors: issuesToErrors(result.issues) });
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
}));
|
|
273
|
+
|
|
274
|
+
export default ilha
|
|
275
|
+
.on("form@submit", ({ event }) => {
|
|
276
|
+
event.preventDefault();
|
|
277
|
+
formStore.getState().submit(event);
|
|
278
|
+
})
|
|
279
|
+
.render(() => {
|
|
280
|
+
const errors = formStore.getState().errors;
|
|
281
|
+
return html`
|
|
282
|
+
<form>
|
|
283
|
+
<label>
|
|
284
|
+
Name
|
|
285
|
+
<input name="name" />
|
|
286
|
+
${errors.name ? html`<p role="alert">${errors.name.join(", ")}</p>` : ""}
|
|
287
|
+
</label>
|
|
288
|
+
<label>
|
|
289
|
+
Email
|
|
290
|
+
<input name="email" type="email" />
|
|
291
|
+
${errors.email ? html`<p role="alert">${errors.email.join(", ")}</p>` : ""}
|
|
292
|
+
</label>
|
|
293
|
+
<label>
|
|
294
|
+
Message
|
|
295
|
+
<textarea name="message"></textarea>
|
|
296
|
+
${errors.message ? html`<p role="alert">${errors.message.join(", ")}</p>` : ""}
|
|
297
|
+
</label>
|
|
298
|
+
<button type="submit">Send</button>
|
|
299
|
+
</form>
|
|
300
|
+
`;
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The store holds errors, the schema drives types, and `extractFormData` + `validateWithSchema` + `issuesToErrors` form a straight pipeline from DOM to error state.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
195
308
|
## TypeScript
|
|
196
309
|
|
|
197
310
|
Key exported types:
|
|
@@ -206,6 +319,12 @@ import type {
|
|
|
206
319
|
RenderResult, // string | RawHtml
|
|
207
320
|
Unsub, // () => void
|
|
208
321
|
} from "@ilha/store";
|
|
322
|
+
|
|
323
|
+
import type {
|
|
324
|
+
StandardSchemaV1, // the Standard Schema spec interface
|
|
325
|
+
FormResult, // discriminated union: { ok: true, data } | { ok: false, issues }
|
|
326
|
+
FormErrors, // Record<string, string[]>
|
|
327
|
+
} from "@ilha/store/form";
|
|
209
328
|
```
|
|
210
329
|
|
|
211
330
|
---
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/form.d.ts
|
|
2
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
3
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
4
|
+
}
|
|
5
|
+
declare namespace StandardSchemaV1 {
|
|
6
|
+
interface Props<Input = unknown, Output = Input> {
|
|
7
|
+
readonly version: 1;
|
|
8
|
+
readonly vendor: string;
|
|
9
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
10
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
11
|
+
}
|
|
12
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
13
|
+
interface SuccessResult<Output> {
|
|
14
|
+
readonly value: Output;
|
|
15
|
+
readonly issues?: undefined;
|
|
16
|
+
}
|
|
17
|
+
interface FailureResult {
|
|
18
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
19
|
+
}
|
|
20
|
+
interface Issue {
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
23
|
+
}
|
|
24
|
+
interface PathSegment {
|
|
25
|
+
readonly key: PropertyKey;
|
|
26
|
+
}
|
|
27
|
+
interface Types<Input, Output> {
|
|
28
|
+
readonly input: Input;
|
|
29
|
+
readonly output: Output;
|
|
30
|
+
}
|
|
31
|
+
type InferInput<S extends StandardSchemaV1> = NonNullable<S["~standard"]["types"]>["input"];
|
|
32
|
+
type InferOutput<S extends StandardSchemaV1> = NonNullable<S["~standard"]["types"]>["output"];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Discriminated union result from validating form values.
|
|
36
|
+
* Never throws — validation failures are returned as data.
|
|
37
|
+
*/
|
|
38
|
+
type FormResult<T> = {
|
|
39
|
+
ok: true;
|
|
40
|
+
data: T;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
issues: ReadonlyArray<StandardSchemaV1.Issue>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Per-field error map. Keys are dot-separated field paths matching the
|
|
47
|
+
* schema's issue path (e.g. `"user.email"`). Values are arrays of messages
|
|
48
|
+
* so multiple failing rules on a single field are all surfaced.
|
|
49
|
+
*/
|
|
50
|
+
type FormErrors = Record<string, string[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Extract a plain object from a `<form>` element or `FormData` instance.
|
|
53
|
+
* Duplicate keys (checkbox groups, `<select multiple>`) collapse to a
|
|
54
|
+
* `FormDataEntryValue[]`; single keys stay as `FormDataEntryValue`. File
|
|
55
|
+
* inputs are preserved as `File` values — pass them straight through to
|
|
56
|
+
* your schema.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ilha.on("form@submit", ({ event }) => {
|
|
60
|
+
* event.preventDefault();
|
|
61
|
+
* const data = extractFormData(event.target as HTMLFormElement);
|
|
62
|
+
* const result = validateWithSchema(SignInSchema, data);
|
|
63
|
+
* // ...
|
|
64
|
+
* });
|
|
65
|
+
*/
|
|
66
|
+
declare function extractFormData(source: HTMLFormElement | FormData): Record<string, unknown>;
|
|
67
|
+
/**
|
|
68
|
+
* Run a Standard Schema synchronously and return a discriminated union.
|
|
69
|
+
* Never throws. If the schema returns a Promise (i.e. it has async refinements),
|
|
70
|
+
* a warning is logged and a failure result is returned — pair it with an
|
|
71
|
+
* async schema by awaiting `schema["~standard"].validate(...)` directly instead.
|
|
72
|
+
*
|
|
73
|
+
* Compatible with Zod, Valibot, ArkType, and any other Standard Schema library.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const result = validateWithSchema(SignInSchema, extractFormData(form));
|
|
77
|
+
* if (result.ok) {
|
|
78
|
+
* formStore.setState({ data: result.data, errors: {} });
|
|
79
|
+
* } else {
|
|
80
|
+
* formStore.setState({ errors: issuesToErrors(result.issues) });
|
|
81
|
+
* }
|
|
82
|
+
*/
|
|
83
|
+
declare function validateWithSchema<S extends StandardSchemaV1>(schema: S, data: unknown): FormResult<StandardSchemaV1.InferOutput<S>>;
|
|
84
|
+
/**
|
|
85
|
+
* Async variant of {@link validateWithSchema}. Always returns a Promise,
|
|
86
|
+
* supports both sync and async schemas. Use this when your schema has
|
|
87
|
+
* async refinements (e.g. uniqueness checks against a server).
|
|
88
|
+
*/
|
|
89
|
+
declare function validateWithSchemaAsync<S extends StandardSchemaV1>(schema: S, data: unknown): Promise<FormResult<StandardSchemaV1.InferOutput<S>>>;
|
|
90
|
+
/**
|
|
91
|
+
* Flatten Standard Schema issues into a per-field error map keyed by
|
|
92
|
+
* dot-separated path. Issues without a path are grouped under the empty
|
|
93
|
+
* string key — useful for form-level errors.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* // issues: [{ message: "Invalid email", path: ["email"] }]
|
|
97
|
+
* issuesToErrors(issues);
|
|
98
|
+
* // => { email: ["Invalid email"] }
|
|
99
|
+
*/
|
|
100
|
+
declare function issuesToErrors(issues: ReadonlyArray<StandardSchemaV1.Issue>): FormErrors;
|
|
101
|
+
//#endregion
|
|
102
|
+
export { FormErrors, FormResult, StandardSchemaV1, extractFormData, issuesToErrors, validateWithSchema, validateWithSchemaAsync };
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//#region src/form.ts
|
|
2
|
+
/**
|
|
3
|
+
* Extract a plain object from a `<form>` element or `FormData` instance.
|
|
4
|
+
* Duplicate keys (checkbox groups, `<select multiple>`) collapse to a
|
|
5
|
+
* `FormDataEntryValue[]`; single keys stay as `FormDataEntryValue`. File
|
|
6
|
+
* inputs are preserved as `File` values — pass them straight through to
|
|
7
|
+
* your schema.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ilha.on("form@submit", ({ event }) => {
|
|
11
|
+
* event.preventDefault();
|
|
12
|
+
* const data = extractFormData(event.target as HTMLFormElement);
|
|
13
|
+
* const result = validateWithSchema(SignInSchema, data);
|
|
14
|
+
* // ...
|
|
15
|
+
* });
|
|
16
|
+
*/
|
|
17
|
+
function extractFormData(source) {
|
|
18
|
+
const data = source instanceof FormData ? source : new FormData(source);
|
|
19
|
+
const result = Object.create(null);
|
|
20
|
+
for (const key of new Set(data.keys())) {
|
|
21
|
+
const values = data.getAll(key);
|
|
22
|
+
result[key] = values.length === 1 ? values[0] : values;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Run a Standard Schema synchronously and return a discriminated union.
|
|
28
|
+
* Never throws. If the schema returns a Promise (i.e. it has async refinements),
|
|
29
|
+
* a warning is logged and a failure result is returned — pair it with an
|
|
30
|
+
* async schema by awaiting `schema["~standard"].validate(...)` directly instead.
|
|
31
|
+
*
|
|
32
|
+
* Compatible with Zod, Valibot, ArkType, and any other Standard Schema library.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const result = validateWithSchema(SignInSchema, extractFormData(form));
|
|
36
|
+
* if (result.ok) {
|
|
37
|
+
* formStore.setState({ data: result.data, errors: {} });
|
|
38
|
+
* } else {
|
|
39
|
+
* formStore.setState({ errors: issuesToErrors(result.issues) });
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
function validateWithSchema(schema, data) {
|
|
43
|
+
let result;
|
|
44
|
+
try {
|
|
45
|
+
result = schema["~standard"].validate(data);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : "Schema validation threw: " + String(error);
|
|
48
|
+
console.warn("[@ilha/store/form] Schema validation threw an exception:", message);
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
issues: [{ message }]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (result instanceof Promise) {
|
|
55
|
+
console.warn("[@ilha/store/form] Schema validation returned a Promise. validateWithSchema is synchronous — use validateWithSchemaAsync or call schema['~standard'].validate(...) directly for async schemas.");
|
|
56
|
+
result.catch(() => {});
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
issues: [{ message: "Async schema validation is not supported by validateWithSchema." }]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (result.issues !== void 0) return {
|
|
63
|
+
ok: false,
|
|
64
|
+
issues: result.issues
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
data: result.value
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Async variant of {@link validateWithSchema}. Always returns a Promise,
|
|
73
|
+
* supports both sync and async schemas. Use this when your schema has
|
|
74
|
+
* async refinements (e.g. uniqueness checks against a server).
|
|
75
|
+
*/
|
|
76
|
+
async function validateWithSchemaAsync(schema, data) {
|
|
77
|
+
let result;
|
|
78
|
+
try {
|
|
79
|
+
result = await schema["~standard"].validate(data);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : "Schema validation threw: " + String(error);
|
|
82
|
+
console.warn("[@ilha/store/form] Schema validation threw an exception:", message);
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
issues: [{ message }]
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (result.issues !== void 0) return {
|
|
89
|
+
ok: false,
|
|
90
|
+
issues: result.issues
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
data: result.value
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Flatten Standard Schema issues into a per-field error map keyed by
|
|
99
|
+
* dot-separated path. Issues without a path are grouped under the empty
|
|
100
|
+
* string key — useful for form-level errors.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // issues: [{ message: "Invalid email", path: ["email"] }]
|
|
104
|
+
* issuesToErrors(issues);
|
|
105
|
+
* // => { email: ["Invalid email"] }
|
|
106
|
+
*/
|
|
107
|
+
function issuesToErrors(issues) {
|
|
108
|
+
const errors = Object.create(null);
|
|
109
|
+
for (const issue of issues) {
|
|
110
|
+
const path = issue.path?.map((p) => typeof p === "object" ? String(p.key) : String(p)).join(".") ?? "";
|
|
111
|
+
if (!errors[path]) errors[path] = [];
|
|
112
|
+
errors[path].push(issue.message);
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { extractFormData, issuesToErrors, validateWithSchema, validateWithSchemaAsync };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ilha/store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Typed store.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryuz <ryuzer@proton.me>",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./form": {
|
|
21
|
+
"types": "./dist/form.d.ts",
|
|
22
|
+
"import": "./dist/form.js"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"scripts": {
|
|
@@ -25,7 +29,7 @@
|
|
|
25
29
|
},
|
|
26
30
|
"dependencies": {
|
|
27
31
|
"alien-signals": "3.1.2",
|
|
28
|
-
"ilha": "0.3.
|
|
32
|
+
"ilha": "0.3.1"
|
|
29
33
|
},
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"zod": "^4.3.6"
|