@typeonce/effect-machine-devtools 0.23.0 → 0.25.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.
- package/NOTICE +4 -0
- package/README.md +14 -8
- package/dist/DevServer.d.ts +2 -1
- package/dist/DevServer.d.ts.map +1 -1
- package/dist/DevServer.js.map +1 -1
- package/dist/DevToolsProtocol.d.ts +167 -17
- package/dist/DevToolsProtocol.d.ts.map +1 -1
- package/dist/DevToolsProtocol.js +1 -1
- package/dist/MachineDocument.d.ts +98 -2
- package/dist/MachineDocument.d.ts.map +1 -1
- package/dist/MachineDocument.js +35 -1
- package/dist/MachineDocument.js.map +1 -1
- package/dist/MachineRegistry.d.ts +56 -6
- package/dist/MachineRegistry.d.ts.map +1 -1
- package/dist/MachineWalkthrough.d.ts +171 -0
- package/dist/MachineWalkthrough.d.ts.map +1 -0
- package/dist/MachineWalkthrough.js +98 -0
- package/dist/MachineWalkthrough.js.map +1 -0
- package/dist/client/assets/index-BTOAhezX.js +37 -0
- package/dist/client/assets/index-Cg7xzSkz.css +1 -0
- package/dist/client/index.html +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/devServer.d.ts +2 -1
- package/dist/internal/devServer.d.ts.map +1 -1
- package/dist/internal/devServer.js +4 -3
- package/dist/internal/devServer.js.map +1 -1
- package/dist/internal/evaluationWorker.d.ts.map +1 -1
- package/dist/internal/evaluationWorker.js +28 -9
- package/dist/internal/evaluationWorker.js.map +1 -1
- package/dist/internal/machineDocument.d.ts.map +1 -1
- package/dist/internal/machineDocument.js +65 -2
- package/dist/internal/machineDocument.js.map +1 -1
- package/dist/internal/machineWalkthrough.d.ts +26 -0
- package/dist/internal/machineWalkthrough.d.ts.map +1 -0
- package/dist/internal/machineWalkthrough.js +256 -0
- package/dist/internal/machineWalkthrough.js.map +1 -0
- package/dist/internal/projectInspector.d.ts.map +1 -1
- package/dist/internal/projectInspector.js +15 -9
- package/dist/internal/projectInspector.js.map +1 -1
- package/index.html +2 -2
- package/package.json +4 -3
- package/src/DevServer.ts +6 -2
- package/src/DevToolsProtocol.ts +1 -1
- package/src/MachineDocument.ts +56 -1
- package/src/MachineWalkthrough.ts +199 -0
- package/src/index.ts +2 -2
- package/src/internal/browser/chart-layout.ts +433 -0
- package/src/internal/browser/chart-model.ts +234 -0
- package/src/internal/browser/chart-renderer.ts +661 -0
- package/src/internal/browser/example-machine.ts +15 -6
- package/src/internal/browser/input-form.ts +252 -0
- package/src/internal/browser/invoke-outcomes-example.ts +231 -0
- package/src/internal/browser/parallel-completion-example.ts +199 -0
- package/src/internal/browser/planner-example.ts +145 -0
- package/src/internal/browser/protocol-events-example.ts +200 -0
- package/src/internal/browser/styles.css +959 -185
- package/src/internal/browser/transition-semantics-example.ts +236 -0
- package/src/internal/browser/visualizer-app.ts +818 -389
- package/src/internal/browser/visualizer-model.ts +104 -0
- package/src/internal/browser/visualizer.ts +1 -1
- package/src/internal/devServer.ts +9 -4
- package/src/internal/evaluationWorker.ts +28 -10
- package/src/internal/machineDocument.ts +77 -2
- package/src/internal/machineWalkthrough.ts +355 -0
- package/src/internal/projectInspector.ts +29 -14
- package/dist/MachineSimulator.d.ts +0 -169
- package/dist/MachineSimulator.d.ts.map +0 -1
- package/dist/MachineSimulator.js +0 -98
- package/dist/MachineSimulator.js.map +0 -1
- package/dist/client/assets/index-BGOhE3Ng.css +0 -1
- package/dist/client/assets/index-DiqGhsGR.js +0 -14
- package/dist/internal/machineSimulator.d.ts +0 -5
- package/dist/internal/machineSimulator.d.ts.map +0 -1
- package/dist/internal/machineSimulator.js +0 -156
- package/dist/internal/machineSimulator.js.map +0 -1
- package/src/MachineSimulator.ts +0 -154
- package/src/internal/machineSimulator.ts +0 -199
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { InputSchema } from "../../MachineDocument.js"
|
|
2
|
+
|
|
3
|
+
type JsonPrimitive = string | number | boolean | null
|
|
4
|
+
|
|
5
|
+
export type InputField =
|
|
6
|
+
| {
|
|
7
|
+
readonly _tag: "String"
|
|
8
|
+
readonly title: string | undefined
|
|
9
|
+
readonly description: string | undefined
|
|
10
|
+
readonly defaultValue: string | undefined
|
|
11
|
+
readonly format: string | undefined
|
|
12
|
+
readonly minLength: number | undefined
|
|
13
|
+
readonly maxLength: number | undefined
|
|
14
|
+
readonly pattern: string | undefined
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
readonly _tag: "Number"
|
|
18
|
+
readonly title: string | undefined
|
|
19
|
+
readonly description: string | undefined
|
|
20
|
+
readonly defaultValue: number | undefined
|
|
21
|
+
readonly integer: boolean
|
|
22
|
+
readonly minimum: number | undefined
|
|
23
|
+
readonly maximum: number | undefined
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
readonly _tag: "Boolean"
|
|
27
|
+
readonly title: string | undefined
|
|
28
|
+
readonly description: string | undefined
|
|
29
|
+
readonly defaultValue: boolean | undefined
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
readonly _tag: "Enum"
|
|
33
|
+
readonly title: string | undefined
|
|
34
|
+
readonly description: string | undefined
|
|
35
|
+
readonly values: ReadonlyArray<JsonPrimitive>
|
|
36
|
+
readonly defaultValue: JsonPrimitive | undefined
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
readonly _tag: "Literal"
|
|
40
|
+
readonly title: string | undefined
|
|
41
|
+
readonly description: string | undefined
|
|
42
|
+
readonly value: JsonPrimitive
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
readonly _tag: "Object"
|
|
46
|
+
readonly title: string | undefined
|
|
47
|
+
readonly description: string | undefined
|
|
48
|
+
readonly fields: ReadonlyArray<{
|
|
49
|
+
readonly key: string
|
|
50
|
+
readonly required: boolean
|
|
51
|
+
readonly field: InputField
|
|
52
|
+
}>
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
readonly _tag: "Array"
|
|
56
|
+
readonly title: string | undefined
|
|
57
|
+
readonly description: string | undefined
|
|
58
|
+
readonly item: InputField
|
|
59
|
+
readonly minItems: number
|
|
60
|
+
readonly maxItems: number | undefined
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
readonly _tag: "Union"
|
|
64
|
+
readonly title: string | undefined
|
|
65
|
+
readonly description: string | undefined
|
|
66
|
+
readonly alternatives: ReadonlyArray<InputField>
|
|
67
|
+
}
|
|
68
|
+
| {
|
|
69
|
+
readonly _tag: "Unsupported"
|
|
70
|
+
readonly title: string | undefined
|
|
71
|
+
readonly description: string | undefined
|
|
72
|
+
readonly reason: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
76
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
77
|
+
|
|
78
|
+
const stringValue = (value: unknown): string | undefined => typeof value === "string" ? value : undefined
|
|
79
|
+
|
|
80
|
+
const numberValue = (value: unknown): number | undefined =>
|
|
81
|
+
typeof value === "number" && Number.isFinite(value) ? value : undefined
|
|
82
|
+
|
|
83
|
+
const primitiveValue = (value: unknown): JsonPrimitive | undefined =>
|
|
84
|
+
value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean"
|
|
85
|
+
? value
|
|
86
|
+
: undefined
|
|
87
|
+
|
|
88
|
+
const resolveReference = (
|
|
89
|
+
value: unknown,
|
|
90
|
+
definitions: Readonly<Record<string, unknown>>,
|
|
91
|
+
seen: ReadonlySet<string> = new Set()
|
|
92
|
+
): unknown => {
|
|
93
|
+
if (!isRecord(value) || typeof value.$ref !== "string" || !value.$ref.startsWith("#/$defs/")) return value
|
|
94
|
+
const name = decodeURIComponent(value.$ref.slice("#/$defs/".length))
|
|
95
|
+
if (seen.has(name)) return undefined
|
|
96
|
+
const next = definitions[name]
|
|
97
|
+
return resolveReference(next, definitions, new Set([...seen, name]))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const annotations = (schema: Record<string, unknown>) => ({
|
|
101
|
+
title: stringValue(schema.title),
|
|
102
|
+
description: stringValue(schema.description)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const mergeAllOf = (
|
|
106
|
+
schema: Record<string, unknown>,
|
|
107
|
+
definitions: Readonly<Record<string, unknown>>
|
|
108
|
+
): Record<string, unknown> => {
|
|
109
|
+
if (!Array.isArray(schema.allOf)) return schema
|
|
110
|
+
const { allOf, ...base } = schema
|
|
111
|
+
return Object.assign(
|
|
112
|
+
base,
|
|
113
|
+
...allOf
|
|
114
|
+
.map((part) => resolveReference(part, definitions))
|
|
115
|
+
.filter(isRecord)
|
|
116
|
+
.map((part) => mergeAllOf(part, definitions))
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const effectNumberAlternative = (
|
|
121
|
+
alternatives: ReadonlyArray<unknown>,
|
|
122
|
+
definitions: Readonly<Record<string, unknown>>
|
|
123
|
+
): Record<string, unknown> | undefined => {
|
|
124
|
+
if (alternatives.length !== 2) return undefined
|
|
125
|
+
const resolved = alternatives.map((alternative) => resolveReference(alternative, definitions))
|
|
126
|
+
const number = resolved.find((alternative) => isRecord(alternative) && alternative.type === "number")
|
|
127
|
+
const encodedNonFinite = resolved.find((alternative) => {
|
|
128
|
+
if (!isRecord(alternative) || alternative.type !== "string" || !Array.isArray(alternative.enum)) return false
|
|
129
|
+
const enumValues: ReadonlyArray<unknown> = alternative.enum
|
|
130
|
+
return enumValues.length === 3 &&
|
|
131
|
+
["Infinity", "-Infinity", "NaN"].every((value) => enumValues.includes(value))
|
|
132
|
+
})
|
|
133
|
+
return isRecord(number) && encodedNonFinite !== undefined ? number : undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const project = (
|
|
137
|
+
value: unknown,
|
|
138
|
+
definitions: Readonly<Record<string, unknown>>
|
|
139
|
+
): InputField => {
|
|
140
|
+
const referenced = resolveReference(value, definitions)
|
|
141
|
+
if (!isRecord(referenced)) {
|
|
142
|
+
return {
|
|
143
|
+
_tag: "Unsupported",
|
|
144
|
+
title: undefined,
|
|
145
|
+
description: undefined,
|
|
146
|
+
reason: "This input schema cannot be represented as fields."
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const resolved = mergeAllOf(referenced, definitions)
|
|
150
|
+
const common = annotations(resolved)
|
|
151
|
+
const alternatives = Array.isArray(resolved.oneOf)
|
|
152
|
+
? resolved.oneOf
|
|
153
|
+
: Array.isArray(resolved.anyOf)
|
|
154
|
+
? resolved.anyOf
|
|
155
|
+
: undefined
|
|
156
|
+
if (alternatives !== undefined) {
|
|
157
|
+
const effectNumber = effectNumberAlternative(alternatives, definitions)
|
|
158
|
+
if (effectNumber !== undefined) {
|
|
159
|
+
return {
|
|
160
|
+
_tag: "Number",
|
|
161
|
+
...common,
|
|
162
|
+
defaultValue: numberValue(resolved.default ?? effectNumber.default),
|
|
163
|
+
integer: false,
|
|
164
|
+
minimum: numberValue(resolved.minimum ?? effectNumber.minimum),
|
|
165
|
+
maximum: numberValue(resolved.maximum ?? effectNumber.maximum)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
_tag: "Union",
|
|
170
|
+
...common,
|
|
171
|
+
alternatives: alternatives.map((alternative) => project(alternative, definitions))
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const constant = primitiveValue(resolved.const)
|
|
175
|
+
if (constant !== undefined || resolved.const === null) {
|
|
176
|
+
return { _tag: "Literal", ...common, value: constant ?? null }
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(resolved.enum)) {
|
|
179
|
+
const values = resolved.enum.map(primitiveValue).filter((item): item is JsonPrimitive => item !== undefined)
|
|
180
|
+
if (values.length > 0) {
|
|
181
|
+
return {
|
|
182
|
+
_tag: "Enum",
|
|
183
|
+
...common,
|
|
184
|
+
values,
|
|
185
|
+
defaultValue: primitiveValue(resolved.default)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
switch (resolved.type) {
|
|
190
|
+
case "string":
|
|
191
|
+
return {
|
|
192
|
+
_tag: "String",
|
|
193
|
+
...common,
|
|
194
|
+
defaultValue: stringValue(resolved.default),
|
|
195
|
+
format: stringValue(resolved.format),
|
|
196
|
+
minLength: numberValue(resolved.minLength),
|
|
197
|
+
maxLength: numberValue(resolved.maxLength),
|
|
198
|
+
pattern: stringValue(resolved.pattern)
|
|
199
|
+
}
|
|
200
|
+
case "integer":
|
|
201
|
+
case "number":
|
|
202
|
+
return {
|
|
203
|
+
_tag: "Number",
|
|
204
|
+
...common,
|
|
205
|
+
defaultValue: numberValue(resolved.default),
|
|
206
|
+
integer: resolved.type === "integer",
|
|
207
|
+
minimum: numberValue(resolved.minimum),
|
|
208
|
+
maximum: numberValue(resolved.maximum)
|
|
209
|
+
}
|
|
210
|
+
case "boolean":
|
|
211
|
+
return {
|
|
212
|
+
_tag: "Boolean",
|
|
213
|
+
...common,
|
|
214
|
+
defaultValue: typeof resolved.default === "boolean" ? resolved.default : undefined
|
|
215
|
+
}
|
|
216
|
+
case "null":
|
|
217
|
+
return { _tag: "Literal", ...common, value: null }
|
|
218
|
+
case "object": {
|
|
219
|
+
if (!isRecord(resolved.properties)) return { _tag: "Object", ...common, fields: [] }
|
|
220
|
+
const required = new Set(
|
|
221
|
+
Array.isArray(resolved.required)
|
|
222
|
+
? resolved.required.filter((item): item is string => typeof item === "string")
|
|
223
|
+
: []
|
|
224
|
+
)
|
|
225
|
+
return {
|
|
226
|
+
_tag: "Object",
|
|
227
|
+
...common,
|
|
228
|
+
fields: Object.entries(resolved.properties).map(([key, child]) => ({
|
|
229
|
+
key,
|
|
230
|
+
required: required.has(key),
|
|
231
|
+
field: project(child, definitions)
|
|
232
|
+
}))
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
case "array":
|
|
236
|
+
return {
|
|
237
|
+
_tag: "Array",
|
|
238
|
+
...common,
|
|
239
|
+
item: project(resolved.items, definitions),
|
|
240
|
+
minItems: numberValue(resolved.minItems) ?? 0,
|
|
241
|
+
maxItems: numberValue(resolved.maxItems)
|
|
242
|
+
}
|
|
243
|
+
default:
|
|
244
|
+
return {
|
|
245
|
+
_tag: "Unsupported",
|
|
246
|
+
...common,
|
|
247
|
+
reason: "This input schema has no concrete JSON shape to render."
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export const projectInputSchema = (document: InputSchema): InputField => project(document.schema, document.definitions)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Machine } from "@typeonce/effect-machine"
|
|
2
|
+
import { Effect, Schema, Stream } from "effect"
|
|
3
|
+
|
|
4
|
+
class ChildWorking extends Schema.TaggedClass<ChildWorking>("InvokeGalleryChildWorking")("Working", {
|
|
5
|
+
task: Schema.String
|
|
6
|
+
}) {}
|
|
7
|
+
class ChildDone extends Schema.TaggedClass<ChildDone>("InvokeGalleryChildDone")("Done", {
|
|
8
|
+
result: Schema.String
|
|
9
|
+
}) {}
|
|
10
|
+
class FinishChild extends Schema.TaggedClass<FinishChild>("InvokeGalleryFinishChild")("FinishChild", {}) {}
|
|
11
|
+
|
|
12
|
+
const ChildStates = Machine.states({
|
|
13
|
+
Working: ChildWorking,
|
|
14
|
+
Done: { schema: ChildDone, type: "final", output: Schema.String }
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const invokeGalleryChildMachine = Machine.make({
|
|
18
|
+
id: "invoke-gallery-child",
|
|
19
|
+
states: ChildStates.states,
|
|
20
|
+
events: Machine.events(FinishChild),
|
|
21
|
+
initial: (to) => to.Working().resolve(({ target }) => target.decoded(new ChildWorking({ task: "render-preview" })))
|
|
22
|
+
}).handle({
|
|
23
|
+
Working: {
|
|
24
|
+
on: {
|
|
25
|
+
FinishChild: (to) =>
|
|
26
|
+
to.full.Done().resolve(({ state, target }) =>
|
|
27
|
+
target.decoded(new ChildDone({ result: `${state.task}:complete` }))
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
Done: {
|
|
32
|
+
output: ({ state }) => state.result
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const InvokedChild = Machine.child("preview-worker", invokeGalleryChildMachine)
|
|
37
|
+
|
|
38
|
+
class Gallery extends Schema.TaggedClass<Gallery>("InvokeGallery")("Gallery", {
|
|
39
|
+
selectedDemo: Schema.NullOr(Schema.String)
|
|
40
|
+
}) {}
|
|
41
|
+
class Choose extends Schema.TaggedClass<Choose>("InvokeGalleryChoose")("Choose", {}) {}
|
|
42
|
+
class LoadingDocument extends Schema.TaggedClass<LoadingDocument>("InvokeGalleryLoadingDocument")(
|
|
43
|
+
"LoadingDocument",
|
|
44
|
+
{ request: Schema.String }
|
|
45
|
+
) {}
|
|
46
|
+
class StreamingUpdates extends Schema.TaggedClass<StreamingUpdates>("InvokeGalleryStreamingUpdates")(
|
|
47
|
+
"StreamingUpdates",
|
|
48
|
+
{ values: Schema.Array(Schema.Number) }
|
|
49
|
+
) {}
|
|
50
|
+
class WaitingForTimeout extends Schema.TaggedClass<WaitingForTimeout>("InvokeGalleryWaitingForTimeout")(
|
|
51
|
+
"WaitingForTimeout",
|
|
52
|
+
{ delay: Schema.Literal("2 seconds") }
|
|
53
|
+
) {}
|
|
54
|
+
class WatchingProcess extends Schema.TaggedClass<WatchingProcess>("InvokeGalleryWatchingProcess")(
|
|
55
|
+
"WatchingProcess",
|
|
56
|
+
{ revision: Schema.Number }
|
|
57
|
+
) {}
|
|
58
|
+
class RunningChild extends Schema.TaggedClass<RunningChild>("InvokeGalleryRunningChild")("RunningChild", {}) {}
|
|
59
|
+
class Completed extends Schema.TaggedClass<Completed>("InvokeGalleryCompleted")("Completed", {
|
|
60
|
+
source: Schema.String,
|
|
61
|
+
result: Schema.String
|
|
62
|
+
}) {}
|
|
63
|
+
class Failed extends Schema.TaggedClass<Failed>("InvokeGalleryFailed")("Failed", {
|
|
64
|
+
source: Schema.String,
|
|
65
|
+
message: Schema.String
|
|
66
|
+
}) {}
|
|
67
|
+
|
|
68
|
+
class RunEffect extends Schema.TaggedClass<RunEffect>("InvokeGalleryRunEffect")("RunEffect", {
|
|
69
|
+
request: Schema.String
|
|
70
|
+
}) {}
|
|
71
|
+
class RunStream extends Schema.TaggedClass<RunStream>("InvokeGalleryRunStream")("RunStream", {}) {}
|
|
72
|
+
class RunTimer extends Schema.TaggedClass<RunTimer>("InvokeGalleryRunTimer")("RunTimer", {}) {}
|
|
73
|
+
class RunProcess extends Schema.TaggedClass<RunProcess>("InvokeGalleryRunProcess")("RunProcess", {}) {}
|
|
74
|
+
class RunChild extends Schema.TaggedClass<RunChild>("InvokeGalleryRunChild")("RunChild", {}) {}
|
|
75
|
+
class Reset extends Schema.TaggedClass<Reset>("InvokeGalleryReset")("Reset", {}) {}
|
|
76
|
+
class StreamValue extends Schema.TaggedClass<StreamValue>("InvokeGalleryStreamValue")("StreamValue", {
|
|
77
|
+
value: Schema.Number
|
|
78
|
+
}) {}
|
|
79
|
+
|
|
80
|
+
const GalleryEvents = Machine.events(RunEffect, RunStream, RunTimer, RunProcess, RunChild, Reset)
|
|
81
|
+
const GalleryInternalEvents = Machine.internalEvents(StreamValue)
|
|
82
|
+
const processLogic = Machine.logic({
|
|
83
|
+
initial: "starting" as "starting" | "ready",
|
|
84
|
+
run: () => Effect.fail("process stopped")
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const GalleryStates = Machine.states({
|
|
88
|
+
Gallery: {
|
|
89
|
+
schema: Gallery,
|
|
90
|
+
initial: "Choose",
|
|
91
|
+
states: {
|
|
92
|
+
Choose,
|
|
93
|
+
LoadingDocument,
|
|
94
|
+
StreamingUpdates,
|
|
95
|
+
WaitingForTimeout,
|
|
96
|
+
WatchingProcess,
|
|
97
|
+
RunningChild
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
Completed,
|
|
101
|
+
Failed
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
export const invokeOutcomesMachine = Machine.make({
|
|
105
|
+
id: "invoke-outcomes",
|
|
106
|
+
states: GalleryStates.states,
|
|
107
|
+
events: GalleryEvents,
|
|
108
|
+
internalEvents: GalleryInternalEvents,
|
|
109
|
+
initial: (to) =>
|
|
110
|
+
to.Gallery.initial.resolve(({ target }) =>
|
|
111
|
+
target.decoded(
|
|
112
|
+
new Gallery({ selectedDemo: null }),
|
|
113
|
+
(gallery) => gallery.Choose.decoded(new Choose({}))
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
}).handle({
|
|
117
|
+
Gallery: {
|
|
118
|
+
initialize: ({ builder }) => builder.decoded(new Choose({})),
|
|
119
|
+
on: {
|
|
120
|
+
Reset: (to) => to.local.Choose().resolve(({ target }) => target.decoded(new Choose({})))
|
|
121
|
+
},
|
|
122
|
+
states: {
|
|
123
|
+
Choose: {
|
|
124
|
+
on: {
|
|
125
|
+
RunEffect: (to) =>
|
|
126
|
+
to.local.LoadingDocument().resolve(({ event, target }) =>
|
|
127
|
+
target.decoded(new LoadingDocument({ request: event.request }))
|
|
128
|
+
),
|
|
129
|
+
RunStream: (to) =>
|
|
130
|
+
to.local.StreamingUpdates().resolve(({ target }) => target.decoded(new StreamingUpdates({ values: [] }))),
|
|
131
|
+
RunTimer: (to) =>
|
|
132
|
+
to.local.WaitingForTimeout().resolve(({ target }) =>
|
|
133
|
+
target.decoded(new WaitingForTimeout({ delay: "2 seconds" }))
|
|
134
|
+
),
|
|
135
|
+
RunProcess: (to) =>
|
|
136
|
+
to.local.WatchingProcess().resolve(({ target }) => target.decoded(new WatchingProcess({ revision: 1 }))),
|
|
137
|
+
RunChild: (to) => to.local.RunningChild().resolve(({ target }) => target.decoded(new RunningChild({})))
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
LoadingDocument: {
|
|
141
|
+
invoke: (from) =>
|
|
142
|
+
from.effect(
|
|
143
|
+
"load-document",
|
|
144
|
+
({ state }) =>
|
|
145
|
+
state.request === "fail" ? Effect.fail("document unavailable") : Effect.succeed("document loaded")
|
|
146
|
+
).onDone((to) =>
|
|
147
|
+
to.full.Completed().resolve(({ output, target }) =>
|
|
148
|
+
target.decoded(new Completed({ source: "effect", result: output }))
|
|
149
|
+
)
|
|
150
|
+
).onFailure((to) =>
|
|
151
|
+
to.full.Failed().resolve(({ error, target }) =>
|
|
152
|
+
target.decoded(new Failed({ source: "effect", message: error }))
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
},
|
|
156
|
+
StreamingUpdates: {
|
|
157
|
+
invoke: (from) =>
|
|
158
|
+
from.stream(
|
|
159
|
+
"document-updates",
|
|
160
|
+
() => Stream.fromIterable([1, 2, 3]).pipe(Stream.concat(Stream.fail("stream disconnected")))
|
|
161
|
+
).onElement((to) =>
|
|
162
|
+
to.none.resolve(({ element }, enqueue) => {
|
|
163
|
+
enqueue.raise(GalleryInternalEvents.StreamValue({ value: element }))
|
|
164
|
+
})
|
|
165
|
+
).onDone((to) =>
|
|
166
|
+
to.full.Completed().resolve(({ state, target }) =>
|
|
167
|
+
target.decoded(new Completed({ source: "stream", result: state.values.join(", ") }))
|
|
168
|
+
)
|
|
169
|
+
).onFailure((to) =>
|
|
170
|
+
to.full.Failed().resolve(({ error, target }) =>
|
|
171
|
+
target.decoded(new Failed({ source: "stream", message: error }))
|
|
172
|
+
)
|
|
173
|
+
),
|
|
174
|
+
on: {
|
|
175
|
+
StreamValue: (to) =>
|
|
176
|
+
to.local.StreamingUpdates().resolve(({ event, state, target }) =>
|
|
177
|
+
target.decoded(new StreamingUpdates({ values: [...state.values, event.value] }))
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
WaitingForTimeout: {
|
|
182
|
+
invoke: (from) =>
|
|
183
|
+
from.timer("request-timeout", "2 seconds").onDone((to) =>
|
|
184
|
+
to.full.Completed().resolve(({ target }) =>
|
|
185
|
+
target.decoded(new Completed({ source: "timer", result: "timeout elapsed" }))
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
},
|
|
189
|
+
WatchingProcess: {
|
|
190
|
+
invoke: (from) =>
|
|
191
|
+
from.logic("status-worker", {
|
|
192
|
+
address: Machine.childAddress("status-worker"),
|
|
193
|
+
logic: processLogic
|
|
194
|
+
}).onFailure((to) =>
|
|
195
|
+
to.full.Failed().resolve(({ error, target }) =>
|
|
196
|
+
target.decoded(new Failed({ source: "process", message: String(error) }))
|
|
197
|
+
)
|
|
198
|
+
).onSnapshot((to) =>
|
|
199
|
+
to.branches({
|
|
200
|
+
ready: { title: "Worker reports ready", target: to.full.Completed() },
|
|
201
|
+
waiting: { title: "Worker is still starting", target: to.none }
|
|
202
|
+
}).resolve(({ select, snapshot }) =>
|
|
203
|
+
snapshot.state === "ready"
|
|
204
|
+
? select.ready.decoded(new Completed({ source: "process", result: "ready" }))
|
|
205
|
+
: select.waiting()
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
},
|
|
209
|
+
RunningChild: {
|
|
210
|
+
invoke: (from) =>
|
|
211
|
+
from.child(InvokedChild).onDone((to) =>
|
|
212
|
+
to.full.Completed().resolve(({ output, target }) =>
|
|
213
|
+
target.decoded(new Completed({ source: "child machine", result: output }))
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
Completed: {
|
|
220
|
+
on: {
|
|
221
|
+
Reset: (to) =>
|
|
222
|
+
to.full.Gallery.initial.resolve(({ target }) => target.decoded(new Gallery({ selectedDemo: null })))
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
Failed: {
|
|
226
|
+
on: {
|
|
227
|
+
Reset: (to) =>
|
|
228
|
+
to.full.Gallery.initial.resolve(({ target }) => target.decoded(new Gallery({ selectedDemo: null })))
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Machine } from "@typeonce/effect-machine"
|
|
2
|
+
import { Schema } from "effect"
|
|
3
|
+
|
|
4
|
+
class Cart extends Schema.TaggedClass<Cart>("ParallelCart")("Cart", {
|
|
5
|
+
items: Schema.Number
|
|
6
|
+
}) {}
|
|
7
|
+
class Order extends Schema.TaggedClass<Order>("ParallelOrder")("Order", {
|
|
8
|
+
orderId: Schema.String,
|
|
9
|
+
total: Schema.Number
|
|
10
|
+
}) {}
|
|
11
|
+
class Payment extends Schema.TaggedClass<Payment>("ParallelPayment")("Payment", {
|
|
12
|
+
attempts: Schema.Number
|
|
13
|
+
}) {}
|
|
14
|
+
class AwaitingAuthorization extends Schema.TaggedClass<AwaitingAuthorization>("ParallelAwaitingAuthorization")(
|
|
15
|
+
"AwaitingAuthorization",
|
|
16
|
+
{}
|
|
17
|
+
) {}
|
|
18
|
+
class Authorized extends Schema.TaggedClass<Authorized>("ParallelAuthorized")("Authorized", {
|
|
19
|
+
authorizationId: Schema.String
|
|
20
|
+
}) {}
|
|
21
|
+
class Fulfillment extends Schema.TaggedClass<Fulfillment>("ParallelFulfillment")("Fulfillment", {
|
|
22
|
+
warehouse: Schema.String
|
|
23
|
+
}) {}
|
|
24
|
+
class WaitingForPayment extends Schema.TaggedClass<WaitingForPayment>("ParallelWaitingForPayment")(
|
|
25
|
+
"WaitingForPayment",
|
|
26
|
+
{}
|
|
27
|
+
) {}
|
|
28
|
+
class Packing extends Schema.TaggedClass<Packing>("ParallelPacking")("Packing", {
|
|
29
|
+
packageCount: Schema.Number
|
|
30
|
+
}) {}
|
|
31
|
+
class Shipped extends Schema.TaggedClass<Shipped>("ParallelShipped")("Shipped", {
|
|
32
|
+
trackingCode: Schema.String
|
|
33
|
+
}) {}
|
|
34
|
+
class OrderComplete extends Schema.TaggedClass<OrderComplete>("ParallelOrderComplete")("OrderComplete", {
|
|
35
|
+
orderId: Schema.String
|
|
36
|
+
}) {}
|
|
37
|
+
class OrderCancelled extends Schema.TaggedClass<OrderCancelled>("ParallelOrderCancelled")(
|
|
38
|
+
"OrderCancelled",
|
|
39
|
+
{ reason: Schema.String }
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
class Checkout extends Schema.TaggedClass<Checkout>("ParallelCheckout")("Checkout", {
|
|
43
|
+
orderId: Schema.String,
|
|
44
|
+
total: Schema.Number
|
|
45
|
+
}) {}
|
|
46
|
+
class Authorize extends Schema.TaggedClass<Authorize>("ParallelAuthorize")("Authorize", {
|
|
47
|
+
authorizationId: Schema.String
|
|
48
|
+
}) {}
|
|
49
|
+
class DeclinePayment extends Schema.TaggedClass<DeclinePayment>("ParallelDeclinePayment")(
|
|
50
|
+
"DeclinePayment",
|
|
51
|
+
{ reason: Schema.String }
|
|
52
|
+
) {}
|
|
53
|
+
class Pack extends Schema.TaggedClass<Pack>("ParallelPack")("Pack", { packages: Schema.Number }) {}
|
|
54
|
+
class Ship extends Schema.TaggedClass<Ship>("ParallelShip")("Ship", { trackingCode: Schema.String }) {}
|
|
55
|
+
class CompleteAll extends Schema.TaggedClass<CompleteAll>("ParallelCompleteAll")("CompleteAll", {
|
|
56
|
+
authorizationId: Schema.String,
|
|
57
|
+
trackingCode: Schema.String
|
|
58
|
+
}) {}
|
|
59
|
+
class CancelOrder extends Schema.TaggedClass<CancelOrder>("ParallelCancelOrder")("CancelOrder", {
|
|
60
|
+
reason: Schema.String
|
|
61
|
+
}) {}
|
|
62
|
+
class RetryOrder extends Schema.TaggedClass<RetryOrder>("ParallelRetryOrder")("RetryOrder", {}) {}
|
|
63
|
+
class AutoShip extends Schema.TaggedClass<AutoShip>("ParallelAutoShip")("AutoShip", {}) {}
|
|
64
|
+
|
|
65
|
+
const ParallelInternalEvents = Machine.internalEvents(AutoShip)
|
|
66
|
+
const ParallelStates = Machine.states({
|
|
67
|
+
Cart,
|
|
68
|
+
Order: {
|
|
69
|
+
schema: Order,
|
|
70
|
+
type: "parallel",
|
|
71
|
+
states: {
|
|
72
|
+
payment: {
|
|
73
|
+
schema: Payment,
|
|
74
|
+
initial: "AwaitingAuthorization",
|
|
75
|
+
states: {
|
|
76
|
+
AwaitingAuthorization,
|
|
77
|
+
Authorized: { schema: Authorized, type: "final" }
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
fulfillment: {
|
|
81
|
+
schema: Fulfillment,
|
|
82
|
+
initial: "WaitingForPayment",
|
|
83
|
+
states: {
|
|
84
|
+
WaitingForPayment,
|
|
85
|
+
Packing,
|
|
86
|
+
Shipped: { schema: Shipped, type: "final" }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
Complete: { schema: OrderComplete, type: "final", output: Schema.String },
|
|
92
|
+
Cancelled: OrderCancelled
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
export const parallelCompletionMachine = Machine.make({
|
|
96
|
+
id: "parallel-completion",
|
|
97
|
+
states: ParallelStates.states,
|
|
98
|
+
events: Machine.events(
|
|
99
|
+
Checkout,
|
|
100
|
+
Authorize,
|
|
101
|
+
DeclinePayment,
|
|
102
|
+
Pack,
|
|
103
|
+
Ship,
|
|
104
|
+
CompleteAll,
|
|
105
|
+
CancelOrder,
|
|
106
|
+
RetryOrder
|
|
107
|
+
),
|
|
108
|
+
internalEvents: ParallelInternalEvents,
|
|
109
|
+
initial: (to) => to.Cart().resolve(({ target }) => target.decoded(new Cart({ items: 2 })))
|
|
110
|
+
}).handle({
|
|
111
|
+
Cart: {
|
|
112
|
+
on: {
|
|
113
|
+
Checkout: (to) =>
|
|
114
|
+
to.full.Order.initial.resolve(({ event, target }) =>
|
|
115
|
+
target.decoded(new Order({ orderId: event.orderId, total: event.total }))
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
Order: {
|
|
120
|
+
initialize: ({ builder }) => builder.payment.from({ attempts: 0 }).fulfillment.from({ warehouse: "north" }),
|
|
121
|
+
on: {
|
|
122
|
+
CancelOrder: (to) =>
|
|
123
|
+
to.full.Cancelled().resolve(({ event, target }) => target.decoded(new OrderCancelled({ reason: event.reason })))
|
|
124
|
+
},
|
|
125
|
+
onDone: (to) =>
|
|
126
|
+
to.full.Complete().resolve(({ target }) => target.decoded(new OrderComplete({ orderId: "completed-order" }))),
|
|
127
|
+
states: {
|
|
128
|
+
payment: {
|
|
129
|
+
initialize: ({ builder }) => builder.from(),
|
|
130
|
+
states: {
|
|
131
|
+
AwaitingAuthorization: {
|
|
132
|
+
on: {
|
|
133
|
+
Authorize: (to) =>
|
|
134
|
+
to.local.Authorized().resolve(({ event, target }) =>
|
|
135
|
+
target.decoded(new Authorized({ authorizationId: event.authorizationId }))
|
|
136
|
+
),
|
|
137
|
+
CompleteAll: (to) =>
|
|
138
|
+
to.local.Authorized().resolve(({ event, target }) =>
|
|
139
|
+
target.decoded(new Authorized({ authorizationId: event.authorizationId }))
|
|
140
|
+
),
|
|
141
|
+
DeclinePayment: (to) =>
|
|
142
|
+
to.full.Cancelled().resolve(({ event, target }) =>
|
|
143
|
+
target.decoded(new OrderCancelled({ reason: event.reason }))
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
fulfillment: {
|
|
150
|
+
initialize: ({ builder }) => builder.from(),
|
|
151
|
+
states: {
|
|
152
|
+
WaitingForPayment: {
|
|
153
|
+
on: {
|
|
154
|
+
Authorize: (to) =>
|
|
155
|
+
to.local.Packing().resolve(({ target }) => target.decoded(new Packing({ packageCount: 1 }))),
|
|
156
|
+
Pack: (to) =>
|
|
157
|
+
to.local.Packing().resolve(({ event, target }) =>
|
|
158
|
+
target.decoded(new Packing({ packageCount: event.packages }))
|
|
159
|
+
),
|
|
160
|
+
CompleteAll: (to) =>
|
|
161
|
+
to.local.Shipped().resolve(({ event, target }) =>
|
|
162
|
+
target.decoded(new Shipped({ trackingCode: event.trackingCode }))
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
Packing: {
|
|
167
|
+
invoke: (from) =>
|
|
168
|
+
from.timer("packing-sla", "5 seconds").onDone((to) =>
|
|
169
|
+
to.none.resolve((_, enqueue) => {
|
|
170
|
+
enqueue.raise(ParallelInternalEvents.AutoShip())
|
|
171
|
+
})
|
|
172
|
+
),
|
|
173
|
+
on: {
|
|
174
|
+
AutoShip: (to) =>
|
|
175
|
+
to.local.Shipped().resolve(({ target }) => target.decoded(new Shipped({ trackingCode: "automatic" }))),
|
|
176
|
+
Ship: (to) =>
|
|
177
|
+
to.local.Shipped().resolve(({ event, target }) =>
|
|
178
|
+
target.decoded(new Shipped({ trackingCode: event.trackingCode }))
|
|
179
|
+
),
|
|
180
|
+
CompleteAll: (to) =>
|
|
181
|
+
to.local.Shipped().resolve(({ event, target }) =>
|
|
182
|
+
target.decoded(new Shipped({ trackingCode: event.trackingCode }))
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
Complete: {
|
|
191
|
+
output: ({ state }) => state.orderId
|
|
192
|
+
},
|
|
193
|
+
Cancelled: {
|
|
194
|
+
on: {
|
|
195
|
+
RetryOrder: (to) =>
|
|
196
|
+
to.full.Order.initial.resolve(({ target }) => target.decoded(new Order({ orderId: "retry", total: 0 })))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
})
|