@shivaedev/form 0.10.0 → 0.12.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/CHANGELOG.md +29 -0
- package/README.md +62 -22
- package/dist/adapters/browserDraftStorage.d.ts +4 -0
- package/dist/adapters/browserDraftStorage.d.ts.map +1 -0
- package/dist/adapters/browserDraftStorage.js +4 -0
- package/dist/adapters/browserDraftStorage.js.map +1 -0
- package/dist/adapters/draftPersistence.d.ts +7 -0
- package/dist/adapters/draftPersistence.d.ts.map +1 -0
- package/dist/adapters/draftPersistence.js +8 -0
- package/dist/adapters/draftPersistence.js.map +1 -0
- package/dist/adapters/persistence.d.ts +4 -0
- package/dist/adapters/persistence.d.ts.map +1 -0
- package/dist/adapters/persistence.js +84 -0
- package/dist/adapters/persistence.js.map +1 -0
- package/dist/{client → adapters}/react.d.ts +3 -6
- package/dist/adapters/react.d.ts.map +1 -0
- package/dist/{client → adapters}/react.js +1 -4
- package/dist/adapters/react.js.map +1 -0
- package/dist/checkFeedback.d.ts +6 -0
- package/dist/checkFeedback.d.ts.map +1 -0
- package/dist/checkFeedback.js +27 -0
- package/dist/checkFeedback.js.map +1 -0
- package/dist/draft.d.ts +1 -1
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +12 -2
- package/dist/draft.js.map +1 -1
- package/dist/fieldRejection.d.ts +22 -0
- package/dist/fieldRejection.d.ts.map +1 -0
- package/dist/fieldRejection.js +21 -0
- package/dist/fieldRejection.js.map +1 -0
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +20 -28
- package/dist/form.js.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +9 -18
- package/dist/messages.js.map +1 -1
- package/dist/persistence.d.ts +5 -2
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +2 -82
- package/dist/persistence.js.map +1 -1
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +1 -2
- package/dist/refs.js.map +1 -1
- package/dist/shape.d.ts +8 -1
- package/dist/shape.d.ts.map +1 -1
- package/dist/shape.js.map +1 -1
- package/dist/statusOf.d.ts.map +1 -1
- package/dist/statusOf.js +1 -2
- package/dist/statusOf.js.map +1 -1
- package/dist/validation.d.ts +7 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +11 -0
- package/dist/validation.js.map +1 -0
- package/package.json +7 -4
- package/src/adapters/browserDraftStorage.ts +4 -0
- package/src/adapters/draftPersistence.ts +8 -0
- package/src/adapters/persistence.ts +87 -0
- package/src/{client → adapters}/react.ts +5 -11
- package/src/checkFeedback.ts +34 -0
- package/src/draft.ts +12 -4
- package/src/fieldRejection.ts +47 -0
- package/src/form.ts +22 -42
- package/src/messages.ts +7 -16
- package/src/persistence.ts +3 -86
- package/src/refs.ts +3 -2
- package/src/shape.ts +9 -1
- package/src/statusOf.ts +1 -2
- package/src/validation.ts +18 -0
- package/dist/client/react.d.ts.map +0 -1
- package/dist/client/react.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0 - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Form save rejection mappings pass nested paths to the submitter, so feedback appears on the rejected value. Automatic recognition uses the shared `@shivaedev/errors` decoder.
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- Import `FieldRejection`, `RejectField`, `FieldRejectionMapping`, `fieldRejectionOf` and `rejecting` from `@shivaedev/form/fieldRejection.ts` instead of `@shivaedev/react/fieldRejection.ts`. The React module is removed. `FieldRejection.field` now accepts a form path tuple as well as a top-level name; consumers that read it must handle both. Return `{ field: ["profile", "name"] as const, message }` from `rejectField` to attach a save rejection to a nested value.
|
|
12
|
+
|
|
13
|
+
## 0.11.0 - 2026-11-02
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Read whole-form Schema refinement messages with `errorAt([])` after submission and with `Invalid.formMessage` on invalid submissions.
|
|
18
|
+
- Form and React submission configurations accept a `savedValues` mapping to normalize a successful submission against its captured baseline while retaining later field edits.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Hide previous asynchronous Schema messages on top-level fields while decoding changed input.
|
|
23
|
+
- Editors preserve newer received baselines when an earlier save completes, and merge normalized saved fields without erasing later edits.
|
|
24
|
+
- Create forms retain optional keys removed while a submission is in flight when resetting their inputs.
|
|
25
|
+
- Nested field refs read and write object properties and array elements without overflowing the stack.
|
|
26
|
+
- Keep a field's server rejection when it loses focus without a value change. Changing the rejected input still clears the server message.
|
|
27
|
+
|
|
28
|
+
### Breaking
|
|
29
|
+
|
|
30
|
+
- `messageAtPath(issue, [])` now selects only whole-form issues. Pass a field path such as `["name"]` to select that field's first message.
|
|
31
|
+
|
|
3
32
|
## 0.10.0 - 2026-10-31
|
|
4
33
|
|
|
5
34
|
### Added
|
package/README.md
CHANGED
|
@@ -81,6 +81,17 @@ An empty name shows that message after blur or submission, including through `er
|
|
|
81
81
|
|
|
82
82
|
The handler has three arguments: decoded values, the submitter, and the submitted encoded snapshot. Use the third argument when coordinating work against what was submitted, rather than reading live fields after an asynchronous decode. The `react` create adapter uses that snapshot to preserve input typed while decoding.
|
|
83
83
|
|
|
84
|
+
### Validate without saving
|
|
85
|
+
|
|
86
|
+
Write the `validate` atom to reveal validation for every field and whole-form refinement without calling `onSubmit`:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
registry.set(order.validate, undefined);
|
|
90
|
+
const snapshot = await Effect.runPromise(AtomRegistry.getResult(registry, order.validate, { suspendOnWaiting: true }));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The result is a `Snapshot<F>` with `encoded` input captured before decoding and `decoded` domain values. Validation uses the form's runtime, including asynchronous and service-based decoding. Invalid input fails with `Invalid`, carrying the same field and whole-form feedback as submit. Validation leaves the input, dirty baseline and persisted draft intact; it does not save or accept changes.
|
|
94
|
+
|
|
84
95
|
### In the UI: bind the same form
|
|
85
96
|
|
|
86
97
|
```tsx
|
|
@@ -90,7 +101,7 @@ import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
|
90
101
|
import { useState } from "react";
|
|
91
102
|
import { createRoot } from "react-dom/client";
|
|
92
103
|
import { form } from "@shivaedev/form/form.ts";
|
|
93
|
-
import { useDirty, useField, useSubmit } from "@shivaedev/form/
|
|
104
|
+
import { useDirty, useField, useSubmit } from "@shivaedev/form/adapters/react.ts";
|
|
94
105
|
|
|
95
106
|
const fields = Schema.Struct({ name: Schema.NonEmptyString, quantity: Schema.NumberFromString });
|
|
96
107
|
|
|
@@ -164,32 +175,36 @@ Call `receive` with encoded values from a refreshed or saved row. Encode domain
|
|
|
164
175
|
| The handler fails | Keeps input; an intervening refresh remains the baseline. |
|
|
165
176
|
| `revert()` is called | Restores the latest baseline, including omitted optional keys. |
|
|
166
177
|
|
|
167
|
-
|
|
178
|
+
By default, the form does not replace submitted encoded input with its decoded representation. A trimmed name can be accepted as saved while the input still contains its spaces. Set `savedValues: (saved) => encodedValues` to display normalized values from the successful handler result. Fields changed since submission keep their input; other fields adopt the normalized values. If a refresh arrived during the save, its newer baseline stays in place and the saved mapping does not replace it. `useEditor` from `react` supplies this mapping from its `values` callback.
|
|
168
179
|
|
|
169
180
|
### Persist input across reloads
|
|
170
181
|
|
|
171
182
|
Opt in with `persistedDraft`. The application chooses a key for one logical editing session. Keep that key stable across server revisions so a refresh can find the same draft; choose a new key when the encoded field shape changes. Stored input includes its baseline and edited field names, so edited fields survive reload while untouched fields adopt the latest received values.
|
|
172
183
|
|
|
173
|
-
|
|
184
|
+
Acquire draft persistence during the application's Effect setup. `draftPersistence` reads `DraftStorageProvider` and `Clock.Clock` from the environment, returning the storage provider and a callback that reads that clock for every timestamp and expiry check. The browser adapter supplies `localStorage`; storage lookup stays lazy so a browser refusal remains best-effort. Supply a different provider with `Layer.succeed(DraftStorageProvider, () => storage)`.
|
|
174
185
|
|
|
175
186
|
```ts
|
|
176
|
-
|
|
177
|
-
|
|
187
|
+
import { Effect } from "effect";
|
|
188
|
+
import { browserDraftStorage } from "@shivaedev/form/adapters/browserDraftStorage.ts";
|
|
189
|
+
import { draftPersistence } from "@shivaedev/form/adapters/draftPersistence.ts";
|
|
190
|
+
|
|
191
|
+
const persistence = yield* draftPersistence.pipe(Effect.provide(browserDraftStorage));
|
|
178
192
|
const order = form(fields, {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
193
|
+
initialValues: { name: "Paper", quantity: "300" },
|
|
194
|
+
onSubmit: Effect.succeed,
|
|
195
|
+
persistedDraft: { key: "order:123", ...persistence },
|
|
182
196
|
});
|
|
183
197
|
```
|
|
184
198
|
|
|
185
|
-
For a React form,
|
|
199
|
+
For a React form, pass the acquired persistence into `usePersistedDraft` before creating the form. The hook attaches the current `SessionBoundary` draft owner:
|
|
186
200
|
|
|
187
201
|
```tsx
|
|
188
202
|
import { useState } from "react";
|
|
189
|
-
import { usePersistedDraft } from "@shivaedev/form/
|
|
203
|
+
import { usePersistedDraft } from "@shivaedev/form/adapters/react.ts";
|
|
204
|
+
import type { PersistedDraftConfig } from "@shivaedev/form/persistence.ts";
|
|
190
205
|
|
|
191
|
-
function OrderEditor({
|
|
192
|
-
const persistedDraft = usePersistedDraft({ key: "order:123",
|
|
206
|
+
function OrderEditor({ persistence }: { readonly persistence: Omit<PersistedDraftConfig, "key"> }) {
|
|
207
|
+
const persistedDraft = usePersistedDraft({ key: "order:123", ...persistence });
|
|
193
208
|
const [order] = useState(() =>
|
|
194
209
|
form(fields, {
|
|
195
210
|
initialValues: { name: "Paper", quantity: "300" },
|
|
@@ -201,7 +216,7 @@ function OrderEditor({ now }: { readonly now: () => number }) {
|
|
|
201
216
|
}
|
|
202
217
|
```
|
|
203
218
|
|
|
204
|
-
`OrderFields` is the application's field UI. Keep this component mounted for one record; remount it when its identity changes. `useEditor` and `useCreate` from `react` accept `persistedDraft: { key,
|
|
219
|
+
`OrderFields` is the application's field UI. Keep this component mounted for one record; remount it when its identity changes. `useEditor` and `useCreate` from `react` accept `persistedDraft: { key, ...persistence }` directly and attach the same owner. A committed `SessionBoundary` session replacement or sign-out drops its tracked drafts, including screens already unmounted, and disables writes from its old forms. A suspended replacement that is canceled preserves the visible session's stored draft and future persistence. Ordinary unmounts and React effect replay preserve stored drafts. A stored draft with a different session generation is discarded when read.
|
|
205
220
|
|
|
206
221
|
Each stored record defaults to at most 256 KiB of UTF-8 JSON and seven days since its last write. Set `maxBytes` and `maxAgeMs` to finite positive limits to change those bounds. Oversized, expired, malformed or incompatible records are dropped when encountered. Persistence requires JSON-compatible encoded input; invalid input such as an empty required string or an unfinished numeric string still persists. Storage access, serialization and quota failures leave the form usable.
|
|
207
222
|
|
|
@@ -230,6 +245,27 @@ This check produces advisory feedback after the configured debounce and completi
|
|
|
230
245
|
|
|
231
246
|
For a nested value held in one top-level field, `errorAt(["answers", index, "text"])` reads Schema feedback and handler rejections for that answer box. A blur or change of the top-level `answers` field, or a submission, reveals its nested messages. The path selects the first message at or beneath that path; a sibling message does not appear there. `error("answers")` retains the top-level summary. Advisory checks remain top-level. Nested values are still edited as a whole top-level field.
|
|
232
247
|
|
|
248
|
+
Whole-form Schema refinements are available through `errorAt([])` after submission. This root path selects only a whole-form message; field errors stay on their field paths. In React, render it with `useAtomValue(form.errorAt([]))`. A failed submission also keeps that message in `Invalid.formMessage`. Matching the refinement clears the feedback. Schema feedback is hidden while a new asynchronous decode is pending.
|
|
249
|
+
|
|
250
|
+
### Map save rejections to fields
|
|
251
|
+
|
|
252
|
+
`rejecting` from `@shivaedev/form/fieldRejection.ts` wraps a save with field feedback. It takes the form schema, the save function and a `rejectField` callback or `undefined`. With `undefined`, `fieldRejectionOf(schema)` uses `@shivaedev/errors` to recognize tagged string-field rejections and selects names in that schema. Other failures keep their original error.
|
|
253
|
+
|
|
254
|
+
For a service whose rejection names a nested value, return a path tuple from the callback:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { rejecting } from "@shivaedev/form/fieldRejection.ts";
|
|
258
|
+
|
|
259
|
+
const profile = form(Profile, {
|
|
260
|
+
initialValues: { profile: { email: "alice@example.com", name: "Taken" } },
|
|
261
|
+
onSubmit: rejecting(Profile, saveProfile, (error) =>
|
|
262
|
+
error.field === "name" ? { field: ["profile", "name"] as const, message: error.message } : undefined,
|
|
263
|
+
),
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`Profile` is the application's struct with a nested `profile` value, and `saveProfile` is its Effect save function. The mapped message appears at `errorAt(["profile", "name"])`; the email sibling stays clear. Returning `undefined` leaves the save failure unchanged.
|
|
268
|
+
|
|
233
269
|
### Errors on array elements
|
|
234
270
|
|
|
235
271
|
Pass a path tuple to `submitter.fail` to reject one element or a property inside it. Paths use indices in the submitted encoded snapshot. Optional `arrayKeys` selectors receive encoded elements of top-level array fields and return their stable, unique keys:
|
|
@@ -271,13 +307,14 @@ For a form made with these fields, `order.choices("visibility")` returns the enc
|
|
|
271
307
|
|
|
272
308
|
Import the module that defines a name; there is no root entry.
|
|
273
309
|
|
|
274
|
-
| Module
|
|
275
|
-
|
|
|
276
|
-
| `@shivaedev/form/form.ts`
|
|
277
|
-
| `@shivaedev/form/shape.ts`
|
|
278
|
-
| `@shivaedev/form/
|
|
279
|
-
| `@shivaedev/form/
|
|
280
|
-
| `@shivaedev/form/
|
|
310
|
+
| Module | Application-facing exports |
|
|
311
|
+
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
312
|
+
| `@shivaedev/form/form.ts` | `form(schema, config)` |
|
|
313
|
+
| `@shivaedev/form/shape.ts` | `Form`, `Submitter`, `FieldFailure`, `Invalid` and the field types they use |
|
|
314
|
+
| `@shivaedev/form/fieldRejection.ts` | `rejecting`, `fieldRejectionOf`, `FieldRejection`, `RejectField`, `FieldRejectionMapping` |
|
|
315
|
+
| `@shivaedev/form/adapters/react.ts` | `useField`, `useSubmit`, `useDirty`, `usePersistedDraft`, `DraftSessionContext` |
|
|
316
|
+
| `@shivaedev/form/draftSession.ts` | `draftSession(id)` |
|
|
317
|
+
| `@shivaedev/form/emptyAsNull.ts` | `emptyAsNull` |
|
|
281
318
|
|
|
282
319
|
`form` takes a `Schema.Struct` and this configuration:
|
|
283
320
|
|
|
@@ -286,10 +323,11 @@ Import the module that defines a name; there is no root entry.
|
|
|
286
323
|
| `initialValues` | `Encoded<F>` |
|
|
287
324
|
| `runtime` | Optional when no application services are needed; otherwise an atom runtime for handler/check and schema decoding services |
|
|
288
325
|
| `onSubmit` | `(values, submitter, submitted) => Effect<A, E, R>`; the three arguments are decoded values, field failure helper and encoded snapshot |
|
|
326
|
+
| `savedValues` | Optional `(saved: A) => Encoded<F>`; normalizes successful submissions while preserving later edits and newer received baselines |
|
|
289
327
|
| `checks` | Optional functions keyed by field, from encoded input to an Effect of `string \| undefined` |
|
|
290
328
|
| `arrayKeys` | Optional `ArrayKeys<F>` selectors keyed by top-level array field, from encoded element to stable `PropertyKey`; retains nested feedback across reordering |
|
|
291
329
|
| `debounce` | Optional Effect `Duration.Input` for checks |
|
|
292
|
-
| `persistedDraft` | Optional `{ key, storage, maxBytes?, maxAgeMs?, session? }`; React's `usePersistedDraft` supplies
|
|
330
|
+
| `persistedDraft` | Optional `{ key, now, storage, maxBytes?, maxAgeMs?, session? }`; React's `usePersistedDraft` supplies session ownership |
|
|
293
331
|
|
|
294
332
|
The returned form exposes:
|
|
295
333
|
|
|
@@ -301,14 +339,16 @@ The returned form exposes:
|
|
|
301
339
|
| `blur(name)` | Field blur operation, supplied as `useField(...).onBlur` |
|
|
302
340
|
| `error(name)` | Atom of `string \| undefined` |
|
|
303
341
|
| `errorAt([name, ...path])` | Atom of nested Schema feedback and handler rejections; visibility follows the top-level field |
|
|
342
|
+
| `errorAt([])` | Atom of whole-form Schema feedback, visible after validation or submission |
|
|
304
343
|
| `choices(name)` | Encoded choices or `undefined` |
|
|
305
344
|
| `dirty` | Atom of `boolean` |
|
|
306
345
|
| `receive(values)` | Merge received encoded values |
|
|
307
346
|
| `revert()` | Restore the baseline |
|
|
347
|
+
| `validate` | `AtomResultFn<void, Snapshot<F>, ER \| Invalid>`; reveal feedback and decode without saving |
|
|
308
348
|
| `submit` | `AtomResultFn<void, A, E \| ER \| FieldFailure \| Invalid>` |
|
|
309
349
|
| `submitting` | Atom of `boolean` for submission waiting state |
|
|
310
350
|
|
|
311
|
-
`F` is the schema's fields, `A` and `E` are the handler's success and failure types, and `ER` is the atom runtime's failure type. `FieldFailure` contains the top-level string `path`, `message` and optional submitted path `segments`; `Invalid` contains `messages`, a `FieldMessages` record.
|
|
351
|
+
`F` is the schema's fields, `A` and `E` are the handler's success and failure types, and `ER` is the atom runtime's failure type. `FieldFailure` contains the top-level string `path`, `message` and optional submitted path `segments`; `Invalid` contains `messages`, a `FieldMessages` record, and optional `formMessage` for a whole-form refinement.
|
|
312
352
|
|
|
313
353
|
The lower-level `draft.ts` (`draft`), `refs.ts` (`fromRef`, `propertyRef`) and `statusOf.ts` (`statusOf`) modules hold the machinery the form composes; the examples use the form API instead.
|
|
314
354
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserDraftStorage.d.ts","sourceRoot":"","sources":["../../src/adapters/browserDraftStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,eAAO,MAAM,mBAAmB,iDAAqE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserDraftStorage.js","sourceRoot":"","sources":["../../src/adapters/browserDraftStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { DraftStorageProvider } from "#persistence.ts";
|
|
3
|
+
export declare const draftPersistence: Effect.Effect<{
|
|
4
|
+
now: () => number;
|
|
5
|
+
storage: () => import("#persistence.ts").DraftStorage;
|
|
6
|
+
}, never, DraftStorageProvider>;
|
|
7
|
+
//# sourceMappingURL=draftPersistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draftPersistence.d.ts","sourceRoot":"","sources":["../../src/adapters/draftPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,eAAO,MAAM,gBAAgB;;;+BAI3B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Clock, Effect } from "effect";
|
|
2
|
+
import { DraftStorageProvider } from "#persistence.ts";
|
|
3
|
+
export const draftPersistence = Effect.gen(function* () {
|
|
4
|
+
const storage = yield* DraftStorageProvider;
|
|
5
|
+
const clock = yield* Clock.Clock;
|
|
6
|
+
return { now: () => clock.currentTimeMillisUnsafe(), storage };
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=draftPersistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draftPersistence.js","sourceRoot":"","sources":["../../src/adapters/draftPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACjC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,OAAO,EAAE,CAAC;AAChE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import type { PersistedDraftConfig, Persistence } from "#persistence.ts";
|
|
3
|
+
export declare function persistence<TSchema extends Schema.Constraint>(schema: TSchema, config: PersistedDraftConfig): Persistence<TSchema["Encoded"]>;
|
|
4
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/adapters/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEzE,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,oBAAoB,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAmF7I"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Schema, SchemaParser } from "effect";
|
|
2
|
+
export function persistence(schema, config) {
|
|
3
|
+
const encoded = Schema.toEncoded(schema);
|
|
4
|
+
const StoredDraft = Schema.Struct({
|
|
5
|
+
baseline: Schema.Unknown,
|
|
6
|
+
edited: Schema.Array(Schema.String),
|
|
7
|
+
session: Schema.String,
|
|
8
|
+
updatedAt: Schema.Number,
|
|
9
|
+
values: Schema.Unknown,
|
|
10
|
+
version: Schema.Literal(1),
|
|
11
|
+
});
|
|
12
|
+
const valid = Schema.is(StoredDraft);
|
|
13
|
+
const decode = SchemaParser.decodeUnknownSync(encoded, { disableChecks: true });
|
|
14
|
+
const maxAgeMs = config.maxAgeMs ?? 7 * 24 * 60 * 60 * 1000;
|
|
15
|
+
const maxBytes = config.maxBytes ?? 256 * 1024;
|
|
16
|
+
function bytes(value) {
|
|
17
|
+
return new TextEncoder().encode(value).byteLength;
|
|
18
|
+
}
|
|
19
|
+
const storage = config.storage;
|
|
20
|
+
const session = config.session?.id ?? "";
|
|
21
|
+
function active() {
|
|
22
|
+
return config.session?.active !== false;
|
|
23
|
+
}
|
|
24
|
+
function attempt(run, failed) {
|
|
25
|
+
try {
|
|
26
|
+
return run();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
failed?.();
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function clear() {
|
|
34
|
+
attempt(() => storage().removeItem(config.key));
|
|
35
|
+
}
|
|
36
|
+
config.session?.track(config.key, clear);
|
|
37
|
+
return {
|
|
38
|
+
clear: () => {
|
|
39
|
+
if (active()) {
|
|
40
|
+
clear();
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
read: () => attempt(() => {
|
|
44
|
+
if (!active()) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const raw = storage().getItem(config.key);
|
|
48
|
+
if (raw === null) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const value = bytes(raw) <= maxBytes ? JSON.parse(raw) : undefined;
|
|
52
|
+
const now = config.now();
|
|
53
|
+
if (valid(value)
|
|
54
|
+
&& value.session === session
|
|
55
|
+
&& Number.isFinite(maxAgeMs)
|
|
56
|
+
&& Number.isFinite(maxBytes)
|
|
57
|
+
&& maxAgeMs > 0
|
|
58
|
+
&& maxBytes > 0
|
|
59
|
+
&& now - value.updatedAt <= maxAgeMs
|
|
60
|
+
&& value.updatedAt <= now) {
|
|
61
|
+
return { baseline: decode(value.baseline), edited: value.edited, values: decode(value.values) };
|
|
62
|
+
}
|
|
63
|
+
clear();
|
|
64
|
+
return undefined;
|
|
65
|
+
}, clear),
|
|
66
|
+
write: (baseline, values, edited) => {
|
|
67
|
+
if (!active()) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
attempt(() => {
|
|
71
|
+
const raw = JSON.stringify({ baseline, edited, session, updatedAt: config.now(), values, version: 1 });
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
if (!(maxAgeMs > 0 && Number.isFinite(maxAgeMs) && Number.isFinite(maxBytes) && bytes(raw) <= maxBytes && valid(parsed))) {
|
|
74
|
+
clear();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
decode(parsed.baseline);
|
|
78
|
+
decode(parsed.values);
|
|
79
|
+
storage().setItem(config.key, raw);
|
|
80
|
+
}, clear);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/adapters/persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type * as AsyncResult from "effect/reactivity/AsyncResult";
|
|
2
2
|
import type { DraftSession } from "#draftSession.ts";
|
|
3
|
-
import type {
|
|
3
|
+
import type { PersistedDraftConfig } from "#persistence.ts";
|
|
4
4
|
import type { Encoded, FieldFailure, Fields, Form, Invalid, Name } from "#shape.ts";
|
|
5
5
|
export declare const DraftSessionContext: import("react").Context<DraftSession | undefined>;
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
export declare function usePersistedDraft(config: BrowserDraftConfig): PersistedDraftConfig;
|
|
10
|
-
export declare function usePersistedDraft(config: BrowserDraftConfig | undefined): PersistedDraftConfig | undefined;
|
|
6
|
+
export declare function usePersistedDraft(config: PersistedDraftConfig): PersistedDraftConfig;
|
|
7
|
+
export declare function usePersistedDraft(config: PersistedDraftConfig | undefined): PersistedDraftConfig | undefined;
|
|
11
8
|
export interface Field<TFields extends Fields, TKey extends Name<TFields>> {
|
|
12
9
|
readonly choices: readonly Encoded<TFields>[TKey][] | undefined;
|
|
13
10
|
readonly error: string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/adapters/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,WAAW,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpF,eAAO,MAAM,mBAAmB,mDAAqD,CAAC;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAAC;AAK9G,MAAM,WAAW,KAAK,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC;IACxE,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;CACvC;AACD,wBAAgB,QAAQ,CAAC,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,EAC/F,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,EACxC,IAAI,EAAE,IAAI,GACR,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAOtB;AACD,MAAM,WAAW,MAAM,CAAC,CAAC,EAAE,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC7B;AACD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EACpE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,GACtC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,aAAa,GAAG,YAAY,GAAG,OAAO,CAAC,CAKvD;AACD,wBAAgB,QAAQ,CAAC,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,GAAG,OAAO,CAEvH"}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { useAtomRef, useAtomSet, useAtomValue } from "@effect/atom-react";
|
|
2
2
|
import { createContext, useCallback, useContext, useMemo } from "react";
|
|
3
3
|
export const DraftSessionContext = createContext(undefined);
|
|
4
|
-
function browserStorage() {
|
|
5
|
-
return globalThis.localStorage;
|
|
6
|
-
}
|
|
7
4
|
export function usePersistedDraft(config) {
|
|
8
5
|
const session = useContext(DraftSessionContext);
|
|
9
|
-
return config ? { ...config, session: session ?? config.session
|
|
6
|
+
return config ? { ...config, session: session ?? config.session } : undefined;
|
|
10
7
|
}
|
|
11
8
|
export function useField(form, name) {
|
|
12
9
|
const value = useAtomRef(form.field(name));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/adapters/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAIxE,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAA2B,SAAS,CAAC,CAAC;AAGtF,MAAM,UAAU,iBAAiB,CAAC,MAAwC;IACzE,MAAM,OAAO,GAAG,UAAU,CAAC,mBAAmB,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC;AASD,MAAM,UAAU,QAAQ,CACvB,IAAwC,EACxC,IAAU;IAEV,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,IAA4B,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACtG,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC;AAMD,MAAM,UAAU,SAAS,CACxB,IAAwC;IAExC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;AACpD,CAAC;AACD,MAAM,UAAU,QAAQ,CAA8C,IAAwC;IAC7G,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Duration, Effect } from "effect";
|
|
2
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
3
|
+
import type * as AtomRef from "effect/reactivity/AtomRef";
|
|
4
|
+
import type { Encoded, Fields, Name, Services } from "./shape.ts";
|
|
5
|
+
export declare function checkFeedback<TFields extends Fields, R, E>(runtime: Atom.AtomRuntime<Services<TFields, R>, E>, checks: ReadonlyMap<string, (value: Encoded<TFields>[Name<TFields>]) => Effect.Effect<string | undefined, never, R>>, refFor: <TKey extends Name<TFields>>(name: TKey) => AtomRef.AtomRef<Encoded<TFields>[TKey]>, debounce: Duration.Input): (arg: Name<TFields>) => Atom.Atom<string | undefined>;
|
|
6
|
+
//# sourceMappingURL=checkFeedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkFeedback.d.ts","sourceRoot":"","sources":["../src/checkFeedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAS,MAAM,QAAQ,CAAC;AAEtD,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAElE,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EACzD,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAClD,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EACpH,MAAM,EAAE,CAAC,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,EAC3F,QAAQ,EAAE,QAAQ,CAAC,KAAK,yDAsBxB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Equal } from "effect";
|
|
2
|
+
import * as AsyncResult from "effect/reactivity/AsyncResult";
|
|
3
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
4
|
+
import { fromRef } from "./refs.js";
|
|
5
|
+
export function checkFeedback(runtime, checks, refFor, debounce) {
|
|
6
|
+
return Atom.family((name) => {
|
|
7
|
+
const check = checks.get(name);
|
|
8
|
+
if (check === undefined) {
|
|
9
|
+
return Atom.make(() => undefined);
|
|
10
|
+
}
|
|
11
|
+
const current = fromRef(refFor(name));
|
|
12
|
+
const settled = Atom.debounce(current, debounce);
|
|
13
|
+
const answered = runtime.atom((get) => {
|
|
14
|
+
const input = get(settled);
|
|
15
|
+
return Effect.map(check(input), (message) => ({
|
|
16
|
+
input,
|
|
17
|
+
message,
|
|
18
|
+
}));
|
|
19
|
+
});
|
|
20
|
+
return Atom.readable((get) => {
|
|
21
|
+
const result = get(answered);
|
|
22
|
+
const value = get(current);
|
|
23
|
+
return AsyncResult.isSuccess(result) && !result.waiting && Equal.equals(result.value.input, value) ? result.value.message : undefined;
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=checkFeedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkFeedback.js","sourceRoot":"","sources":["../src/checkFeedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,WAAW,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,UAAU,aAAa,CAC5B,OAAkD,EAClD,MAAoH,EACpH,MAA2F,EAC3F,QAAwB;IAExB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,IAAmB,EAAiC,EAAE;QACzE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAuB,EAAE,CAAC,SAAS,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC7C,KAAK;gBACL,OAAO;aACP,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAsB,EAAE;YAChD,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACvI,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/draft.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function draft<TValues extends Entries>(initial: TValues, persist
|
|
|
7
7
|
receive: (incoming: TValues) => void;
|
|
8
8
|
revert: () => void;
|
|
9
9
|
submission: () => {
|
|
10
|
-
accept: () => void;
|
|
10
|
+
accept: (incoming?: TValues) => void;
|
|
11
11
|
values: TValues;
|
|
12
12
|
};
|
|
13
13
|
values: AtomRef.AtomRef<TValues>;
|
package/dist/draft.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,KAAK,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsCjD,wBAAgB,KAAK,CAAC,OAAO,SAAS,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAuBjG,KAAK;IAIL,OAAO,aAAa,OAAO,KAAG,IAAI;IAOlC,MAAM,QAAM,IAAI;IAOhB,UAAU;QAGR,MAAM,
|
|
1
|
+
{"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,KAAK,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsCjD,wBAAgB,KAAK,CAAC,OAAO,SAAS,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAuBjG,KAAK;IAIL,OAAO,aAAa,OAAO,KAAG,IAAI;IAOlC,MAAM,QAAM,IAAI;IAOhB,UAAU;QAGR,MAAM,cAAc,OAAO,KAAG,IAAI;QAalC,MAAM;;IAGR,MAAM;EAEP"}
|
package/dist/draft.js
CHANGED
|
@@ -68,8 +68,18 @@ export function draft(initial, persistence) {
|
|
|
68
68
|
submission: () => {
|
|
69
69
|
const { baseline, current: submitted } = state.value;
|
|
70
70
|
return {
|
|
71
|
-
accept: () => {
|
|
72
|
-
state.update((held) =>
|
|
71
|
+
accept: (incoming) => {
|
|
72
|
+
state.update((held) => {
|
|
73
|
+
if (held.baseline !== baseline) {
|
|
74
|
+
return { ...held, settled: matching(held.current, submitted) };
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
...held,
|
|
78
|
+
baseline: incoming ?? submitted,
|
|
79
|
+
current: incoming === undefined ? held.current : merged({ ...held, baseline: submitted, settled: none }, incoming),
|
|
80
|
+
settled: none,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
73
83
|
},
|
|
74
84
|
values: submitted,
|
|
75
85
|
};
|
package/dist/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Effect, type Schema } from "effect";
|
|
2
|
+
import type { FieldFailure, Fields, Name, Path, Submitter } from "#shape.ts";
|
|
3
|
+
export interface FieldRejection<TName extends string> {
|
|
4
|
+
readonly field: TName | Path<TName>;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
}
|
|
7
|
+
export type RejectField<TFields extends Fields, TError> = (error: TError) => FieldRejection<Name<TFields>> | undefined;
|
|
8
|
+
type Unmatched<TFields extends Fields, TError> = TError extends {
|
|
9
|
+
readonly _tag: string;
|
|
10
|
+
readonly message: string;
|
|
11
|
+
} ? "field" extends keyof TError ? Exclude<TError["field"], Name<TFields> | undefined> : never : never;
|
|
12
|
+
export type FieldRejectionMapping<TFields extends Fields, TError> = {
|
|
13
|
+
readonly rejectField?: RejectField<TFields, TError>;
|
|
14
|
+
} & ([
|
|
15
|
+
Unmatched<TFields, TError>
|
|
16
|
+
] extends [never] ? unknown : {
|
|
17
|
+
readonly rejectField: RejectField<TFields, TError>;
|
|
18
|
+
});
|
|
19
|
+
export declare function fieldRejectionOf<TFields extends Fields>(schema: Schema.Struct<TFields>): (error: unknown) => FieldRejection<Name<TFields>> | undefined;
|
|
20
|
+
export declare function rejecting<TFields extends Fields, TValues, TResult, TError, TRequirements>(schema: Schema.Struct<TFields>, save: (values: TValues) => Effect.Effect<TResult, TError, TRequirements>, reject: RejectField<TFields, TError> | undefined): (values: TValues, submitter: Submitter<Name<TFields>>) => Effect.Effect<TResult, TError | FieldFailure, TRequirements>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=fieldRejection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldRejection.d.ts","sourceRoot":"","sources":["../src/fieldRejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE7E,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,MAAM;IACnD,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;AAEvH,KAAK,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,IAAI,MAAM,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChH,OAAO,SAAS,MAAM,MAAM,GAC3B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,GACnD,KAAK,GACN,KAAK,CAAC;AAET,MAAM,MAAM,qBAAqB,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,IAAI;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC;IAC9H,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,SAAS,CAAC,KAAK,CAAC,GACd,OAAO,GACP;IAAE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAE3D,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WACvE,OAAO,KAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CASlE;AAED,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EACxF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAC9B,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACxE,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,SAAS,YAGhC,OAAO,aAAa,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,aAAa,CAAC,CAK3H"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { rejectedField } from "@shivaedev/errors/rejectedField.ts";
|
|
3
|
+
export function fieldRejectionOf(schema) {
|
|
4
|
+
return (error) => {
|
|
5
|
+
function isName(name) {
|
|
6
|
+
return Object.hasOwn(schema.fields, name);
|
|
7
|
+
}
|
|
8
|
+
return Option.match(rejectedField(error), {
|
|
9
|
+
onNone: () => undefined,
|
|
10
|
+
onSome: ({ field, message }) => (isName(field) ? { field, message } : undefined),
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function rejecting(schema, save, reject) {
|
|
15
|
+
const rejection = reject ?? fieldRejectionOf(schema);
|
|
16
|
+
return (values, submitter) => Effect.catch(save(values), (error) => {
|
|
17
|
+
const found = rejection(error);
|
|
18
|
+
return found === undefined ? Effect.fail(error) : submitter.fail(found.field, found.message);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=fieldRejection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldRejection.js","sourceRoot":"","sources":["../src/fieldRejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAsBnE,MAAM,UAAU,gBAAgB,CAAyB,MAA8B;IACtF,OAAO,CAAC,KAAc,EAA6C,EAAE;QACpE,SAAS,MAAM,CAAC,IAAY;YAC3B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;YACzC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS;YACvB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;SAChF,CAAC,CAAC;IACJ,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CACxB,MAA8B,EAC9B,IAAwE,EACxE,MAAgD;IAEhD,MAAM,SAAS,GAAG,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO,CAAC,MAAe,EAAE,SAAmC,EAAgE,EAAE,CAC7H,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAA+C,EAAE;QACjF,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/form.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,MAAM,EAAkC,MAAM,QAAQ,CAAC;AAUvF,OAAO,EAAE,KAAK,MAAM,EAA4C,KAAK,MAAM,EAAE,KAAK,IAAI,EAA4C,MAAM,YAAY,CAAC;AAcrJ,wBAAgB,IAAI,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,aAAa,GAAG,KAAK,EACpG,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,GAC/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAqG9C"}
|