@praxisjs/fsm 1.0.12 → 2.0.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @praxisjs/fsm
2
2
 
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [378cc54]
8
+ - @praxisjs/core@1.7.0
9
+ - @praxisjs/decorators@1.1.0
10
+
11
+ ## 2.0.0
12
+
13
+ ### Major Changes
14
+
15
+ - c54d96b: `@StateMachine` converted from a class decorator to a field decorator.
16
+
17
+ Declare the machine as a typed field — no `declare`, no `propertyKey` second argument. The field name is the machine reference used in `@Transition`.
18
+
19
+ ```tsx
20
+ // before
21
+ @StateMachine({ initial: 'idle', states: { ... } }, 'machine')
22
+ @Component()
23
+ class Foo extends StatefulComponent {
24
+ declare machine: Machine<State, Event>
25
+ }
26
+
27
+ // after
28
+ @Component()
29
+ class Foo extends StatefulComponent {
30
+ @StateMachine({ initial: 'idle', states: { ... } })
31
+ machine!: Machine<State, Event>
32
+ }
33
+ ```
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [cfb0de2]
38
+ - @praxisjs/decorators@1.0.2
39
+
3
40
  ## 1.0.12
4
41
 
5
42
  ### Patch Changes
@@ -8,15 +8,27 @@ const TOGGLE_DEF = {
8
8
  on: { on: { toggle: "off" } },
9
9
  },
10
10
  };
11
+ // Helper to simulate TC39 field decorator context
12
+ function fieldCtx(name) {
13
+ const initializers = [];
14
+ return {
15
+ ctx: {
16
+ name,
17
+ kind: "field",
18
+ addInitializer(fn) { initializers.push(fn); },
19
+ },
20
+ run(instance) {
21
+ initializers.forEach((fn) => { fn.call(instance); });
22
+ },
23
+ };
24
+ }
11
25
  // Helper to simulate TC39 method decorator context
12
26
  function mockMethodContext(name) {
13
27
  const initializers = [];
14
28
  return {
15
29
  name,
16
30
  kind: "method",
17
- addInitializer(fn) {
18
- initializers.push(fn);
19
- },
31
+ addInitializer(fn) { initializers.push(fn); },
20
32
  runInitializers(instance) {
21
33
  initializers.forEach((fn) => { fn.call(instance); });
22
34
  },
@@ -24,38 +36,49 @@ function mockMethodContext(name) {
24
36
  }
25
37
  // ── StateMachine ──────────────────────────────────────────────────────────────
26
38
  describe("StateMachine decorator", () => {
27
- it("adds a machine property via prototype", () => {
28
- class Light {
29
- }
30
- const Wrapped = StateMachine(TOGGLE_DEF)(Light, {});
31
- const instance = new Wrapped();
39
+ it("injects a machine via field decorator", () => {
40
+ const { ctx, run } = fieldCtx("machine");
41
+ StateMachine(TOGGLE_DEF)(undefined, ctx);
42
+ const instance = {};
43
+ run(instance);
32
44
  expect(instance.machine).toBeDefined();
33
45
  expect(instance.machine.state()).toBe("off");
34
46
  });
35
47
  it("returns a separate machine per instance", () => {
36
- class Bulb {
37
- }
38
- const Wrapped = StateMachine(TOGGLE_DEF)(Bulb, {});
39
- const a = new Wrapped();
40
- const b = new Wrapped();
48
+ const { ctx, run } = fieldCtx("machine");
49
+ StateMachine(TOGGLE_DEF)(undefined, ctx);
50
+ const a = {};
51
+ const b = {};
52
+ run(a);
53
+ run(b);
41
54
  a.machine.send("toggle");
42
55
  expect(a.machine.state()).toBe("on");
43
56
  expect(b.machine.state()).toBe("off");
44
57
  });
45
- it("uses a custom property key", () => {
46
- class Widget {
47
- }
48
- const Wrapped = StateMachine(TOGGLE_DEF, "fsm")(Widget, {});
49
- const instance = new Wrapped();
58
+ it("supports a custom field name", () => {
59
+ const { ctx, run } = fieldCtx("fsm");
60
+ StateMachine(TOGGLE_DEF)(undefined, ctx);
61
+ const instance = {};
62
+ run(instance);
50
63
  expect(instance.fsm).toBeDefined();
64
+ expect(instance.fsm.state()).toBe("off");
51
65
  });
52
- it("returns the same instance on repeated access", () => {
53
- class Btn {
54
- }
55
- const Wrapped = StateMachine(TOGGLE_DEF)(Btn, {});
56
- const instance = new Wrapped();
66
+ it("returns the same machine instance on repeated access", () => {
67
+ const { ctx, run } = fieldCtx("machine");
68
+ StateMachine(TOGGLE_DEF)(undefined, ctx);
69
+ const instance = {};
70
+ run(instance);
57
71
  expect(instance.machine).toBe(instance.machine);
58
72
  });
73
+ it("setter is a no-op — assigning to the field does not replace the machine", () => {
74
+ const { ctx, run } = fieldCtx("machine");
75
+ StateMachine(TOGGLE_DEF)(undefined, ctx);
76
+ const instance = {};
77
+ run(instance);
78
+ const original = instance.machine;
79
+ instance.machine = "overwrite-attempt"; // triggers set(): void {} (no-op)
80
+ expect(instance.machine).toBe(original);
81
+ });
59
82
  });
60
83
  // ── Transition ────────────────────────────────────────────────────────────────
61
84
  describe("Transition decorator", () => {
@@ -91,7 +114,6 @@ describe("Transition decorator", () => {
91
114
  };
92
115
  ctx.runInitializers(instance);
93
116
  const method = instance.doTransition;
94
- // "toggle" is not a valid event from "on" state
95
117
  method();
96
118
  expect(original).not.toHaveBeenCalled();
97
119
  });
@@ -105,12 +127,11 @@ describe("Transition decorator", () => {
105
127
  method(42);
106
128
  expect(original).toHaveBeenCalledWith(42);
107
129
  });
108
- it("@Transition on method in class without @StateMachine — warns and returns undefined", () => {
130
+ it("@Transition on method without @StateMachine — warns and returns undefined", () => {
109
131
  const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
110
132
  const original = vi.fn(() => "ran");
111
133
  const ctx = mockMethodContext("doTransition");
112
134
  Transition("machine", "toggle")(original, ctx);
113
- // Instance has no "machine" property at all
114
135
  const instance = {};
115
136
  ctx.runInitializers(instance);
116
137
  const method = instance.doTransition;
@@ -121,29 +142,4 @@ describe("Transition decorator", () => {
121
142
  warn.mockRestore();
122
143
  });
123
144
  });
124
- // ── StateMachine on subclass ───────────────────────────────────────────────────
125
- describe("StateMachine decorator on subclass", () => {
126
- it("machine is accessible on subclass instance", () => {
127
- class Base {
128
- }
129
- class Child extends Base {
130
- }
131
- const Wrapped = StateMachine(TOGGLE_DEF)(Child, {});
132
- const instance = new Wrapped();
133
- expect(instance.machine).toBeDefined();
134
- expect(instance.machine.state()).toBe("off");
135
- });
136
- it("instances of the decorated subclass are isolated", () => {
137
- class Base {
138
- }
139
- class Child extends Base {
140
- }
141
- const Wrapped = StateMachine(TOGGLE_DEF)(Child, {});
142
- const a = new Wrapped();
143
- const b = new Wrapped();
144
- a.machine.send("toggle");
145
- expect(a.machine.state()).toBe("on");
146
- expect(b.machine.state()).toBe("off");
147
- });
148
- });
149
145
  //# sourceMappingURL=decorators.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.test.js","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.test.js","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { type MachineDefinition } from "./machine";
1
+ import { type Machine, type MachineDefinition } from "./machine";
2
2
  export declare function Transition(machineProp: string, event: string): (value: (...args: unknown[]) => unknown, context: ClassMethodDecoratorContext<any>) => void;
3
- export declare function StateMachine<S extends string, E extends string>(definition: MachineDefinition<S, E>, propertyKey?: string): (value: abstract new (...args: unknown[]) => unknown, context: ClassDecoratorContext) => abstract new (...args: unknown[]) => unknown;
3
+ export declare function StateMachine<S extends string, E extends string>(definition: MachineDefinition<S, E>): <T extends object>(_value: undefined, context: ClassFieldDecoratorContext<T, Machine<S, E>>) => void;
4
4
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAQA,OAAO,EAA+B,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEhF,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAiB5B,CAC7B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAEtC,OAAO,EAAE,2BAA2B,CAAC,GAAG,CAAC,KACtC,IAAI,CACV;AAqCD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAC7D,UAAU,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EACnC,WAAW,SAAY,GAOQ,CAC7B,KAAK,EAAE,QAAQ,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EACnD,OAAO,EAAE,qBAAqB,KAC3B,QAAQ,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAClD"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,OAAO,EAAE,KAAK,iBAAiB,EAAG,MAAM,WAAW,CAAC;AAEjF,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAiB5B,CAC7B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAEtC,OAAO,EAAE,2BAA2B,CAAC,GAAG,CAAC,KACtC,IAAI,CACV;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAC7D,UAAU,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,IAIlB,CAAC,SAAS,MAAM,EAC/B,QAAQ,SAAS,EACjB,SAAS,0BAA0B,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KACpD,IAAI,CAgBR"}
@@ -1,4 +1,4 @@
1
- import { createMethodDecorator, createClassDecorator, ClassBehavior, } from "@praxisjs/decorators";
1
+ import { createMethodDecorator, } from "@praxisjs/decorators";
2
2
  import { createMachine } from "./machine";
3
3
  export function Transition(machineProp, event) {
4
4
  const decorator = createMethodDecorator({
@@ -17,33 +17,24 @@ export function Transition(machineProp, event) {
17
17
  // FSM decorators work on any class, not just StatefulComponent
18
18
  return decorator;
19
19
  }
20
- class StateMachineBehavior extends ClassBehavior {
21
- constructor(definition, propertyKey) {
22
- super();
23
- this.definition = definition;
24
- this.propertyKey = propertyKey;
25
- }
26
- create(_instance) {
27
- return {};
28
- }
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- initialize(Enhanced) {
31
- const machines = new WeakMap();
32
- const { definition, propertyKey } = this;
33
- Object.defineProperty(Enhanced.prototype, propertyKey, {
34
- get() {
35
- if (!machines.has(this))
36
- machines.set(this, createMachine(definition));
37
- return machines.get(this);
38
- },
39
- enumerable: true,
40
- configurable: true,
20
+ export function StateMachine(definition) {
21
+ const machines = new WeakMap();
22
+ return function (_value, context) {
23
+ context.addInitializer(function () {
24
+ const name = context.name;
25
+ Object.defineProperty(this, name, {
26
+ get() {
27
+ const self = this;
28
+ if (!machines.has(self))
29
+ machines.set(self, createMachine(definition));
30
+ return machines.get(self);
31
+ },
32
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
33
+ set() { },
34
+ enumerable: true,
35
+ configurable: true,
36
+ });
41
37
  });
42
- }
43
- }
44
- export function StateMachine(definition, propertyKey = "machine") {
45
- const decorator = createClassDecorator(new StateMachineBehavior(definition, propertyKey));
46
- // FSM decorators work on any class, not just RootComponent
47
- return decorator;
38
+ };
48
39
  }
49
40
  //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,GAEd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,aAAa,EAAwC,MAAM,WAAW,CAAC;AAEhF,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,KAAa;IAC3D,MAAM,SAAS,GAAG,qBAAqB,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,QAAQ;YACrB,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE;gBAC5B,MAAM,OAAO,GAAI,QAAoC,CAAC,WAAW,CAEpD,CAAC;gBACd,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CAAC,iBAAiB,WAAW,2BAA2B,CAAC,CAAC;oBACtE,OAAO;gBACT,CAAC;gBACD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;oBACrB,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAY,CAAC;YACrD,CAAC,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IACH,+DAA+D;IAC/D,OAAO,SAIE,CAAC;AACZ,CAAC;AAED,MAAM,oBAGJ,SAAQ,aAAa;IACrB,YACmB,UAAmC,EACnC,WAAmB;QAEpC,KAAK,EAAE,CAAC;QAHS,eAAU,GAAV,UAAU,CAAyB;QACnC,gBAAW,GAAX,WAAW,CAAQ;IAGtC,CAAC;IAED,MAAM,CAAC,SAAwB;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8DAA8D;IAC9D,UAAU,CAAC,QAAyC;QAClD,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAyB,CAAC;QACtD,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;QACzC,MAAM,CAAC,cAAc,CAClB,QAAkC,CAAC,SAAS,EAC7C,WAAW,EACX;YACE,GAAG;gBACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;oBACrB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;gBAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAkB,CAAC;YAC7C,CAAC;YACD,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,IAAI;SACnB,CACF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAC1B,UAAmC,EACnC,WAAW,GAAG,SAAS;IAEvB,MAAM,SAAS,GAAG,oBAAoB,CACpC,IAAI,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,CAClD,CAAC;IACF,2DAA2D;IAE3D,OAAO,SAG0C,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,aAAa,EAAyC,MAAM,WAAW,CAAC;AAEjF,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,KAAa;IAC3D,MAAM,SAAS,GAAG,qBAAqB,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,QAAQ;YACrB,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE;gBAC5B,MAAM,OAAO,GAAI,QAAoC,CAAC,WAAW,CAEpD,CAAC;gBACd,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CAAC,iBAAiB,WAAW,2BAA2B,CAAC,CAAC;oBACtE,OAAO;gBACT,CAAC;gBACD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;oBACrB,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAY,CAAC;YACrD,CAAC,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IACH,+DAA+D;IAC/D,OAAO,SAIE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,UAAmC;IAEnC,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAyB,CAAC;IAEtD,OAAO,UACL,MAAiB,EACjB,OAAqD;QAErD,OAAO,CAAC,cAAc,CAAC;YACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAC1B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE;gBAChC,GAAG;oBACD,MAAM,IAAI,GAAG,IAAc,CAAC;oBAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;oBACvE,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAkB,CAAC;gBAC7C,CAAC;gBACD,gEAAgE;gBAChE,GAAG,KAAU,CAAC;gBACd,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,IAAI;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisjs/fsm",
3
- "version": "1.0.12",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,9 +14,9 @@
14
14
  "typescript": "^5.9.3"
15
15
  },
16
16
  "dependencies": {
17
- "@praxisjs/core": "1.6.0",
18
- "@praxisjs/shared": "0.2.0",
19
- "@praxisjs/decorators": "1.0.1"
17
+ "@praxisjs/core": "1.7.0",
18
+ "@praxisjs/decorators": "1.1.0",
19
+ "@praxisjs/shared": "0.2.0"
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsc",
@@ -2,24 +2,38 @@ import { describe, it, expect, vi } from "vitest";
2
2
 
3
3
  import { Transition, StateMachine } from "../decorators";
4
4
  import { createMachine } from "../machine";
5
+ import type { Machine } from "../machine";
5
6
 
6
7
  const TOGGLE_DEF = {
7
8
  initial: "off" as const,
8
9
  states: {
9
10
  off: { on: { toggle: "on" as const } },
10
- on: { on: { toggle: "off" as const } },
11
+ on: { on: { toggle: "off" as const } },
11
12
  },
12
13
  } as const;
13
14
 
15
+ // Helper to simulate TC39 field decorator context
16
+ function fieldCtx(name: string) {
17
+ const initializers: Array<(this: object) => void> = [];
18
+ return {
19
+ ctx: {
20
+ name,
21
+ kind: "field" as const,
22
+ addInitializer(fn: (this: object) => void) { initializers.push(fn); },
23
+ } as ClassFieldDecoratorContext,
24
+ run(instance: object) {
25
+ initializers.forEach((fn) => { fn.call(instance); });
26
+ },
27
+ };
28
+ }
29
+
14
30
  // Helper to simulate TC39 method decorator context
15
31
  function mockMethodContext(name: string) {
16
32
  const initializers: Array<(this: object) => void> = [];
17
33
  return {
18
34
  name,
19
35
  kind: "method" as const,
20
- addInitializer(fn: (this: object) => void) {
21
- initializers.push(fn);
22
- },
36
+ addInitializer(fn: (this: object) => void) { initializers.push(fn); },
23
37
  runInitializers(instance: object) {
24
38
  initializers.forEach((fn) => { fn.call(instance); });
25
39
  },
@@ -29,37 +43,58 @@ function mockMethodContext(name: string) {
29
43
  // ── StateMachine ──────────────────────────────────────────────────────────────
30
44
 
31
45
  describe("StateMachine decorator", () => {
32
- it("adds a machine property via prototype", () => {
33
- class Light {}
34
- const Wrapped = StateMachine(TOGGLE_DEF)(Light, {} as ClassDecoratorContext) as unknown as typeof Light;
35
- const instance = new Wrapped() as Record<string, unknown>;
46
+ it("injects a machine via field decorator", () => {
47
+ const { ctx, run } = fieldCtx("machine");
48
+ StateMachine(TOGGLE_DEF)(undefined, ctx as ClassFieldDecoratorContext<object, Machine<"off"|"on","toggle">>);
49
+
50
+ const instance: Record<string, unknown> = {};
51
+ run(instance);
36
52
  expect(instance.machine).toBeDefined();
37
- expect((instance.machine as ReturnType<typeof createMachine>).state()).toBe("off");
53
+ expect((instance.machine as Machine<string, string>).state()).toBe("off");
38
54
  });
39
55
 
40
56
  it("returns a separate machine per instance", () => {
41
- class Bulb {}
42
- const Wrapped = StateMachine(TOGGLE_DEF)(Bulb, {} as ClassDecoratorContext) as unknown as typeof Bulb;
43
- const a = new Wrapped() as Record<string, unknown>;
44
- const b = new Wrapped() as Record<string, unknown>;
45
- (a.machine as ReturnType<typeof createMachine>).send("toggle");
46
- expect((a.machine as ReturnType<typeof createMachine>).state()).toBe("on");
47
- expect((b.machine as ReturnType<typeof createMachine>).state()).toBe("off");
57
+ const { ctx, run } = fieldCtx("machine");
58
+ StateMachine(TOGGLE_DEF)(undefined, ctx as ClassFieldDecoratorContext<object, Machine<"off"|"on","toggle">>);
59
+
60
+ const a: Record<string, unknown> = {};
61
+ const b: Record<string, unknown> = {};
62
+ run(a);
63
+ run(b);
64
+ (a.machine as Machine<string, string>).send("toggle");
65
+ expect((a.machine as Machine<string, string>).state()).toBe("on");
66
+ expect((b.machine as Machine<string, string>).state()).toBe("off");
48
67
  });
49
68
 
50
- it("uses a custom property key", () => {
51
- class Widget {}
52
- const Wrapped = StateMachine(TOGGLE_DEF, "fsm")(Widget, {} as ClassDecoratorContext) as unknown as typeof Widget;
53
- const instance = new Wrapped() as Record<string, unknown>;
69
+ it("supports a custom field name", () => {
70
+ const { ctx, run } = fieldCtx("fsm");
71
+ StateMachine(TOGGLE_DEF)(undefined, ctx as ClassFieldDecoratorContext<object, Machine<"off"|"on","toggle">>);
72
+
73
+ const instance: Record<string, unknown> = {};
74
+ run(instance);
54
75
  expect(instance.fsm).toBeDefined();
76
+ expect((instance.fsm as Machine<string, string>).state()).toBe("off");
55
77
  });
56
78
 
57
- it("returns the same instance on repeated access", () => {
58
- class Btn {}
59
- const Wrapped = StateMachine(TOGGLE_DEF)(Btn, {} as ClassDecoratorContext) as unknown as typeof Btn;
60
- const instance = new Wrapped() as Record<string, unknown>;
79
+ it("returns the same machine instance on repeated access", () => {
80
+ const { ctx, run } = fieldCtx("machine");
81
+ StateMachine(TOGGLE_DEF)(undefined, ctx as ClassFieldDecoratorContext<object, Machine<"off"|"on","toggle">>);
82
+
83
+ const instance: Record<string, unknown> = {};
84
+ run(instance);
61
85
  expect(instance.machine).toBe(instance.machine);
62
86
  });
87
+
88
+ it("setter is a no-op — assigning to the field does not replace the machine", () => {
89
+ const { ctx, run } = fieldCtx("machine");
90
+ StateMachine(TOGGLE_DEF)(undefined, ctx as ClassFieldDecoratorContext<object, Machine<"off"|"on","toggle">>);
91
+
92
+ const instance: Record<string, unknown> = {};
93
+ run(instance);
94
+ const original = instance.machine;
95
+ instance.machine = "overwrite-attempt"; // triggers set(): void {} (no-op)
96
+ expect(instance.machine).toBe(original);
97
+ });
63
98
  });
64
99
 
65
100
  // ── Transition ────────────────────────────────────────────────────────────────
@@ -105,7 +140,6 @@ describe("Transition decorator", () => {
105
140
  ctx.runInitializers(instance);
106
141
  const method = (instance as Record<string, () => unknown>).doTransition;
107
142
 
108
- // "toggle" is not a valid event from "on" state
109
143
  method();
110
144
  expect(original).not.toHaveBeenCalled();
111
145
  });
@@ -123,13 +157,12 @@ describe("Transition decorator", () => {
123
157
  expect(original).toHaveBeenCalledWith(42);
124
158
  });
125
159
 
126
- it("@Transition on method in class without @StateMachine — warns and returns undefined", () => {
160
+ it("@Transition on method without @StateMachine — warns and returns undefined", () => {
127
161
  const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
128
162
  const original = vi.fn(() => "ran");
129
163
  const ctx = mockMethodContext("doTransition");
130
164
  Transition("machine", "toggle")(original, ctx as unknown as ClassMethodDecoratorContext);
131
165
 
132
- // Instance has no "machine" property at all
133
166
  const instance = {} as Record<string, unknown>;
134
167
  ctx.runInitializers(instance);
135
168
  const method = (instance as Record<string, () => unknown>).doTransition;
@@ -141,27 +174,3 @@ describe("Transition decorator", () => {
141
174
  warn.mockRestore();
142
175
  });
143
176
  });
144
-
145
- // ── StateMachine on subclass ───────────────────────────────────────────────────
146
-
147
- describe("StateMachine decorator on subclass", () => {
148
- it("machine is accessible on subclass instance", () => {
149
- class Base {}
150
- class Child extends Base {}
151
- const Wrapped = StateMachine(TOGGLE_DEF)(Child, {} as ClassDecoratorContext) as unknown as typeof Child;
152
- const instance = new Wrapped() as Record<string, unknown>;
153
- expect(instance.machine).toBeDefined();
154
- expect((instance.machine as ReturnType<typeof createMachine>).state()).toBe("off");
155
- });
156
-
157
- it("instances of the decorated subclass are isolated", () => {
158
- class Base {}
159
- class Child extends Base {}
160
- const Wrapped = StateMachine(TOGGLE_DEF)(Child, {} as ClassDecoratorContext) as unknown as typeof Child;
161
- const a = new Wrapped() as Record<string, unknown>;
162
- const b = new Wrapped() as Record<string, unknown>;
163
- (a.machine as ReturnType<typeof createMachine>).send("toggle");
164
- expect((a.machine as ReturnType<typeof createMachine>).state()).toBe("on");
165
- expect((b.machine as ReturnType<typeof createMachine>).state()).toBe("off");
166
- });
167
- });
package/src/decorators.ts CHANGED
@@ -1,12 +1,8 @@
1
- import type { RootComponent } from "@praxisjs/core/internal";
2
1
  import {
3
2
  createMethodDecorator,
4
- createClassDecorator,
5
- ClassBehavior,
6
- type ClassEnhancement,
7
3
  } from "@praxisjs/decorators";
8
4
 
9
- import { createMachine, type Machine, type MachineDefinition } from "./machine";
5
+ import { createMachine, type Machine, type MachineDefinition } from "./machine";
10
6
 
11
7
  export function Transition(machineProp: string, event: string) {
12
8
  const decorator = createMethodDecorator({
@@ -32,52 +28,28 @@ export function Transition(machineProp: string, event: string) {
32
28
  ) => void;
33
29
  }
34
30
 
35
- class StateMachineBehavior<
36
- S extends string,
37
- E extends string,
38
- > extends ClassBehavior {
39
- constructor(
40
- private readonly definition: MachineDefinition<S, E>,
41
- private readonly propertyKey: string,
42
- ) {
43
- super();
44
- }
45
-
46
- create(_instance: RootComponent): ClassEnhancement {
47
- return {};
48
- }
49
-
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- initialize(Enhanced: new (...args: any[]) => unknown): void {
52
- const machines = new WeakMap<object, Machine<S, E>>();
53
- const { definition, propertyKey } = this;
54
- Object.defineProperty(
55
- (Enhanced as { prototype: object }).prototype,
56
- propertyKey,
57
- {
58
- get(this: object): Machine<S, E> {
59
- if (!machines.has(this))
60
- machines.set(this, createMachine(definition));
61
- return machines.get(this) as Machine<S, E>;
62
- },
63
- enumerable: true,
64
- configurable: true,
65
- },
66
- );
67
- }
68
- }
69
-
70
31
  export function StateMachine<S extends string, E extends string>(
71
32
  definition: MachineDefinition<S, E>,
72
- propertyKey = "machine",
73
33
  ) {
74
- const decorator = createClassDecorator(
75
- new StateMachineBehavior(definition, propertyKey),
76
- );
77
- // FSM decorators work on any class, not just RootComponent
34
+ const machines = new WeakMap<object, Machine<S, E>>();
78
35
 
79
- return decorator as unknown as (
80
- value: abstract new (...args: unknown[]) => unknown,
81
- context: ClassDecoratorContext,
82
- ) => abstract new (...args: unknown[]) => unknown;
36
+ return function <T extends object>(
37
+ _value: undefined,
38
+ context: ClassFieldDecoratorContext<T, Machine<S, E>>,
39
+ ): void {
40
+ context.addInitializer(function (this: object) {
41
+ const name = context.name;
42
+ Object.defineProperty(this, name, {
43
+ get(): Machine<S, E> {
44
+ const self = this as object;
45
+ if (!machines.has(self)) machines.set(self, createMachine(definition));
46
+ return machines.get(self) as Machine<S, E>;
47
+ },
48
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
49
+ set(): void {},
50
+ enumerable: true,
51
+ configurable: true,
52
+ });
53
+ });
54
+ };
83
55
  }