effect-machine 0.1.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 +221 -0
- package/package.json +63 -0
- package/src/actor.ts +942 -0
- package/src/cluster/entity-machine.ts +202 -0
- package/src/cluster/index.ts +43 -0
- package/src/cluster/to-entity.ts +99 -0
- package/src/errors.ts +64 -0
- package/src/index.ts +102 -0
- package/src/inspection.ts +132 -0
- package/src/internal/brands.ts +51 -0
- package/src/internal/transition.ts +427 -0
- package/src/internal/utils.ts +80 -0
- package/src/machine.ts +685 -0
- package/src/persistence/adapter.ts +169 -0
- package/src/persistence/adapters/in-memory.ts +275 -0
- package/src/persistence/index.ts +24 -0
- package/src/persistence/persistent-actor.ts +601 -0
- package/src/persistence/persistent-machine.ts +131 -0
- package/src/schema.ts +316 -0
- package/src/slot.ts +281 -0
- package/src/testing.ts +282 -0
- package/tsconfig.json +68 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Schema, Schedule } from "effect";
|
|
2
|
+
|
|
3
|
+
import type { Machine } from "../machine.js";
|
|
4
|
+
import type { StateBrand, EventBrand } from "../internal/brands.js";
|
|
5
|
+
import { MissingSchemaError } from "../errors.js";
|
|
6
|
+
|
|
7
|
+
// Branded type constraints
|
|
8
|
+
type BrandedState = { readonly _tag: string } & StateBrand;
|
|
9
|
+
type BrandedEvent = { readonly _tag: string } & EventBrand;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for persistence behavior (after resolution).
|
|
13
|
+
* Schemas are required at runtime - the persist function ensures this.
|
|
14
|
+
*
|
|
15
|
+
* Note: Schema types S and E should match the structural shape of the machine's
|
|
16
|
+
* state and event types (without brands). The schemas don't know about brands.
|
|
17
|
+
*/
|
|
18
|
+
export interface PersistenceConfig<S, E, SSI = unknown, ESI = unknown> {
|
|
19
|
+
/**
|
|
20
|
+
* Schedule controlling when snapshots are taken.
|
|
21
|
+
* Input is the new state after each transition.
|
|
22
|
+
*
|
|
23
|
+
* Examples:
|
|
24
|
+
* - Schedule.forever — snapshot every transition
|
|
25
|
+
* - Schedule.spaced("5 seconds") — debounced snapshots
|
|
26
|
+
* - Schedule.recurs(100) — every N transitions
|
|
27
|
+
*/
|
|
28
|
+
readonly snapshotSchedule: Schedule.Schedule<unknown, S>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether to journal events for replay capability.
|
|
32
|
+
* When true, all events are appended to the event log.
|
|
33
|
+
*/
|
|
34
|
+
readonly journalEvents: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Schema for serializing/deserializing state.
|
|
38
|
+
* Always present at runtime (resolved from config or machine).
|
|
39
|
+
*/
|
|
40
|
+
readonly stateSchema: Schema.Schema<S, SSI, never>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Schema for serializing/deserializing events.
|
|
44
|
+
* Always present at runtime (resolved from config or machine).
|
|
45
|
+
*/
|
|
46
|
+
readonly eventSchema: Schema.Schema<E, ESI, never>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* User-provided identifier for the machine type.
|
|
50
|
+
* Used for filtering actors in restoreAll.
|
|
51
|
+
* Optional — defaults to "unknown" if not provided.
|
|
52
|
+
*/
|
|
53
|
+
readonly machineType?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Machine with persistence configuration attached.
|
|
58
|
+
* Spawn auto-detects this and returns PersistentActorRef.
|
|
59
|
+
*/
|
|
60
|
+
export interface PersistentMachine<
|
|
61
|
+
S extends { readonly _tag: string },
|
|
62
|
+
E extends { readonly _tag: string },
|
|
63
|
+
R = never,
|
|
64
|
+
> {
|
|
65
|
+
readonly _tag: "PersistentMachine";
|
|
66
|
+
readonly machine: Machine<S, E, R>;
|
|
67
|
+
readonly persistence: PersistenceConfig<S, E>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Type guard to check if a value is a PersistentMachine
|
|
72
|
+
*/
|
|
73
|
+
export const isPersistentMachine = (
|
|
74
|
+
value: unknown,
|
|
75
|
+
): value is PersistentMachine<{ readonly _tag: string }, { readonly _tag: string }, unknown> =>
|
|
76
|
+
typeof value === "object" &&
|
|
77
|
+
value !== null &&
|
|
78
|
+
"_tag" in value &&
|
|
79
|
+
(value as { _tag: unknown })._tag === "PersistentMachine";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Attach persistence configuration to a machine.
|
|
83
|
+
*
|
|
84
|
+
* Schemas are read from the machine - must use `Machine.make({ state, event, initial })`.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const orderMachine = Machine.make({
|
|
89
|
+
* state: OrderState,
|
|
90
|
+
* event: OrderEvent,
|
|
91
|
+
* initial: OrderState.Idle(),
|
|
92
|
+
* }).pipe(
|
|
93
|
+
* Machine.on(OrderState.Idle, OrderEvent.Submit, ({ event }) =>
|
|
94
|
+
* OrderState.Pending({ orderId: event.orderId })
|
|
95
|
+
* ),
|
|
96
|
+
* Machine.final(OrderState.Paid),
|
|
97
|
+
* Machine.persist({
|
|
98
|
+
* snapshotSchedule: Schedule.forever,
|
|
99
|
+
* journalEvents: true,
|
|
100
|
+
* }),
|
|
101
|
+
* );
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export interface WithPersistenceConfig {
|
|
105
|
+
readonly snapshotSchedule: Schedule.Schedule<unknown, { readonly _tag: string }>;
|
|
106
|
+
readonly journalEvents: boolean;
|
|
107
|
+
readonly machineType?: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const persist =
|
|
111
|
+
(config: WithPersistenceConfig) =>
|
|
112
|
+
<S extends BrandedState, E extends BrandedEvent, R>(
|
|
113
|
+
machine: Machine<S, E, R>,
|
|
114
|
+
): PersistentMachine<S, E, R> => {
|
|
115
|
+
const stateSchema = machine.stateSchema;
|
|
116
|
+
const eventSchema = machine.eventSchema;
|
|
117
|
+
|
|
118
|
+
if (stateSchema === undefined || eventSchema === undefined) {
|
|
119
|
+
throw new MissingSchemaError({ operation: "persist" });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
_tag: "PersistentMachine",
|
|
124
|
+
machine,
|
|
125
|
+
persistence: {
|
|
126
|
+
...config,
|
|
127
|
+
stateSchema,
|
|
128
|
+
eventSchema,
|
|
129
|
+
} as unknown as PersistenceConfig<S, E>,
|
|
130
|
+
};
|
|
131
|
+
};
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-first State/Event definitions for effect-machine.
|
|
3
|
+
*
|
|
4
|
+
* MachineSchema provides a single source of truth that combines:
|
|
5
|
+
* - Schema for validation/serialization
|
|
6
|
+
* - Variant constructors (like Data.taggedEnum)
|
|
7
|
+
* - $is and $match helpers for pattern matching
|
|
8
|
+
* - Brand integration for compile-time safety
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { State, Event, Machine } from "effect-machine"
|
|
13
|
+
*
|
|
14
|
+
* // Define schema-first state
|
|
15
|
+
* const OrderState = State({
|
|
16
|
+
* Pending: { orderId: Schema.String },
|
|
17
|
+
* Shipped: { trackingId: Schema.String },
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // Infer type from schema
|
|
21
|
+
* type OrderState = typeof OrderState.Type
|
|
22
|
+
*
|
|
23
|
+
* // Use constructors
|
|
24
|
+
* const pending = OrderState.Pending({ orderId: "123" })
|
|
25
|
+
*
|
|
26
|
+
* // Pattern match
|
|
27
|
+
* OrderState.$match(state, {
|
|
28
|
+
* Pending: (s) => `Order ${s.orderId} pending`,
|
|
29
|
+
* Shipped: (s) => `Shipped: ${s.trackingId}`,
|
|
30
|
+
* })
|
|
31
|
+
*
|
|
32
|
+
* // Use as Schema for persistence/cluster
|
|
33
|
+
* machine.pipe(Machine.persist({ stateSchema: OrderState, ... }))
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import { Schema } from "effect";
|
|
39
|
+
import type { FullStateBrand, FullEventBrand } from "./internal/brands.js";
|
|
40
|
+
import { InvalidSchemaError, MissingMatchHandlerError } from "./errors.js";
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Type Helpers
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract the TypeScript type from a TaggedStruct schema
|
|
48
|
+
*/
|
|
49
|
+
type TaggedStructType<Tag extends string, Fields extends Schema.Struct.Fields> = Schema.Schema.Type<
|
|
50
|
+
Schema.TaggedStruct<Tag, Fields>
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build variant schemas type from definition
|
|
55
|
+
*/
|
|
56
|
+
type VariantSchemas<D extends Record<string, Schema.Struct.Fields>> = {
|
|
57
|
+
readonly [K in keyof D & string]: Schema.TaggedStruct<K, D[K]>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build union type from variant schemas.
|
|
62
|
+
* Used for constraining fluent method type params.
|
|
63
|
+
*/
|
|
64
|
+
export type VariantsUnion<D extends Record<string, Schema.Struct.Fields>> = {
|
|
65
|
+
[K in keyof D & string]: TaggedStructType<K, D[K]>;
|
|
66
|
+
}[keyof D & string];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if fields are empty (no required properties)
|
|
70
|
+
*/
|
|
71
|
+
type IsEmptyFields<Fields extends Schema.Struct.Fields> = keyof Fields extends never ? true : false;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Constructor functions for each variant.
|
|
75
|
+
* Empty structs: plain values with `_tag`: `State.Idle`
|
|
76
|
+
* Non-empty structs require args: `State.Loading({ url })`
|
|
77
|
+
*/
|
|
78
|
+
type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = {
|
|
79
|
+
readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true
|
|
80
|
+
? TaggedStructType<K, D[K]> & Brand
|
|
81
|
+
: (args: Schema.Struct.Constructor<D[K]>) => TaggedStructType<K, D[K]> & Brand;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Pattern matching cases type
|
|
86
|
+
*/
|
|
87
|
+
type MatchCases<D extends Record<string, Schema.Struct.Fields>, R> = {
|
|
88
|
+
readonly [K in keyof D & string]: (value: TaggedStructType<K, D[K]>) => R;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Base schema interface with pattern matching helpers
|
|
93
|
+
*/
|
|
94
|
+
interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Brand> {
|
|
95
|
+
/**
|
|
96
|
+
* Per-variant schemas for fine-grained operations
|
|
97
|
+
*/
|
|
98
|
+
readonly variants: VariantSchemas<D>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Type guard: `OrderState.$is("Pending")(value)`
|
|
102
|
+
*/
|
|
103
|
+
readonly $is: <Tag extends keyof D & string>(
|
|
104
|
+
tag: Tag,
|
|
105
|
+
) => (u: unknown) => u is TaggedStructType<Tag, D[Tag]> & Brand;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Pattern matching (curried and uncurried)
|
|
109
|
+
*/
|
|
110
|
+
readonly $match: {
|
|
111
|
+
// Curried: $match(cases)(value)
|
|
112
|
+
<R>(cases: MatchCases<D, R>): (value: VariantsUnion<D> & Brand) => R;
|
|
113
|
+
// Uncurried: $match(value, cases)
|
|
114
|
+
<R>(value: VariantsUnion<D> & Brand, cases: MatchCases<D, R>): R;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// MachineStateSchema Type
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Schema-first state definition that provides:
|
|
124
|
+
* - Schema for encode/decode/validate
|
|
125
|
+
* - Variant constructors: `OrderState.Pending({ orderId: "x" })`
|
|
126
|
+
* - Pattern matching: `$is`, `$match`
|
|
127
|
+
* - Type inference: `typeof OrderState.Type`
|
|
128
|
+
*
|
|
129
|
+
* The D type parameter captures the definition, creating a unique brand
|
|
130
|
+
* per distinct schema definition shape.
|
|
131
|
+
*/
|
|
132
|
+
export type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<
|
|
133
|
+
VariantsUnion<D> & FullStateBrand<D>,
|
|
134
|
+
VariantsUnion<D>,
|
|
135
|
+
never
|
|
136
|
+
> &
|
|
137
|
+
MachineSchemaBase<D, FullStateBrand<D>> &
|
|
138
|
+
VariantConstructors<D, FullStateBrand<D>>;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Schema-first event definition (same structure as state, different brand)
|
|
142
|
+
*
|
|
143
|
+
* The D type parameter captures the definition, creating a unique brand
|
|
144
|
+
* per distinct schema definition shape.
|
|
145
|
+
*/
|
|
146
|
+
export type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<
|
|
147
|
+
VariantsUnion<D> & FullEventBrand<D>,
|
|
148
|
+
VariantsUnion<D>,
|
|
149
|
+
never
|
|
150
|
+
> &
|
|
151
|
+
MachineSchemaBase<D, FullEventBrand<D>> &
|
|
152
|
+
VariantConstructors<D, FullEventBrand<D>>;
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// Implementation
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Build a schema-first definition from a record of tag -> fields
|
|
160
|
+
*/
|
|
161
|
+
const buildMachineSchema = <D extends Record<string, Schema.Struct.Fields>>(
|
|
162
|
+
definition: D,
|
|
163
|
+
): {
|
|
164
|
+
schema: Schema.Schema<VariantsUnion<D>, VariantsUnion<D>, never>;
|
|
165
|
+
variants: VariantSchemas<D>;
|
|
166
|
+
constructors: Record<string, (args: Record<string, unknown>) => Record<string, unknown>>;
|
|
167
|
+
$is: <Tag extends string>(tag: Tag) => (u: unknown) => boolean;
|
|
168
|
+
$match: (valueOrCases: unknown, maybeCases?: unknown) => unknown;
|
|
169
|
+
} => {
|
|
170
|
+
// Build variant schemas
|
|
171
|
+
const variants = {} as Record<string, Schema.TaggedStruct<string, Schema.Struct.Fields>>;
|
|
172
|
+
const constructors = {} as Record<
|
|
173
|
+
string,
|
|
174
|
+
(args: Record<string, unknown>) => Record<string, unknown>
|
|
175
|
+
>;
|
|
176
|
+
|
|
177
|
+
for (const tag of Object.keys(definition)) {
|
|
178
|
+
const fields = definition[tag];
|
|
179
|
+
if (fields === undefined) continue;
|
|
180
|
+
|
|
181
|
+
const variantSchema = Schema.TaggedStruct(tag, fields);
|
|
182
|
+
variants[tag] = variantSchema;
|
|
183
|
+
|
|
184
|
+
// Create constructor that builds tagged struct directly
|
|
185
|
+
// Like Data.taggedEnum, this doesn't validate at construction time
|
|
186
|
+
// Use Schema.decode for validation when needed
|
|
187
|
+
const hasFields = Object.keys(fields).length > 0;
|
|
188
|
+
|
|
189
|
+
if (hasFields) {
|
|
190
|
+
// Non-empty: constructor function requiring args
|
|
191
|
+
const constructor = (args: Record<string, unknown>) => ({ ...args, _tag: tag });
|
|
192
|
+
constructor._tag = tag;
|
|
193
|
+
constructors[tag] = constructor;
|
|
194
|
+
} else {
|
|
195
|
+
// Empty: plain value, not callable
|
|
196
|
+
constructors[tag] = { _tag: tag } as never;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Build union schema from all variants
|
|
201
|
+
const variantArray = Object.values(variants);
|
|
202
|
+
if (variantArray.length === 0) {
|
|
203
|
+
throw new InvalidSchemaError();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Schema.Union requires at least 2 members, handle single variant case
|
|
207
|
+
const unionSchema =
|
|
208
|
+
variantArray.length === 1
|
|
209
|
+
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- checked length above
|
|
210
|
+
variantArray[0]!
|
|
211
|
+
: // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic schema union
|
|
212
|
+
Schema.Union(...(variantArray as [any, any, ...any[]]));
|
|
213
|
+
|
|
214
|
+
// Type guard
|
|
215
|
+
const $is =
|
|
216
|
+
<Tag extends string>(tag: Tag) =>
|
|
217
|
+
(u: unknown): boolean =>
|
|
218
|
+
typeof u === "object" && u !== null && "_tag" in u && (u as { _tag: string })._tag === tag;
|
|
219
|
+
|
|
220
|
+
// Pattern matching
|
|
221
|
+
const $match = (valueOrCases: unknown, maybeCases?: unknown): unknown => {
|
|
222
|
+
if (maybeCases !== undefined) {
|
|
223
|
+
// Uncurried: $match(value, cases)
|
|
224
|
+
const value = valueOrCases as { _tag: string };
|
|
225
|
+
const cases = maybeCases as Record<string, (v: unknown) => unknown>;
|
|
226
|
+
const handler = cases[value._tag];
|
|
227
|
+
if (handler === undefined) {
|
|
228
|
+
throw new MissingMatchHandlerError({ tag: value._tag });
|
|
229
|
+
}
|
|
230
|
+
return handler(value);
|
|
231
|
+
}
|
|
232
|
+
// Curried: $match(cases) -> (value) => result
|
|
233
|
+
const cases = valueOrCases as Record<string, (v: unknown) => unknown>;
|
|
234
|
+
return (value: { _tag: string }): unknown => {
|
|
235
|
+
const handler = cases[value._tag];
|
|
236
|
+
if (handler === undefined) {
|
|
237
|
+
throw new MissingMatchHandlerError({ tag: value._tag });
|
|
238
|
+
}
|
|
239
|
+
return handler(value);
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
schema: unionSchema as unknown as Schema.Schema<VariantsUnion<D>, VariantsUnion<D>, never>,
|
|
245
|
+
variants: variants as unknown as VariantSchemas<D>,
|
|
246
|
+
constructors,
|
|
247
|
+
$is,
|
|
248
|
+
$match,
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Internal helper to create a machine schema (shared by State and Event).
|
|
254
|
+
* Builds the schema object with variants, constructors, $is, and $match.
|
|
255
|
+
*/
|
|
256
|
+
const createMachineSchema = <D extends Record<string, Schema.Struct.Fields>>(definition: D) => {
|
|
257
|
+
const { schema, variants, constructors, $is, $match } = buildMachineSchema(definition);
|
|
258
|
+
return Object.assign(Object.create(schema), { variants, $is, $match, ...constructors });
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Create a schema-first State definition.
|
|
263
|
+
*
|
|
264
|
+
* The schema's definition type D creates a unique brand, preventing
|
|
265
|
+
* accidental use of constructors from different state schemas
|
|
266
|
+
* (unless they have identical definitions).
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* const OrderState = MachineSchema.State({
|
|
271
|
+
* Pending: { orderId: Schema.String },
|
|
272
|
+
* Shipped: { trackingId: Schema.String },
|
|
273
|
+
* })
|
|
274
|
+
*
|
|
275
|
+
* type OrderState = typeof OrderState.Type
|
|
276
|
+
*
|
|
277
|
+
* // Construct
|
|
278
|
+
* const s = OrderState.Pending({ orderId: "123" })
|
|
279
|
+
*
|
|
280
|
+
* // Pattern match
|
|
281
|
+
* OrderState.$match(s, {
|
|
282
|
+
* Pending: (v) => v.orderId,
|
|
283
|
+
* Shipped: (v) => v.trackingId,
|
|
284
|
+
* })
|
|
285
|
+
*
|
|
286
|
+
* // Validate
|
|
287
|
+
* Schema.decodeUnknownSync(OrderState)(rawJson)
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
export const State = <const D extends Record<string, Schema.Struct.Fields>>(
|
|
291
|
+
definition: D,
|
|
292
|
+
): MachineStateSchema<D> => createMachineSchema(definition) as MachineStateSchema<D>;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Create a schema-first Event definition.
|
|
296
|
+
*
|
|
297
|
+
* The schema's definition type D creates a unique brand, preventing
|
|
298
|
+
* accidental use of constructors from different event schemas
|
|
299
|
+
* (unless they have identical definitions).
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```ts
|
|
303
|
+
* const OrderEvent = MachineSchema.Event({
|
|
304
|
+
* Ship: { trackingId: Schema.String },
|
|
305
|
+
* Cancel: {},
|
|
306
|
+
* })
|
|
307
|
+
*
|
|
308
|
+
* type OrderEvent = typeof OrderEvent.Type
|
|
309
|
+
*
|
|
310
|
+
* // Construct
|
|
311
|
+
* const e = OrderEvent.Ship({ trackingId: "abc" })
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
export const Event = <const D extends Record<string, Schema.Struct.Fields>>(
|
|
315
|
+
definition: D,
|
|
316
|
+
): MachineEventSchema<D> => createMachineSchema(definition) as MachineEventSchema<D>;
|