-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
363 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
/scxml-xsd | ||
/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
{ | ||
"cSpell.words": [ | ||
"scxml" | ||
] | ||
"cSpell.words": ["scxml"], | ||
"editor.rulers": [ | ||
120 | ||
], | ||
"typescript.tsdk": "node_modules\\typescript\\lib" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { Machine } from "."; | ||
import { Test } from "ts-toolbelt" | ||
|
||
let test1 = Machine({ | ||
initial: "a", | ||
states: { a: {} }, | ||
}); | ||
let diagnosis1 = Machine.dignose({ | ||
initial: "a", | ||
states: { a: {} }, | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis1, "All good!", Test.Pass>() | ||
]) | ||
|
||
|
||
let test2 = Machine({ | ||
// @ts-expect-error | ||
initial: "b", | ||
states: { a: {} } | ||
}) | ||
let diagnosis2 = Machine.dignose({ | ||
initial: "b", | ||
states: { a: {} } | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis2, [{ | ||
error: ["state", "b", "is not defined in states"], | ||
at: ["initial"] | ||
}], Test.Pass>() | ||
]) | ||
|
||
|
||
let test3 = Machine({ | ||
initial: "a", | ||
states: { | ||
a: { | ||
// @ts-expect-error | ||
initial: "x", | ||
states: { | ||
b: {} | ||
} | ||
} | ||
} | ||
}) | ||
let diagnosis3 = Machine.dignose({ | ||
initial: "a", | ||
states: { | ||
a: { | ||
initial: "x", | ||
states: { | ||
b: {} | ||
} | ||
} | ||
} | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis3, [{ | ||
error: ["state", "x", "is not defined in states"]; | ||
at: ["states", "a", "initial"]; | ||
}], Test.Pass>() | ||
]) | ||
|
||
// @ts-expect-error | ||
let test4 = Machine({ | ||
states: { a: {} } | ||
}) | ||
let diagnosis4 = Machine.dignose({ | ||
states: { a: {} } | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis4, [{ | ||
error: "initial state is required"; | ||
at: "root"; | ||
}], Test.Pass>() | ||
]) | ||
|
||
|
||
let test5 = Machine({ | ||
initial: 1, | ||
// @ts-expect-error | ||
states: { 1: {} } | ||
}) | ||
let diagnosis5 = Machine.dignose({ | ||
// @ts-ignore weird | ||
initial: 1, | ||
states: { 1: {} } | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis5, [{ | ||
error: "state identifiers should be only strings"; | ||
at: ["states"]; | ||
}], Test.Pass>() | ||
]) | ||
|
||
|
||
// @ts-expect-error | ||
let test6 = Machine({ | ||
type: "atomic", | ||
initial: "a", | ||
states: { a: {} } | ||
}) | ||
let diagnosis6 = Machine.dignose({ | ||
type: "atomic", | ||
initial: "a", | ||
states: { a: {} } | ||
}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis6, [{ | ||
error: "The state node is atomic meaning no nested states, so can't have an initial property"; | ||
at: ["initial"]; | ||
}, { | ||
error: "The state node is atomic meaning no nested states, so can't have an states property"; | ||
at: ["states"]; | ||
}], Test.Pass>() | ||
]) | ||
|
||
|
||
let test7 = Machine({}) | ||
let diagnosis7 = Machine.dignose({}) | ||
Test.checks([ | ||
Test.check<typeof diagnosis7, "All good!", Test.Pass>() | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import { O, A, U, L, B } from "ts-toolbelt"; | ||
import { P } from "Object/_api"; | ||
|
||
export declare const Machine: { | ||
<D extends MachineDefinition.Of<D, {}>>(definition: D): MachineHandle.Of<D, {}> | ||
<D extends MachineDefinition.Of<D, I>, I extends MachineDefinition.Implementations.Of<D, I>>( | ||
definition: D, | ||
implementations: I | ||
): MachineHandle.Of<D, I> | ||
|
||
dignose: | ||
<D extends OE.DeepReadonly<D>>(defintion: D) => D.Show<MachineDefinition.Dignostics.Of<A.Cast<D, object>, {}>> | ||
} | ||
|
||
namespace MachineDefinition { | ||
export type Of<Definition extends object, Implementations extends object> = | ||
& StateNode.Of<Definition, Implementations, []> | ||
& { context?: "TODO" }; | ||
|
||
|
||
export namespace Dignostics { | ||
export type Of<Definition extends object, Implementations extends object> = | ||
StateNode.Dignostics.Of<Definition, Implementations, []> | ||
} | ||
|
||
export namespace StateNode { | ||
export type Of< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends string[], | ||
Self extends object = A.Cast<O.Path<Definition, Path>, object>, | ||
Initial = OE.At<Self, "initial">, | ||
States = OE.At<Self, "states">, | ||
Type = OE.At<Self, "type", "compound">, | ||
Id = O.At<Self, "id"> | ||
> = | ||
& ( | ||
| & { type?: | ||
| "compound" | ||
| "parallel" | ||
| "final" | ||
| "history" | ||
, states?: | ||
& { [StateIdentifier in U.Intersect<keyof O.At<Self, "states">, string>]: | ||
StateNode.Of<Definition, Implementations, L.Concat<Path, ["states", StateIdentifier]>> | ||
} | ||
& { [_ in U.Filter<keyof O.At<Self, "states">, string>]?: never } // disallow non-string keys | ||
} | ||
& (B.Not<A.Equals<States, undefined>> extends B.True ? { initial: keyof States } : {}) | ||
| { type: "atomic" | ||
, initial?: never | ||
, states?: never | ||
} | ||
) | ||
& { id?: string | ||
} | ||
|
||
export namespace Dignostics { | ||
export type Of< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends PropertyKey[], | ||
Self extends object = A.Cast<O.Path<Definition, Path>, object> | ||
> = | ||
[ | ||
...Initial<Definition, Implementations, Path>, | ||
...(O.Has<Self, "states"> extends B.True | ||
? States<Definition, Implementations, Path> | ||
: [] | ||
) | ||
]; | ||
|
||
export type Initial< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends PropertyKey[], | ||
StateNode extends object = A.Cast<O.Path<Definition, Path>, object>, | ||
Initial = OE.At<StateNode, "initial">, | ||
States = OE.At<StateNode, "states">, | ||
Type = OE.At<StateNode, "type", "compound"> | ||
> = | ||
B.And<A.Equals<Initial, undefined>, B.Not<A.Equals<States, undefined>>> extends B.True | ||
? [D.WithPath<Path, "initial state is required">] : | ||
A.Equals<Initial, undefined> extends B.True | ||
? [] : | ||
A.Equals<Type, "atomic"> extends B.True | ||
? [ D.WithPath<L.Append<Path, "initial"> | ||
, "The state node is atomic meaning no nested states, so can't have an initial property"> ] : | ||
A.Equals<States, undefined> extends B.True | ||
? [ D.WithPath<L.Append<Path, "initial"> | ||
, "There are no states defined hence can't have an initial state">] : | ||
A.Contains<keyof States, number | symbol> extends B.True | ||
? [] : | ||
B.Not<A.Contains<Initial, keyof States>> extends B.True | ||
? [ D.WithPath<L.Append<Path, "initial"> | ||
, ["state", O.At<StateNode, "initial">, "is not defined in states"]>] : | ||
[]; | ||
|
||
export type States< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends PropertyKey[], | ||
StateNode extends object = A.Cast<O.Path<Definition, Path>, object>, | ||
States = OE.At<StateNode, "states">, | ||
Type = OE.At<StateNode, "type", "compound"> | ||
> = | ||
A.Equals<States, undefined> extends B.True ? [] : | ||
A.Equals<States, {}> extends B.True ? [] : | ||
[ | ||
...( | ||
A.Contains<keyof States, number | symbol> extends B.True | ||
? [D.WithPath<L.Append<Path, "states">, "state identifiers should be only strings">] | ||
: [] | ||
), | ||
...( | ||
B.And<A.Equals<Type, "atomic">, B.Not<A.Equals<States, undefined>>> extends B.True | ||
? [ D.WithPath<L.Append<Path, "states"> | ||
, "The state node is atomic meaning no nested states, so can't have an states property">] | ||
: [] | ||
), | ||
...( | ||
A.Equals<States, {}> extends B.True | ||
? [] | ||
: L.Flatten<U.ListOf<{ | ||
[S in keyof States]: | ||
Of_<Definition, Implementations, A.Cast<L.Concat<Path, ["states", S]>, PropertyKey[]>> | ||
}[keyof States]>> | ||
), | ||
]; | ||
|
||
export type Of_< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends PropertyKey[], | ||
Self extends object = A.Cast<O.Path<Definition, Path>, object> | ||
> = | ||
[ | ||
...Initial<Definition, Implementations, Path>, | ||
...(O.Has<Self, "states"> extends B.True | ||
? States_<Definition, Implementations, Path> | ||
: [] | ||
) | ||
]; | ||
|
||
export type States_< | ||
Definition extends object, | ||
Implementations extends object, | ||
Path extends PropertyKey[], | ||
StateNode extends object = A.Cast<O.Path<Definition, Path>, object>, | ||
States = OE.At<StateNode, "states">, | ||
Type = OE.At<StateNode, "type", "compound"> | ||
> = | ||
A.Equals<States, undefined> extends B.True ? [] : | ||
A.Equals<States, {}> extends B.True ? [] : | ||
[ | ||
...( | ||
A.Contains<keyof States, number | symbol> extends B.True | ||
? [D.WithPath<L.Append<Path, "states">, "state identifiers should be only strings">] | ||
: [] | ||
), | ||
...( | ||
B.And<A.Equals<Type, "atomic">, B.Not<A.Equals<States, undefined>>> extends B.True | ||
? [ D.WithPath<L.Append<Path, "states"> | ||
, "The state node is atomic meaning no nested states, so can't have an states property">] | ||
: [] | ||
) | ||
]; | ||
} | ||
} | ||
|
||
export namespace Implementations { | ||
export type Of<Definition extends object, Implementations extends object> = | ||
{} // TODO; | ||
} | ||
} | ||
|
||
namespace D { | ||
export type WithPath<P extends PropertyKey[], M> = | ||
{ error: M, at: A.Equals<P, []> extends B.True ? "root" : P } | ||
|
||
export type Show<T> = | ||
A.Is<T, []> extends B.True | ||
? "All good!" | ||
: { [I in keyof T]: | ||
T[I] extends { error: infer M, at: infer P } | ||
? { error: M, at: P } | ||
: never | ||
}; | ||
} | ||
|
||
namespace MachineHandle { | ||
export type Of<D, I> = {} // TODO; | ||
} | ||
|
||
namespace OE { | ||
export type At<T, K, F = undefined> = | ||
K extends keyof T | ||
? A.Equals<T[K], undefined> extends B.True | ||
? F | ||
: T[K] | ||
: F; | ||
|
||
export type DeepReadonly<T> = { | ||
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : string | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"devDependencies": { | ||
"ts-toolbelt": "^8.0.3", | ||
"typescript": "^4.0.0-beta" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es5", | ||
"module": "commonjs", | ||
"strict": true, | ||
"noImplicitAny": true, | ||
"esModuleInterop": true, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | ||
# yarn lockfile v1 | ||
|
||
|
||
ts-toolbelt@^8.0.3: | ||
version "8.0.3" | ||
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-8.0.3.tgz#d4136466dfb731e8ad1f890303dae176beb27710" | ||
integrity sha512-gxgyIVxf5B1ToZfA4cojFS3h2jPSxYWjvKehkuCVejMdnATeBdxY0tqCSr3okkwfSA3V5TBoWeYGVfLpMBfSMA== | ||
|
||
typescript@^4.0.0-beta: | ||
version "4.0.0-beta" | ||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-beta.tgz#a6a65e430562131de69496a3ef5484346bc0cdd2" | ||
integrity sha512-d3s/CogGtB2uPZ2Z8ts6eoUxxyB9PH3R27/UrzvpthuOvpCg4FWWnBbBiqJ0K4eu6eTlgmLiqQkh2dquReJweA== |