@praxisjs/fsm 2.2.0 → 2.2.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 +32 -0
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +25 -20
- package/dist/decorators.js.map +1 -1
- package/dist/machine.d.ts.map +1 -1
- package/dist/machine.js +31 -36
- package/dist/machine.js.map +1 -1
- package/package.json +15 -6
- package/src/decorators.ts +26 -21
- package/src/machine.ts +40 -42
- package/dist/__tests__/decorators.test.d.ts +0 -2
- package/dist/__tests__/decorators.test.d.ts.map +0 -1
- package/dist/__tests__/decorators.test.js +0 -182
- package/dist/__tests__/decorators.test.js.map +0 -1
- package/dist/__tests__/machine.test.d.ts +0 -2
- package/dist/__tests__/machine.test.d.ts.map +0 -1
- package/dist/__tests__/machine.test.js +0 -448
- package/dist/__tests__/machine.test.js.map +0 -1
- package/src/__tests__/decorators.test.ts +0 -219
- package/src/__tests__/machine.test.ts +0 -499
- package/tsconfig.json +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @praxisjs/fsm
|
|
2
2
|
|
|
3
|
+
## 2.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 75115a9: Decorated fields are defined by their innermost decorator. With `@praxisjs/vite-plugin`, `@State()`, `@Prop()`, every built-in field decorator and `createFieldDecorator` bindings install their property directly with the field's initial value instead of replacing a plain value, which kept every instance in V8's slow dictionary mode. New `addClaimableInitializer(context, init)` export for field decorators written against the TC39 API.
|
|
8
|
+
- 75115a9: Field decorators throw a `TypeError` when the class is defined if they decorate an `accessor`, naming the member to fix. Covers `@State()`, `@Prop()`, every built-in field decorator and `createFieldDecorator` bindings. New `assertFieldContext(context, label?)` export for field decorators written against the TC39 API directly.
|
|
9
|
+
- 75115a9: Published packages no longer contain their tests: package builds compile `src` without `src/**/__tests__` (through a `tsconfig.build.json`), and each package's `files` leaves test sources and stale compiled tests out of the tarball. `pnpm typecheck` still type-checks the tests.
|
|
10
|
+
- 75115a9: Performance pass and bug fixes across the feature packages.
|
|
11
|
+
- `@praxisjs/router`: static paths are matched through an index and dynamic ones with a single regex run; `RouterView` keeps a shared layout mounted across navigations; new `router.isActive(path)` (used by `Link`, so a navigation re-evaluates only the links whose state changed) and `router.destroy()`. Fixes regex characters in route paths (`.`, `(`, `+`...) being treated as patterns, and a router replaced by `createRouter()` still reacting to `popstate`. `@Route` no longer wraps the class, and the field decorators share one accessor.
|
|
12
|
+
- `@praxisjs/store`: plugin-wrapped actions keep their identity across reads, extensions are bound once, and writing a field inside an effect no longer subscribes the effect to it. `constructor` and inherited `Object.prototype` members are no longer reported as actions. `@Store` caches the singleton.
|
|
13
|
+
- `@praxisjs/motion`: `@Tween` and `@Spring` share one accessor per field and no longer start a frame loop at construction; `tween()`/`spring()` react to target writes without an effect. Fixes `reset()` leaving a tween marked as playing.
|
|
14
|
+
- `@praxisjs/di`: cached singletons and tokens resolve without allocating, `@Injectable` registers the class itself instead of a subclass, and a dependency cycle through `inject()` calls in constructors now reports `[DI] Circular dependency detected` instead of overflowing the stack.
|
|
15
|
+
- `@praxisjs/fsm`: `send()` and `reset()` no longer subscribe the calling effect to the machine state, transitions allocate nothing per send, and `@StateMachine` shares one accessor.
|
|
16
|
+
- `@praxisjs/head`: tag diffing without `JSON.stringify` or a full `<head>` query per update; updating or removing an entry below the top one leaves the document alone, and removing an unknown id no longer resets the title.
|
|
17
|
+
- `@praxisjs/content`: each entry is loaded, parsed and rendered once and shared by `getCollection`, `getPage` and `getEntry`; `getEntry` looks the slug up in an index; schema defaults are built once per class.
|
|
18
|
+
|
|
19
|
+
- 75115a9: Smaller production bundles. The framework and feature packages are marked `"sideEffects": false`, so bundlers leave out the modules an app does not use (`For`/`Index` and `selector()` were kept in every bundle by their shape keepers). `@praxisjs/core`'s graph gates its observer instrumentation on a boolean that minifiers fold away when no observer is ever installed. `@praxisjs/runtime` passes `Index`'s positional update to the list view, so `For`-only apps leave it out, and builds its event prop table from camel-case names. `@praxisjs/devtools` is built one file per module and its decorators check `process.env.NODE_ENV` inline, so `@Trace`/`@Debug` in production code no longer bundle the registry and the panel. `@praxisjs/vite-plugin` serves TypeScript's decorator helpers and the claimed fields' helper from one virtual module (`virtual:praxisjs/decorators`) instead of writing them into every decorated file, and replaces the identical copies `tsc` wrote into ES module libraries.
|
|
20
|
+
- Updated dependencies [75115a9]
|
|
21
|
+
- Updated dependencies [75115a9]
|
|
22
|
+
- Updated dependencies [75115a9]
|
|
23
|
+
- Updated dependencies [75115a9]
|
|
24
|
+
- Updated dependencies [75115a9]
|
|
25
|
+
- Updated dependencies [75115a9]
|
|
26
|
+
- Updated dependencies [75115a9]
|
|
27
|
+
- Updated dependencies [75115a9]
|
|
28
|
+
- Updated dependencies [75115a9]
|
|
29
|
+
- Updated dependencies [75115a9]
|
|
30
|
+
- Updated dependencies [75115a9]
|
|
31
|
+
- @praxisjs/decorators@2.0.0
|
|
32
|
+
- @praxisjs/core@2.3.0
|
|
33
|
+
- @praxisjs/shared@0.3.2
|
|
34
|
+
|
|
3
35
|
## 2.2.0
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAMA,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,CAC1B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAEhB,CAAC,SAAS,MAAM,GAAG,GAAG,EAGtB,UAAU,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,KAAK,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAEnE,CAAC,SAAS,MAAM,EAC/B,QAAQ,SAAS,EACjB,SAAS,0BAA0B,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KACpD,IAAI,CA2BR"}
|
package/dist/decorators.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createMethodDecorator, } from "@praxisjs/decorators";
|
|
1
|
+
import { createMethodDecorator, addClaimableInitializer, assertFieldContext, } from "@praxisjs/decorators";
|
|
2
2
|
import { createMachine } from "./machine";
|
|
3
3
|
export function Transition(machineProp, event) {
|
|
4
4
|
const decorator = createMethodDecorator({
|
|
@@ -20,26 +20,31 @@ export function Transition(machineProp, event) {
|
|
|
20
20
|
export function StateMachine(
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
22
|
definition) {
|
|
23
|
-
const machines = new WeakMap();
|
|
24
23
|
return function (_value, context) {
|
|
25
|
-
context
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
assertFieldContext(context);
|
|
25
|
+
const name = context.name;
|
|
26
|
+
// Each instance's machine lives under this symbol, created on first read; the accessor itself
|
|
27
|
+
// is shared by every instance.
|
|
28
|
+
const slot = Symbol(`praxis.fsm.${String(name)}`);
|
|
29
|
+
const descriptor = {
|
|
30
|
+
get() {
|
|
31
|
+
let machine = this[slot];
|
|
32
|
+
if (machine === undefined) {
|
|
33
|
+
const def = typeof definition === "function" ? definition.call(this) : definition;
|
|
34
|
+
machine = createMachine(def, this);
|
|
35
|
+
this[slot] = machine;
|
|
36
|
+
}
|
|
37
|
+
return machine;
|
|
38
|
+
},
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
40
|
+
set() { },
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
};
|
|
44
|
+
addClaimableInitializer(context, function () {
|
|
45
|
+
// Reserving the slot up front keeps every instance of the class on the same shape.
|
|
46
|
+
this[slot] = undefined;
|
|
47
|
+
Object.defineProperty(this, name, descriptor);
|
|
43
48
|
});
|
|
44
49
|
};
|
|
45
50
|
}
|
package/dist/decorators.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,GACnB,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;AAM1B,8DAA8D;AAC9D,UAAoF;IAEpF,OAAO,UACL,MAAiB,EACjB,OAAqD;QAErD,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,8FAA8F;QAC9F,+BAA+B;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,UAAU,GAAuB;YACrC,GAAG;gBACD,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,MAAM,GAAG,GAAG,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;oBAClF,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,IAAoB,CAAC,CAAC;oBACnD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;gBACvB,CAAC;gBACD,OAAO,OAAO,CAAC;YACjB,CAAC;YACD,gEAAgE;YAChE,GAAG,KAAU,CAAC;YACd,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,IAAI;SACnB,CAAC;QACF,uBAAuB,CAAC,OAAO,EAAE;YAC/B,mFAAmF;YAClF,IAAgC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;YACpD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/machine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM;IAC3E,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,OAAO,CAAC;IACjC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,CAC1F,CAAC,EACD;IACE,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;CAClD,CACF,CAAC;AAEF,MAAM,WAAW,iBAAiB,CAChC,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,GAAG,MAAM;IAEzB,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC;IAClD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM;IACzD,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACxB,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACvB,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM;IAC3E,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,OAAO,CAAC;IACjC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,CAC1F,CAAC,EACD;IACE,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;CAClD,CACF,CAAC;AAEF,MAAM,WAAW,iBAAiB,CAChC,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,GAAG,MAAM;IAEzB,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC;IAClD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM;IACzD,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACxB,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACvB,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;CACf;AAgBD,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,UAAU,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAE,CAAW,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAmD9E"}
|
package/dist/machine.js
CHANGED
|
@@ -1,59 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
return null;
|
|
5
|
-
if (typeof config === "string")
|
|
6
|
-
return { target: config };
|
|
7
|
-
const { target, guard, action } = config;
|
|
8
|
-
return {
|
|
9
|
-
target,
|
|
10
|
-
guard: guard ? () => guard(instance) : undefined,
|
|
11
|
-
action: action ? () => { action(instance); } : undefined,
|
|
12
|
-
};
|
|
1
|
+
import { computed, readSignal, SignalNode, writeSignal } from "@praxisjs/core/internal";
|
|
2
|
+
function normalize(config) {
|
|
3
|
+
return typeof config === "string" ? { target: config } : config;
|
|
13
4
|
}
|
|
14
5
|
const DEFAULT_HISTORY_LIMIT = 100;
|
|
15
6
|
export function createMachine(definition, instance = {}) {
|
|
16
|
-
const
|
|
17
|
-
const
|
|
7
|
+
const stateNode = new SignalNode(definition.initial);
|
|
8
|
+
const historyNode = new SignalNode([]);
|
|
18
9
|
const limit = definition.historyLimit ?? DEFAULT_HISTORY_LIMIT;
|
|
10
|
+
function transitionFor(state, event) {
|
|
11
|
+
const config = definition.states[state].on?.[event];
|
|
12
|
+
return config == null ? null : normalize(config);
|
|
13
|
+
}
|
|
14
|
+
// Reads the current state without tracking: sending is a write, so an effect that sends must not
|
|
15
|
+
// re-run because the state it just changed moved.
|
|
19
16
|
function send(event) {
|
|
20
|
-
const current =
|
|
21
|
-
const
|
|
22
|
-
if (!
|
|
17
|
+
const current = stateNode.value;
|
|
18
|
+
const transition = transitionFor(current, event);
|
|
19
|
+
if (!transition)
|
|
23
20
|
return false;
|
|
24
|
-
if (
|
|
21
|
+
if (transition.guard && !transition.guard(instance))
|
|
25
22
|
return false;
|
|
26
|
-
const
|
|
23
|
+
const nextState = transition.target;
|
|
27
24
|
definition.states[current].onExit?.({ event, to: nextState });
|
|
28
|
-
|
|
25
|
+
writeSignal(stateNode, nextState);
|
|
29
26
|
if (limit !== 0) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return [...h.slice(h.length - limit + 1), entry];
|
|
35
|
-
});
|
|
27
|
+
const prev = historyNode.value;
|
|
28
|
+
const next = prev.length < limit ? prev.slice() : prev.slice(prev.length - limit + 1);
|
|
29
|
+
next.push({ from: current, event, to: nextState });
|
|
30
|
+
writeSignal(historyNode, next);
|
|
36
31
|
}
|
|
37
32
|
definition.onTransition?.(current, event, nextState);
|
|
38
|
-
|
|
33
|
+
transition.action?.(instance);
|
|
39
34
|
definition.states[nextState].onEnter?.({ event, from: current });
|
|
40
35
|
return true;
|
|
41
36
|
}
|
|
42
37
|
return {
|
|
43
|
-
state: computed(() =>
|
|
44
|
-
history: computed(() =>
|
|
38
|
+
state: computed(() => readSignal(stateNode)),
|
|
39
|
+
history: computed(() => readSignal(historyNode)),
|
|
45
40
|
send,
|
|
46
41
|
can: (event) => {
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
42
|
+
const transition = transitionFor(readSignal(stateNode), event);
|
|
43
|
+
if (!transition)
|
|
49
44
|
return false;
|
|
50
|
-
return !
|
|
45
|
+
return !transition.guard || transition.guard(instance);
|
|
51
46
|
},
|
|
52
|
-
is: (state) =>
|
|
47
|
+
is: (state) => readSignal(stateNode) === state,
|
|
53
48
|
reset: () => {
|
|
54
|
-
definition.states[
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
definition.states[stateNode.value].onExit?.(undefined);
|
|
50
|
+
writeSignal(stateNode, definition.initial);
|
|
51
|
+
writeSignal(historyNode, []);
|
|
57
52
|
definition.states[definition.initial].onEnter?.(undefined);
|
|
58
53
|
},
|
|
59
54
|
};
|
package/dist/machine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisjs/fsm",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
@@ -10,16 +11,24 @@
|
|
|
10
11
|
"types": "./dist/index.d.ts"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"!dist/**/__tests__",
|
|
18
|
+
"!src/**/__tests__",
|
|
19
|
+
"CHANGELOG.md"
|
|
20
|
+
],
|
|
13
21
|
"devDependencies": {
|
|
14
22
|
"typescript": "^6.0.3"
|
|
15
23
|
},
|
|
16
24
|
"dependencies": {
|
|
17
|
-
"@praxisjs/core": "2.
|
|
18
|
-
"@praxisjs/decorators": "
|
|
19
|
-
"@praxisjs/shared": "0.3.
|
|
25
|
+
"@praxisjs/core": "2.3.0",
|
|
26
|
+
"@praxisjs/decorators": "2.0.0",
|
|
27
|
+
"@praxisjs/shared": "0.3.2"
|
|
20
28
|
},
|
|
21
29
|
"scripts": {
|
|
22
|
-
"build": "tsc",
|
|
23
|
-
"dev": "tsc --watch"
|
|
30
|
+
"build": "tsc -p tsconfig.build.json",
|
|
31
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
24
33
|
}
|
|
25
34
|
}
|
package/src/decorators.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMethodDecorator,
|
|
3
|
+
addClaimableInitializer,
|
|
4
|
+
assertFieldContext,
|
|
3
5
|
} from "@praxisjs/decorators";
|
|
4
6
|
|
|
5
7
|
import { createMachine, type Machine, type MachineDefinition } from "./machine";
|
|
@@ -37,31 +39,34 @@ export function StateMachine<
|
|
|
37
39
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
40
|
definition: MachineDefinition<S, E, T> | ((this: any) => MachineDefinition<S, E, T>),
|
|
39
41
|
) {
|
|
40
|
-
const machines = new WeakMap<object, Machine<S, E>>();
|
|
41
|
-
|
|
42
42
|
return function <U extends object>(
|
|
43
43
|
_value: undefined,
|
|
44
44
|
context: ClassFieldDecoratorContext<U, Machine<S, E>>,
|
|
45
45
|
): void {
|
|
46
|
-
context
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
assertFieldContext(context);
|
|
47
|
+
const name = context.name;
|
|
48
|
+
// Each instance's machine lives under this symbol, created on first read; the accessor itself
|
|
49
|
+
// is shared by every instance.
|
|
50
|
+
const slot = Symbol(`praxis.fsm.${String(name)}`);
|
|
51
|
+
const descriptor: PropertyDescriptor = {
|
|
52
|
+
get(this: Record<symbol, Machine<S, E> | undefined>): Machine<S, E> {
|
|
53
|
+
let machine = this[slot];
|
|
54
|
+
if (machine === undefined) {
|
|
55
|
+
const def = typeof definition === "function" ? definition.call(this) : definition;
|
|
56
|
+
machine = createMachine(def, this as unknown as T);
|
|
57
|
+
this[slot] = machine;
|
|
58
|
+
}
|
|
59
|
+
return machine;
|
|
60
|
+
},
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
62
|
+
set(): void {},
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
};
|
|
66
|
+
addClaimableInitializer(context, function () {
|
|
67
|
+
// Reserving the slot up front keeps every instance of the class on the same shape.
|
|
68
|
+
(this as Record<symbol, unknown>)[slot] = undefined;
|
|
69
|
+
Object.defineProperty(this, name, descriptor);
|
|
65
70
|
});
|
|
66
71
|
};
|
|
67
72
|
}
|
package/src/machine.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computed, readSignal, SignalNode, writeSignal } from "@praxisjs/core/internal";
|
|
2
2
|
import type { Computed } from "@praxisjs/shared";
|
|
3
3
|
|
|
4
4
|
export interface TransitionTarget<S extends string, T extends object = object> {
|
|
@@ -41,18 +41,16 @@ export interface Machine<S extends string, E extends string> {
|
|
|
41
41
|
reset(): void;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
action: action ? () => { action(instance); } : undefined,
|
|
55
|
-
};
|
|
44
|
+
interface HistoryEntry<S extends string, E extends string> {
|
|
45
|
+
from: S;
|
|
46
|
+
event: E;
|
|
47
|
+
to: S;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalize<S extends string, T extends object>(
|
|
51
|
+
config: S | TransitionTarget<S, T>,
|
|
52
|
+
): TransitionTarget<S, T> {
|
|
53
|
+
return typeof config === "string" ? { target: config } : config;
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
const DEFAULT_HISTORY_LIMIT = 100;
|
|
@@ -62,53 +60,53 @@ export function createMachine<
|
|
|
62
60
|
E extends string,
|
|
63
61
|
T extends object = object,
|
|
64
62
|
>(definition: MachineDefinition<S, E, T>, instance: T = {} as T): Machine<S, E> {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
63
|
+
const stateNode = new SignalNode(definition.initial);
|
|
64
|
+
const historyNode = new SignalNode<Array<HistoryEntry<S, E>>>([]);
|
|
67
65
|
const limit = definition.historyLimit ?? DEFAULT_HISTORY_LIMIT;
|
|
68
66
|
|
|
67
|
+
function transitionFor(state: S, event: E): TransitionTarget<S, T> | null {
|
|
68
|
+
const config = definition.states[state].on?.[event];
|
|
69
|
+
return config == null ? null : normalize(config);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Reads the current state without tracking: sending is a write, so an effect that sends must not
|
|
73
|
+
// re-run because the state it just changed moved.
|
|
69
74
|
function send(event: E): boolean {
|
|
70
|
-
const current =
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
if (!resolved) return false;
|
|
76
|
-
if (resolved.guard && !resolved.guard()) return false;
|
|
75
|
+
const current = stateNode.value;
|
|
76
|
+
const transition = transitionFor(current, event);
|
|
77
|
+
if (!transition) return false;
|
|
78
|
+
if (transition.guard && !transition.guard(instance)) return false;
|
|
77
79
|
|
|
78
|
-
const
|
|
80
|
+
const nextState = transition.target;
|
|
79
81
|
|
|
80
82
|
definition.states[current].onExit?.({ event, to: nextState });
|
|
81
|
-
|
|
83
|
+
writeSignal(stateNode, nextState);
|
|
82
84
|
if (limit !== 0) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
85
|
+
const prev = historyNode.value;
|
|
86
|
+
const next = prev.length < limit ? prev.slice() : prev.slice(prev.length - limit + 1);
|
|
87
|
+
next.push({ from: current, event, to: nextState });
|
|
88
|
+
writeSignal(historyNode, next);
|
|
88
89
|
}
|
|
89
90
|
definition.onTransition?.(current, event, nextState);
|
|
90
|
-
|
|
91
|
+
transition.action?.(instance);
|
|
91
92
|
definition.states[nextState].onEnter?.({ event, from: current });
|
|
92
93
|
return true;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
return {
|
|
96
|
-
state: computed(() =>
|
|
97
|
-
history: computed(() =>
|
|
97
|
+
state: computed(() => readSignal(stateNode)),
|
|
98
|
+
history: computed(() => readSignal(historyNode)),
|
|
98
99
|
send,
|
|
99
100
|
can: (event) => {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
104
|
-
if (!resolved) return false;
|
|
105
|
-
return !resolved.guard || resolved.guard();
|
|
101
|
+
const transition = transitionFor(readSignal(stateNode), event);
|
|
102
|
+
if (!transition) return false;
|
|
103
|
+
return !transition.guard || transition.guard(instance);
|
|
106
104
|
},
|
|
107
|
-
is: (state) =>
|
|
105
|
+
is: (state) => readSignal(stateNode) === state,
|
|
108
106
|
reset: () => {
|
|
109
|
-
definition.states[
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
definition.states[stateNode.value].onExit?.(undefined);
|
|
108
|
+
writeSignal(stateNode, definition.initial);
|
|
109
|
+
writeSignal(historyNode, []);
|
|
112
110
|
definition.states[definition.initial].onEnter?.(undefined);
|
|
113
111
|
},
|
|
114
112
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":""}
|