@playfast/reform-forms 1.1.1 → 1.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 CHANGED
@@ -13,7 +13,7 @@ Values, validation, field metadata, list operations, submit flow, and correlatio
13
13
 
14
14
  ---
15
15
 
16
- `@playfast/reform-forms` owns everything a form needs *except* its widgets. It has no DOM and no React API — it produces a typed, platform-neutral view that any renderer can map. Pair it with [`@playfast/reform-forms-react`](https://www.npmjs.com/package/@playfast/reform-forms-react) for typed JSX, or consume the view directly from a custom host.
16
+ `@playfast/reform-forms` owns everything a form needs _except_ its widgets. It has no DOM and no React API — it produces a typed, platform-neutral view that any renderer can map. Pair it with [`@playfast/reform-forms-react`](https://www.npmjs.com/package/@playfast/reform-forms-react) for typed JSX, or consume the view directly from a custom host.
17
17
 
18
18
  ## Install
19
19
 
@@ -61,9 +61,7 @@ const CheckoutFormLive = Form.live(CheckoutForm, {
61
61
  items: { minItems: 1, maxItems: inputs.settings.maxItems },
62
62
  }),
63
63
  validate: ({ values }) =>
64
- values.email.includes('@')
65
- ? undefined
66
- : Form.error('email', 'Email must contain @'),
64
+ values.email.includes('@') ? undefined : Form.error('email', 'Email must contain @'),
67
65
  submit: ({ decoded, inputs }) => saveCheckout(decoded, inputs.user.id),
68
66
  })
69
67
  ```
@@ -71,7 +69,7 @@ const CheckoutFormLive = Form.live(CheckoutForm, {
71
69
  Read the form inside compositions with `Form.view(CheckoutForm)` — a typed, platform-neutral object:
72
70
 
73
71
  ```ts
74
- const form = yield* Form.view(CheckoutForm)
72
+ const form = yield * Form.view(CheckoutForm)
75
73
 
76
74
  form.field('email').set('a@example.com')
77
75
  form.array('items').append({ name: 'Milk', qty: 1 })
package/package.json CHANGED
@@ -1,37 +1,39 @@
1
1
  {
2
2
  "name": "@playfast/reform-forms",
3
- "playbook": "./playbook",
4
- "version": "1.1.1",
5
- "type": "module",
3
+ "version": "1.2.0",
6
4
  "description": "Headless, schema-driven form state for reform — values, validation, field limitations, list operations, and submit flow, rendering nothing.",
7
5
  "keywords": [
8
- "reform",
9
6
  "effect",
10
- "forms",
11
7
  "form-state",
12
- "validation",
8
+ "forms",
9
+ "headless",
10
+ "reform",
13
11
  "schema",
14
- "headless"
12
+ "validation"
15
13
  ],
14
+ "bugs": {
15
+ "url": "https://github.com/playfast/reform/issues"
16
+ },
16
17
  "license": "MIT",
17
18
  "repository": {
18
19
  "type": "git",
19
20
  "url": "https://github.com/playfast/reform.git",
20
21
  "directory": "packages/forms"
21
22
  },
22
- "bugs": {
23
- "url": "https://github.com/playfast/reform/issues"
24
- },
23
+ "files": [
24
+ "src",
25
+ "README.md"
26
+ ],
27
+ "type": "module",
25
28
  "sideEffects": false,
26
29
  "exports": {
27
30
  "./package.json": "./package.json",
28
31
  ".": "./src/index.ts",
29
32
  "./*": "./src/*.ts"
30
33
  },
31
- "files": [
32
- "src",
33
- "README.md"
34
- ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
35
37
  "scripts": {
36
38
  "clean": "rm -rf dist .tsbuildinfo",
37
39
  "check": "tsc --noEmit",
@@ -43,10 +45,8 @@
43
45
  "lint:fix": "oxlint --fix src"
44
46
  },
45
47
  "peerDependencies": {
46
- "effect": "*",
47
- "@playfast/reform": "*"
48
+ "@playfast/reform": "*",
49
+ "effect": "*"
48
50
  },
49
- "publishConfig": {
50
- "access": "public"
51
- }
51
+ "playbook": "./playbook"
52
52
  }
package/src/form.test.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { expect, it } from "@effect/vitest";
2
- import { Effect, Layer, Option, Schema as S } from "effect";
3
- import { Engine } from "@playfast/reform";
4
- import * as Form from "./form";
1
+ import { expect, it } from '@effect/vitest'
2
+ import { Effect, Layer, Option, Schema as S } from 'effect'
3
+ import { Engine } from '@playfast/reform'
4
+ import * as Form from './form'
5
5
 
6
6
  const waitUntil = <A, R>(
7
7
  read: Effect.Effect<A, never, R>,
@@ -11,81 +11,66 @@ const waitUntil = <A, R>(
11
11
  Effect.flatMap(read, (value) =>
12
12
  pred(value) || rounds <= 0
13
13
  ? Effect.succeed(value)
14
- : Effect.flatMap(Effect.yieldNow(), () =>
15
- waitUntil(read, pred, rounds - 1),
16
- ),
17
- );
14
+ : Effect.flatMap(Effect.yieldNow(), () => waitUntil(read, pred, rounds - 1)),
15
+ )
18
16
 
19
17
  const CheckoutSchema = S.Struct({
20
18
  email: S.String,
21
19
  items: S.Array(S.Struct({ name: S.String, qty: S.Number })),
22
20
  payment: S.Union(
23
- S.TaggedStruct("Card", { cardNumber: S.String }),
24
- S.TaggedStruct("Paypal", { email: S.String }),
21
+ S.TaggedStruct('Card', { cardNumber: S.String }),
22
+ S.TaggedStruct('Paypal', { email: S.String }),
25
23
  ),
26
- });
24
+ })
27
25
 
28
- class CheckoutForm extends Form.make("CheckoutForm", {
26
+ class CheckoutForm extends Form.make('CheckoutForm', {
29
27
  schema: CheckoutSchema,
30
28
  }) {}
31
29
 
32
30
  const layer = Form.live(CheckoutForm, {
33
31
  initial: {
34
- email: "",
32
+ email: '',
35
33
  items: [],
36
- payment: { _tag: "Card", cardNumber: "" },
34
+ payment: { _tag: 'Card', cardNumber: '' },
37
35
  },
38
36
  validate: ({ values }) =>
39
- values.email.includes("@")
40
- ? undefined
41
- : Form.error("email", "Email must contain @"),
42
- }).pipe(Layer.provideMerge(Engine));
37
+ values.email.includes('@') ? undefined : Form.error('email', 'Email must contain @'),
38
+ }).pipe(Layer.provideMerge(Engine))
43
39
 
44
- it.scopedLive("field bindings write through the form reducer", () =>
40
+ it.scopedLive('field bindings write through the form reducer', () =>
45
41
  Effect.gen(function* () {
46
- const view = yield* Form.view(CheckoutForm);
47
- view.field("email").set("a@example.com");
42
+ const view = yield* Form.view(CheckoutForm)
43
+ view.field('email').set('a@example.com')
48
44
 
49
45
  const next = yield* waitUntil(
50
46
  Form.view(CheckoutForm),
51
- (v) => v.values.email === "a@example.com",
52
- );
53
- expect(next.field("email").dirty).toBe(true);
47
+ (v) => v.values.email === 'a@example.com',
48
+ )
49
+ expect(next.field('email').dirty).toBe(true)
54
50
  }).pipe(Effect.provide(layer)),
55
- );
51
+ )
56
52
 
57
- it.scopedLive("array bindings append and remove items", () =>
53
+ it.scopedLive('array bindings append and remove items', () =>
58
54
  Effect.gen(function* () {
59
- const view = yield* Form.view(CheckoutForm);
60
- view.array("items").append({ name: "milk", qty: 1 });
55
+ const view = yield* Form.view(CheckoutForm)
56
+ view.array('items').append({ name: 'milk', qty: 1 })
61
57
 
62
- const withItem = yield* waitUntil(
63
- Form.view(CheckoutForm),
64
- (v) => v.values.items.length === 1,
65
- );
66
- expect(withItem.array("items").items[0]?.key).toMatch(/^form-item-/);
58
+ const withItem = yield* waitUntil(Form.view(CheckoutForm), (v) => v.values.items.length === 1)
59
+ expect(withItem.array('items').items[0]?.key).toMatch(/^form-item-/)
67
60
 
68
- withItem.array("items").remove(0);
69
- const empty = yield* waitUntil(
70
- Form.view(CheckoutForm),
71
- (v) => v.values.items.length === 0,
72
- );
73
- expect(empty.values.items).toEqual([]);
61
+ withItem.array('items').remove(0)
62
+ const empty = yield* waitUntil(Form.view(CheckoutForm), (v) => v.values.items.length === 0)
63
+ expect(empty.values.items).toEqual([])
74
64
  }).pipe(Effect.provide(layer)),
75
- );
65
+ )
76
66
 
77
- it.scopedLive("submit routes custom validation errors to fields", () =>
67
+ it.scopedLive('submit routes custom validation errors to fields', () =>
78
68
  Effect.gen(function* () {
79
- const view = yield* Form.view(CheckoutForm);
80
- view.submit();
69
+ const view = yield* Form.view(CheckoutForm)
70
+ view.submit()
81
71
 
82
- const invalid = yield* waitUntil(
83
- Form.view(CheckoutForm),
84
- (v) => v.validationCount > 0,
85
- );
86
- const error = invalid.field("email").error;
87
- expect(Option.isSome(error) ? error.value : undefined).toBe(
88
- "Email must contain @",
89
- );
72
+ const invalid = yield* waitUntil(Form.view(CheckoutForm), (v) => v.validationCount > 0)
73
+ const error = invalid.field('email').error
74
+ expect(Option.isSome(error) ? error.value : undefined).toBe('Email must contain @')
90
75
  }).pipe(Effect.provide(layer)),
91
- );
76
+ )