@jarenjs/flow 0.72.3 → 0.75.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.
@@ -0,0 +1,186 @@
1
+ # Jaren statecharts — jaren-fsm 0.2
2
+
3
+ `compileStatechart` compiles a JSON document into pure `start`, `step` and
4
+ `advance` functions. It provides compound states, parallel regions, shallow
5
+ and deep history, completion/eventless transitions and delayed transitions.
6
+ It executes no effect and reads no clock. `compileFsm`, `createFsmSession`,
7
+ `fsmToApp` and the 0.1 schemas retain their existing contracts.
8
+
9
+ The canonical grammar is [jaren-statechart.schema.json](../schemas/jaren-statechart.schema.json)
10
+ (`https://jarenjs.dev/schemas/jaren-fsm/0.2`), with draft-07 and authoring twins.
11
+ Register the query grammar when validating. Compilation checks references,
12
+ hierarchy and transition domains that JSON Schema cannot prove. Unknown
13
+ statechart, state and transition members are compile errors.
14
+
15
+ ## 1. Document and state tree
16
+
17
+ ```json
18
+ {
19
+ "$fsm": "0.2",
20
+ "initial": "working",
21
+ "states": [
22
+ { "id": "working", "initial": "editing" },
23
+ { "id": "editing", "parent": "working" },
24
+ { "id": "review", "parent": "working" },
25
+ { "id": "memory", "parent": "working", "history": "deep" },
26
+ "paused",
27
+ { "id": "expired", "final": true }
28
+ ],
29
+ "transitions": [
30
+ { "from": "editing", "event": "submit", "to": "review" },
31
+ { "from": "working", "event": "pause", "to": "paused" },
32
+ { "from": "paused", "event": "resume", "to": "memory" },
33
+ { "from": "review", "after": 60000, "to": "expired" }
34
+ ]
35
+ }
36
+ ```
37
+
38
+ States have globally unique, nonempty string ids. A bare string declares an
39
+ atomic state. An object supports `id`, `parent`, `initial`, `type`, `history`,
40
+ `final`, `entry` and `exit`. The parent relation MUST be acyclic.
41
+
42
+ | Type | Contract |
43
+ |---|---|
44
+ | `atomic` | No children. The default when no ordinary children exist. |
45
+ | `compound` | Exactly one ordinary child active; `initial` MUST name an immediate ordinary child. Inferred when ordinary children exist. |
46
+ | `parallel` | All ordinary children active. Each region recursively enters its initial configuration. No `initial` on the parallel state itself. |
47
+ | `final` | No children or outgoing transitions. `final: true` is shorthand. |
48
+ | `history` | A pseudo-state with a parent and `history: "shallow"` or `"deep"`; no children, outgoing transitions or effects. `history` infers this type. |
49
+
50
+ The document's `initial` MUST name an ordinary root state. State effects use
51
+ FLOW-FORMAT's `{run,with?}` descriptors. Final and history declarations cannot
52
+ conflict with an explicit `type`. Parents precede children in runtime tree
53
+ order even if the flattened declarations list a child first; siblings retain
54
+ declaration order.
55
+
56
+ Entering a compound target recursively enters its initial child. Entering a
57
+ parallel target recursively enters every region. An explicit descendant target
58
+ enters its ancestors and fills any unmentioned parallel regions with defaults.
59
+
60
+ ## 2. Selection, effects and completion
61
+
62
+ A transition supports `from`, `to`, `guard`, `effects`, `type` and at most one
63
+ trigger: `event`, `after`, `always: true` or `done: true`. An absent or null
64
+ event is a wildcard over **external string events**, as in 0.1. It does not
65
+ match timers or completion. A transition MUST name declared endpoints.
66
+
67
+ For each active leaf, search its own transitions first, then its ancestors
68
+ nearest first. Within a source, document order decides; a wildcard before a
69
+ named transition wins. Evaluate a shared ancestor guard once per microstep.
70
+ Independent parallel regions can transition in the same step. Conflicting
71
+ exit sets are resolved in favor of the descendant source; unrelated conflicts
72
+ use tree order of the selecting leaves. The selected transition effects run
73
+ in transition document order.
74
+
75
+ `type: "external"` is the default in **0.2**, including self-transitions.
76
+ Its domain is the least common **proper compound ancestor** of source and
77
+ target (or the virtual root); a parallel ancestor is not such a domain.
78
+ Exiting that domain's active descendants makes a cross-region transition
79
+ leave and re-enter the parallel state, restoring untargeted regions to their
80
+ defaults. `type: "internal"` can target the source or its descendants: the
81
+ source stays entered, while its active descendants exit. An atomic internal
82
+ self-transition therefore runs only its transition effects. This is an
83
+ explicit version difference from 0.1's implicit internal self-transition.
84
+
85
+ Each microstep resolves effects against its pre-transition scope in order:
86
+ exit effects in reverse tree order, transition effects in document order,
87
+ entry effects in tree order. `$.state` is the ordered **array of active leaf
88
+ ids**; `$.event` is the external name, or null for initial entry/timers;
89
+ `$.payload` and `$.context` are caller values, defaulting to null. Guards
90
+ use query effective boolean value. Evaluation failures retain FLOW-FORMAT's
91
+ JF2003/JF2004 records and fail-closed behavior.
92
+
93
+ `always` transitions participate in automatic stabilization after initial
94
+ entry, an external event or a timer. `done` is enabled when its compound
95
+ source has an active immediate final child, or all regions of its parallel
96
+ source have completed. Completion sources MUST be compound/parallel. A
97
+ completed root reports `final: true`; it is still possible to send events to
98
+ that root's ancestors/sources until the host chooses to stop. There is no
99
+ implicit context assignment, raised-event queue or invocation machinery.
100
+
101
+ `maxMicrosteps` (default 1000, positive safe integer) bounds one call's
102
+ automatic transitions and timer processing. Exhaustion throws JF2012; pure
103
+ inputs and session state remain unchanged. There is no partial-result commit.
104
+ These rules borrow hierarchy/history and transition-domain definitions from
105
+ [SCXML's control semantics](https://www.w3.org/TR/scxml/), but this JSON format
106
+ is not an SCXML implementation or an SCXML conformance claim.
107
+
108
+ ## 3. History
109
+
110
+ Immediately before exiting a compound/parallel state, remember its active
111
+ configuration for each declared history child. Shallow history stores the
112
+ active immediate ordinary children; re-entry initializes their descendants.
113
+ Deep history stores active atomic/final leaves, including all parallel
114
+ regions, and restores those leaves. Targeting history before any record exists
115
+ uses the parent's default initial configuration. Entering the parent normally
116
+ uses its defaults even if history was recorded; history is an explicit target.
117
+
118
+ History restores **control**, not elapsed timers or host context. Restored
119
+ states run their entry effects and schedule fresh delays. A timer that belonged
120
+ to an exited state remains cancelled.
121
+
122
+ ## 4. Time is input
123
+
124
+ ```js
125
+ const chart = compileStatechart(doc);
126
+ const initial = chart.start({ now: 1000, context });
127
+ const next = chart.step(initial.state, 'submit', { now: 1200, context });
128
+ const later = chart.advance(next.state, 61200, { context });
129
+ ```
130
+
131
+ `now` is a finite number in the host's millisecond time domain; fresh starts
132
+ default to zero. Omitted step time preserves the snapshot's time. Time MUST
133
+ NOT move backwards (JF2011). The engine never calls `Date.now`, starts a host
134
+ timer, or waits. A host may arm a timer from the earliest returned deadline
135
+ and call `advance` when notified, including after process restart.
136
+
137
+ `after` is a finite nonnegative duration. Entry creates one timer per delayed
138
+ transition, storing its absolute deadline, transition index and monotonically
139
+ increasing token. Exit cancels the source's timers. A timer is consumed once,
140
+ even if its guard is false. Equal deadlines use transition order. Newly entered
141
+ delays use logical firing time, not the time the host eventually delivered the
142
+ wake-up. Overflowing deadlines/tokens are JF2011.
143
+
144
+ `step` does not implicitly fire overdue timers. The host chooses whether an
145
+ external event or a due timer happens first. `advance` consumes due timers up
146
+ to its supplied time and stabilizes after each. `{one: true}` consumes at most
147
+ one timer plus its stabilization, leaving time at that logical firing instant;
148
+ when none is due it advances to the requested time. The composition bridge
149
+ uses this mode to persist visit counts between timer deliveries. Timer indices
150
+ cannot be forged as string events: external dispatch and time advancement are
151
+ separate APIs.
152
+
153
+ ## 5. Snapshot and session
154
+
155
+ `StatechartState` is the whole control snapshot:
156
+
157
+ ```jsonc
158
+ {
159
+ "active": ["review"],
160
+ "history": { "memory": ["review"] },
161
+ "timers": [{ "transition": 3, "at": 61200, "token": 1 }],
162
+ "serial": 1,
163
+ "time": 1200
164
+ }
165
+ ```
166
+
167
+ Snapshots are frozen JSON. `restore(snapshot)` validates legal active and
168
+ history configurations, timer sources/deadlines/tokens and numeric counters;
169
+ it normalizes leaf/history/timer ordering and returns an independent frozen
170
+ copy. Invalid snapshots are JF2010. A snapshot's schema is
171
+ [jaren-statechart-state.schema.json](../schemas/jaren-statechart-state.schema.json).
172
+ Standalone callers bind snapshots to their machine revision; composed workflows
173
+ perform that comparison themselves.
174
+
175
+ Results carry `changed`, `state`, `final`, `effects`, `errors`, `entered`,
176
+ `exited` and `transitions` (indices into the compiled document). `changed`
177
+ means a transition fired. An ignored event or a consumed false timer can still
178
+ advance snapshot time, so persist the returned state even when `changed` is false.
179
+
180
+ `createStatechartSession(chart, {state?, now?, context?, store?})` exposes
181
+ `state`, `done`, `initial`, `send`, `advance` and `can`. `initial` contains entry
182
+ effects for a fresh start and is null when restoring; the host executes these
183
+ and later result effects. Optional synchronous `store.load()`/`store.save(state)`
184
+ persist a fresh initial state and every successful send/advance **before** the
185
+ session advances. A failed save leaves the session unchanged. Async stores
186
+ belong at the workflow boundary. `can` is a pure dry run with no store write.
@@ -0,0 +1,260 @@
1
+ # Composed workflows — jaren-workflow 0.2
2
+
3
+ `compileWorkflow(document, {tasks, store?})` lowers a neutral JSON workflow
4
+ onto `compileStatechart` and `compileDag`. The statechart chooses control;
5
+ the DAG executor owns dependency scheduling, fan-out/fan-in and task abort.
6
+ The bridge transfers results, persists snapshots and dispatches completion.
7
+ Host roles, prompts, model settings, network clients and tools remain in the
8
+ versioned task registry.
9
+
10
+ The composed format uses `$workflow: "0.2"` and schema id
11
+ `https://jarenjs.dev/schemas/jaren-workflow/0.2`. This deliberately avoids
12
+ reusing Mermaid's historical `jaren-workflow/0.1` schema identity, which denotes
13
+ the unversioned flat-FSM projection. The new schema is
14
+ [jaren-workflow.schema.json](../schemas/jaren-workflow.schema.json), with
15
+ draft-07 and derived authoring twins. Register the DAG, query and JSLT grammars
16
+ for full validation; schema acceptance is followed by compilation.
17
+
18
+ ## 1. The document
19
+
20
+ ```json
21
+ {
22
+ "$workflow": "0.2",
23
+ "revision": "review/1",
24
+ "initial": "prepare",
25
+ "states": {
26
+ "prepare": {
27
+ "work": { "task": "prepare", "version": "1" },
28
+ "then": "check",
29
+ "limit": 3
30
+ },
31
+ "check": {
32
+ "choose": [{ "guard": "$.context.data.ready", "to": "review" }],
33
+ "otherwise": "prepare"
34
+ },
35
+ "review": {
36
+ "on": [
37
+ { "event": "approve", "guard": "$.payload.accept", "to": "done" },
38
+ { "event": "retry", "to": "prepare" }
39
+ ],
40
+ "after": { "ms": 60000, "to": "expired" }
41
+ },
42
+ "done": { "final": true },
43
+ "expired": { "final": true }
44
+ }
45
+ }
46
+ ```
47
+
48
+ `revision` is a nonblank host-declared revision. `initial` names a state in
49
+ `states`, a nonempty object keyed by nonempty ids. References are local to
50
+ that object. Each state declares exactly one form; unknown members refuse.
51
+
52
+ | Form | Members and meaning |
53
+ |---|---|
54
+ | Work | `work: {task, version, with?}` or `work: {dag: <jaren-dag document>}`; `then` required, `input` and `catch` optional. |
55
+ | Choice | `choose: [{guard,to}, …]` and `otherwise`; first true guard wins. |
56
+ | Wait | `on: [{event,to,guard?}, …]`; optional `after: {ms,to}`. At least one event or delay is required. |
57
+ | Nested flow | `flow: {initial,states}` and `then`; a compound state whose child final completes it. |
58
+ | Final | `final: true`; ends its scope and returns its current data. |
59
+
60
+ Every state may declare `limit`, a positive safe integer. It bounds total
61
+ entries to that fully qualified state **for the whole run**, including
62
+ re-entries through an external event or nested flow. A loop is a back-edge in
63
+ the control graph. Compilation proves that every automatically traversable
64
+ cycle crosses a limited state; otherwise it rejects with JF0022. External
65
+ event edges do not advance themselves and are excluded from this proof.
66
+ Timer edges ARE automatic. Runtime admission counts all entered states and
67
+ rejects an excess with JF2015 before starting work in them. Nested depth is
68
+ bounded at 64. An exhausted bound is an error, not successful completion.
69
+
70
+ Static concurrent branches belong inside `work.dag`, using its existing port,
71
+ query, stylesheet and task vocabulary. Parallel statechart regions are available
72
+ through `compileStatechart`; a composed workflow's dynamic control intentionally
73
+ has one active leaf and one pending work region. Nested control composes by
74
+ lowering; it does not start a second workflow scheduler.
75
+
76
+ ## 2. Scope, data and errors
77
+
78
+ Guards and a work state's `input` query read
79
+ `{state,event,payload,context}`. `state` is the active-leaf array. Context is:
80
+
81
+ - `input`: the original run input, unchanged across the workflow;
82
+ - `data`: the current value, initially the input and replaced after each work;
83
+ - `event`: the last accepted external `{type,payload?}` event, initially null;
84
+ - `results`: the latest completed work result by qualified state id;
85
+ - `visits`: entry counts by qualified state id.
86
+
87
+ The default work input is `$.context.data`. A custom `input` query also sees
88
+ `$.context.activation = {runId,state,visit,key}`. `key` is the canonical JSON
89
+ encoding of `[runId, qualifiedStateId, visit]`; it remains the same after a
90
+ crash/resume and differs between loop iterations. For an effectful task, pass
91
+ it explicitly with the work input and have the external system deduplicate it:
92
+
93
+ ```jsonc
94
+ {
95
+ "work": { "task": "charge", "version": "billing/3",
96
+ "with": { "idempotencyKey": "$.key", "order": "$.data" } },
97
+ "input": { "key": "$.context.activation.key", "data": "$.context.data" },
98
+ "then": "done"
99
+ }
100
+ ```
101
+
102
+ A task handler has the existing DAG signature `({with,input}, signal)`.
103
+ `work.with` reads the **DAG input**, not the outer control scope. Missing
104
+ query results become null. Work inputs, outputs and snapshots must survive
105
+ canonical JSON serialization. Snapshot ownership is separate from task-owned
106
+ values; restoring a checkpoint does not hand a task mutable durable state.
107
+
108
+ On success, the bridge records the result, replaces `data`, clears pending
109
+ work and sends the lowered completion event. A `catch` target handles JF2006
110
+ node failures by replacing data with `{error:{code,message,nodeId}}` and
111
+ transitioning there. Store, provenance, cancellation and visit-bound errors
112
+ are never swallowed by `catch`. Uncaught task failures reject; persisted
113
+ checkpoints remain available for a later retry.
114
+
115
+ Unlike a standalone statechart, a workflow rejects a guard evaluation error
116
+ as JF2016 before advancing control: choosing a fallback after a broken work
117
+ predicate is not a successful workflow decision. The lower-level diagnostic
118
+ path remains on the error. Task effects are at-least-once across crash windows;
119
+ checkpointing does not make an external service exactly-once. Implementation
120
+ versions remain host declarations: the compiler cannot detect code changes
121
+ hidden behind an unchanged version token.
122
+
123
+ ## 3. Deterministic lowering
124
+
125
+ `lowerWorkflow(document)` returns frozen JSON containing the original
126
+ `document`, lowering `version`, declared `revision`, `fsm`, `dags`, `specs` and
127
+ `sources`. It resolves no host handler and performs no work. Each state gets
128
+ a stable JSON Pointer id, for example `/states/prepare` or
129
+ `/states/round/flow/states/prepare`; each id segment escapes `~` and `/`.
130
+
131
+ Work shorthand lowers to input → versioned checkpointed task → checkpointed
132
+ output. Inline DAGs retain their wiring and checkpoint declarations, with
133
+ the output checkpoint enabled because a completed region is serializable.
134
+ Consequently **every task in a composed workflow must declare a version**, and
135
+ its registry entry must supply the same version. Intermediate undeclared
136
+ checkpoints recompute, per FLOW-FORMAT §7.6.
137
+
138
+ Choices and work completions become explicit internal event names. Wait
139
+ names receive a separate `event:` prefix, so an external caller cannot forge
140
+ a work-completion event. Delays lower to statechart `after`; nested flows to
141
+ compound states and `done` transitions. No third node scheduler is generated.
142
+
143
+ Compilation exposes `lowered`, `revisions` (control and DAG content
144
+ fingerprints), and a sorted `taskVersions` map qualified by control and node
145
+ path. Trace and checkpoint records refer to these lowered identities. Exact
146
+ canonical documents and maps, rather than a short content hash alone, decide
147
+ resume compatibility. Runtime traces can vary in completion timing; values,
148
+ control decisions and lowering are deterministic for equal inputs/events,
149
+ explicit time and equal task results.
150
+
151
+ ## 4. Run, wait and resume
152
+
153
+ ```js
154
+ const workflow = compileWorkflow(doc, {
155
+ tasks: { prepare: { version: '1', run: async ({input}, signal) => prepare(input, signal) } },
156
+ store,
157
+ });
158
+ const first = await workflow.run(input, { runId: 'review-7', now: 1000 });
159
+ // first.status: 'waiting' or 'done'; first.snapshot is frozen JSON.
160
+ const next = await workflow.run(input, {
161
+ runId: 'review-7', now: 2000,
162
+ expectedGeneration: first.snapshot.generation,
163
+ event: { type: 'approve', payload: { accept: true } },
164
+ });
165
+ ```
166
+
167
+ A run proceeds until a final state or an unhandled wait. The return value is
168
+ `{status: 'waiting'|'done', result, snapshot}`; `result` is current data on
169
+ completion and null while waiting. With no store, pass the previous snapshot
170
+ as `snapshot` on the next call. Store and explicit snapshot are mutually
171
+ exclusive. Resume requires the original input and matching exact identities;
172
+ JF2013 is raised before loading any node result when they differ.
173
+
174
+ One supplied event is delivered to the first wait reached by that call, then
175
+ consumed even if no guard accepts it. Unknown events leave a wait unchanged.
176
+ `expectedGeneration` rejects a stale event/resume with JF2014 before work.
177
+ An accepted event is persisted in `context.event` before subsequent work, so
178
+ an input query can consume `$.context.event.payload` after a durable wait.
179
+ Callers needing durable event receipts maintain those in their host; duplicate
180
+ external-event delivery is not an exactly-once input protocol.
181
+
182
+ `now` is explicit statechart time. A resumed computation uses the supplied
183
+ wake-up time for its next control transition. At a wait the bridge processes a due timer
184
+ before the supplied external event, persisting each admission; this ordering
185
+ is fixed. Fresh runs default to time zero. Work is not automatically timed
186
+ out; cancellation belongs to the supplied `signal`. A host can wake a saved
187
+ workflow using its snapshot's earliest deadline. No sleeping timer lives in
188
+ the bridge.
189
+
190
+ `onTrace` receives frozen records with `runId`, source `revision`, and lowered
191
+ `revisions`. Node records add `type:'node'`, qualified `state`, activation
192
+ visit, and the DAG's `id/status/ms`; transition records add ordered transition
193
+ indices, entered/exited ids, active leaves and snapshot generation. A final
194
+ `waiting`/`done` record names the settled control. Observer exceptions are
195
+ isolated. Aborted DAG stragglers can still produce observation records; they
196
+ cannot commit new work checkpoints after cancellation.
197
+
198
+ ## 5. Store, atomicity and crash windows
199
+
200
+ A store implements async-or-sync methods:
201
+
202
+ ```js
203
+ load(runId); // snapshot or null
204
+ save(runId, snapshot, expectedGeneration); // true on atomic success, false if stale
205
+ ```
206
+
207
+ `save` MUST atomically compare the existing generation, replace the complete
208
+ snapshot and return true. Expected generation zero means the run is absent.
209
+ The bridge claims a fresh generation before work, serializes concurrent DAG
210
+ checkpoint writes, and awaits every durable write before the corresponding
211
+ control change is visible. A failed write is JF2009; a failed comparison is
212
+ JF2014. Same-instance concurrent calls for one run id refuse immediately;
213
+ independent compiles/processes are fenced by the store's CAS. This is optimistic
214
+ fencing, not an execution lease: overlapping workers can start external work
215
+ before one loses its next CAS, so side effects still need the idempotency key.
216
+
217
+ A snapshot carries format, run id, exact identities, generation, statechart
218
+ control, data context, status and nullable pending DAG activation. Pending
219
+ work carries state id, visit, captured input, per-node values, DAG provenance
220
+ and, after completion, its result. On resume, declared node checkpoints seed
221
+ the existing DAG memo; a recorded completed region skips the DAG altogether.
222
+ Malformed control, visits, pending provenance and node declarations refuse.
223
+
224
+ A crash after an effect succeeds but before its node checkpoint repeats that
225
+ effect on resume. A crash after region completion but before control changes
226
+ reuses the region result. Store implementations choose transaction durability
227
+ and retention; the library promises no fsync, queue leasing or automatic
228
+ compaction. All run data is currently stored in one bounded-by-the-host JSON
229
+ snapshot; large histories should use a store with appropriate limits.
230
+
231
+ Standalone DAGs can opt into the same exact provenance comparison with
232
+ `compileDag(doc, {tasks,checkpoint,revision})`; see FLOW-FORMAT §7.9.
233
+
234
+ ## 6. Repository consumer and verification
235
+
236
+ [The flow benchmark document](../../../benchmark/fixtures/flow-workflow.json)
237
+ is the workflow executed by `npm run benchmark:flow`. Two correctness suites
238
+ fan out in one DAG. A nested flow then runs the existing FSM and DAG benchmarks
239
+ **sequentially**, retaining their measurement isolation and original assertions.
240
+ A report task assembles their values. A choice optionally enters a persisted
241
+ review wait; `retry` loops through at most three measurement rounds.
242
+
243
+ ```sh
244
+ npm run benchmark:flow -- --quick
245
+ npm run benchmark:flow -- --quick --review --checkpoint /tmp/flow-runs.json
246
+ npm run benchmark:flow -- --checkpoint /tmp/flow-runs.json --accept
247
+ ```
248
+
249
+ Omit `--quick` for normal benchmark iterations. Quick timings are smoke data,
250
+ not replacement release measurements. Use a new `--run-id` for a fresh run;
251
+ resuming a completed run intentionally returns its saved result. The file
252
+ store uses an exclusive short lock plus atomic compare/rename; a lock left by
253
+ a killed process must be inspected and removed by the host, never stolen based
254
+ on elapsed time. Node/browser hosts may inject another CAS store.
255
+
256
+ Tests cover this exact document's concurrency, ordered measurements and saved
257
+ review/retry, plus general task/choice/loop/nesting/wait, failure/resume,
258
+ checkpoint reuse, generation races, changed identities, cancellation and
259
+ schema/compiler agreement. Streaming input, graphical editing, action-document
260
+ awaiting and domain orchestration projections are separate contracts.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/flow",
3
3
  "private": false,
4
- "version": "0.72.3",
4
+ "version": "0.75.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -20,7 +20,7 @@
20
20
  "docs/",
21
21
  "schemas/"
22
22
  ],
23
- "description": "Executable workflow documents for the Jaren suite: the jaren-fsm finite-state-machine format compiled to a pure step function, with query-document guards and effect descriptors as data",
23
+ "description": "Executable JSON workflows: pure FSMs and statecharts, concurrent DAGs, deterministic composition and durable checkpoint/resume",
24
24
  "author": "joham",
25
25
  "repository": {
26
26
  "type": "git",
@@ -49,7 +49,7 @@
49
49
  "prepack": "npm run build:types"
50
50
  },
51
51
  "dependencies": {
52
- "@jarenjs/core": "^0.72.3",
53
- "@jarenjs/json": "^0.72.3"
52
+ "@jarenjs/core": "^0.75.0",
53
+ "@jarenjs/json": "^0.75.0"
54
54
  }
55
55
  }
@@ -0,0 +1,72 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://jarenjs.dev/schemas/jaren-fsm/0.2/state/draft-07",
4
+ "title": "Jaren statechart snapshot",
5
+ "type": "object",
6
+ "properties": {
7
+ "active": {
8
+ "type": "array",
9
+ "items": {
10
+ "type": "string",
11
+ "minLength": 1
12
+ },
13
+ "minItems": 1,
14
+ "uniqueItems": true
15
+ },
16
+ "history": {
17
+ "type": "object",
18
+ "additionalProperties": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "string",
22
+ "minLength": 1
23
+ },
24
+ "minItems": 1,
25
+ "uniqueItems": true
26
+ }
27
+ },
28
+ "timers": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "properties": {
33
+ "transition": {
34
+ "type": "integer",
35
+ "minimum": 0
36
+ },
37
+ "at": {
38
+ "type": "number"
39
+ },
40
+ "token": {
41
+ "type": "integer",
42
+ "minimum": 1,
43
+ "maximum": 9007199254740991
44
+ }
45
+ },
46
+ "required": [
47
+ "transition",
48
+ "at",
49
+ "token"
50
+ ],
51
+ "additionalProperties": false
52
+ },
53
+ "minItems": 0
54
+ },
55
+ "serial": {
56
+ "type": "integer",
57
+ "minimum": 0,
58
+ "maximum": 9007199254740991
59
+ },
60
+ "time": {
61
+ "type": "number"
62
+ }
63
+ },
64
+ "required": [
65
+ "active",
66
+ "history",
67
+ "timers",
68
+ "serial",
69
+ "time"
70
+ ],
71
+ "additionalProperties": false
72
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://jarenjs.dev/schemas/jaren-fsm/0.2/state",
4
+ "title": "Jaren statechart snapshot",
5
+ "type": "object",
6
+ "properties": {
7
+ "active": {
8
+ "type": "array",
9
+ "items": {
10
+ "type": "string",
11
+ "minLength": 1
12
+ },
13
+ "minItems": 1,
14
+ "uniqueItems": true
15
+ },
16
+ "history": {
17
+ "type": "object",
18
+ "additionalProperties": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "string",
22
+ "minLength": 1
23
+ },
24
+ "minItems": 1,
25
+ "uniqueItems": true
26
+ }
27
+ },
28
+ "timers": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "properties": {
33
+ "transition": {
34
+ "type": "integer",
35
+ "minimum": 0
36
+ },
37
+ "at": {
38
+ "type": "number"
39
+ },
40
+ "token": {
41
+ "type": "integer",
42
+ "minimum": 1,
43
+ "maximum": 9007199254740991
44
+ }
45
+ },
46
+ "required": [
47
+ "transition",
48
+ "at",
49
+ "token"
50
+ ],
51
+ "additionalProperties": false
52
+ },
53
+ "minItems": 0
54
+ },
55
+ "serial": {
56
+ "type": "integer",
57
+ "minimum": 0,
58
+ "maximum": 9007199254740991
59
+ },
60
+ "time": {
61
+ "type": "number"
62
+ }
63
+ },
64
+ "required": [
65
+ "active",
66
+ "history",
67
+ "timers",
68
+ "serial",
69
+ "time"
70
+ ],
71
+ "additionalProperties": false
72
+ }