anbaric 1.57.0 → 1.58.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.
@@ -84,6 +84,7 @@ readonly id : string
84
84
  actions : Array<Action | Await>
85
85
  transitions : Array<Transition>
86
86
  readonly isTerminal : boolean
87
+ prewarm : Reads // default: Reads.everything
87
88
 
88
89
  constructor(
89
90
  id : string,
@@ -98,6 +99,29 @@ subscribe(action : Action | Await) : void // append an action after constructi
98
99
  A state's `actions` may mix `Action`s and `Await`s; they are considered in order
99
100
  when a job is processed.
100
101
 
102
+ ### `prewarm` and `Reads`
103
+
104
+ ```ts
105
+ type Reads = (definitions : Array<PropertyDefinition>) => Array<string>
106
+
107
+ Reads.everything // every property (the default)
108
+ Reads.nothing // none
109
+ Reads.only("summary", "articles") // a fixed few
110
+ Reads.where(definition => definition.id.startsWith("article")) // whatever matches
111
+
112
+ scoring.prewarm = Reads.only("summary");
113
+ ```
114
+
115
+ Before a job is processed in a state, the properties the state `prewarm`s are
116
+ loaded in one fetch. Anything a step reads that wasn't prewarmed still loads,
117
+ on demand, the moment it's asked for — so `prewarm` is never something a step
118
+ depends on for correctness, only what keeps a pass to one round trip. Given the
119
+ machine's definitions, so a mapping written once follows the schema. Narrow it
120
+ for a state that needs a little of a job that holds a lot: a workflow carrying
121
+ hundreds of article summaries stops moving all of them for a step that scores
122
+ one, and an [agentic action](../features/ai-agents.md) in that state sends the
123
+ model only what was loaded.
124
+
101
125
  ---
102
126
 
103
127
  ## `Terminal`
@@ -136,47 +160,24 @@ actor : Actor
136
160
  constructor(name : string, actor : Actor, description : string = "", id? : string)
137
161
 
138
162
  // Replaceable function fields — assign your own:
139
- reads : Reads // default: Reads.everything
140
- predicate : (job : Job) => boolean // default: () => true
141
- run : (job : Job) => Promise<Map<string, any>> // default: async () => new Map()
163
+ predicate : (job : Job) => boolean | Promise<boolean> // default: () => true
164
+ run : (job : Job) => Promise<Map<string, any>> // default: async () => new Map()
142
165
  ```
143
166
 
144
- You configure an action by assigning `predicate` and `run`, and `reads` when
145
- the job holds more than the action needs:
167
+ You configure an action by assigning `predicate` and `run`:
146
168
 
147
169
  ```ts
148
170
  const sendWelcome = new Action("Send welcome email", new Code("welcome"));
149
- sendWelcome.reads = Reads.only("email", "name");
171
+ sendWelcome.predicate = async (job) => ! await job.properties.has("welcomeSent");
150
172
  sendWelcome.run = async (job) => new Map([["welcomeSent", true]]);
151
173
  ```
152
174
 
153
- - **`reads`** — which properties are loaded for the job before `predicate` and
154
- `run` see it. Given the machine's property definitions, so it can be written
155
- once and follow the schema. See [`Reads`](#reads).
156
- - **`predicate`** — return `false` to skip this action for a given job.
175
+ - **`predicate`** — return `false` to skip this action for a given job. May be
176
+ `async`, since [reading a property](#jobproperties) is.
157
177
  - **`run`** — return a `Map` of the properties to change. Only properties in the
158
178
  machine's schema are applied; others are ignored with a warning. Only the
159
179
  properties that actually changed are written back.
160
180
 
161
- ### `Reads`
162
-
163
- ```ts
164
- type Reads = (definitions : Array<PropertyDefinition>) => Array<string>
165
-
166
- Reads.everything // every property (the default)
167
- Reads.nothing // none
168
- Reads.only("summary", "articles") // a fixed few
169
- Reads.where(definition => definition.id.startsWith("article")) // whatever matches
170
- ```
171
-
172
- A job is loaded with the union of what the current state's actions, awaits and
173
- transitions read, and nothing else: a property that wasn't declared is simply
174
- absent from `job.properties`. A step that reads everything loads everything, so
175
- nothing changes until you declare. Declaring matters for jobs that carry a lot
176
- — a workflow holding hundreds of article summaries stops moving all of them
177
- for a step that scores one — and for [agentic actions](../features/ai-agents.md),
178
- whose prompt carries the loaded properties and nothing more.
179
-
180
181
  See [Actions and actors](../features/actions-and-actors.md).
181
182
 
182
183
  ---
@@ -196,13 +197,12 @@ name : string
196
197
  description : string
197
198
  waitingFor? : AwaitParty
198
199
  fields : Array<string> = []
199
- resolveUrl : string | ((job : Job) => string) = ""
200
- metadata : (job : Job) => Map<string, any> // default: () => new Map()
201
- reads : Reads // what resolveUrl and metadata read; default: Reads.everything
200
+ resolveUrl : string | ((job : Job) => string | Promise<string>) = ""
201
+ metadata : (job : Job) => Map<string, any> | Promise<Map<string, any>> // default: () => new Map()
202
202
 
203
203
  constructor(name : string, waitingFor? : AwaitParty, description : string = "", id? : string)
204
204
 
205
- waitForInput(job : Job) : WaitForInput
205
+ waitForInput(job : Job) : Promise<WaitForInput>
206
206
  ```
207
207
 
208
208
  - **`fields`** — the input you expect back (property names).
@@ -244,21 +244,20 @@ transition whose predicate holds moves the job to `to`.
244
244
 
245
245
  ```ts
246
246
  to : string
247
- predicate : (job : Job) => boolean // default: () => true
248
- reads : Reads // default: everything if guarded, nothing if not
247
+ predicate : (job : Job) => boolean | Promise<boolean> // default: () => true
249
248
 
250
- constructor(to : string, predicate? : (job : Job) => boolean, reads? : Reads)
249
+ constructor(to : string, predicate? : (job : Job) => boolean | Promise<boolean>)
251
250
  ```
252
251
 
253
252
  ```ts
254
- new Transition("active", (job) => job.properties.get("welcomeSent") === true, Reads.only("welcomeSent"))
253
+ new Transition("active", async (job) => await job.properties.get("welcomeSent") === true)
255
254
  new Transition("scoring") // unguarded: the actions run, then the job moves on
256
255
  ```
257
256
 
258
257
  The predicate is optional. Omit it when a state's actions simply run and the job
259
258
  should move on, rather than inventing a sentinel property for the transition to
260
- read. Guard a transition only when the move is conditional, and say what the
261
- guard reads so the job is loaded with only that.
259
+ read. Guard a transition only when the move is conditional. A guard may be
260
+ `async`, since reading a property is.
262
261
 
263
262
  ---
264
263
 
@@ -297,18 +296,34 @@ A job whose action threw is `FAILED`, with the reason in its audit trail. It
297
296
  stays in its state rather than transitioning, and an update that moves it on
298
297
  returns it to `ACTIVE` — so a failure is recoverable, not terminal.
299
298
 
299
+ ### `JobProperties`
300
+
301
+ `job.properties` is not a `Map`: it reads on demand, so a job may hold a great
302
+ deal while a step pays only for what it asks for.
303
+
304
+ ```ts
305
+ await job.properties.get(key) : Promise<any>
306
+ await job.properties.has(key) : Promise<boolean>
307
+ await job.properties.getMany(keys) : Promise<Map<string, any>>
308
+ await job.properties.prewarm(keys?) : Promise<void> // fetch these (or everything) in one go
309
+ await job.properties.toMap() : Promise<Map<string, any>> // everything, loading what isn't held
310
+ job.properties.snapshot() : Map<string, any> // what is held right now, loading nothing
311
+ ```
312
+
313
+ The first read of a property not yet held fetches it; the state's
314
+ [`prewarm`](#prewarm-and-reads) fetches a set up front. Reads are cached for
315
+ the pass. `snapshot()` is what serialisation and an agentic prompt use.
316
+
300
317
  ### Reading a job
301
318
 
302
319
  ```ts
303
- persistence.retrieve(id, actor) : Promise<Job> // every property
304
- persistence.retrieve(id, actor, ["summary", "score"]) : Promise<Job> // only those
320
+ persistence.retrieve(id, actor) : Promise<Job> // holding every property
321
+ persistence.retrieve(id, actor, ["summary", "score"]) : Promise<Job> // holding those; the rest on demand
305
322
  ```
306
323
 
307
- Properties are stored one by one, so a job can be read with only the keys
308
- wanted — that's what the machine does for every step, from what the step
309
- [declares it reads](#reads) — and a save writes only the properties that
310
- changed. A property absent from a partial read is never touched by a save of
311
- that job, and no save ever removes a property. Over HTTP the same read is
324
+ Properties are stored one by one, and a save writes only the properties that
325
+ changed — never the ones a pass read, let alone the ones it never loaded — and
326
+ no save ever removes a property. Over HTTP the same read is
312
327
  `GET /api/v2/jobs/<id>?keys=summary,score`, and the properties in a `PUT` body
313
328
  are the ones written.
314
329
 
@@ -13,9 +13,9 @@ import {Action, Code} from "anbaric";
13
13
 
14
14
  const chargeCard = new Action("Charge the card", new Code("billing"));
15
15
 
16
- chargeCard.predicate = (job) => job.properties.get("paid") !== true; // skip if already paid
16
+ chargeCard.predicate = async (job) => await job.properties.get("paid") !== true; // skip if already paid
17
17
  chargeCard.run = async (job) => {
18
- const amount = job.properties.get("total");
18
+ const amount = await job.properties.get("total");
19
19
  // ... call your payment provider ...
20
20
  return new Map([["paid", true], ["chargedAmount", amount]]);
21
21
  };
@@ -32,31 +32,35 @@ Actions in a state run **in order** each time a job is processed. An action that
32
32
  returns an unchanged value doesn't churn the job — the machine only advances (or
33
33
  schedules a re-check) when something actually changes.
34
34
 
35
- ### Declare what an action reads
35
+ ### Reading properties is asynchronous
36
36
 
37
- A job can hold a lot — hundreds of article summaries, say — while a given
38
- action needs one property of it. Declare that, and only that is loaded:
37
+ `job.properties` reads on demand — `await job.properties.get("total")` — so a
38
+ job can hold a lot (hundreds of article summaries, say) while an action that
39
+ needs one property pays for one. That's why `predicate` may be `async`, as in
40
+ the example above, and why a transition's guard may be too. Afterwards the
41
+ machine writes back only the properties that changed — never the ones the pass
42
+ read, let alone the ones it never loaded.
43
+
44
+ ### Prewarm what a state needs
45
+
46
+ Each property read that wasn't already held is a fetch. A state can name what
47
+ to load up front, in one go:
39
48
 
40
49
  ```ts
41
50
  import {Reads} from "anbaric";
42
51
 
43
- chargeCard.reads = Reads.only("total", "paid");
52
+ const scoring = new State("scoring", [score], [new Transition("grouped")]);
53
+ scoring.prewarm = Reads.only("summary");
44
54
  ```
45
55
 
46
- `reads` is a function of the machine's property definitions, so a mapping can
47
- be written once and keep up as the schema grows: `Reads.everything` (the
48
- default — nothing changes until you declare), `Reads.nothing`,
49
- `Reads.only(...keys)`, or `Reads.where(definition => …)` to pick by name. A
50
- transition's guard takes a `reads` too, and an `Await`'s `resolveUrl` and
51
- `metadata` have one.
52
-
53
- Before a job is processed in a state, the machine loads the union of what that
54
- state's actions, awaits and transitions read; a property that wasn't declared
55
- is simply absent from `job.properties`, so declare honestly. Afterwards it
56
- writes back only the properties that changed — never the ones it read, and
57
- never the ones it didn't load. For an [agentic action](ai-agents.md) the
58
- declaration also decides what reaches the model: the prompt carries the loaded
59
- properties and nothing more.
56
+ By default a state prewarms everything, so nothing changes until you narrow it;
57
+ anything a step reads beyond the prewarm still loads on demand, so a narrow
58
+ prewarm can never break a step, only cost it a fetch. `Reads` is a function of
59
+ the machine's property definitions — `Reads.everything`, `Reads.nothing`,
60
+ `Reads.only(...keys)`, `Reads.where(definition => …)` — so a mapping written
61
+ once keeps up as the schema grows. For an [agentic action](ai-agents.md) the
62
+ prewarm also decides what reaches the model: the prompt carries the properties
63
+ the job holds when the action runs.
60
64
 
61
65
  ## Actors: who does the work
62
66
 
@@ -34,7 +34,7 @@ const triage = new RemoteLLMAgenticAction(
34
34
 
35
35
  const support = new StateMachine("support", [
36
36
  new State("open", [triage], [
37
- new Transition("prioritised", (job) => job.properties.has("priority")),
37
+ new Transition("prioritised", async (job) => await job.properties.has("priority")),
38
38
  ]),
39
39
  new State("prioritised"),
40
40
  ]);
@@ -44,12 +44,12 @@ When a job is processed in `open`, the agent is asked to produce a `priority`
44
44
  constrained to the schema; the result is applied to the job, and the transition
45
45
  advances it. The decision is recorded against `triager`.
46
46
 
47
- The job's current properties are appended to the prompt automatically, so the
48
- model sees the data it's reasoning about — the properties the action
49
- [declares it reads](actions-and-actors.md#declare-what-an-action-reads), which
50
- is all of them until you say otherwise. On a job that holds a lot, declare
51
- them: `triage.reads = Reads.only("subject")` keeps a ticket's attachments,
52
- history and everything else out of every call.
47
+ The job's properties are appended to the prompt automatically, so the model
48
+ sees the data it's reasoning about — the properties the job holds when the
49
+ action runs, which is all of them until the state says otherwise. On a job
50
+ that holds a lot, [prewarm](actions-and-actors.md#prewarm-what-a-state-needs)
51
+ just what the model needs: `open.prewarm = Reads.only("subject")` keeps a
52
+ ticket's attachments, history and everything else out of every call.
53
53
 
54
54
  ## The output schema
55
55
 
@@ -22,7 +22,7 @@ approve.resolveUrl = (job) => `/approve?job=${job.id}`; // where a person prov
22
22
 
23
23
  const fulfilment = new StateMachine("fulfilment", [
24
24
  new State("review", [approve], [
25
- new Transition("approved", (job) => job.properties.get("approved") === true),
25
+ new Transition("approved", async (job) => await job.properties.get("approved") === true),
26
26
  ]),
27
27
  new State("approved"),
28
28
  ]);
@@ -52,7 +52,7 @@ of the job** so you can build a per-job link — for example putting the job id
52
52
  the query string so your form knows which job it's resolving:
53
53
 
54
54
  ```ts
55
- approve.resolveUrl = (job) => `/approve?job=${job.id}&total=${job.properties.get("total")}`;
55
+ approve.resolveUrl = async (job) => `/approve?job=${job.id}&total=${await job.properties.get("total")}`;
56
56
  ```
57
57
 
58
58
  Write it **app-relative** (an absolute path like `/approve`, as above) — it's a
@@ -43,11 +43,12 @@ against your schema before applying them. Every property a job holds must have a
43
43
  `PropertyDefinition`, or the change is rejected. This is what keeps a job's data
44
44
  trustworthy no matter who or what wrote it.
45
45
 
46
- Properties are stored one by one, and a step is given only the ones it
47
- [declares it reads](actions-and-actors.md#declare-what-an-action-reads) — by
48
- default all of them. So a job can carry a great deal of data without every
49
- step paying for all of it: a step that scores one summary loads one summary,
50
- and writes back only what it changed.
46
+ Properties are stored one by one and read on demand — `await
47
+ job.properties.get("name")` — so a job can carry a great deal of data without
48
+ every step paying for all of it. A state can
49
+ [prewarm](actions-and-actors.md#prewarm-what-a-state-needs) what it needs in
50
+ one fetch; a step that scores one summary loads one summary, and writes back
51
+ only what it changed.
51
52
 
52
53
  ## Nothing is hard-wired
53
54
 
@@ -66,7 +66,7 @@ Every scheduled job carries the run it belongs to:
66
66
 
67
67
  ```ts
68
68
  processRun.run = async (job) => {
69
- const scheduledFor = new Date(job.properties.get("scheduledFor"));
69
+ const scheduledFor = new Date(await job.properties.get("scheduledFor"));
70
70
  // …reconcile everything up to scheduledFor
71
71
  };
72
72
  ```
@@ -13,7 +13,7 @@ import {Action, Code, PropertyDefinition, State, StateMachine, Transition} from
13
13
 
14
14
  const sendWelcome = new Action("Send welcome email", new Code("welcome"));
15
15
  sendWelcome.run = async (job) => {
16
- const email = job.properties.get("email");
16
+ const email = await job.properties.get("email");
17
17
  console.log(`Sending welcome email to ${email}`);
18
18
  return new Map([["welcomeSent", true]]); // properties to add to the job
19
19
  };
@@ -25,7 +25,7 @@ const onboarding = new StateMachine(
25
25
  "onboarding",
26
26
  [
27
27
  new State("new", [sendWelcome], [
28
- new Transition("active", (job) => job.properties.get("welcomeSent") === true),
28
+ new Transition("active", async (job) => await job.properties.get("welcomeSent") === true),
29
29
  ]),
30
30
  new State("active"),
31
31
  ],
@@ -67,7 +67,7 @@ once on entry. That is deliberate — it lets a predicate be time-based
67
67
  work that must happen only once, guard it:
68
68
 
69
69
  ```ts
70
- fetchReport.predicate = (job) => !job.properties.has("report");
70
+ fetchReport.predicate = async (job) => ! await job.properties.has("report");
71
71
  ```
72
72
 
73
73
  ## Failing a job
@@ -79,7 +79,7 @@ job cannot proceed":
79
79
 
80
80
  ```ts
81
81
  chargeCard.run = async (job) => {
82
- const outcome = await payments.charge(job.properties.get("amount"));
82
+ const outcome = await payments.charge(await job.properties.get("amount"));
83
83
  if (!outcome.ok) throw new Error(`Card declined for job ${job.id}: ${outcome.reason}`);
84
84
  return new Map([["charged", true]]);
85
85
  };
@@ -97,7 +97,7 @@ Mark the end of a process with `Terminal`, which carries an outcome:
97
97
  import {Terminal, Transition} from "anbaric";
98
98
 
99
99
  new State("packing", [packItems], [
100
- new Transition("shipped", (job) => job.properties.get("packed") === true),
100
+ new Transition("shipped", async (job) => await job.properties.get("packed") === true),
101
101
  ]),
102
102
  new Terminal("shipped", Terminal.Outcome.SUCCESS),
103
103
  new Terminal("cancelled", Terminal.Outcome.FAILURE),
@@ -127,8 +127,8 @@ specific conditions first.
127
127
 
128
128
  ```ts
129
129
  new State("placed", [], [
130
- new Transition("cancelled", (job) => job.properties.get("cancelled") === true),
131
- new Transition("packing", (job) => job.properties.get("paid") === true),
130
+ new Transition("cancelled", async (job) => await job.properties.get("cancelled") === true),
131
+ new Transition("packing", async (job) => await job.properties.get("paid") === true),
132
132
  ]),
133
133
  ```
134
134
 
@@ -45,17 +45,17 @@ review.resolveUrl = (job) => `/review?job=${job.id}`;
45
45
  // An automated payment step.
46
46
  const payOut = new Action("Pay the expense", new Code("payments"));
47
47
  payOut.run = async (job) => {
48
- // ... call your payment provider with job.properties.get("amount") ...
48
+ // ... call your payment provider with await job.properties.get("amount") ...
49
49
  return new Map([["paid", true]]);
50
50
  };
51
51
 
52
52
  const expenses = new StateMachine("expenses", [
53
53
  new State("submitted", [review], [
54
- new Transition("approved", (job) => job.properties.get("approved") === true),
55
- new Transition("rejected", (job) => job.properties.get("approved") === false),
54
+ new Transition("approved", async (job) => await job.properties.get("approved") === true),
55
+ new Transition("rejected", async (job) => await job.properties.get("approved") === false),
56
56
  ]),
57
57
  new State("approved", [payOut], [
58
- new Transition("paid", (job) => job.properties.get("paid") === true),
58
+ new Transition("paid", async (job) => await job.properties.get("paid") === true),
59
59
  ]),
60
60
  new Terminal("paid", Terminal.Outcome.SUCCESS),
61
61
  new Terminal("rejected", Terminal.Outcome.FAILURE),
@@ -18,8 +18,8 @@ approve.fields = ["approved", "note"];
18
18
  approve.resolveUrl = (job) => `/approve?job=${job.id}`;
19
19
 
20
20
  new State("review", [approve], [
21
- new Transition("approved", (job) => job.properties.get("approved") === true),
22
- new Transition("rejected", (job) => job.properties.get("approved") === false),
21
+ new Transition("approved", async (job) => await job.properties.get("approved") === true),
22
+ new Transition("rejected", async (job) => await job.properties.get("approved") === false),
23
23
  ]),
24
24
  ```
25
25
 
@@ -29,8 +29,8 @@ Because `resolveUrl` is a function of the job, you can encode everything the pag
29
29
  needs in the link — most importantly the **job id**:
30
30
 
31
31
  ```ts
32
- approve.resolveUrl = (job) =>
33
- `/approve?job=${job.id}&amount=${job.properties.get("amount")}`;
32
+ approve.resolveUrl = async (job) =>
33
+ `/approve?job=${job.id}&amount=${await job.properties.get("amount")}`;
34
34
  ```
35
35
 
36
36
  The admin console lists every job **awaiting a human** and turns this
@@ -57,9 +57,9 @@ matching `approved` fires. The decision is recorded against the user in the
57
57
  Use `metadata` to carry extra context with the wait (shown alongside the task):
58
58
 
59
59
  ```ts
60
- approve.metadata = (job) => new Map([
61
- ["submittedBy", job.properties.get("submitter")],
62
- ["amount", job.properties.get("amount")],
60
+ approve.metadata = async (job) => new Map([
61
+ ["submittedBy", await job.properties.get("submitter")],
62
+ ["amount", await job.properties.get("amount")],
63
63
  ]);
64
64
  ```
65
65
 
@@ -72,10 +72,10 @@ Chain approvals by giving each its own state and `Await`. Because actions after
72
72
 
73
73
  ```ts
74
74
  new State("manager-review", [managerApprove], [
75
- new Transition("finance-review", (job) => job.properties.get("managerApproved") === true),
75
+ new Transition("finance-review", async (job) => await job.properties.get("managerApproved") === true),
76
76
  ]),
77
77
  new State("finance-review", [financeApprove], [
78
- new Transition("approved", (job) => job.properties.get("financeApproved") === true),
78
+ new Transition("approved", async (job) => await job.properties.get("financeApproved") === true),
79
79
  ]),
80
80
  ```
81
81
 
@@ -13,7 +13,7 @@ job park until the system reports back:
13
13
  ```ts
14
14
  const startSigning = new Action("Send for signature", new Code("esign"));
15
15
  startSigning.run = async (job) => {
16
- const ref = await eSignProvider.createEnvelope(job.properties.get("documentUrl"));
16
+ const ref = await eSignProvider.createEnvelope(await job.properties.get("documentUrl"));
17
17
  return new Map([["envelopeRef", ref]]);
18
18
  };
19
19
 
@@ -21,7 +21,7 @@ const awaitSignature = new Await("Await signature", "EXTERNAL_SYSTEM");
21
21
  awaitSignature.fields = ["signed"];
22
22
 
23
23
  new State("signing", [startSigning, awaitSignature], [
24
- new Transition("signed", (job) => job.properties.get("signed") === true),
24
+ new Transition("signed", async (job) => await job.properties.get("signed") === true),
25
25
  ]),
26
26
  ```
27
27
 
@@ -59,12 +59,12 @@ re-checks after a back-off until the status flips:
59
59
  ```ts
60
60
  const checkStatus = new Action("Poll payment status", new Code("payments"));
61
61
  checkStatus.run = async (job) => {
62
- const status = await gateway.status(job.properties.get("paymentRef"));
62
+ const status = await gateway.status(await job.properties.get("paymentRef"));
63
63
  return status === "settled" ? new Map([["settled", true]]) : new Map();
64
64
  };
65
65
 
66
66
  new State("charging", [checkStatus], [
67
- new Transition("settled", (job) => job.properties.get("settled") === true),
67
+ new Transition("settled", async (job) => await job.properties.get("settled") === true),
68
68
  ]),
69
69
  ```
70
70
 
@@ -28,7 +28,7 @@ transition reacts. This keeps each piece testable and the flow visible.
28
28
 
29
29
  ```ts
30
30
  new State("packing", [packItems], [
31
- new Transition("shipped", (job) => job.properties.get("packed") === true),
31
+ new Transition("shipped", async (job) => await job.properties.get("packed") === true),
32
32
  ]),
33
33
  ```
34
34
 
@@ -39,8 +39,8 @@ transition wins. Put more specific transitions first:
39
39
 
40
40
  ```ts
41
41
  new State("placed", [], [
42
- new Transition("cancelled", (job) => job.properties.get("cancelled") === true),
43
- new Transition("packing", (job) => job.properties.get("paid") === true),
42
+ new Transition("cancelled", async (job) => await job.properties.get("cancelled") === true),
43
+ new Transition("packing", async (job) => await job.properties.get("paid") === true),
44
44
  ]),
45
45
  ```
46
46
 
@@ -51,10 +51,10 @@ routes work without extra states:
51
51
 
52
52
  ```ts
53
53
  const autoTriage = new Action("Auto triage", new Code("triage-bot"));
54
- autoTriage.predicate = (job) => job.properties.get("priority") === "low";
54
+ autoTriage.predicate = async (job) => await job.properties.get("priority") === "low";
55
55
 
56
56
  const escalate = new Action("Escalate", new Code("rules"));
57
- escalate.predicate = (job) => job.properties.get("priority") === "high";
57
+ escalate.predicate = async (job) => await job.properties.get("priority") === "high";
58
58
 
59
59
  new State("open", [autoTriage, escalate], [/* transitions */]);
60
60
  ```
@@ -67,7 +67,7 @@ parks cleanly and resumes when the input arrives:
67
67
 
68
68
  ```ts
69
69
  new State("review", [new Await("Approve", "HUMAN")], [
70
- new Transition("approved", (job) => job.properties.get("approved") === true),
70
+ new Transition("approved", async (job) => await job.properties.get("approved") === true),
71
71
  ]),
72
72
  ```
73
73
 
@@ -81,7 +81,7 @@ after a back-off — so a state can gently poll without a busy loop.
81
81
  ```ts
82
82
  const poll = new Action("Check delivery status", new Code("carrier"));
83
83
  poll.run = async (job) => {
84
- const status = await fetchStatus(job.properties.get("tracking"));
84
+ const status = await fetchStatus(await job.properties.get("tracking"));
85
85
  return status === "delivered" ? new Map([["delivered", true]]) : new Map();
86
86
  };
87
87
  ```
@@ -109,7 +109,7 @@ const companies = new PropertyDefinition("companies");
109
109
  companies.example = [{ slug: "acme", companyName: "Acme Corp" }];
110
110
 
111
111
  // in an action
112
- const companies = job.properties.get("companies") ?? [];
112
+ const companies = await job.properties.get("companies") ?? [];
113
113
  ```
114
114
 
115
115
  ## See also
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric",
3
- "version": "1.57.0",
3
+ "version": "1.58.1",
4
4
  "description": "Everything needed to write an Anbaric app: state machines, jobs, document and secret stores, local in-memory implementations and the Anbaric Cloud clients",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,9 +24,9 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
- "anbaric-impl-cloud": "^1.57.0",
28
- "anbaric-data-store": "^1.57.0",
29
- "anbaric-state-machine": "^1.57.0",
30
- "anbaric-tsapi": "^1.57.0"
27
+ "anbaric-impl-cloud": "^1.58.1",
28
+ "anbaric-data-store": "^1.58.1",
29
+ "anbaric-state-machine": "^1.58.1",
30
+ "anbaric-tsapi": "^1.58.1"
31
31
  }
32
32
  }