effect-machine 0.11.0 → 0.12.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.
Files changed (58) hide show
  1. package/dist/actor.d.ts +10 -4
  2. package/dist/actor.js +33 -5
  3. package/dist/cluster/adapters/in-memory.d.ts +28 -0
  4. package/dist/cluster/adapters/in-memory.js +79 -0
  5. package/dist/cluster/entity-actor-ref.d.ts +56 -0
  6. package/dist/cluster/entity-actor-ref.js +33 -0
  7. package/dist/cluster/entity-machine.d.ts +31 -49
  8. package/dist/cluster/entity-machine.js +167 -52
  9. package/dist/cluster/index.d.ts +5 -2
  10. package/dist/cluster/index.js +4 -1
  11. package/dist/cluster/persistence.d.ts +49 -0
  12. package/dist/cluster/persistence.js +18 -0
  13. package/dist/cluster/to-entity.d.ts +9 -3
  14. package/dist/cluster/to-entity.js +16 -4
  15. package/dist/errors.d.ts +12 -1
  16. package/dist/errors.js +8 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/internal/brands.d.ts +14 -1
  19. package/dist/internal/runtime.d.ts +67 -0
  20. package/dist/internal/runtime.js +248 -0
  21. package/dist/internal/transition.d.ts +5 -0
  22. package/dist/internal/transition.js +15 -3
  23. package/dist/internal/utils.d.ts +42 -6
  24. package/dist/internal/utils.js +27 -1
  25. package/dist/machine.d.ts +26 -13
  26. package/dist/machine.js +14 -3
  27. package/dist/schema.d.ts +35 -34
  28. package/dist/schema.js +32 -3
  29. package/dist/testing.js +4 -2
  30. package/package.json +3 -3
  31. package/v3/dist/actor.d.ts +4 -3
  32. package/v3/dist/actor.js +15 -3
  33. package/v3/dist/cluster/adapters/in-memory.d.ts +15 -0
  34. package/v3/dist/cluster/adapters/in-memory.js +62 -0
  35. package/v3/dist/cluster/entity-actor-ref.d.ts +49 -0
  36. package/v3/dist/cluster/entity-actor-ref.js +19 -0
  37. package/v3/dist/cluster/entity-machine.d.ts +34 -49
  38. package/v3/dist/cluster/entity-machine.js +134 -50
  39. package/v3/dist/cluster/index.d.ts +5 -2
  40. package/v3/dist/cluster/index.js +4 -1
  41. package/v3/dist/cluster/persistence.d.ts +48 -0
  42. package/v3/dist/cluster/persistence.js +14 -0
  43. package/v3/dist/cluster/to-entity.d.ts +5 -2
  44. package/v3/dist/cluster/to-entity.js +12 -4
  45. package/v3/dist/errors.d.ts +16 -1
  46. package/v3/dist/errors.js +8 -1
  47. package/v3/dist/index.d.ts +3 -2
  48. package/v3/dist/internal/brands.d.ts +15 -1
  49. package/v3/dist/internal/runtime.d.ts +65 -0
  50. package/v3/dist/internal/runtime.js +236 -0
  51. package/v3/dist/internal/transition.d.ts +5 -0
  52. package/v3/dist/internal/transition.js +15 -3
  53. package/v3/dist/internal/utils.d.ts +42 -6
  54. package/v3/dist/internal/utils.js +27 -1
  55. package/v3/dist/machine.d.ts +19 -13
  56. package/v3/dist/machine.js +10 -2
  57. package/v3/dist/schema.d.ts +35 -34
  58. package/v3/dist/schema.js +29 -3
@@ -1,7 +1,17 @@
1
- import { FullEventBrand, FullStateBrand } from "./internal/brands.js";
1
+ import { FullEventBrand, FullStateBrand, ReplyTypeBrand } from "./internal/brands.js";
2
2
  import { Schema } from "effect";
3
3
 
4
4
  //#region src/schema.d.ts
5
+ declare const ReplySchemaSymbol: unique symbol;
6
+ type ReplySchemaSymbol = typeof ReplySchemaSymbol;
7
+ /**
8
+ * Fields annotated with a reply schema.
9
+ * Structurally identical to Schema.Struct.Fields at runtime,
10
+ * but carries the reply schema type at compile time.
11
+ */
12
+ type ReplyFields<F extends Schema.Struct.Fields, RS extends Schema.Schema.Any> = F & {
13
+ readonly [ReplySchemaSymbol]: RS;
14
+ };
5
15
  /**
6
16
  * Extract the TypeScript type from a TaggedStruct schema
7
17
  */
@@ -12,20 +22,23 @@ type TaggedStructType<Tag extends string, Fields extends Schema.Struct.Fields> =
12
22
  type VariantSchemas<D extends Record<string, Schema.Struct.Fields>> = { readonly [K in keyof D & string]: Schema.TaggedStruct<K, D[K]> };
13
23
  /**
14
24
  * Build union type from variant schemas.
15
- * Used for constraining fluent method type params.
25
+ * Reply-bearing variants carry ReplyTypeBrand<R> for ask() inference.
16
26
  */
17
- type VariantsUnion<D extends Record<string, Schema.Struct.Fields>> = { [K in keyof D & string]: TaggedStructType<K, D[K]> }[keyof D & string];
27
+ type VariantsUnion<D extends Record<string, Schema.Struct.Fields>> = { [K in keyof D & string]: TaggedStructType<K, D[K]> & (D[K] extends {
28
+ readonly [ReplySchemaSymbol]: Schema.Schema<infer R, infer _I, infer _RR>;
29
+ } ? ReplyTypeBrand<R> : unknown) }[keyof D & string];
18
30
  /**
19
- * Check if fields are empty (no required properties)
31
+ * Check if fields are empty (no required string properties).
32
+ * Symbol keys (like ReplySchemaSymbol) are metadata, not payload fields.
20
33
  */
21
- type IsEmptyFields<Fields extends Schema.Struct.Fields> = keyof Fields extends never ? true : false;
34
+ type IsEmptyFields<Fields extends Schema.Struct.Fields> = string & keyof Fields extends never ? true : false;
22
35
  /**
23
- * Constructor functions for each variant.
24
- * Empty structs: plain values with `_tag`: `State.Idle`
25
- * Non-empty structs require args: `State.Loading({ url })`
26
- *
27
- * Each variant also has a `derive` method for constructing from a source object.
36
+ * Resolve the reply brand for a variant's fields.
37
+ * If fields carry ReplySchemaSymbol, adds ReplyTypeBrand<R>.
28
38
  */
39
+ type VariantReplyBrand<Fields extends Schema.Struct.Fields> = Fields extends {
40
+ readonly [ReplySchemaSymbol]: Schema.Schema<infer R, infer _I, infer _RR>;
41
+ } ? ReplyTypeBrand<R> : unknown;
29
42
  /**
30
43
  * Constructor functions for each variant.
31
44
  * Empty structs: plain values with `_tag`: `State.Idle`
@@ -33,10 +46,11 @@ type IsEmptyFields<Fields extends Schema.Struct.Fields> = keyof Fields extends n
33
46
  *
34
47
  * Each variant also has a `derive` method for constructing from a source object.
35
48
  * The source type uses `object` to accept branded state types without index signature issues.
49
+ * Reply-bearing variants carry ReplyTypeBrand<R> for ask() type inference.
36
50
  */
37
- type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = { readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true ? TaggedStructType<K, D[K]> & Brand & {
51
+ type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = { readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true ? TaggedStructType<K, D[K]> & Brand & VariantReplyBrand<D[K]> & {
38
52
  readonly derive: (source: object) => TaggedStructType<K, D[K]> & Brand;
39
- } : ((args: Schema.Struct.Constructor<D[K]>) => TaggedStructType<K, D[K]> & Brand) & {
53
+ } : ((args: Schema.Struct.Constructor<D[K]>) => TaggedStructType<K, D[K]> & Brand & VariantReplyBrand<D[K]>) & {
40
54
  readonly derive: (source: object, partial?: Partial<Schema.Struct.Constructor<D[K]>>) => TaggedStructType<K, D[K]> & Brand;
41
55
  readonly _tag: K;
42
56
  } };
@@ -56,6 +70,11 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
56
70
  * Per-variant schemas for fine-grained operations
57
71
  */
58
72
  readonly variants: VariantSchemas<D>;
73
+ /**
74
+ * Reply schemas per variant tag. Only populated for event schemas
75
+ * with variants defined via `Event.reply()`.
76
+ */
77
+ readonly _replySchemas: ReadonlyMap<string, Schema.Schema.Any>;
59
78
  /**
60
79
  * Type guard: `OrderState.$is("Pending")(value)`
61
80
  */
@@ -116,26 +135,8 @@ type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema
116
135
  * ```
117
136
  */
118
137
  declare const State: <const D extends Record<string, Schema.Struct.Fields>>(definition: D) => MachineStateSchema<D>;
119
- /**
120
- * Create a schema-first Event definition.
121
- *
122
- * The schema's definition type D creates a unique brand, preventing
123
- * accidental use of constructors from different event schemas
124
- * (unless they have identical definitions).
125
- *
126
- * @example
127
- * ```ts
128
- * const OrderEvent = MachineSchema.Event({
129
- * Ship: { trackingId: Schema.String },
130
- * Cancel: {},
131
- * })
132
- *
133
- * type OrderEvent = typeof OrderEvent.Type
134
- *
135
- * // Construct
136
- * const e = OrderEvent.Ship({ trackingId: "abc" })
137
- * ```
138
- */
139
- declare const Event: <const D extends Record<string, Schema.Struct.Fields>>(definition: D) => MachineEventSchema<D>;
138
+ declare const Event: (<const D extends Record<string, Schema.Struct.Fields>>(definition: D) => MachineEventSchema<D>) & {
139
+ reply: <F extends Schema.Struct.Fields, RS extends Schema.Schema.Any>(fields: F, replySchema: RS) => ReplyFields<F, RS>;
140
+ };
140
141
  //#endregion
141
- export { Event, MachineEventSchema, MachineStateSchema, State, VariantsUnion };
142
+ export { Event, MachineEventSchema, MachineStateSchema, ReplyFields, ReplySchemaSymbol, State, VariantsUnion };
package/v3/dist/schema.js CHANGED
@@ -38,15 +38,21 @@ import { Schema } from "effect";
38
38
  *
39
39
  * @module
40
40
  */
41
+ const ReplySchemaSymbol = Symbol.for("effect-machine/ReplySchema");
41
42
  /**
42
43
  * Build a schema-first definition from a record of tag -> fields
43
44
  */
44
45
  const buildMachineSchema = (definition) => {
45
46
  const variants = {};
46
47
  const constructors = {};
48
+ const replySchemas = /* @__PURE__ */ new Map();
47
49
  for (const tag of Object.keys(definition)) {
48
50
  const fields = definition[tag];
49
51
  if (fields === void 0) continue;
52
+ if (ReplySchemaSymbol in fields) {
53
+ const rs = fields[ReplySchemaSymbol];
54
+ if (rs !== void 0) replySchemas.set(tag, rs);
55
+ }
50
56
  variants[tag] = Schema.TaggedStruct(tag, fields);
51
57
  const fieldNames = new Set(Object.keys(fields));
52
58
  if (fieldNames.size > 0) {
@@ -93,6 +99,7 @@ const buildMachineSchema = (definition) => {
93
99
  variants,
94
100
  constructors,
95
101
  _definition: definition,
102
+ _replySchemas: replySchemas,
96
103
  $is,
97
104
  $match
98
105
  };
@@ -102,10 +109,11 @@ const buildMachineSchema = (definition) => {
102
109
  * Builds the schema object with variants, constructors, $is, and $match.
103
110
  */
104
111
  const createMachineSchema = (definition) => {
105
- const { schema, variants, constructors, _definition, $is, $match } = buildMachineSchema(definition);
112
+ const { schema, variants, constructors, _definition, _replySchemas, $is, $match } = buildMachineSchema(definition);
106
113
  return Object.assign(Object.create(schema), {
107
114
  variants,
108
115
  _definition,
116
+ _replySchemas,
109
117
  $is,
110
118
  $match,
111
119
  ...constructors
@@ -148,11 +156,15 @@ const State = (definition) => createMachineSchema(definition);
148
156
  * accidental use of constructors from different event schemas
149
157
  * (unless they have identical definitions).
150
158
  *
159
+ * Use `Event.reply(fields, replySchema)` to define events that support
160
+ * typed `ask()` replies.
161
+ *
151
162
  * @example
152
163
  * ```ts
153
- * const OrderEvent = MachineSchema.Event({
164
+ * const OrderEvent = Event({
154
165
  * Ship: { trackingId: Schema.String },
155
166
  * Cancel: {},
167
+ * GetTotal: Event.reply({}, Schema.Number),
156
168
  * })
157
169
  *
158
170
  * type OrderEvent = typeof OrderEvent.Type
@@ -161,6 +173,20 @@ const State = (definition) => createMachineSchema(definition);
161
173
  * const e = OrderEvent.Ship({ trackingId: "abc" })
162
174
  * ```
163
175
  */
164
- const Event = (definition) => createMachineSchema(definition);
176
+ const EventImpl = (definition) => createMachineSchema(definition);
177
+ /**
178
+ * Annotate event fields with a reply schema.
179
+ * Events defined with `Event.reply(fields, replySchema)` enable typed `ask()`.
180
+ */
181
+ const replyFieldsFn = (fields, replySchema) => {
182
+ const annotated = { ...fields };
183
+ Object.defineProperty(annotated, ReplySchemaSymbol, {
184
+ value: replySchema,
185
+ enumerable: false,
186
+ writable: false
187
+ });
188
+ return annotated;
189
+ };
190
+ const Event = Object.assign(EventImpl, { reply: replyFieldsFn });
165
191
  //#endregion
166
192
  export { Event, State };