@playfast/reform-proof 0.0.7

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 ADDED
@@ -0,0 +1,87 @@
1
+ <div align="center">
2
+
3
+ # `@playfast/proof`
4
+
5
+ **A headless testing toolkit for [reform](https://www.npmjs.com/package/@playfast/reform).**
6
+ Drive a scene's compositions and assert on what they compute — deterministically, with no DOM, no timers, and no text matching.
7
+
8
+ [![npm](https://img.shields.io/npm/v/@playfast/proof.svg)](https://www.npmjs.com/package/@playfast/proof)
9
+ [![license](https://img.shields.io/npm/l/@playfast/proof.svg)](#license)
10
+ [![built with Effect](https://img.shields.io/badge/built%20with-Effect-5a67d8.svg)](https://effect.website)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ A reform *scene* is renderer-neutral, so it can be exercised without a host. `@playfast/proof` runs that same scene against a capturing UI: it dispatches typed events, settles the engine, and lets you assert on the **props a composition computed** — pure data, never rendered markup. The scene you prove is the exact scene `@playfast/react` mounts, so there is no seam between a passing test and production behavior.
17
+
18
+ ## Install
19
+
20
+ ```sh
21
+ bun add -d @playfast/proof @playfast/reform effect react
22
+ ```
23
+
24
+ Peers: `effect`, `react` (`^19`), and `@playfast/reform`.
25
+
26
+ ## Quick start
27
+
28
+ A proof binds a human-readable **requirement** to a composition, then implements it as a generator that drives the scene's `app` facade:
29
+
30
+ ```ts
31
+ import { Layer } from 'effect'
32
+ import { scene } from '@playfast/reform'
33
+ import { Product, Proof, ProductRequirement, expect } from '@playfast/reform-proof'
34
+ import { Counter } from './counter.compose'
35
+ import { CounterLive } from './counter.layer'
36
+
37
+ const CounterScene = scene(Counter, { provide: [CounterLive] })
38
+
39
+ class Bumps extends ProductRequirement.make(Counter, 'bumps the count') {}
40
+
41
+ const bumps = Proof.implement(Bumps, CounterScene, function* (app) {
42
+ yield* app.actions.bump({}) // dispatch a contract event, settle
43
+ const props = yield* app.props // read what the composition computed
44
+ yield* expect(props.count).toBe(1)
45
+ })
46
+
47
+ const product = Product.make(Counter, { requirements: [Bumps] })
48
+ const suite = Proof.suite(product, { proofs: [bumps] })
49
+
50
+ const result = await Proof.run(suite) // { product, results, ok }
51
+ ```
52
+
53
+ ## The workflow
54
+
55
+ | step | API |
56
+ | --- | --- |
57
+ | Declare a requirement | `ProductRequirement.make(composition, statement)` |
58
+ | Group requirements | `Product.make(composition, { requirements })` |
59
+ | Implement a proof | `Proof.implement(requirement, scene, function* (app) { … })` |
60
+ | Compose a suite | `Proof.suite(product, { proofs })` |
61
+ | Run it | `Proof.run(suite) → Promise<SuiteResult>` |
62
+ | Step it (for tooling) | `Proof.driver(suite) → ProofDriver[]` |
63
+
64
+ The `statement` is a string-literal type, matched at `Proof.implement` so a requirement's name and its proof can never drift apart.
65
+
66
+ ## The facade
67
+
68
+ Inside a proof body, `app` is a typed handle derived from the composition's UI contract:
69
+
70
+ - `yield* app.actions.<event>(payload)` — dispatch a contract event and settle.
71
+ - `yield* app.props` — re-render and return the latest computed props struct.
72
+ - `yield* app.slots.<name>.first` / `.at(i)` / `.all` — descend into child composition facades; a slot used directly defaults to its first instance.
73
+ - `yield* app.frame` — force a settle (re-render and wait for engine stability).
74
+
75
+ Assertions are Effects: `expect(actual).toBe(…)`, `.toEqual(…)`, `.toContain(…)`. Mis-targeting a contract surfaces as a tagged failure — `UnknownAction`, `UnknownSlot`, or `AssertionFailed` — rather than an undefined read.
76
+
77
+ ## Why it's deterministic
78
+
79
+ There is no real clock. `settle` cooperatively drains forked fibers and loops until the render tree reaches a two-frame fixpoint, so genuinely async flows (queries, procedures, remote state) resolve without flakiness from timing races. `Proof.driver` exposes the same run one frame at a time, capturing what each step saw — which is what powers the editor's test-play timeline.
80
+
81
+ ## The reform family
82
+
83
+ Core [`@playfast/reform`](https://www.npmjs.com/package/@playfast/reform) · hosts [`@playfast/react`](https://www.npmjs.com/package/@playfast/react) / [`@playfast/react-native`](https://www.npmjs.com/package/@playfast/react-native) · forms [`@playfast/forms`](https://www.npmjs.com/package/@playfast/forms) + [`@playfast/forms-react`](https://www.npmjs.com/package/@playfast/forms-react)
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,18 @@
1
+ import type { DriveResult, Proof, ProofResult } from './index';
2
+ /**
3
+ * Run one proof against a fresh runtime, returning its pass/fail result. Each
4
+ * proof gets its own isolated environment, so nothing leaks between proofs. Boots
5
+ * the scene as the host would, settles, then runs the proof body to completion.
6
+ */
7
+ export declare const executeProof: (proof: Proof) => Promise<ProofResult>;
8
+ /**
9
+ * Drive one proof a step at a time, recording a `StepFrame` after each yielded
10
+ * effect settles. Reuses the same makeFacade runtime/facade/settle as
11
+ * `executeProof`, but instead of handing the body to `Effect.gen` (which runs it
12
+ * to completion), it pumps the generator by hand — `gen.next(value)` yields the
13
+ * next effect, we run it on the live runtime, snapshot the sink, and feed the
14
+ * result back. So the editor gets the per-step timeline with no change to the
15
+ * proof authoring API.
16
+ */
17
+ export declare const driveProof: (proof: Proof) => Promise<DriveResult>;
18
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAU,WAAW,EAAU,KAAK,EAAE,WAAW,EAAyB,MAAM,SAAS,CAAA;AA0PrG;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,KAAK,KAAG,OAAO,CAAC,WAAW,CAqBpE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAU,OAAO,KAAK,KAAG,OAAO,CAAC,WAAW,CAkClE,CAAA"}
package/dist/engine.js ADDED
@@ -0,0 +1,259 @@
1
+ import { Effect, Layer, ManagedRuntime } from 'effect';
2
+ import { yieldWrapGet } from 'effect/Utils';
3
+ import { isValidElement } from 'react';
4
+ import { UnknownAction, UnknownSlot } from './errors';
5
+ import { Bus, CaptureSink, Composition, isFeatureBinding, publish, } from '@playfast/reform';
6
+ // The drain loop, channels, RPC, and reducers run on forked fibers, so a
7
+ // dispatched event takes an unknown number of ticks to flow through. Each poll
8
+ // first drains COOPERATIVELY — a burst of `yieldNow` lets those forked fibers
9
+ // cascade (event → channel → procedure → in-memory RPC → fact → reducer) with no
10
+ // real time — so a synchronous flow reaches its final state within one poll. A
11
+ // real-time `sleep` is still paid each poll to remain the correctness authority
12
+ // for a genuinely time-delayed client (the cooperative drain alone could read a
13
+ // flow waiting on a real timer as falsely "stable" and settle early). The render
14
+ // then re-runs until the captured tree stops changing, bounded so a stuck flow
15
+ // fails fast instead of hanging.
16
+ const SETTLE_DRAIN = 30;
17
+ const SETTLE_STEP = '1 milli';
18
+ const SETTLE_MAX_RENDERS = 100;
19
+ /** Cooperatively run forked engine fibers to a fixpoint without advancing real time. */
20
+ const settleDrain = Effect.yieldNow().pipe(Effect.repeatN(SETTLE_DRAIN));
21
+ // A keyed view backed by a getter — the only place dynamic keys are needed.
22
+ const keyed = (get) => {
23
+ const target = Object.create(null);
24
+ return new Proxy(target, { get: (_t, key) => get(String(key)) });
25
+ };
26
+ const uiNameOf = (comp) => comp.manifest.ui.manifest.name;
27
+ const makeSink = () => {
28
+ // A const holder whose array we swap on reset (no reassigned binding).
29
+ const state = { captures: [] };
30
+ return {
31
+ api: { record: (capture) => state.captures.push(capture) },
32
+ get captures() {
33
+ return state.captures;
34
+ },
35
+ reset: () => {
36
+ state.captures = [];
37
+ },
38
+ };
39
+ };
40
+ /**
41
+ * Build a proof's runtime layer: the scene's closed wiring with the capture sink
42
+ * merged in, so `Ui`'s `serviceOption(CaptureSink)` finds it and records each
43
+ * render (absent in production, the same layer renders to React). The scene's
44
+ * `provide` is typed to the host-read subset (`MountedServices`); a proof also
45
+ * resolves slot-binding tags (`CompositionClass`), which the same closed layer
46
+ * supplies at runtime — restate that broader `RuntimeServices` surface here. This
47
+ * is the one erasure boundary, the same seam the react host crosses when it reads
48
+ * a slot tag with the requirement erased.
49
+ */
50
+ const proofLayer = (scene, sink) => {
51
+ const sceneLayer = scene.provide.reduce((a, b) => Layer.merge(a, b));
52
+ return Layer.provideMerge(sceneLayer, Layer.succeed(CaptureSink, sink.api));
53
+ };
54
+ /**
55
+ * Walk a rendered node tree and invoke any slot components it contains (JSX
56
+ * defers them to the host; headless we drive them ourselves), enqueuing the
57
+ * child each stands for. Slot components are matched by identity, so this never
58
+ * calls — and never needs hooks from — ordinary components.
59
+ */
60
+ const drive = (node, enqueueBy) => {
61
+ if (Array.isArray(node)) {
62
+ for (const child of node)
63
+ drive(child, enqueueBy);
64
+ return;
65
+ }
66
+ if (!isValidElement(node))
67
+ return;
68
+ if (node.type instanceof Function) {
69
+ const enqueue = enqueueBy.get(node.type);
70
+ if (enqueue !== undefined) {
71
+ enqueue(node.props);
72
+ return;
73
+ }
74
+ }
75
+ drive(node.props.children, enqueueBy);
76
+ };
77
+ const makeFacade = (runtime, root, sink) => {
78
+ // Slot bindings (`provide(slot, composition)`) are static, so resolve the
79
+ // whole child tree once, up front — keeping `renderTree` and slot navigation
80
+ // synchronous (no nested runtime drive).
81
+ const bindings = new Map();
82
+ // A slot's child is a plain composition or a `FeatureBinding`. A proof drives the
83
+ // composition the feature mounts; for a `default` feature that composition's logic
84
+ // is already live (its eager `.live` was merged into the scene by `provide`), so
85
+ // unwrapping is all that's needed. (Driving a `lazy` feature — load gap +
86
+ // placeholders — is the proof host's Part B increment; until then a scene wires
87
+ // features as `default`, the eagerly-resolvable form.)
88
+ const childComposition = (child) => isFeatureBinding(child) ? child.composition : child;
89
+ const collect = (comp) => Effect.gen(function* () {
90
+ for (const slotClass of Object.values(comp.manifest.slots ?? {})) {
91
+ if (bindings.has(slotClass))
92
+ continue;
93
+ const child = childComposition(yield* slotClass.tag);
94
+ bindings.set(slotClass, child);
95
+ yield* collect(child);
96
+ }
97
+ });
98
+ runtime.runSync(collect(root));
99
+ // One breadth-first render of the whole tree. Each view reports its
100
+ // `(props, events)` to the sink; each slot it renders enqueues a child (built
101
+ // on the next level), so a single sweep renders everything.
102
+ const renderLevel = (frontier) => Effect.gen(function* () {
103
+ if (frontier.length === 0)
104
+ return;
105
+ const next = [];
106
+ for (const { comp, props } of frontier) {
107
+ const service = yield* comp.tag;
108
+ // A slot renders to a deferred component (JSX `<slots.Item/>`); map each
109
+ // back to the child it stands for so the tree walk can enqueue it.
110
+ const enqueueBy = new Map();
111
+ const slots = {
112
+ slot: (name) => {
113
+ const slotClass = comp.manifest.slots?.[name];
114
+ const child = slotClass && bindings.get(slotClass);
115
+ const component = () => null;
116
+ if (child)
117
+ enqueueBy.set(component, (childProps) => next.push({ comp: child, props: childProps }));
118
+ return component;
119
+ },
120
+ };
121
+ const env = { props, tracker: { add: () => { } }, slots };
122
+ const node = yield* Composition.render(service, env);
123
+ drive(node, enqueueBy);
124
+ }
125
+ yield* renderLevel(next);
126
+ });
127
+ const renderTree = Effect.gen(function* () {
128
+ sink.reset();
129
+ yield* renderLevel([{ comp: root, props: {} }]);
130
+ });
131
+ const capturesFor = (name) => sink.captures.filter((capture) => capture.name === name);
132
+ // A cheap fingerprint of the rendered tree; when two successive renders match,
133
+ // the engine has stopped producing new state and the read is safe.
134
+ const fingerprint = () => JSON.stringify(sink.captures.map((capture) => [capture.name, capture.props]));
135
+ // Re-render until the tree is stable for two consecutive renders, or give up.
136
+ const settleFrom = (previous, remaining) => Effect.gen(function* () {
137
+ if (remaining === 0)
138
+ return;
139
+ yield* settleDrain;
140
+ yield* Effect.sleep(SETTLE_STEP);
141
+ yield* renderTree;
142
+ const current = fingerprint();
143
+ if (current === previous)
144
+ return;
145
+ yield* settleFrom(current, remaining - 1);
146
+ });
147
+ const settle = Effect.gen(function* () {
148
+ yield* settleDrain;
149
+ yield* renderTree;
150
+ yield* settleFrom(fingerprint(), SETTLE_MAX_RENDERS);
151
+ });
152
+ const triggerOf = (name, index, event) => {
153
+ const trigger = capturesFor(name)[index]?.events[event];
154
+ if (trigger === undefined) {
155
+ throw new UnknownAction({ composition: name, action: event, rendered: capturesFor(name).length });
156
+ }
157
+ return trigger;
158
+ };
159
+ const nodeFacade = (name, index, comp) => ({
160
+ props: Effect.map(renderTree, () => capturesFor(name)[index]?.props),
161
+ // `keyed` resolves event/slot names at runtime; cast the string-keyed proxy
162
+ // to the contract-typed surface. This is the one reflection boundary, the
163
+ // same seam as `definitionClass` — every name the proxy serves is a real
164
+ // contract member, so the cast is sound.
165
+ actions: keyed((event) => (payload) => renderTree.pipe(Effect.flatMap(() => Effect.sync(() => triggerOf(name, index, event)(payload))), Effect.flatMap(() => settle))),
166
+ slots: keyed((slotName) => slotFacade(comp, slotName)),
167
+ frame: settle,
168
+ });
169
+ const slotFacade = (parent, slotName) => {
170
+ const slotClass = parent.manifest.slots?.[slotName];
171
+ const child = slotClass && bindings.get(slotClass);
172
+ if (child === undefined) {
173
+ throw new UnknownSlot({ parent: uiNameOf(parent), slot: slotName });
174
+ }
175
+ const childName = uiNameOf(child);
176
+ const at = (index) => Effect.as(renderTree, nodeFacade(childName, index, child));
177
+ const first = nodeFacade(childName, 0, child);
178
+ return {
179
+ first: at(0),
180
+ at,
181
+ all: Effect.map(renderTree, () => capturesFor(childName).map((_capture, index) => nodeFacade(childName, index, child))),
182
+ // Used directly, a slot behaves as its first instance.
183
+ props: first.props,
184
+ actions: first.actions,
185
+ slots: first.slots,
186
+ frame: first.frame,
187
+ };
188
+ };
189
+ return { facade: nodeFacade(uiNameOf(root), 0, root), settle };
190
+ };
191
+ /**
192
+ * Run one proof against a fresh runtime, returning its pass/fail result. Each
193
+ * proof gets its own isolated environment, so nothing leaks between proofs. Boots
194
+ * the scene as the host would, settles, then runs the proof body to completion.
195
+ */
196
+ export const executeProof = async (proof) => {
197
+ const sink = makeSink();
198
+ const runtime = ManagedRuntime.make(proofLayer(proof.scene, sink));
199
+ try {
200
+ const { facade, settle } = makeFacade(runtime, proof.scene.composition, sink);
201
+ await runtime.runPromise(Effect.forEach(proof.scene.boot ?? [], (event) => publish('High', event)).pipe(Effect.flatMap(() => settle)));
202
+ await runtime.runPromise(Effect.gen(() => proof.body(facade)));
203
+ return { requirement: proof.requirement.manifest.statement, ok: true };
204
+ }
205
+ catch (error) {
206
+ return {
207
+ requirement: proof.requirement.manifest.statement,
208
+ ok: false,
209
+ error: error instanceof Error ? error.message : String(error),
210
+ };
211
+ }
212
+ finally {
213
+ await runtime.dispose();
214
+ }
215
+ };
216
+ /**
217
+ * Drive one proof a step at a time, recording a `StepFrame` after each yielded
218
+ * effect settles. Reuses the same makeFacade runtime/facade/settle as
219
+ * `executeProof`, but instead of handing the body to `Effect.gen` (which runs it
220
+ * to completion), it pumps the generator by hand — `gen.next(value)` yields the
221
+ * next effect, we run it on the live runtime, snapshot the sink, and feed the
222
+ * result back. So the editor gets the per-step timeline with no change to the
223
+ * proof authoring API.
224
+ */
225
+ export const driveProof = async (proof) => {
226
+ const sink = makeSink();
227
+ const runtime = ManagedRuntime.make(proofLayer(proof.scene, sink));
228
+ const frames = [];
229
+ const statement = proof.requirement.manifest.statement;
230
+ try {
231
+ const { facade, settle } = makeFacade(runtime, proof.scene.composition, sink);
232
+ await runtime.runPromise(Effect.forEach(proof.scene.boot ?? [], (event) => publish('High', event)).pipe(Effect.flatMap(() => settle)));
233
+ frames.push({ index: 0, captures: [...sink.captures] });
234
+ const generator = proof.body(facade);
235
+ // Pump the generator: run each yielded effect on the runtime, snapshot, recur.
236
+ const pump = async (input, index) => {
237
+ const step = generator.next(input);
238
+ if (step.done === true)
239
+ return;
240
+ const value = await runtime.runPromise(yieldWrapGet(step.value));
241
+ frames.push({ index, captures: [...sink.captures] });
242
+ await pump(value, index + 1);
243
+ };
244
+ await pump(undefined, 1);
245
+ return { requirement: statement, frames, ok: true };
246
+ }
247
+ catch (error) {
248
+ return {
249
+ requirement: statement,
250
+ frames,
251
+ ok: false,
252
+ error: error instanceof Error ? error.message : String(error),
253
+ };
254
+ }
255
+ finally {
256
+ await runtime.dispose();
257
+ }
258
+ };
259
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type Cause } from 'effect';
2
+ /**
3
+ * Tagged errors for the proof harness. A failed assertion or a malformed proof
4
+ * navigation throws one of these — tagged and structured, never a bare `Error` —
5
+ * so a runner can distinguish an assertion failure from a harness misuse.
6
+ */
7
+ /**
8
+ * The constructor shape `Data.TaggedError(tag)<A>` produces, named so the
9
+ * generated `.d.ts` can describe the `extends` base under `isolatedDeclarations`
10
+ * (which forbids an inferred expression in an extends clause).
11
+ */
12
+ type TaggedErrorClass<Tag extends string, A extends Record<string, unknown>> = new (args: A) => Cause.YieldableError & {
13
+ readonly _tag: Tag;
14
+ } & Readonly<A>;
15
+ declare const AssertionFailedBase: TaggedErrorClass<'reform-proof/AssertionFailed', {
16
+ readonly detail: string;
17
+ }>;
18
+ /** An `expect(...)` matcher did not hold. */
19
+ export declare class AssertionFailed extends AssertionFailedBase {
20
+ get message(): string;
21
+ }
22
+ declare const UnknownActionBase: TaggedErrorClass<'reform-proof/UnknownAction', {
23
+ readonly composition: string;
24
+ readonly action: string;
25
+ readonly rendered: number;
26
+ }>;
27
+ /** A proof referenced an action the rendered composition never exposed. */
28
+ export declare class UnknownAction extends UnknownActionBase {
29
+ get message(): string;
30
+ }
31
+ declare const UnknownSlotBase: TaggedErrorClass<'reform-proof/UnknownSlot', {
32
+ readonly parent: string;
33
+ readonly slot: string;
34
+ }>;
35
+ /** A proof navigated into a slot the parent composition does not declare. */
36
+ export declare class UnknownSlot extends UnknownSlotBase {
37
+ get message(): string;
38
+ }
39
+ export {};
40
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAQ,MAAM,QAAQ,CAAA;AAEzC;;;;GAIG;AAEH;;;;GAIG;AACH,KAAK,gBAAgB,CAAC,GAAG,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,KAC7E,IAAI,EAAE,CAAC,KACJ,KAAK,CAAC,cAAc,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;AAEhE,QAAA,MAAM,mBAAmB,EAAE,gBAAgB,CACzC,8BAA8B,EAC9B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACoD,CAAA;AAEjF,6CAA6C;AAC7C,qBAAa,eAAgB,SAAQ,mBAAmB;IACtD,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;AAED,QAAA,MAAM,iBAAiB,EAAE,gBAAgB,CACvC,4BAA4B,EAC5B;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKpF,CAAA;AAEF,2EAA2E;AAC3E,qBAAa,aAAc,SAAQ,iBAAiB;IAClD,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;AAED,QAAA,MAAM,eAAe,EAAE,gBAAgB,CACrC,0BAA0B,EAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CACgD,CAAA;AAEpG,6EAA6E;AAC7E,qBAAa,WAAY,SAAQ,eAAe;IAC9C,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF"}
package/dist/errors.js ADDED
@@ -0,0 +1,23 @@
1
+ import { Data } from 'effect';
2
+ const AssertionFailedBase = (Data.TaggedError('reform-proof/AssertionFailed'));
3
+ /** An `expect(...)` matcher did not hold. */
4
+ export class AssertionFailed extends AssertionFailedBase {
5
+ get message() {
6
+ return `reform-proof: assertion failed — ${this.detail}`;
7
+ }
8
+ }
9
+ const UnknownActionBase = (Data.TaggedError('reform-proof/UnknownAction'));
10
+ /** A proof referenced an action the rendered composition never exposed. */
11
+ export class UnknownAction extends UnknownActionBase {
12
+ get message() {
13
+ return `reform-proof: '${this.composition}' has no action '${this.action}' (rendered ${this.rendered} instance(s))`;
14
+ }
15
+ }
16
+ const UnknownSlotBase = (Data.TaggedError('reform-proof/UnknownSlot'));
17
+ /** A proof navigated into a slot the parent composition does not declare. */
18
+ export class UnknownSlot extends UnknownSlotBase {
19
+ get message() {
20
+ return `reform-proof: '${this.parent}' has no slot '${this.slot}'`;
21
+ }
22
+ }
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,QAAQ,CAAA;AAiBzC,MAAM,mBAAmB,GAGrB,CAAA,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAA8B,CAAA,CAAA;AAEjF,6CAA6C;AAC7C,MAAM,OAAO,eAAgB,SAAQ,mBAAmB;IACtD,IAAa,OAAO;QAClB,OAAO,oCAAoC,IAAI,CAAC,MAAM,EAAE,CAAA;IAC1D,CAAC;CACF;AAED,MAAM,iBAAiB,GAGnB,CAAA,IAAI,CAAC,WAAW,CAAC,4BAA4B,CAI/C,CAAA,CAAA;AAEF,2EAA2E;AAC3E,MAAM,OAAO,aAAc,SAAQ,iBAAiB;IAClD,IAAa,OAAO;QAClB,OAAO,kBAAkB,IAAI,CAAC,WAAW,oBAAoB,IAAI,CAAC,MAAM,eAAe,IAAI,CAAC,QAAQ,eAAe,CAAA;IACrH,CAAC;CACF;AAED,MAAM,eAAe,GAGjB,CAAA,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAAqD,CAAA,CAAA;AAEpG,6EAA6E;AAC7E,MAAM,OAAO,WAAY,SAAQ,eAAe;IAC9C,IAAa,OAAO;QAClB,OAAO,kBAAkB,IAAI,CAAC,MAAM,kBAAkB,IAAI,CAAC,IAAI,GAAG,CAAA;IACpE,CAAC;CACF"}
@@ -0,0 +1,176 @@
1
+ import { Effect } from 'effect';
2
+ import type { YieldWrap } from 'effect/Utils';
3
+ import { ProofRunner, type ProofRunnerApi, proofRunnerLayer, withProofRunner } from './runner';
4
+ import type { CompositionClass, CompositionService, Scene, SlotInstance, Trigger, UiCapture, UiContract } from '@playfast/reform';
5
+ export { ProofRunner, proofRunnerLayer, withProofRunner };
6
+ export type { ProofRunnerApi };
7
+ /** Any composition, used where the contract type is irrelevant. */
8
+ type AnyComposition = CompositionClass<any, any>;
9
+ export interface RequirementManifest<Comp extends AnyComposition, Statement extends string> {
10
+ readonly kind: 'ProductRequirement';
11
+ readonly name: Statement;
12
+ /** The human-readable behavioural statement; doubles as the manifest name. */
13
+ readonly statement: Statement;
14
+ /** The composition this requirement specifies — its contract types the facade. */
15
+ readonly composition: Comp;
16
+ }
17
+ export interface RequirementClass<Comp extends AnyComposition, Statement extends string> {
18
+ new (): {};
19
+ readonly manifest: RequirementManifest<Comp, Statement>;
20
+ }
21
+ export interface ProductManifest<Comp extends AnyComposition> {
22
+ readonly kind: 'Product';
23
+ readonly name: string;
24
+ readonly composition: Comp;
25
+ readonly requirements: ReadonlyArray<RequirementClass<Comp, string>>;
26
+ }
27
+ export interface ProductClass<Comp extends AnyComposition = AnyComposition> {
28
+ new (): {};
29
+ readonly manifest: ProductManifest<Comp>;
30
+ }
31
+ /**
32
+ * A named, human-readable behavioural statement bound to the composition it
33
+ * specifies. The composition types the proof facade; the statement's literal
34
+ * type is re-stated (and enforced) at `Proof.implement`.
35
+ */
36
+ declare const makeRequirement: <Comp extends AnyComposition, const Statement extends string>(composition: Comp, statement: Statement) => RequirementClass<Comp, Statement>;
37
+ export declare const ProductRequirement: {
38
+ readonly make: typeof makeRequirement;
39
+ };
40
+ /**
41
+ * Group a composition with the full list of requirements that specify it. The
42
+ * shared `Comp` type-checks that every requirement targets this composition.
43
+ */
44
+ declare const makeProduct: <Comp extends AnyComposition>(composition: Comp, config: {
45
+ readonly requirements: ReadonlyArray<RequirementClass<Comp, string>>;
46
+ }) => ProductClass<Comp>;
47
+ export declare const Product: {
48
+ readonly make: typeof makeProduct;
49
+ };
50
+ export declare const expect: <A>(actual: A) => {
51
+ toBe: (expected: A) => Effect.Effect<void>;
52
+ toEqual: (expected: A) => Effect.Effect<void>;
53
+ toContain: (expected: A extends ReadonlyArray<infer E> ? E : unknown) => Effect.Effect<void>;
54
+ };
55
+ /** The event payloads of a contract, keyed by event name. */
56
+ type EventsOf<C extends UiContract> = C extends {
57
+ events: infer E;
58
+ } ? E : Record<never, never>;
59
+ /** The slot instances of a contract, keyed by slot name. */
60
+ type SlotsOf<C extends UiContract> = C extends {
61
+ slots: infer S;
62
+ } ? S : Record<never, never>;
63
+ /** The payload a trigger accepts. */
64
+ type PayloadOf<T> = T extends Trigger<infer P> ? P : never;
65
+ /** The UI contract a composition resolves. */
66
+ export type ContractOf<Comp> = Comp extends CompositionClass<any, infer C> ? C : never;
67
+ /** The child contract a slot stands for — the contract of the composition it holds. */
68
+ type ContractOfSlot<S> = S extends SlotInstance<infer Comp> ? ContractOf<Comp> : never;
69
+ /** The contract's events as facade actions: payload in, dispatch-and-settle Effect out. */
70
+ export type ActionsOf<C extends UiContract> = {
71
+ readonly [K in keyof EventsOf<C>]: (payload: PayloadOf<EventsOf<C>[K]>) => Effect.Effect<void, never, CompositionService>;
72
+ };
73
+ /** The contract's slots as child facades, each typed by the child's own contract. */
74
+ export type SlotFacadesOf<C extends UiContract> = {
75
+ readonly [K in keyof SlotsOf<C>]: SlotFacade<ContractOfSlot<SlotsOf<C>[K]>>;
76
+ };
77
+ /** The Effect a facade action returns: dispatch the event, then settle. */
78
+ export type Action = (payload: unknown) => Effect.Effect<void, never, CompositionService>;
79
+ /**
80
+ * The handle a proof drives — the same surface the production UI receives, fully
81
+ * typed from the composition's contract `C`: read state (`props`), trigger events
82
+ * (`actions`), reach children (`slots`), and wait for the next frame (`frame`).
83
+ * Each access yields a real Effect/SlotFacade against the running engine.
84
+ */
85
+ export interface Facade<C extends UiContract> {
86
+ /** Re-render the tree and read the props this node last computed. */
87
+ readonly props: Effect.Effect<C['props'], never, CompositionService>;
88
+ /** The contract's events as callables; calling one dispatches and settles. */
89
+ readonly actions: ActionsOf<C>;
90
+ /** Child composition facades, keyed by slot name. */
91
+ readonly slots: SlotFacadesOf<C>;
92
+ /** Settle the engine and re-render — wait for the next stable frame. */
93
+ readonly frame: Effect.Effect<void, never, CompositionService>;
94
+ }
95
+ /** A slot may hold many instances (a list); it is also usable as its first one. */
96
+ export interface SlotFacade<C extends UiContract> extends Facade<C> {
97
+ readonly first: Effect.Effect<Facade<C>, never, CompositionService>;
98
+ readonly at: (index: number) => Effect.Effect<Facade<C>, never, CompositionService>;
99
+ readonly all: Effect.Effect<ReadonlyArray<Facade<C>>, never, CompositionService>;
100
+ }
101
+ /** The generator a proof body produces — its yielded effects run on the engine. */
102
+ type ProofGenerator = Generator<YieldWrap<Effect.Effect<unknown, unknown, CompositionService>>, void, unknown>;
103
+ /** An erased facade — the runtime shape before the contract type is re-attached. */
104
+ type AnyFacade = Facade<UiContract>;
105
+ /** The erased body stored on a `Proof` value; `implement` types the contract in. */
106
+ export type ProofBody = (app: AnyFacade) => ProofGenerator;
107
+ export interface Proof {
108
+ readonly requirement: RequirementClass<AnyComposition, string>;
109
+ /** The scene this proof runs against: its closed wiring + boot events. */
110
+ readonly scene: Scene;
111
+ readonly body: ProofBody;
112
+ }
113
+ export interface ProofSuite {
114
+ /** Brand so adapters can duck-type a suite among a module's exports. */
115
+ readonly kind: 'ProofSuite';
116
+ readonly product: ProductClass;
117
+ readonly proofs: ReadonlyArray<Proof>;
118
+ }
119
+ export interface ProofResult {
120
+ readonly requirement: string;
121
+ readonly ok: boolean;
122
+ readonly error?: string;
123
+ }
124
+ export interface SuiteResult {
125
+ readonly product: string;
126
+ readonly results: ReadonlyArray<ProofResult>;
127
+ readonly ok: boolean;
128
+ }
129
+ /**
130
+ * Implement (prove) a requirement by driving the facade against a scene. A proof
131
+ * is a VALUE. The scene supplies the runtime (its closed `provide` + `boot`) and
132
+ * the composition that types the facade; its contract must equal the
133
+ * requirement's composition contract, so a scene for the wrong composition — or
134
+ * one whose contract has drifted — is a compile error. The `app` facade is fully
135
+ * typed from that contract.
136
+ */
137
+ declare const implement: <Comp extends AnyComposition>(requirement: RequirementClass<Comp, string>, scene: Scene<ContractOf<Comp>>, body: (app: Facade<ContractOf<Comp>>) => ProofGenerator) => Proof;
138
+ /** Compose proofs with the product whose requirements they prove. */
139
+ declare const suite: (product: ProductClass, config: {
140
+ readonly proofs: ReadonlyArray<Proof>;
141
+ }) => ProofSuite;
142
+ /** Duck-type a `ProofSuite` among arbitrary module exports (the adapter's seam). */
143
+ export declare const isProofSuite: (value: unknown) => value is ProofSuite;
144
+ /**
145
+ * Run every proof against a fresh runtime, returning per-requirement results.
146
+ * Each proof gets its own environment, so nothing leaks between proofs. Execution
147
+ * goes through the injected `ProofRunner` (the headless engine by default).
148
+ */
149
+ declare const run: (proofSuite: ProofSuite) => Promise<SuiteResult>;
150
+ /** One step of a driven proof: the rendered tree captured right after the
151
+ * proof's i-th yielded effect settled. Index 0 is the booted, pre-drive tree. */
152
+ export interface StepFrame {
153
+ readonly index: number;
154
+ readonly captures: ReadonlyArray<UiCapture>;
155
+ }
156
+ export interface DriveResult {
157
+ readonly requirement: string;
158
+ readonly frames: ReadonlyArray<StepFrame>;
159
+ readonly ok: boolean;
160
+ readonly error?: string;
161
+ }
162
+ export interface ProofDriver {
163
+ readonly requirement: string;
164
+ /** Run the proof, snapshotting the captured tree after each yielded step. */
165
+ readonly run: () => Promise<DriveResult>;
166
+ }
167
+ /** A driver per proof in the suite — the editor's Test-play entry point. Each
168
+ * `run` resolves the same `ProofRunner` layer and steps that proof. */
169
+ declare const driver: (proofSuite: ProofSuite) => ReadonlyArray<ProofDriver>;
170
+ export declare const Proof: {
171
+ readonly implement: typeof implement;
172
+ readonly suite: typeof suite;
173
+ readonly run: typeof run;
174
+ readonly driver: typeof driver;
175
+ };
176
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ import { Effect } from 'effect';
2
+ import { AssertionFailed } from './errors';
3
+ import { ProofRunner, proofRunnerLayer, withProofRunner } from './runner';
4
+ // reform-proof — the stability story for AI authorship. Product behaviour is
5
+ // declared as human-readable requirements (the definition) and proven by tests
6
+ // (the implementation), the same split as the rest of reform. A proof drives the
7
+ // real reduce loop through a headless FACADE: no DOM, no text matching. It reads
8
+ // the props each composition computed and calls the contract's events as
9
+ // callbacks — exactly what the production UI receives. HOW a proof runs is the
10
+ // injectable `ProofRunner` layer (./runner); the headless engine (./engine) is its
11
+ // default. `Proof.run`/`Proof.driver` resolve that layer, so the vitest adapter
12
+ // can drive the very same proofs as native tests.
13
+ // The injectable execution seam is re-exported so adapters can resolve it.
14
+ export { ProofRunner, proofRunnerLayer, withProofRunner };
15
+ /**
16
+ * A named, human-readable behavioural statement bound to the composition it
17
+ * specifies. The composition types the proof facade; the statement's literal
18
+ * type is re-stated (and enforced) at `Proof.implement`.
19
+ */
20
+ const makeRequirement = (composition, statement) => Object.assign(class {
21
+ }, {
22
+ manifest: { kind: 'ProductRequirement', name: statement, statement, composition },
23
+ });
24
+ export const ProductRequirement = {
25
+ make: makeRequirement,
26
+ };
27
+ /**
28
+ * Group a composition with the full list of requirements that specify it. The
29
+ * shared `Comp` type-checks that every requirement targets this composition.
30
+ */
31
+ const makeProduct = (composition, config) => Object.assign(class {
32
+ }, {
33
+ manifest: {
34
+ kind: 'Product',
35
+ name: composition.manifest.name,
36
+ composition,
37
+ requirements: config.requirements,
38
+ },
39
+ });
40
+ export const Product = { make: makeProduct };
41
+ // ---------------------------------------------------------------------------
42
+ // Assertions — Effect-returning matchers; a failed match fails the proof.
43
+ // ---------------------------------------------------------------------------
44
+ const deepEqual = (a, b) => Object.is(a, b) || JSON.stringify(a) === JSON.stringify(b);
45
+ const fail = (message) => Effect.sync(() => {
46
+ throw new AssertionFailed({ detail: message });
47
+ });
48
+ export const expect = (actual) => ({
49
+ toBe: (expected) => Object.is(actual, expected)
50
+ ? Effect.void
51
+ : fail(`expected ${JSON.stringify(actual)} to be ${JSON.stringify(expected)}`),
52
+ toEqual: (expected) => deepEqual(actual, expected)
53
+ ? Effect.void
54
+ : fail(`expected ${JSON.stringify(actual)} to equal ${JSON.stringify(expected)}`),
55
+ toContain: (expected) => Array.isArray(actual) && actual.some((item) => deepEqual(item, expected))
56
+ ? Effect.void
57
+ : fail(`expected ${JSON.stringify(actual)} to contain ${JSON.stringify(expected)}`),
58
+ });
59
+ /**
60
+ * Implement (prove) a requirement by driving the facade against a scene. A proof
61
+ * is a VALUE. The scene supplies the runtime (its closed `provide` + `boot`) and
62
+ * the composition that types the facade; its contract must equal the
63
+ * requirement's composition contract, so a scene for the wrong composition — or
64
+ * one whose contract has drifted — is a compile error. The `app` facade is fully
65
+ * typed from that contract.
66
+ */
67
+ const implement = (requirement, scene, body) => ({ requirement, scene, body: body });
68
+ /** Compose proofs with the product whose requirements they prove. */
69
+ const suite = (product, config) => ({ kind: 'ProofSuite', product, proofs: config.proofs });
70
+ const isRecord = (value) => typeof value === 'object' && value !== null;
71
+ /** Duck-type a `ProofSuite` among arbitrary module exports (the adapter's seam). */
72
+ export const isProofSuite = (value) => isRecord(value) && value['kind'] === 'ProofSuite' && Array.isArray(value['proofs']);
73
+ /**
74
+ * Run every proof against a fresh runtime, returning per-requirement results.
75
+ * Each proof gets its own environment, so nothing leaks between proofs. Execution
76
+ * goes through the injected `ProofRunner` (the headless engine by default).
77
+ */
78
+ const run = (proofSuite) => withProofRunner(async (runner) => {
79
+ const results = [];
80
+ for (const proof of proofSuite.proofs) {
81
+ results.push(await runner.executeProof(proof));
82
+ }
83
+ return { product: proofSuite.product.manifest.name, results, ok: results.every((r) => r.ok) };
84
+ });
85
+ /** A driver per proof in the suite — the editor's Test-play entry point. Each
86
+ * `run` resolves the same `ProofRunner` layer and steps that proof. */
87
+ const driver = (proofSuite) => proofSuite.proofs.map((proof) => ({
88
+ requirement: proof.requirement.manifest.statement,
89
+ run: () => withProofRunner((runner) => runner.driveProof(proof)),
90
+ }));
91
+ export const Proof = { implement, suite, run, driver };
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Context, Layer } from 'effect';
2
+ import type { DriveResult, Proof, ProofResult } from './index';
3
+ export interface ProofRunnerApi {
4
+ /** Run a proof to completion, returning its pass/fail result. */
5
+ readonly executeProof: (proof: Proof) => Promise<ProofResult>;
6
+ /** Step a proof a frame at a time (the editor's Test-play timeline). */
7
+ readonly driveProof: (proof: Proof) => Promise<DriveResult>;
8
+ }
9
+ declare const ProofRunnerBase: Context.TagClass<ProofRunner, 'reform-proof/ProofRunner', ProofRunnerApi>;
10
+ /** The service identity for the proof execution strategy. */
11
+ export declare class ProofRunner extends ProofRunnerBase {
12
+ }
13
+ /** The default runner: the headless engine that drives the real reduce loop. */
14
+ export declare const proofRunnerLayer: Layer.Layer<ProofRunner>;
15
+ /**
16
+ * Resolve the `ProofRunner` from `proofRunnerLayer` and use it — the single seam
17
+ * the proof system (`Proof.run`/`Proof.driver`) and the vitest adapter both run
18
+ * through, so every proof executes via the same injectable engine.
19
+ */
20
+ export declare const withProofRunner: <A>(use: (runner: ProofRunnerApi) => Promise<A>) => Promise<A>;
21
+ export {};
22
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAS9D,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7D,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;CAC5D;AAED,QAAA,MAAM,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,EAAE,cAAc,CACvB,CAAA;AAExE,6DAA6D;AAC7D,qBAAa,WAAY,SAAQ,eAAe;CAAG;AAEnD,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAGpD,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAMvF,CAAA"}
package/dist/runner.js ADDED
@@ -0,0 +1,18 @@
1
+ import { Context, Effect, Layer } from 'effect';
2
+ import { driveProof, executeProof } from './engine';
3
+ const ProofRunnerBase = Context.Tag('reform-proof/ProofRunner')();
4
+ /** The service identity for the proof execution strategy. */
5
+ export class ProofRunner extends ProofRunnerBase {
6
+ }
7
+ /** The default runner: the headless engine that drives the real reduce loop. */
8
+ export const proofRunnerLayer = Layer.succeed(ProofRunner, {
9
+ executeProof,
10
+ driveProof,
11
+ });
12
+ /**
13
+ * Resolve the `ProofRunner` from `proofRunnerLayer` and use it — the single seam
14
+ * the proof system (`Proof.run`/`Proof.driver`) and the vitest adapter both run
15
+ * through, so every proof executes via the same injectable engine.
16
+ */
17
+ export const withProofRunner = (use) => Effect.runPromise(ProofRunner.pipe(Effect.flatMap((runner) => Effect.promise(() => use(runner))), Effect.provide(proofRunnerLayer)));
18
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAiBnD,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,EAA+B,CAAA;AAExE,6DAA6D;AAC7D,MAAM,OAAO,WAAY,SAAQ,eAAe;CAAG;AAEnD,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAA6B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE;IACnF,YAAY;IACZ,UAAU;CACX,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAI,GAA2C,EAAc,EAAE,CAC5F,MAAM,CAAC,UAAU,CACf,WAAW,CAAC,IAAI,CACd,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAC7D,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CACjC,CACF,CAAA"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@playfast/reform-proof",
3
+ "playbook": "./playbook",
4
+ "version": "0.0.7",
5
+ "type": "module",
6
+ "description": "Headless testing toolkit for reform — drive a scene's compositions and assert on the props they compute, with no DOM, timers, or text matching.",
7
+ "keywords": [
8
+ "reform",
9
+ "effect",
10
+ "testing",
11
+ "headless",
12
+ "proof"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/playfast/reform.git",
18
+ "directory": "packages/proof"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/playfast/reform/issues"
22
+ },
23
+ "sideEffects": false,
24
+ "exports": {
25
+ "./package.json": "./package.json",
26
+ ".": "./src/index.ts",
27
+ "./*": "./src/*.ts"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "clean": "rm -rf dist .tsbuildinfo",
35
+ "check": "tsc --noEmit",
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "coverage": "vitest run --coverage",
40
+ "lint": "oxlint src",
41
+ "lint:fix": "oxlint --fix src"
42
+ },
43
+ "peerDependencies": {
44
+ "effect": "*",
45
+ "react": "^19.0.0",
46
+ "@playfast/reform": "*"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }