@xstate/effect 0.1.0-alpha.7 → 1.0.0-alpha.9
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 +18 -12
- package/dist/declarations/src/effectActor.d.ts +1 -2
- package/dist/declarations/src/{fromEffect.d.ts → effectLogic.d.ts} +21 -21
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/schema.d.ts +2 -2
- package/dist/declarations/src/state.d.ts +8 -7
- package/dist/declarations/src/types.d.ts +1 -1
- package/dist/{state-b3d9b3e2.js → state-a2e5cb31.js} +15 -18
- package/dist/xstate-effect-atom.js +2 -2
- package/dist/xstate-effect.js +25 -25
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ Combine XState's event-driven workflows with Effect services, scopes, clocks and
|
|
|
9
9
|
|
|
10
10
|
This package is experimental. It targets XState v6 alpha and Effect 4.
|
|
11
11
|
|
|
12
|
+
In these docs, "transition effects" are the executable effect descriptors (`ExecutableEffect`) that an XState transition returns. "Effect" means an Effect.ts program.
|
|
13
|
+
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
14
16
|
<!-- package name and peer dependencies from package.json -->
|
|
@@ -29,7 +31,7 @@ A release waits for approval, expires after 30 seconds, and allows cancellation
|
|
|
29
31
|
import { Context, Effect, Schema } from 'effect';
|
|
30
32
|
import {
|
|
31
33
|
createEffectActor,
|
|
32
|
-
|
|
34
|
+
createEffectLogic,
|
|
33
35
|
send,
|
|
34
36
|
setupEffect,
|
|
35
37
|
waitFor
|
|
@@ -40,7 +42,7 @@ export class Deployments extends Context.Service<
|
|
|
40
42
|
{ readonly deploy: (release: string) => Effect.Effect<string, Error> }
|
|
41
43
|
>()('@app/Deployments') {}
|
|
42
44
|
|
|
43
|
-
const deploy =
|
|
45
|
+
const deploy = createEffectLogic({
|
|
44
46
|
schemas: { input: Schema.Struct({ release: Schema.String }) },
|
|
45
47
|
effect: ({ input }) => Deployments.use((api) => api.deploy(input.release))
|
|
46
48
|
});
|
|
@@ -134,7 +136,7 @@ Start Effect-backed logic with `createEffectActor` inside an Effect scope.
|
|
|
134
136
|
| [Atoms and React](docs/atoms-and-react.md) | Registry ownership, approval controls and useSelector. |
|
|
135
137
|
| [Testing and errors](docs/testing-and-errors.md) | TestClock, task retries, supervision and typed failures. |
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
Try the runnable examples in [examples/effect-workflows](../../examples/effect-workflows).
|
|
138
140
|
|
|
139
141
|
## Actors
|
|
140
142
|
|
|
@@ -183,13 +185,13 @@ The actor's later errors are observed through `join` or snapshots. Invalid input
|
|
|
183
185
|
|
|
184
186
|
## Effect actor logic
|
|
185
187
|
|
|
186
|
-
<!--
|
|
188
|
+
<!-- createEffectLogic, createEffectStreamLogic, createEffectEventStreamLogic from src/createEffectLogic.ts -->
|
|
187
189
|
|
|
188
190
|
| Function | Behavior |
|
|
189
191
|
| --- | --- |
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
192
|
+
| `createEffectLogic` | Run a task; its success value is the actor's output. |
|
|
193
|
+
| `createEffectStreamLogic` | Expose the latest stream item as snapshot context. |
|
|
194
|
+
| `createEffectEventStreamLogic` | Relay stream items to the parent as events. |
|
|
193
195
|
|
|
194
196
|
For example, a progress stream can feed an upload UI:
|
|
195
197
|
|
|
@@ -197,10 +199,14 @@ For example, a progress stream can feed an upload UI:
|
|
|
197
199
|
|
|
198
200
|
```ts
|
|
199
201
|
import { Effect, Option, Stream } from 'effect';
|
|
200
|
-
import {
|
|
202
|
+
import {
|
|
203
|
+
createEffectActor,
|
|
204
|
+
createEffectStreamLogic,
|
|
205
|
+
snapshots
|
|
206
|
+
} from '@xstate/effect';
|
|
201
207
|
|
|
202
208
|
// Demo upload progress. Replace with your upload SDK's progress stream.
|
|
203
|
-
const uploadProgress =
|
|
209
|
+
const uploadProgress = createEffectStreamLogic(Stream.make(0, 25, 60, 100));
|
|
204
210
|
|
|
205
211
|
const program = Effect.gen(function* () {
|
|
206
212
|
const actor = yield* createEffectActor(uploadProgress);
|
|
@@ -229,7 +235,7 @@ See [Effect actor logic](docs/effect-logic.md) for tasks and a stream that drive
|
|
|
229
235
|
<!-- effect action contract from src/setupEffect.ts; spawn guard from src/internal.ts -->
|
|
230
236
|
|
|
231
237
|
- Register background Effect actions in `setupEffect({ actions })`; enqueue them with explicit arguments.
|
|
232
|
-
- Invoke `
|
|
238
|
+
- Invoke `createEffectLogic` logic when the machine needs to wait for a result.
|
|
233
239
|
- Register spawned Effect logic in `actors`, then use `enq.spawn(args.actors.worker)`.
|
|
234
240
|
|
|
235
241
|
See [schemas and actions](docs/schemas-and-actions.md) for a complete audit example, runtime schema constraints and inline-source pitfalls.
|
|
@@ -260,6 +266,6 @@ See [testing and errors](docs/testing-and-errors.md) for executable deadline, re
|
|
|
260
266
|
|
|
261
267
|
## Tracing
|
|
262
268
|
|
|
263
|
-
<!-- span names and attributes from src/internal.ts and src/
|
|
269
|
+
<!-- span names and attributes from src/internal.ts and src/createEffectLogic.ts -->
|
|
264
270
|
|
|
265
|
-
Hosted work uses spans named `
|
|
271
|
+
Hosted work uses spans named `createEffectLogic`, `createEffectStreamLogic`, `createEffectEventStreamLogic` and `action.<name>`. Each carries `xstate.actor.id` and `xstate.actor.address`. Provide a `Tracer` to record and export them.
|
|
@@ -53,8 +53,7 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
|
|
|
53
53
|
inspect(observer: Observer<InspectionEvent> | ((event: InspectionEvent) => void)): Subscription;
|
|
54
54
|
[symbolObservable](): this;
|
|
55
55
|
toJSON(): {
|
|
56
|
-
|
|
57
|
-
id: string;
|
|
56
|
+
$actor: string;
|
|
58
57
|
};
|
|
59
58
|
}
|
|
60
59
|
export {};
|
|
@@ -4,8 +4,8 @@ import type { EffectRequirements } from "./types.js";
|
|
|
4
4
|
import { type EffectSchemaLike, type ToStandardSchema, type ValidateEffectActorSchemas } from "./schema.js";
|
|
5
5
|
declare const effectLogicBrand: unique symbol;
|
|
6
6
|
/**
|
|
7
|
-
* Type-level marker attached to logic created by `
|
|
8
|
-
* `
|
|
7
|
+
* Type-level marker attached to logic created by `createEffectLogic`,
|
|
8
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic`. It carries the logic's
|
|
9
9
|
* failure type and its Effect service requirements, which is how
|
|
10
10
|
* `RequirementsFrom` collects the `R` channel of `createEffectActor` without
|
|
11
11
|
* inspecting the logic at runtime.
|
|
@@ -17,7 +17,7 @@ export interface EffectLogicBrand<TError, TRequirements> {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Snapshot of a `
|
|
20
|
+
* Snapshot of a `createEffectLogic` actor. The actor holds no context of its own: it
|
|
21
21
|
* is `active` while the Effect runs, `done` with the Effect's success value as
|
|
22
22
|
* `output`, or `error` with the Effect's failure, defect or
|
|
23
23
|
* `EffectInterruptedError` as `error`.
|
|
@@ -41,7 +41,7 @@ export type EffectSourceArgs<TInput> = {
|
|
|
41
41
|
readonly emit: (event: AnyEventObject) => void;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* What `
|
|
44
|
+
* What `createEffectLogic` accepts as its Effect: either an Effect value, or a
|
|
45
45
|
* function of {@link EffectSourceArgs} returning one. The function form is
|
|
46
46
|
* called once per actor start, so it sees that actor's input.
|
|
47
47
|
*/
|
|
@@ -67,7 +67,7 @@ type EffectSchemaValidationConfig<TSchemas extends EffectActorSchemas> = {
|
|
|
67
67
|
readonly schemas: TSchemas;
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
|
-
* Actor logic produced by `
|
|
70
|
+
* Actor logic produced by `createEffectLogic` and `createEffectEventStreamLogic`. It is
|
|
71
71
|
* ordinary XState actor logic carrying {@link EffectLogicBrand}, so a machine
|
|
72
72
|
* can invoke or spawn it, and `createEffectActor` can collect its
|
|
73
73
|
* requirements.
|
|
@@ -76,7 +76,7 @@ export type EffectActorLogic<TOutput, TError, TInput, TRequirements> = ActorLogi
|
|
|
76
76
|
readonly id?: string;
|
|
77
77
|
} & EffectLogicBrand<TError, TRequirements>;
|
|
78
78
|
/**
|
|
79
|
-
* Snapshot of a `
|
|
79
|
+
* Snapshot of a `createEffectStreamLogic` actor. `context` holds the most recent
|
|
80
80
|
* stream item, or `undefined` before the first one. The actor reaches `done`
|
|
81
81
|
* with no output when the stream completes, and `error` when it fails.
|
|
82
82
|
*/
|
|
@@ -86,7 +86,7 @@ export type EffectStreamSnapshot<TItem, TError, TInput> = Snapshot<undefined> &
|
|
|
86
86
|
readonly error: TError | undefined;
|
|
87
87
|
};
|
|
88
88
|
/**
|
|
89
|
-
* Actor logic produced by `
|
|
89
|
+
* Actor logic produced by `createEffectStreamLogic`. Its snapshot is an
|
|
90
90
|
* {@link EffectStreamSnapshot}, so the parent reads the latest item from the
|
|
91
91
|
* child's `context` rather than from events.
|
|
92
92
|
*/
|
|
@@ -105,11 +105,11 @@ export type EffectStreamActorLogic<TItem, TError, TInput, TRequirements> = Actor
|
|
|
105
105
|
* interruption. Use `withActorScope` to retain a resource until the owning
|
|
106
106
|
* Effect actor stops.
|
|
107
107
|
*/
|
|
108
|
-
export declare function
|
|
108
|
+
export declare function createEffectLogic<const TInputSchema extends EffectSchemaLike, const TOutputSchema extends EffectSchemaLike, TError = unknown, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, SchemaOutput<TInputSchema>, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
109
109
|
input: TInputSchema;
|
|
110
110
|
output: TOutputSchema;
|
|
111
111
|
}>): EffectActorLogic<SchemaOutput<TOutputSchema>, TError, SchemaOutput<TInputSchema>, TRequirements>;
|
|
112
|
-
export declare function
|
|
112
|
+
export declare function createEffectLogic<const TInputSchema extends EffectSchemaLike, TEffect extends Effect.Effect<any, any, any>>(config: Omit<EffectSourceConfig<EffectSuccess<TEffect>, EffectFailure<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>, {
|
|
113
113
|
input: TInputSchema;
|
|
114
114
|
}>, 'effect' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
115
115
|
input: TInputSchema;
|
|
@@ -117,17 +117,17 @@ export declare function fromEffect<const TInputSchema extends EffectSchemaLike,
|
|
|
117
117
|
}> & {
|
|
118
118
|
effect: TEffect | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TEffect);
|
|
119
119
|
}): EffectActorLogic<EffectSuccess<TEffect>, EffectFailure<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>>;
|
|
120
|
-
export declare function
|
|
120
|
+
export declare function createEffectLogic<const TOutputSchema extends EffectSchemaLike, TError = unknown, TInput = undefined, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, TInput, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
121
121
|
input?: never;
|
|
122
122
|
output: TOutputSchema;
|
|
123
123
|
}>): EffectActorLogic<SchemaOutput<TOutputSchema>, TError, TInput, TRequirements>;
|
|
124
|
-
export declare function
|
|
124
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectSourceConfig<TOutput, TError, TInput, TRequirements> & {
|
|
125
125
|
schemas?: undefined;
|
|
126
126
|
}): EffectActorLogic<TOutput, TError, TInput, TRequirements>;
|
|
127
|
-
export declare function
|
|
128
|
-
export declare function
|
|
127
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TRequirements = never>(effect: Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, undefined, TRequirements>;
|
|
128
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(effect: (args: EffectSourceArgs<TInput>) => Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, TInput, TRequirements>;
|
|
129
129
|
/**
|
|
130
|
-
* What `
|
|
130
|
+
* What `createEffectStreamLogic` and `createEffectEventStreamLogic` accept as their stream:
|
|
131
131
|
* either a Stream value, or a function of {@link EffectSourceArgs} returning
|
|
132
132
|
* one. The function form is called once per actor start.
|
|
133
133
|
*/
|
|
@@ -153,29 +153,29 @@ type StreamRequirements<T> = T extends Stream.Stream<any, any, infer R> ? R : ne
|
|
|
153
153
|
* `schemas`, `validator` and `stream`. Its resources close when the stream
|
|
154
154
|
* completes, fails or is interrupted.
|
|
155
155
|
*/
|
|
156
|
-
export declare function
|
|
156
|
+
export declare function createEffectStreamLogic<const TInputSchema extends EffectSchemaLike, TStream extends Stream.Stream<any, any, any>>(config: Omit<EffectStreamConfig<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>, 'stream' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
157
157
|
input: TInputSchema;
|
|
158
158
|
}> & {
|
|
159
159
|
stream: TStream | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TStream);
|
|
160
160
|
}): EffectStreamActorLogic<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>;
|
|
161
|
-
export declare function
|
|
161
|
+
export declare function createEffectStreamLogic<TItem, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TItem, TError, TInput, TRequirements> & {
|
|
162
162
|
schemas?: undefined;
|
|
163
163
|
}): EffectStreamActorLogic<TItem, TError, TInput, TRequirements>;
|
|
164
|
-
export declare function
|
|
164
|
+
export declare function createEffectStreamLogic<TItem, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TItem, TError, TInput, TRequirements>): EffectStreamActorLogic<TItem, TError, TInput, TRequirements>;
|
|
165
165
|
/**
|
|
166
166
|
* Creates actor logic that runs a Stream of events and relays each item to the
|
|
167
167
|
* parent machine as an event, the way `fromEventObservable` does. The actor
|
|
168
168
|
* has no output: it reaches `done` when the stream completes and `error` when
|
|
169
|
-
* it fails. Accepts the same forms as `
|
|
169
|
+
* it fails. Accepts the same forms as `createEffectStreamLogic`. Its resources close
|
|
170
170
|
* when the stream completes, fails or is interrupted.
|
|
171
171
|
*/
|
|
172
|
-
export declare function
|
|
172
|
+
export declare function createEffectEventStreamLogic<const TInputSchema extends EffectSchemaLike, TStream extends Stream.Stream<EventObject, any, any>>(config: Omit<EffectStreamConfig<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>, 'stream' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
173
173
|
input: TInputSchema;
|
|
174
174
|
}> & {
|
|
175
175
|
stream: TStream | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TStream);
|
|
176
176
|
}): EffectActorLogic<undefined, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>;
|
|
177
|
-
export declare function
|
|
177
|
+
export declare function createEffectEventStreamLogic<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TEvent, TError, TInput, TRequirements> & {
|
|
178
178
|
schemas?: undefined;
|
|
179
179
|
}): EffectActorLogic<undefined, TError, TInput, TRequirements>;
|
|
180
|
-
export declare function
|
|
180
|
+
export declare function createEffectEventStreamLogic<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TEvent, TError, TInput, TRequirements>): EffectActorLogic<undefined, TError, TInput, TRequirements>;
|
|
181
181
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createEffectLogic, createEffectEventStreamLogic, createEffectStreamLogic, type EffectActorLogic, type EffectLogicBrand, type EffectSnapshot, type EffectSource, type EffectSourceArgs, type EffectStreamSource, type EffectStreamActorLogic, type EffectStreamSnapshot } from "./effectLogic.js";
|
|
2
2
|
export { createEffectActor, type EffectActorOptions } from "./createEffectActor.js";
|
|
3
3
|
export { EffectActor } from "./effectActor.js";
|
|
4
4
|
export { ActorScope, withActorScope } from "./actorScope.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Schema } from 'effect';
|
|
2
2
|
import type { SetupSchemas, SetupStateSchema, SetupStateSchemas, StandardSchemaV1 } from 'xstate';
|
|
3
3
|
/**
|
|
4
|
-
* An Effect `Schema` accepted by this package. `setupEffect`, `
|
|
5
|
-
* `
|
|
4
|
+
* An Effect `Schema` accepted by this package. `setupEffect`, `createEffectLogic`,
|
|
5
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic` convert it to a Standard
|
|
6
6
|
* Schema, so XState infers the decoded `Schema.Type` without an explicit call
|
|
7
7
|
* to `Schema.toStandardSchemaV1`.
|
|
8
8
|
*/
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { AnyMachineSnapshot, AnyStateMachine, MachineContext, MachineSnapshot, SnapshotFrom, StateContextFromStateValue, StateSchema, StateValueFromStateSchema } from 'xstate';
|
|
2
2
|
type IsUnion<T, U = T> = T extends unknown ? [U] extends [T] ? false : true : never;
|
|
3
3
|
type Join<TPrefix extends string, TKey extends string> = TPrefix extends '' ? TKey : `${TPrefix}.${TKey}`;
|
|
4
|
-
/** The tag of the machine itself, used when the root state is parallel. */
|
|
5
|
-
declare const MACHINE_TAG = "(machine)";
|
|
6
4
|
/**
|
|
7
5
|
* The dotted path of a state value: `'idle'`, `'form.editing'`. A parallel
|
|
8
|
-
* state has no single path, so its tag stops at the parallel state
|
|
9
|
-
*
|
|
6
|
+
* state has no single path, so its tag stops at the parallel state. When the
|
|
7
|
+
* machine itself is parallel, the tag is `TRootTag`: the machine id, or `''`
|
|
8
|
+
* for a machine without one.
|
|
10
9
|
*/
|
|
11
|
-
export type StateTag<TValue, TPrefix extends string = ''> = string extends TValue ? string : TValue extends string ? Join<TPrefix, TValue> : TValue extends object ? string extends keyof TValue ? string : IsUnion<keyof TValue & string> extends true ? TPrefix extends '' ?
|
|
12
|
-
[K in keyof TValue & string]: StateTag<TValue[K], Join<TPrefix, K
|
|
10
|
+
export type StateTag<TValue, TPrefix extends string = '', TRootTag extends string = ''> = string extends TValue ? string : TValue extends string ? Join<TPrefix, TValue> : TValue extends object ? string extends keyof TValue ? string : IsUnion<keyof TValue & string> extends true ? TPrefix extends '' ? TRootTag : TPrefix : {
|
|
11
|
+
[K in keyof TValue & string]: StateTag<TValue[K], Join<TPrefix, K>, TRootTag>;
|
|
13
12
|
}[keyof TValue & string] : never;
|
|
14
13
|
type TaggedStateOf<TSnapshot, TValue, TContext extends MachineContext, TSchema extends StateSchema> = TValue extends unknown ? {
|
|
15
|
-
readonly _tag: StateTag<TValue
|
|
14
|
+
readonly _tag: StateTag<TValue, '', TSchema extends {
|
|
15
|
+
id: infer TId extends string;
|
|
16
|
+
} ? TId : ''>;
|
|
16
17
|
readonly value: TValue;
|
|
17
18
|
readonly context: StateContextFromStateValue<TSchema, TContext, TValue>;
|
|
18
19
|
readonly snapshot: TSnapshot;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effect, Scope } from 'effect';
|
|
2
|
-
import type { EffectLogicBrand } from "./
|
|
2
|
+
import type { EffectLogicBrand } from "./effectLogic.js";
|
|
3
3
|
import { effectActionBrand } from "./brands.js";
|
|
4
4
|
import type { ActorScope } from "./actorScope.js";
|
|
5
5
|
/** The `R` channel of an Effect type, or `never` for anything else. */
|
|
@@ -146,8 +146,7 @@ class EffectActor {
|
|
|
146
146
|
}
|
|
147
147
|
toJSON() {
|
|
148
148
|
return {
|
|
149
|
-
|
|
150
|
-
id: this.id
|
|
149
|
+
$actor: this.address
|
|
151
150
|
};
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -416,7 +415,7 @@ function withActorScope(effect) {
|
|
|
416
415
|
}
|
|
417
416
|
|
|
418
417
|
const XSTATE_TIMER = 'xstate.timer';
|
|
419
|
-
const XSTATE_INIT = '
|
|
418
|
+
const XSTATE_INIT = 'xstate.init';
|
|
420
419
|
|
|
421
420
|
/** Options for {@link createEffectActor}. */
|
|
422
421
|
|
|
@@ -454,7 +453,7 @@ function createEffectActor(logic, ...[options]) {
|
|
|
454
453
|
Queue.offerUnsafe(mailbox, item);
|
|
455
454
|
}
|
|
456
455
|
};
|
|
457
|
-
const timerKey = (source,
|
|
456
|
+
const timerKey = (source, timer) => `${source.sessionId}:${JSON.stringify(timer)}`;
|
|
458
457
|
const inspectors = new Set();
|
|
459
458
|
let rootAnnounced = false;
|
|
460
459
|
const clock = Context.get(context, Clock.Clock);
|
|
@@ -513,14 +512,14 @@ function createEffectActor(logic, ...[options]) {
|
|
|
513
512
|
}
|
|
514
513
|
source._emit(event);
|
|
515
514
|
},
|
|
516
|
-
scheduleTimer: (source,
|
|
517
|
-
const key = timerKey(source,
|
|
515
|
+
scheduleTimer: (source, timer, delay) => {
|
|
516
|
+
const key = timerKey(source, timer);
|
|
518
517
|
timers.get(key)?.interruptUnsafe();
|
|
519
518
|
const fiber = Fiber.runIn(runFork(Effect.andThen(Effect.sleep(Duration.millis(delay)), Effect.sync(() => {
|
|
520
519
|
timers.delete(key);
|
|
521
520
|
const timerEvent = {
|
|
522
521
|
type: XSTATE_TIMER,
|
|
523
|
-
|
|
522
|
+
...timer
|
|
524
523
|
};
|
|
525
524
|
if (isRoot(source)) {
|
|
526
525
|
offer(timerEvent);
|
|
@@ -530,8 +529,8 @@ function createEffectActor(logic, ...[options]) {
|
|
|
530
529
|
}))), actorScope);
|
|
531
530
|
timers.set(key, fiber);
|
|
532
531
|
},
|
|
533
|
-
cancelTimer: (source,
|
|
534
|
-
const key = timerKey(source,
|
|
532
|
+
cancelTimer: (source, timer) => {
|
|
533
|
+
const key = timerKey(source, timer);
|
|
535
534
|
timers.get(key)?.interruptUnsafe();
|
|
536
535
|
timers.delete(key);
|
|
537
536
|
},
|
|
@@ -716,13 +715,11 @@ class NotReadyError extends Data.TaggedError('NotReadyError') {
|
|
|
716
715
|
}
|
|
717
716
|
}
|
|
718
717
|
|
|
719
|
-
/** The tag of the machine itself, used when the root state is parallel. */
|
|
720
|
-
const MACHINE_TAG = '(machine)';
|
|
721
|
-
|
|
722
718
|
/**
|
|
723
719
|
* The dotted path of a state value: `'idle'`, `'form.editing'`. A parallel
|
|
724
|
-
* state has no single path, so its tag stops at the parallel state
|
|
725
|
-
*
|
|
720
|
+
* state has no single path, so its tag stops at the parallel state. When the
|
|
721
|
+
* machine itself is parallel, the tag is `TRootTag`: the machine id, or `''`
|
|
722
|
+
* for a machine without one.
|
|
726
723
|
*/
|
|
727
724
|
|
|
728
725
|
/**
|
|
@@ -734,16 +731,16 @@ const MACHINE_TAG = '(machine)';
|
|
|
734
731
|
/** The tagged state union of a machine. */
|
|
735
732
|
|
|
736
733
|
/** Computes the dotted path tag of a state value. */
|
|
737
|
-
function stateTag(value, prefix = '') {
|
|
734
|
+
function stateTag(value, rootTag, prefix = '') {
|
|
738
735
|
if (typeof value === 'string') {
|
|
739
736
|
return prefix ? `${prefix}.${value}` : value;
|
|
740
737
|
}
|
|
741
738
|
const keys = Object.keys(value);
|
|
742
739
|
if (keys.length !== 1) {
|
|
743
|
-
return prefix ||
|
|
740
|
+
return prefix || rootTag;
|
|
744
741
|
}
|
|
745
742
|
const key = keys[0];
|
|
746
|
-
return stateTag(value[key], prefix ? `${prefix}.${key}` : key);
|
|
743
|
+
return stateTag(value[key], rootTag, prefix ? `${prefix}.${key}` : key);
|
|
747
744
|
}
|
|
748
745
|
|
|
749
746
|
/**
|
|
@@ -763,7 +760,7 @@ function stateTag(value, prefix = '') {
|
|
|
763
760
|
*/
|
|
764
761
|
function taggedState(snapshot) {
|
|
765
762
|
return {
|
|
766
|
-
_tag: stateTag(snapshot.value),
|
|
763
|
+
_tag: stateTag(snapshot.value, snapshot.machine.id),
|
|
767
764
|
value: snapshot.value,
|
|
768
765
|
context: snapshot.context,
|
|
769
766
|
snapshot
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Cause } from 'effect';
|
|
2
2
|
import { Atom, AsyncResult } from 'effect/reactivity';
|
|
3
|
-
import { c as createEffectActor, N as NotReadyError, t as taggedState } from './state-
|
|
4
|
-
export { N as NotReadyError } from './state-
|
|
3
|
+
import { c as createEffectActor, N as NotReadyError, t as taggedState } from './state-a2e5cb31.js';
|
|
4
|
+
export { N as NotReadyError } from './state-a2e5cb31.js';
|
|
5
5
|
import 'xstate';
|
|
6
6
|
import 'xstate/durable';
|
|
7
7
|
|
package/dist/xstate-effect.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Schema, Effect, Stream, Exit, Cause, Queue } from 'effect';
|
|
2
|
-
import {
|
|
3
|
-
import { s as startHostedEffect, r as relayToParent, E as EffectInterruptedError, A as ActorStoppedError, a as runHostedEffect } from './state-
|
|
4
|
-
export { d as ActorScope, A as ActorStoppedError, b as EffectActor, E as EffectInterruptedError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-
|
|
2
|
+
import { createStepLogic, setup } from 'xstate';
|
|
3
|
+
import { s as startHostedEffect, r as relayToParent, E as EffectInterruptedError, A as ActorStoppedError, a as runHostedEffect } from './state-a2e5cb31.js';
|
|
4
|
+
export { d as ActorScope, A as ActorStoppedError, b as EffectActor, E as EffectInterruptedError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-a2e5cb31.js';
|
|
5
5
|
import { dual } from 'effect/Function';
|
|
6
6
|
import 'xstate/durable';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* An Effect `Schema` accepted by this package. `setupEffect`, `
|
|
10
|
-
* `
|
|
9
|
+
* An Effect `Schema` accepted by this package. `setupEffect`, `createEffectLogic`,
|
|
10
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic` convert it to a Standard
|
|
11
11
|
* Schema, so XState infers the decoded `Schema.Type` without an explicit call
|
|
12
12
|
* to `Schema.toStandardSchemaV1`.
|
|
13
13
|
*/
|
|
@@ -74,7 +74,7 @@ function toStandardSetupStates(states) {
|
|
|
74
74
|
}));
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const EFFECT_INIT = '
|
|
77
|
+
const EFFECT_INIT = 'xstate.init';
|
|
78
78
|
const EFFECT_RESOLVE = 'xstate.effect.resolve';
|
|
79
79
|
const EFFECT_REJECT = 'xstate.effect.reject';
|
|
80
80
|
const EFFECT_NEXT = 'xstate.effect.next';
|
|
@@ -82,15 +82,15 @@ const EFFECT_COMPLETE = 'xstate.effect.complete';
|
|
|
82
82
|
const effectLogicBrand = Symbol.for('@xstate/effect/logic');
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Type-level marker attached to logic created by `
|
|
86
|
-
* `
|
|
85
|
+
* Type-level marker attached to logic created by `createEffectLogic`,
|
|
86
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic`. It carries the logic's
|
|
87
87
|
* failure type and its Effect service requirements, which is how
|
|
88
88
|
* `RequirementsFrom` collects the `R` channel of `createEffectActor` without
|
|
89
89
|
* inspecting the logic at runtime.
|
|
90
90
|
*/
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* Snapshot of a `
|
|
93
|
+
* Snapshot of a `createEffectLogic` actor. The actor holds no context of its own: it
|
|
94
94
|
* is `active` while the Effect runs, `done` with the Effect's success value as
|
|
95
95
|
* `output`, or `error` with the Effect's failure, defect or
|
|
96
96
|
* `EffectInterruptedError` as `error`.
|
|
@@ -104,26 +104,26 @@ const effectLogicBrand = Symbol.for('@xstate/effect/logic');
|
|
|
104
104
|
*/
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* What `
|
|
107
|
+
* What `createEffectLogic` accepts as its Effect: either an Effect value, or a
|
|
108
108
|
* function of {@link EffectSourceArgs} returning one. The function form is
|
|
109
109
|
* called once per actor start, so it sees that actor's input.
|
|
110
110
|
*/
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
* Actor logic produced by `
|
|
113
|
+
* Actor logic produced by `createEffectLogic` and `createEffectEventStreamLogic`. It is
|
|
114
114
|
* ordinary XState actor logic carrying {@link EffectLogicBrand}, so a machine
|
|
115
115
|
* can invoke or spawn it, and `createEffectActor` can collect its
|
|
116
116
|
* requirements.
|
|
117
117
|
*/
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
* Snapshot of a `
|
|
120
|
+
* Snapshot of a `createEffectStreamLogic` actor. `context` holds the most recent
|
|
121
121
|
* stream item, or `undefined` before the first one. The actor reaches `done`
|
|
122
122
|
* with no output when the stream completes, and `error` when it fails.
|
|
123
123
|
*/
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* Actor logic produced by `
|
|
126
|
+
* Actor logic produced by `createEffectStreamLogic`. Its snapshot is an
|
|
127
127
|
* {@link EffectStreamSnapshot}, so the parent reads the latest item from the
|
|
128
128
|
* child's `context` rather than from events.
|
|
129
129
|
*/
|
|
@@ -174,7 +174,7 @@ function brandLogic(logic, error, requirements) {
|
|
|
174
174
|
* Effect actor stops.
|
|
175
175
|
*/
|
|
176
176
|
|
|
177
|
-
function
|
|
177
|
+
function createEffectLogic(sourceOrConfig) {
|
|
178
178
|
const config = typeof sourceOrConfig === 'function' || Effect.isEffect(sourceOrConfig) ? {
|
|
179
179
|
effect: sourceOrConfig
|
|
180
180
|
} : sourceOrConfig;
|
|
@@ -187,7 +187,7 @@ function fromEffect(sourceOrConfig) {
|
|
|
187
187
|
output: toStandardSchema(config.schemas.output)
|
|
188
188
|
} : {})
|
|
189
189
|
} : undefined;
|
|
190
|
-
const logic =
|
|
190
|
+
const logic = createStepLogic({
|
|
191
191
|
id: config.id,
|
|
192
192
|
validator: config.validator,
|
|
193
193
|
schemas,
|
|
@@ -216,7 +216,7 @@ function fromEffect(sourceOrConfig) {
|
|
|
216
216
|
return;
|
|
217
217
|
}
|
|
218
218
|
const effect = typeof source === 'function' ? source(createSourceArgs(input, self, system)) : source;
|
|
219
|
-
enq.effect(() => startHostedEffect(self, Effect.scoped(effect), '
|
|
219
|
+
enq.effect(() => startHostedEffect(self, Effect.scoped(effect), 'createEffectLogic', exit => {
|
|
220
220
|
if (self.getSnapshot().status !== 'active') {
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
@@ -238,7 +238,7 @@ function fromEffect(sourceOrConfig) {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
241
|
-
* What `
|
|
241
|
+
* What `createEffectStreamLogic` and `createEffectEventStreamLogic` accept as their stream:
|
|
242
242
|
* either a Stream value, or a function of {@link EffectSourceArgs} returning
|
|
243
243
|
* one. The function form is called once per actor start.
|
|
244
244
|
*/
|
|
@@ -263,10 +263,10 @@ function toLogicSchemas(schemas) {
|
|
|
263
263
|
* completes, fails or is interrupted.
|
|
264
264
|
*/
|
|
265
265
|
|
|
266
|
-
function
|
|
266
|
+
function createEffectStreamLogic(sourceOrConfig) {
|
|
267
267
|
const config = resolveStreamConfig(sourceOrConfig);
|
|
268
268
|
const stream = config.stream;
|
|
269
|
-
const logic =
|
|
269
|
+
const logic = createStepLogic({
|
|
270
270
|
id: config.id,
|
|
271
271
|
validator: config.validator,
|
|
272
272
|
schemas: toLogicSchemas(config.schemas),
|
|
@@ -307,7 +307,7 @@ function fromEffectStream(sourceOrConfig) {
|
|
|
307
307
|
});
|
|
308
308
|
}
|
|
309
309
|
}));
|
|
310
|
-
enq.effect(() => startHostedEffect(self, Effect.scoped(consume), '
|
|
310
|
+
enq.effect(() => startHostedEffect(self, Effect.scoped(consume), 'createEffectStreamLogic', exit => {
|
|
311
311
|
if (self.getSnapshot().status !== 'active') {
|
|
312
312
|
return;
|
|
313
313
|
}
|
|
@@ -331,14 +331,14 @@ function fromEffectStream(sourceOrConfig) {
|
|
|
331
331
|
* Creates actor logic that runs a Stream of events and relays each item to the
|
|
332
332
|
* parent machine as an event, the way `fromEventObservable` does. The actor
|
|
333
333
|
* has no output: it reaches `done` when the stream completes and `error` when
|
|
334
|
-
* it fails. Accepts the same forms as `
|
|
334
|
+
* it fails. Accepts the same forms as `createEffectStreamLogic`. Its resources close
|
|
335
335
|
* when the stream completes, fails or is interrupted.
|
|
336
336
|
*/
|
|
337
337
|
|
|
338
|
-
function
|
|
338
|
+
function createEffectEventStreamLogic(sourceOrConfig) {
|
|
339
339
|
const config = resolveStreamConfig(sourceOrConfig);
|
|
340
340
|
const stream = config.stream;
|
|
341
|
-
const logic =
|
|
341
|
+
const logic = createStepLogic({
|
|
342
342
|
id: config.id,
|
|
343
343
|
validator: config.validator,
|
|
344
344
|
schemas: toLogicSchemas(config.schemas),
|
|
@@ -367,7 +367,7 @@ function fromEffectEventStream(sourceOrConfig) {
|
|
|
367
367
|
}
|
|
368
368
|
const streamValue = typeof stream === 'function' ? stream(createSourceArgs(input, self, system)) : stream;
|
|
369
369
|
const consume = Stream.runForEach(streamValue, value => Effect.sync(() => relayToParent(self, value)));
|
|
370
|
-
enq.effect(() => startHostedEffect(self, Effect.scoped(consume), '
|
|
370
|
+
enq.effect(() => startHostedEffect(self, Effect.scoped(consume), 'createEffectEventStreamLogic', exit => {
|
|
371
371
|
if (self.getSnapshot().status !== 'active') {
|
|
372
372
|
return;
|
|
373
373
|
}
|
|
@@ -710,4 +710,4 @@ function setupEffect(config = {}) {
|
|
|
710
710
|
}));
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
-
export {
|
|
713
|
+
export { createEffectEventStreamLogic, createEffectLogic, createEffectStreamLogic, deadLetters, emitted, inspect, join, send, setupEffect, snapshots, waitFor };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xstate/effect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-alpha.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Effect integration for XState",
|
|
6
6
|
"keywords": [
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"effect": "^4.0.0",
|
|
33
|
-
"xstate": "^6.0.0-alpha.
|
|
33
|
+
"xstate": "^6.0.0-alpha.70"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"effect": "4.0.0",
|
|
37
37
|
"fast-check": "4.10.2",
|
|
38
|
-
"xstate": "^6.0.0-alpha.
|
|
38
|
+
"xstate": "^6.0.0-alpha.70"
|
|
39
39
|
},
|
|
40
40
|
"preconstruct": {
|
|
41
41
|
"entrypoints": [
|