effect-machine 0.3.0 → 0.3.2
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/dist/_virtual/_rolldown/runtime.js +18 -0
- package/dist/actor.d.ts +251 -0
- package/dist/actor.js +385 -0
- package/dist/cluster/entity-machine.d.ts +90 -0
- package/dist/cluster/entity-machine.js +74 -0
- package/dist/cluster/index.d.ts +3 -0
- package/dist/cluster/index.js +4 -0
- package/dist/cluster/to-entity.d.ts +64 -0
- package/dist/cluster/to-entity.js +53 -0
- package/dist/errors.d.ts +61 -0
- package/dist/errors.js +38 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/inspection.d.ts +125 -0
- package/dist/inspection.js +50 -0
- package/dist/internal/brands.d.ts +40 -0
- package/dist/internal/brands.js +0 -0
- package/dist/internal/inspection.d.ts +11 -0
- package/dist/internal/inspection.js +15 -0
- package/dist/internal/transition.d.ts +159 -0
- package/dist/internal/transition.js +235 -0
- package/dist/internal/utils.d.ts +52 -0
- package/dist/internal/utils.js +31 -0
- package/dist/machine.d.ts +271 -0
- package/dist/machine.js +317 -0
- package/{src/persistence/adapter.ts → dist/persistence/adapter.d.ts} +40 -72
- package/dist/persistence/adapter.js +27 -0
- package/dist/persistence/adapters/in-memory.d.ts +32 -0
- package/dist/persistence/adapters/in-memory.js +176 -0
- package/dist/persistence/index.d.ts +5 -0
- package/dist/persistence/index.js +6 -0
- package/dist/persistence/persistent-actor.d.ts +50 -0
- package/dist/persistence/persistent-actor.js +348 -0
- package/{src/persistence/persistent-machine.ts → dist/persistence/persistent-machine.d.ts} +28 -54
- package/dist/persistence/persistent-machine.js +24 -0
- package/dist/schema.d.ts +141 -0
- package/dist/schema.js +165 -0
- package/dist/slot.d.ts +128 -0
- package/dist/slot.js +99 -0
- package/dist/testing.d.ts +142 -0
- package/dist/testing.js +131 -0
- package/package.json +18 -7
- package/src/actor.ts +0 -1050
- package/src/cluster/entity-machine.ts +0 -201
- package/src/cluster/index.ts +0 -43
- package/src/cluster/to-entity.ts +0 -99
- package/src/errors.ts +0 -64
- package/src/index.ts +0 -105
- package/src/inspection.ts +0 -178
- package/src/internal/brands.ts +0 -51
- package/src/internal/inspection.ts +0 -18
- package/src/internal/transition.ts +0 -489
- package/src/internal/utils.ts +0 -80
- package/src/machine.ts +0 -836
- package/src/persistence/adapters/in-memory.ts +0 -294
- package/src/persistence/index.ts +0 -24
- package/src/persistence/persistent-actor.ts +0 -791
- package/src/schema.ts +0 -362
- package/src/slot.ts +0 -281
- package/src/testing.ts +0 -284
- package/tsconfig.json +0 -65
package/src/schema.ts
DELETED
|
@@ -1,362 +0,0 @@
|
|
|
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
|
-
* Each variant also has a `derive` method for constructing from a source object.
|
|
79
|
-
*/
|
|
80
|
-
/**
|
|
81
|
-
* Constructor functions for each variant.
|
|
82
|
-
* Empty structs: plain values with `_tag`: `State.Idle`
|
|
83
|
-
* Non-empty structs require args: `State.Loading({ url })`
|
|
84
|
-
*
|
|
85
|
-
* Each variant also has a `derive` method for constructing from a source object.
|
|
86
|
-
* The source type uses `object` to accept branded state types without index signature issues.
|
|
87
|
-
*/
|
|
88
|
-
type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = {
|
|
89
|
-
readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true
|
|
90
|
-
? TaggedStructType<K, D[K]> &
|
|
91
|
-
Brand & {
|
|
92
|
-
readonly derive: (source: object) => TaggedStructType<K, D[K]> & Brand;
|
|
93
|
-
}
|
|
94
|
-
: ((args: Schema.Struct.Constructor<D[K]>) => TaggedStructType<K, D[K]> & Brand) & {
|
|
95
|
-
readonly derive: (
|
|
96
|
-
source: object,
|
|
97
|
-
partial?: Partial<Schema.Struct.Constructor<D[K]>>,
|
|
98
|
-
) => TaggedStructType<K, D[K]> & Brand;
|
|
99
|
-
readonly _tag: K;
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Pattern matching cases type
|
|
105
|
-
*/
|
|
106
|
-
type MatchCases<D extends Record<string, Schema.Struct.Fields>, R> = {
|
|
107
|
-
readonly [K in keyof D & string]: (value: TaggedStructType<K, D[K]>) => R;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Base schema interface with pattern matching helpers
|
|
112
|
-
*/
|
|
113
|
-
interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Brand> {
|
|
114
|
-
/**
|
|
115
|
-
* Raw definition record for introspection
|
|
116
|
-
*/
|
|
117
|
-
readonly _definition: D;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Per-variant schemas for fine-grained operations
|
|
121
|
-
*/
|
|
122
|
-
readonly variants: VariantSchemas<D>;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Type guard: `OrderState.$is("Pending")(value)`
|
|
126
|
-
*/
|
|
127
|
-
readonly $is: <Tag extends keyof D & string>(
|
|
128
|
-
tag: Tag,
|
|
129
|
-
) => (u: unknown) => u is TaggedStructType<Tag, D[Tag]> & Brand;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Pattern matching (curried and uncurried)
|
|
133
|
-
*/
|
|
134
|
-
readonly $match: {
|
|
135
|
-
// Curried: $match(cases)(value)
|
|
136
|
-
<R>(cases: MatchCases<D, R>): (value: VariantsUnion<D> & Brand) => R;
|
|
137
|
-
// Uncurried: $match(value, cases)
|
|
138
|
-
<R>(value: VariantsUnion<D> & Brand, cases: MatchCases<D, R>): R;
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ============================================================================
|
|
143
|
-
// MachineStateSchema Type
|
|
144
|
-
// ============================================================================
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Schema-first state definition that provides:
|
|
148
|
-
* - Schema for encode/decode/validate
|
|
149
|
-
* - Variant constructors: `OrderState.Pending({ orderId: "x" })`
|
|
150
|
-
* - Pattern matching: `$is`, `$match`
|
|
151
|
-
* - Type inference: `typeof OrderState.Type`
|
|
152
|
-
*
|
|
153
|
-
* The D type parameter captures the definition, creating a unique brand
|
|
154
|
-
* per distinct schema definition shape.
|
|
155
|
-
*/
|
|
156
|
-
export type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<
|
|
157
|
-
VariantsUnion<D> & FullStateBrand<D>,
|
|
158
|
-
VariantsUnion<D>,
|
|
159
|
-
never
|
|
160
|
-
> &
|
|
161
|
-
MachineSchemaBase<D, FullStateBrand<D>> &
|
|
162
|
-
VariantConstructors<D, FullStateBrand<D>>;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Schema-first event definition (same structure as state, different brand)
|
|
166
|
-
*
|
|
167
|
-
* The D type parameter captures the definition, creating a unique brand
|
|
168
|
-
* per distinct schema definition shape.
|
|
169
|
-
*/
|
|
170
|
-
export type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Schema<
|
|
171
|
-
VariantsUnion<D> & FullEventBrand<D>,
|
|
172
|
-
VariantsUnion<D>,
|
|
173
|
-
never
|
|
174
|
-
> &
|
|
175
|
-
MachineSchemaBase<D, FullEventBrand<D>> &
|
|
176
|
-
VariantConstructors<D, FullEventBrand<D>>;
|
|
177
|
-
|
|
178
|
-
// ============================================================================
|
|
179
|
-
// Implementation
|
|
180
|
-
// ============================================================================
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Build a schema-first definition from a record of tag -> fields
|
|
184
|
-
*/
|
|
185
|
-
const buildMachineSchema = <D extends Record<string, Schema.Struct.Fields>>(
|
|
186
|
-
definition: D,
|
|
187
|
-
): {
|
|
188
|
-
schema: Schema.Schema<VariantsUnion<D>, VariantsUnion<D>, never>;
|
|
189
|
-
variants: VariantSchemas<D>;
|
|
190
|
-
constructors: Record<string, (args: Record<string, unknown>) => Record<string, unknown>>;
|
|
191
|
-
_definition: D;
|
|
192
|
-
$is: <Tag extends string>(tag: Tag) => (u: unknown) => boolean;
|
|
193
|
-
$match: (valueOrCases: unknown, maybeCases?: unknown) => unknown;
|
|
194
|
-
} => {
|
|
195
|
-
// Build variant schemas
|
|
196
|
-
const variants = {} as Record<string, Schema.TaggedStruct<string, Schema.Struct.Fields>>;
|
|
197
|
-
const constructors = {} as Record<
|
|
198
|
-
string,
|
|
199
|
-
(args: Record<string, unknown>) => Record<string, unknown>
|
|
200
|
-
>;
|
|
201
|
-
|
|
202
|
-
for (const tag of Object.keys(definition)) {
|
|
203
|
-
const fields = definition[tag];
|
|
204
|
-
if (fields === undefined) continue;
|
|
205
|
-
|
|
206
|
-
const variantSchema = Schema.TaggedStruct(tag, fields);
|
|
207
|
-
variants[tag] = variantSchema;
|
|
208
|
-
|
|
209
|
-
// Create constructor that builds tagged struct directly
|
|
210
|
-
// Like Data.taggedEnum, this doesn't validate at construction time
|
|
211
|
-
// Use Schema.decode for validation when needed
|
|
212
|
-
const fieldNames = new Set(Object.keys(fields));
|
|
213
|
-
const hasFields = fieldNames.size > 0;
|
|
214
|
-
|
|
215
|
-
if (hasFields) {
|
|
216
|
-
// Non-empty: constructor function requiring args
|
|
217
|
-
const constructor = (args: Record<string, unknown>) => ({ ...args, _tag: tag });
|
|
218
|
-
constructor._tag = tag;
|
|
219
|
-
constructor.derive = (source: Record<string, unknown>, partial?: Record<string, unknown>) => {
|
|
220
|
-
const result: Record<string, unknown> = { _tag: tag };
|
|
221
|
-
for (const key of fieldNames) {
|
|
222
|
-
if (key in source) result[key] = source[key];
|
|
223
|
-
}
|
|
224
|
-
if (partial !== undefined) {
|
|
225
|
-
for (const [key, value] of Object.entries(partial)) {
|
|
226
|
-
result[key] = value;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return result;
|
|
230
|
-
};
|
|
231
|
-
constructors[tag] = constructor;
|
|
232
|
-
} else {
|
|
233
|
-
// Empty: plain value, not callable
|
|
234
|
-
constructors[tag] = { _tag: tag, derive: () => ({ _tag: tag }) } as never;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Build union schema from all variants
|
|
239
|
-
const variantArray = Object.values(variants);
|
|
240
|
-
if (variantArray.length === 0) {
|
|
241
|
-
throw new InvalidSchemaError();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Schema.Union requires at least 2 members, handle single variant case
|
|
245
|
-
const unionSchema =
|
|
246
|
-
variantArray.length === 1
|
|
247
|
-
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- checked length above
|
|
248
|
-
variantArray[0]!
|
|
249
|
-
: // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic schema union
|
|
250
|
-
Schema.Union(...(variantArray as [any, any, ...any[]]));
|
|
251
|
-
|
|
252
|
-
// Type guard
|
|
253
|
-
const $is =
|
|
254
|
-
<Tag extends string>(tag: Tag) =>
|
|
255
|
-
(u: unknown): boolean =>
|
|
256
|
-
typeof u === "object" && u !== null && "_tag" in u && (u as { _tag: string })._tag === tag;
|
|
257
|
-
|
|
258
|
-
// Pattern matching
|
|
259
|
-
const $match = (valueOrCases: unknown, maybeCases?: unknown): unknown => {
|
|
260
|
-
if (maybeCases !== undefined) {
|
|
261
|
-
// Uncurried: $match(value, cases)
|
|
262
|
-
const value = valueOrCases as { _tag: string };
|
|
263
|
-
const cases = maybeCases as Record<string, (v: unknown) => unknown>;
|
|
264
|
-
const handler = cases[value._tag];
|
|
265
|
-
if (handler === undefined) {
|
|
266
|
-
throw new MissingMatchHandlerError({ tag: value._tag });
|
|
267
|
-
}
|
|
268
|
-
return handler(value);
|
|
269
|
-
}
|
|
270
|
-
// Curried: $match(cases) -> (value) => result
|
|
271
|
-
const cases = valueOrCases as Record<string, (v: unknown) => unknown>;
|
|
272
|
-
return (value: { _tag: string }): unknown => {
|
|
273
|
-
const handler = cases[value._tag];
|
|
274
|
-
if (handler === undefined) {
|
|
275
|
-
throw new MissingMatchHandlerError({ tag: value._tag });
|
|
276
|
-
}
|
|
277
|
-
return handler(value);
|
|
278
|
-
};
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
schema: unionSchema as unknown as Schema.Schema<VariantsUnion<D>, VariantsUnion<D>, never>,
|
|
283
|
-
variants: variants as unknown as VariantSchemas<D>,
|
|
284
|
-
constructors,
|
|
285
|
-
_definition: definition,
|
|
286
|
-
$is,
|
|
287
|
-
$match,
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Internal helper to create a machine schema (shared by State and Event).
|
|
293
|
-
* Builds the schema object with variants, constructors, $is, and $match.
|
|
294
|
-
*/
|
|
295
|
-
const createMachineSchema = <D extends Record<string, Schema.Struct.Fields>>(definition: D) => {
|
|
296
|
-
const { schema, variants, constructors, _definition, $is, $match } =
|
|
297
|
-
buildMachineSchema(definition);
|
|
298
|
-
return Object.assign(Object.create(schema), {
|
|
299
|
-
variants,
|
|
300
|
-
_definition,
|
|
301
|
-
$is,
|
|
302
|
-
$match,
|
|
303
|
-
...constructors,
|
|
304
|
-
});
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Create a schema-first State definition.
|
|
309
|
-
*
|
|
310
|
-
* The schema's definition type D creates a unique brand, preventing
|
|
311
|
-
* accidental use of constructors from different state schemas
|
|
312
|
-
* (unless they have identical definitions).
|
|
313
|
-
*
|
|
314
|
-
* @example
|
|
315
|
-
* ```ts
|
|
316
|
-
* const OrderState = MachineSchema.State({
|
|
317
|
-
* Pending: { orderId: Schema.String },
|
|
318
|
-
* Shipped: { trackingId: Schema.String },
|
|
319
|
-
* })
|
|
320
|
-
*
|
|
321
|
-
* type OrderState = typeof OrderState.Type
|
|
322
|
-
*
|
|
323
|
-
* // Construct
|
|
324
|
-
* const s = OrderState.Pending({ orderId: "123" })
|
|
325
|
-
*
|
|
326
|
-
* // Pattern match
|
|
327
|
-
* OrderState.$match(s, {
|
|
328
|
-
* Pending: (v) => v.orderId,
|
|
329
|
-
* Shipped: (v) => v.trackingId,
|
|
330
|
-
* })
|
|
331
|
-
*
|
|
332
|
-
* // Validate
|
|
333
|
-
* Schema.decodeUnknownSync(OrderState)(rawJson)
|
|
334
|
-
* ```
|
|
335
|
-
*/
|
|
336
|
-
export const State = <const D extends Record<string, Schema.Struct.Fields>>(
|
|
337
|
-
definition: D,
|
|
338
|
-
): MachineStateSchema<D> => createMachineSchema(definition) as MachineStateSchema<D>;
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Create a schema-first Event definition.
|
|
342
|
-
*
|
|
343
|
-
* The schema's definition type D creates a unique brand, preventing
|
|
344
|
-
* accidental use of constructors from different event schemas
|
|
345
|
-
* (unless they have identical definitions).
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* const OrderEvent = MachineSchema.Event({
|
|
350
|
-
* Ship: { trackingId: Schema.String },
|
|
351
|
-
* Cancel: {},
|
|
352
|
-
* })
|
|
353
|
-
*
|
|
354
|
-
* type OrderEvent = typeof OrderEvent.Type
|
|
355
|
-
*
|
|
356
|
-
* // Construct
|
|
357
|
-
* const e = OrderEvent.Ship({ trackingId: "abc" })
|
|
358
|
-
* ```
|
|
359
|
-
*/
|
|
360
|
-
export const Event = <const D extends Record<string, Schema.Struct.Fields>>(
|
|
361
|
-
definition: D,
|
|
362
|
-
): MachineEventSchema<D> => createMachineSchema(definition) as MachineEventSchema<D>;
|
package/src/slot.ts
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slot module - schema-based, parameterized guards and effects.
|
|
3
|
-
*
|
|
4
|
-
* Guards and Effects are defined with schemas for their parameters,
|
|
5
|
-
* and provided implementations receive typed parameters plus machine context.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { Slot } from "effect-machine"
|
|
10
|
-
* import { Schema } from "effect"
|
|
11
|
-
*
|
|
12
|
-
* const MyGuards = Slot.Guards({
|
|
13
|
-
* canRetry: { max: Schema.Number },
|
|
14
|
-
* isValid: {}, // no params
|
|
15
|
-
* })
|
|
16
|
-
*
|
|
17
|
-
* const MyEffects = Slot.Effects({
|
|
18
|
-
* fetchData: { url: Schema.String },
|
|
19
|
-
* notify: { message: Schema.String },
|
|
20
|
-
* })
|
|
21
|
-
*
|
|
22
|
-
* // Used in handlers:
|
|
23
|
-
* .on(State.X, Event.Y, ({ guards, effects }) =>
|
|
24
|
-
* Effect.gen(function* () {
|
|
25
|
-
* if (yield* guards.canRetry({ max: 3 })) {
|
|
26
|
-
* yield* effects.fetchData({ url: "/api" })
|
|
27
|
-
* return State.Next
|
|
28
|
-
* }
|
|
29
|
-
* return state
|
|
30
|
-
* })
|
|
31
|
-
* )
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* @module
|
|
35
|
-
*/
|
|
36
|
-
import { Context } from "effect";
|
|
37
|
-
import type { Effect, Schema } from "effect";
|
|
38
|
-
|
|
39
|
-
// ============================================================================
|
|
40
|
-
// Type-level utilities
|
|
41
|
-
// ============================================================================
|
|
42
|
-
|
|
43
|
-
/** Schema fields definition (like Schema.Struct.Fields) */
|
|
44
|
-
type Fields = Record<string, Schema.Schema.All>;
|
|
45
|
-
|
|
46
|
-
/** Extract the encoded type from schema fields (used for parameters) */
|
|
47
|
-
type FieldsToParams<F extends Fields> = keyof F extends never
|
|
48
|
-
? void
|
|
49
|
-
: Schema.Schema.Type<Schema.Struct<F>>;
|
|
50
|
-
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// Slot Types
|
|
53
|
-
// ============================================================================
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* A guard slot - callable function that returns Effect<boolean>.
|
|
57
|
-
*/
|
|
58
|
-
export interface GuardSlot<Name extends string, Params> {
|
|
59
|
-
readonly _tag: "GuardSlot";
|
|
60
|
-
readonly name: Name;
|
|
61
|
-
(params: Params): Effect.Effect<boolean>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* An effect slot - callable function that returns Effect<void>.
|
|
66
|
-
*/
|
|
67
|
-
export interface EffectSlot<Name extends string, Params> {
|
|
68
|
-
readonly _tag: "EffectSlot";
|
|
69
|
-
readonly name: Name;
|
|
70
|
-
(params: Params): Effect.Effect<void>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Guard definition - name to schema fields mapping
|
|
75
|
-
*/
|
|
76
|
-
export type GuardsDef = Record<string, Fields>;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Effect definition - name to schema fields mapping
|
|
80
|
-
*/
|
|
81
|
-
export type EffectsDef = Record<string, Fields>;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Convert guard definitions to callable guard slots
|
|
85
|
-
*/
|
|
86
|
-
export type GuardSlots<D extends GuardsDef> = {
|
|
87
|
-
readonly [K in keyof D & string]: GuardSlot<K, FieldsToParams<D[K]>>;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Convert effect definitions to callable effect slots
|
|
92
|
-
*/
|
|
93
|
-
export type EffectSlots<D extends EffectsDef> = {
|
|
94
|
-
readonly [K in keyof D & string]: EffectSlot<K, FieldsToParams<D[K]>>;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// ============================================================================
|
|
98
|
-
// Machine Context Tag
|
|
99
|
-
// ============================================================================
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Type for machine context - state, event, and self reference.
|
|
103
|
-
* Shared across all machines via MachineContextTag.
|
|
104
|
-
*/
|
|
105
|
-
export interface MachineContext<State, Event, Self> {
|
|
106
|
-
readonly state: State;
|
|
107
|
-
readonly event: Event;
|
|
108
|
-
readonly self: Self;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Shared Context tag for all machines.
|
|
113
|
-
* Single module-level tag instead of per-machine allocation.
|
|
114
|
-
* @internal
|
|
115
|
-
*/
|
|
116
|
-
/* eslint-disable @typescript-eslint/no-explicit-any -- generic context tag */
|
|
117
|
-
export const MachineContextTag =
|
|
118
|
-
Context.GenericTag<MachineContext<any, any, any>>("@effect-machine/Context");
|
|
119
|
-
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
120
|
-
|
|
121
|
-
// ============================================================================
|
|
122
|
-
// Handler Types (for provide)
|
|
123
|
-
// ============================================================================
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Guard handler implementation.
|
|
127
|
-
* Receives params and context, returns Effect<boolean>.
|
|
128
|
-
*/
|
|
129
|
-
export type GuardHandler<Params, Ctx, R = never> = (
|
|
130
|
-
params: Params,
|
|
131
|
-
ctx: Ctx,
|
|
132
|
-
) => boolean | Effect.Effect<boolean, never, R>;
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Effect handler implementation.
|
|
136
|
-
* Receives params and context, returns Effect<void>.
|
|
137
|
-
*/
|
|
138
|
-
export type EffectHandler<Params, Ctx, R = never> = (
|
|
139
|
-
params: Params,
|
|
140
|
-
ctx: Ctx,
|
|
141
|
-
) => Effect.Effect<void, never, R>;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Handler types for all guards in a definition
|
|
145
|
-
*/
|
|
146
|
-
export type GuardHandlers<D extends GuardsDef, MachineCtx, R = never> = {
|
|
147
|
-
readonly [K in keyof D & string]: GuardHandler<FieldsToParams<D[K]>, MachineCtx, R>;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Handler types for all effects in a definition
|
|
152
|
-
*/
|
|
153
|
-
export type EffectHandlers<D extends EffectsDef, MachineCtx, R = never> = {
|
|
154
|
-
readonly [K in keyof D & string]: EffectHandler<FieldsToParams<D[K]>, MachineCtx, R>;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// ============================================================================
|
|
158
|
-
// Schema Types (for Machine.make)
|
|
159
|
-
// ============================================================================
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Guards schema - returned by Slot.Guards()
|
|
163
|
-
*/
|
|
164
|
-
export interface GuardsSchema<D extends GuardsDef> {
|
|
165
|
-
readonly _tag: "GuardsSchema";
|
|
166
|
-
readonly definitions: D;
|
|
167
|
-
/** Create callable guard slots (used by Machine internally) */
|
|
168
|
-
readonly _createSlots: (
|
|
169
|
-
resolve: <N extends keyof D & string>(
|
|
170
|
-
name: N,
|
|
171
|
-
params: FieldsToParams<D[N]>,
|
|
172
|
-
) => Effect.Effect<boolean>,
|
|
173
|
-
) => GuardSlots<D>;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Effects schema - returned by Slot.Effects()
|
|
178
|
-
*/
|
|
179
|
-
export interface EffectsSchema<D extends EffectsDef> {
|
|
180
|
-
readonly _tag: "EffectsSchema";
|
|
181
|
-
readonly definitions: D;
|
|
182
|
-
/** Create callable effect slots (used by Machine internally) */
|
|
183
|
-
readonly _createSlots: (
|
|
184
|
-
resolve: <N extends keyof D & string>(
|
|
185
|
-
name: N,
|
|
186
|
-
params: FieldsToParams<D[N]>,
|
|
187
|
-
) => Effect.Effect<void>,
|
|
188
|
-
) => EffectSlots<D>;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// ============================================================================
|
|
192
|
-
// Slot Factories
|
|
193
|
-
// ============================================================================
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Generic slot schema factory. Used internally by Guards() and Effects().
|
|
197
|
-
* @internal
|
|
198
|
-
*/
|
|
199
|
-
const createSlotSchema = <
|
|
200
|
-
Tag extends "GuardsSchema" | "EffectsSchema",
|
|
201
|
-
D extends Record<string, Fields>,
|
|
202
|
-
>(
|
|
203
|
-
tag: Tag,
|
|
204
|
-
slotTag: "GuardSlot" | "EffectSlot",
|
|
205
|
-
definitions: D,
|
|
206
|
-
): {
|
|
207
|
-
readonly _tag: Tag;
|
|
208
|
-
readonly definitions: D;
|
|
209
|
-
readonly _createSlots: (
|
|
210
|
-
resolve: <N extends keyof D & string>(
|
|
211
|
-
name: N,
|
|
212
|
-
params: FieldsToParams<D[N]>,
|
|
213
|
-
) => Effect.Effect<unknown>,
|
|
214
|
-
) => Record<string, unknown>;
|
|
215
|
-
} => ({
|
|
216
|
-
_tag: tag,
|
|
217
|
-
definitions,
|
|
218
|
-
_createSlots: (resolve) => {
|
|
219
|
-
const slots: Record<string, unknown> = {};
|
|
220
|
-
for (const name of Object.keys(definitions)) {
|
|
221
|
-
const slot = (params: unknown) => resolve(name, params as FieldsToParams<D[typeof name]>);
|
|
222
|
-
Object.defineProperty(slot, "_tag", { value: slotTag, enumerable: true });
|
|
223
|
-
Object.defineProperty(slot, "name", { value: name, enumerable: true });
|
|
224
|
-
slots[name] = slot;
|
|
225
|
-
}
|
|
226
|
-
return slots;
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Create a guards schema with parameterized guard definitions.
|
|
232
|
-
*
|
|
233
|
-
* @example
|
|
234
|
-
* ```ts
|
|
235
|
-
* const MyGuards = Slot.Guards({
|
|
236
|
-
* canRetry: { max: Schema.Number },
|
|
237
|
-
* isValid: {},
|
|
238
|
-
* })
|
|
239
|
-
* ```
|
|
240
|
-
*/
|
|
241
|
-
export const Guards = <D extends GuardsDef>(definitions: D): GuardsSchema<D> =>
|
|
242
|
-
createSlotSchema("GuardsSchema", "GuardSlot", definitions) as GuardsSchema<D>;
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Create an effects schema with parameterized effect definitions.
|
|
246
|
-
*
|
|
247
|
-
* @example
|
|
248
|
-
* ```ts
|
|
249
|
-
* const MyEffects = Slot.Effects({
|
|
250
|
-
* fetchData: { url: Schema.String },
|
|
251
|
-
* notify: { message: Schema.String },
|
|
252
|
-
* })
|
|
253
|
-
* ```
|
|
254
|
-
*/
|
|
255
|
-
export const Effects = <D extends EffectsDef>(definitions: D): EffectsSchema<D> =>
|
|
256
|
-
createSlotSchema("EffectsSchema", "EffectSlot", definitions) as EffectsSchema<D>;
|
|
257
|
-
|
|
258
|
-
// ============================================================================
|
|
259
|
-
// Type extraction helpers
|
|
260
|
-
// ============================================================================
|
|
261
|
-
|
|
262
|
-
/** Extract guard definition type from GuardsSchema */
|
|
263
|
-
export type GuardsDefOf<G> = G extends GuardsSchema<infer D> ? D : never;
|
|
264
|
-
|
|
265
|
-
/** Extract effect definition type from EffectsSchema */
|
|
266
|
-
export type EffectsDefOf<E> = E extends EffectsSchema<infer D> ? D : never;
|
|
267
|
-
|
|
268
|
-
/** Extract guard slots type from GuardsSchema */
|
|
269
|
-
export type GuardSlotsOf<G> = G extends GuardsSchema<infer D> ? GuardSlots<D> : never;
|
|
270
|
-
|
|
271
|
-
/** Extract effect slots type from EffectsSchema */
|
|
272
|
-
export type EffectSlotsOf<E> = E extends EffectsSchema<infer D> ? EffectSlots<D> : never;
|
|
273
|
-
|
|
274
|
-
// ============================================================================
|
|
275
|
-
// Slot namespace export
|
|
276
|
-
// ============================================================================
|
|
277
|
-
|
|
278
|
-
export const Slot = {
|
|
279
|
-
Guards,
|
|
280
|
-
Effects,
|
|
281
|
-
} as const;
|