@typeonce/effect-machine 0.29.0 → 0.31.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
@@ -249,8 +249,9 @@ const definition = Machine.make({
249
249
  })
250
250
  ```
251
251
 
252
- Handlers see both protocols. Typed `send` and `Machine.plan` accept only public
253
- events. Event tags must be unique and public/internal tags must be disjoint.
252
+ Handlers see both protocols. Typed `send`, `Machine.can`, and `Machine.plan`
253
+ accept only public events. Event tags must be unique and public/internal tags
254
+ must be disjoint.
254
255
 
255
256
  Export the descriptor returned by `Machine.events` instead of exporting its
256
257
  schemas. This keeps the deferred constructors as the standard way to create
@@ -554,6 +555,23 @@ remain total and cannot use declinable transitions. Completion and invocation
554
555
  outcomes have no ancestor candidate: declining one ignores that lifecycle
555
556
  occurrence and leaves the current configuration active.
556
557
 
558
+ Use `Machine.can` when a caller needs to test a concrete event against a
559
+ snapshot. The direct and machine-specialized forms have the same semantics:
560
+
561
+ ```ts
562
+ const canSubmit = yield * Machine.can(machine, snapshot, Submit({ draft }))
563
+
564
+ const canMachine = Machine.can(machine)
565
+ const canCancel = yield * canMachine(snapshot, Cancel())
566
+ ```
567
+
568
+ `can` returns `true` when at least one required or non-declined handler accepts
569
+ the event. Targetless transitions count as accepted. Invalid event input fails
570
+ with `MachineSchemaDecodeError`; a valid unhandled event returns `false`.
571
+ Declinable resolvers run to decide acceptance, but collected commands,
572
+ emissions, and raised events are discarded. Required resolvers and transition
573
+ lifecycle do not run.
574
+
557
575
  ## Statechart capabilities
558
576
 
559
577
  `Machine.states` supports:
@@ -746,6 +764,19 @@ const counterAtom = AtomMachine.bind(runtime).make(Counter)
746
764
 
747
765
  Binding a shared runtime once is the canonical form for service-backed
748
766
  applications. Service-free machines can use `AtomMachine.make(Counter)`.
767
+ Use `factory` when the same definition constructs several independent bridges:
768
+
769
+ ```ts
770
+ const MachineAtoms = AtomMachine.bind(runtime)
771
+ const makeProcessMachine = MachineAtoms.factory(ProcessMachine)
772
+
773
+ const first = makeProcessMachine({ processId: "first" })
774
+ const second = makeProcessMachine({ processId: "second" })
775
+ type ProcessMachineAtom = ReturnType<typeof makeProcessMachine>
776
+ ```
777
+
778
+ Each call creates a fresh lazy bridge. `factory` does not cache by input;
779
+ `AtomMachine.family` remains the keyed shared-identity interface.
749
780
 
750
781
  The bridge exposes `ref`, `snapshot`, `state`, fail-aware `result`, writable
751
782
  `send` and `stop` atoms, and `child(descriptor)`. Use `AtomMachine.select`,
@@ -753,6 +784,19 @@ The bridge exposes `ref`, `snapshot`, `state`, fail-aware `result`, writable
753
784
  equality-aware derivations. Repeating one of these calls with the same bridge
754
785
  and state path returns the same atom.
755
786
 
787
+ Use `AtomMachine.can` to project concrete event acceptance. Declare the
788
+ projection once and apply it to compatible bridges:
789
+
790
+ ```ts
791
+ const submitAllowed = AtomMachine.can(Events.Submit({ draft }))
792
+ const canSubmitAtom = submitAllowed(machineAtom)
793
+ ```
794
+
795
+ Pass an `Atom<EventInput>` instead when the event payload changes reactively.
796
+ Each projection returns the same derived atom for repeated applications to one
797
+ bridge. Startup and runtime failures remain typed, while done and stopped
798
+ machines return `false`.
799
+
756
800
  Use `useMachineAtom` from `@typeonce/effect-machine-react` when one React
757
801
  subtree owns the machine. It mounts the machine without subscribing the owner
758
802
  to state. Pass the returned machine atom through props or Context, then call
@@ -769,6 +813,7 @@ preserving lazy registry startup and disposal:
769
813
  ```ts
770
814
  const processAtoms = AtomMachine.bind(runtime).family(processMachine, {
771
815
  atoms: {
816
+ canStart: AtomMachine.can(ProcessEvents.Start()),
772
817
  details: AtomMachine.select("Processing"),
773
818
  ready: AtomMachine.matches("Ready"),
774
819
  send: (machine) => machine.send
package/dist/Machine.d.ts CHANGED
@@ -5104,6 +5104,44 @@ export declare const configuration: <M extends Machine.Any>(machine: M, state: M
5104
5104
  * @since 0.4.0
5105
5105
  */
5106
5106
  export declare const enabled: <const States extends Machine.StateSchemas, const Events extends ReadonlyArray<Machine.TaggedSchema>, const Emits extends ReadonlyArray<Machine.TaggedSchema>, const Input extends Schema.Top = typeof Schema.Void, UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>, E = never, R = never, InitialE = never, InitialR = never, FinalStates extends Machine.StateIdentifier<States> = never, Output = never, OutputStates extends Machine.StateIdentifier<States> = never, InputEvents extends ReadonlyArray<Machine.TaggedSchema> = Events, ParentEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly []>(machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits, OutputStates, InputEvents, ParentEvents>, state: Machine.Snapshot<States>) => ReadonlyArray<Machine.TagOf<Events[number]>>;
5107
+ /**
5108
+ * Tests whether a concrete event would select at least one transition from a
5109
+ * decoded snapshot.
5110
+ *
5111
+ * **Details**
5112
+ *
5113
+ * Required handlers are accepted from their structural eligibility.
5114
+ * Declinable handlers run their resolver only far enough to decide whether
5115
+ * they accept the event. Any commands, emissions, or raised events collected
5116
+ * during that check are discarded.
5117
+ *
5118
+ * Event input is decoded through the machine's public event protocol. Invalid
5119
+ * input fails with `MachineSchemaDecodeError`. Final snapshots and valid events
5120
+ * with no accepting handler return `false`.
5121
+ *
5122
+ * **Gotchas**
5123
+ *
5124
+ * This query does not execute transitions or stabilize the resulting machine.
5125
+ * It does not run entry, exit, always, completion, child lifecycle, or command
5126
+ * effects. A `true` result therefore describes event acceptance only.
5127
+ *
5128
+ * **Example**
5129
+ *
5130
+ * ```ts
5131
+ * const canCheckout = Machine.can(checkoutMachine)
5132
+ *
5133
+ * const canSubmit = yield* canCheckout(snapshot, {
5134
+ * _tag: "SubmitOrder"
5135
+ * })
5136
+ * ```
5137
+ *
5138
+ * @category getters
5139
+ * @since 0.30.0
5140
+ */
5141
+ export declare const can: {
5142
+ <const States extends Machine.StateSchemas, const Events extends ReadonlyArray<Machine.TaggedSchema>, const Emits extends ReadonlyArray<Machine.TaggedSchema>, const Input extends Schema.Top = typeof Schema.Void, UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>, E = never, R = never, InitialE = never, InitialR = never, FinalStates extends Machine.StateIdentifier<States> = never, Output = never, OutputStates extends Machine.StateIdentifier<States> = never, InputEvents extends ReadonlyArray<Machine.TaggedSchema> = Events, ParentEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly []>(machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits, OutputStates, InputEvents, ParentEvents> & EnsureExecutable<States, UnhandledStates, OutputStates> & Machine.RootCompatible<ParentEvents>): (state: Machine.Snapshot<States>, event: Machine.EventInputOf<InputEvents>) => Effect.Effect<boolean, MachineSchemaDecodeError>;
5143
+ <const States extends Machine.StateSchemas, const Events extends ReadonlyArray<Machine.TaggedSchema>, const Emits extends ReadonlyArray<Machine.TaggedSchema>, const Input extends Schema.Top = typeof Schema.Void, UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>, E = never, R = never, InitialE = never, InitialR = never, FinalStates extends Machine.StateIdentifier<States> = never, Output = never, OutputStates extends Machine.StateIdentifier<States> = never, InputEvents extends ReadonlyArray<Machine.TaggedSchema> = Events, ParentEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly []>(machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits, OutputStates, InputEvents, ParentEvents> & EnsureExecutable<States, UnhandledStates, OutputStates> & Machine.RootCompatible<ParentEvents>, state: Machine.Snapshot<States>, event: Machine.EventInputOf<InputEvents>): Effect.Effect<boolean, MachineSchemaDecodeError>;
5144
+ };
5107
5145
  /**
5108
5146
  * Plans the next state snapshot synchronously.
5109
5147
  *