anbaric 1.56.9 → 1.58.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.
@@ -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,20 +160,23 @@ actor : Actor
136
160
  constructor(name : string, actor : Actor, description : string = "", id? : string)
137
161
 
138
162
  // Replaceable function fields — assign your own:
139
- predicate : (job : Job) => boolean // default: () => true
140
- 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()
141
165
  ```
142
166
 
143
167
  You configure an action by assigning `predicate` and `run`:
144
168
 
145
169
  ```ts
146
170
  const sendWelcome = new Action("Send welcome email", new Code("welcome"));
171
+ sendWelcome.predicate = async (job) => ! await job.properties.has("welcomeSent");
147
172
  sendWelcome.run = async (job) => new Map([["welcomeSent", true]]);
148
173
  ```
149
174
 
150
- - **`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.
151
177
  - **`run`** — return a `Map` of the properties to change. Only properties in the
152
- machine's schema are applied; others are ignored with a warning.
178
+ machine's schema are applied; others are ignored with a warning. Only the
179
+ properties that actually changed are written back.
153
180
 
154
181
  See [Actions and actors](../features/actions-and-actors.md).
155
182
 
@@ -170,12 +197,12 @@ name : string
170
197
  description : string
171
198
  waitingFor? : AwaitParty
172
199
  fields : Array<string> = []
173
- resolveUrl : string | ((job : Job) => string) = ""
174
- metadata : (job : Job) => Map<string, any> // default: () => new Map()
200
+ resolveUrl : string | ((job : Job) => string | Promise<string>) = ""
201
+ metadata : (job : Job) => Map<string, any> | Promise<Map<string, any>> // default: () => new Map()
175
202
 
176
203
  constructor(name : string, waitingFor? : AwaitParty, description : string = "", id? : string)
177
204
 
178
- waitForInput(job : Job) : WaitForInput
205
+ waitForInput(job : Job) : Promise<WaitForInput>
179
206
  ```
180
207
 
181
208
  - **`fields`** — the input you expect back (property names).
@@ -217,19 +244,20 @@ transition whose predicate holds moves the job to `to`.
217
244
 
218
245
  ```ts
219
246
  to : string
220
- predicate : (job : Job) => boolean // default: () => true
247
+ predicate : (job : Job) => boolean | Promise<boolean> // default: () => true
221
248
 
222
- constructor(to : string, predicate? : (job : Job) => boolean)
249
+ constructor(to : string, predicate? : (job : Job) => boolean | Promise<boolean>)
223
250
  ```
224
251
 
225
252
  ```ts
226
- new Transition("active", (job) => job.properties.get("welcomeSent") === true)
253
+ new Transition("active", async (job) => await job.properties.get("welcomeSent") === true)
227
254
  new Transition("scoring") // unguarded: the actions run, then the job moves on
228
255
  ```
229
256
 
230
257
  The predicate is optional. Omit it when a state's actions simply run and the job
231
258
  should move on, rather than inventing a sentinel property for the transition to
232
- read. Guard a transition only when the move is conditional.
259
+ read. Guard a transition only when the move is conditional. A guard may be
260
+ `async`, since reading a property is.
233
261
 
234
262
  ---
235
263
 
@@ -268,6 +296,37 @@ A job whose action threw is `FAILED`, with the reason in its audit trail. It
268
296
  stays in its state rather than transitioning, and an update that moves it on
269
297
  returns it to `ACTIVE` — so a failure is recoverable, not terminal.
270
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
+
317
+ ### Reading a job
318
+
319
+ ```ts
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
322
+ ```
323
+
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
327
+ `GET /api/v2/jobs/<id>?keys=summary,score`, and the properties in a `PUT` body
328
+ are the ones written.
329
+
271
330
  ### Listing jobs
272
331
 
273
332
  ```ts
package/docs/api/web.md CHANGED
@@ -116,6 +116,17 @@ In code the same listing is `JobPersistenceFactory.instance().list(actor,
116
116
  pageSize, page, query)`; the CLI's `anbaric jobs list` and the MCP tool
117
117
  `anbaric_jobs_list` take the same filters.
118
118
 
119
+ ```
120
+ GET /api/v2/jobs/<id> every property
121
+ GET /api/v2/jobs/<id>?keys=summary,score only those
122
+ PUT /api/v2/jobs/<id> body: a serialised job
123
+ ```
124
+
125
+ A job's properties are stored one by one. A `GET` with `keys` returns only
126
+ those; the properties in a `PUT` body are **upserted** — written if given,
127
+ left alone if not — so a caller that read a few keys can write a few keys
128
+ without disturbing the rest, and no `PUT` ever removes a property.
129
+
119
130
  ### Other resources
120
131
 
121
132
  Also under `/api/v2`, reached through the cloud clients rather than raw HTTP:
@@ -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,6 +32,36 @@ 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
+ ### Reading properties is asynchronous
36
+
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:
48
+
49
+ ```ts
50
+ import {Reads} from "anbaric";
51
+
52
+ const scoring = new State("scoring", [score], [new Transition("grouped")]);
53
+ scoring.prewarm = Reads.only("summary");
54
+ ```
55
+
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.
64
+
35
65
  ## Actors: who does the work
36
66
 
37
67
  An actor is a small identity object — `type`, `id`, and `roles`. There are four
@@ -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,8 +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.
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.
49
53
 
50
54
  ## The output schema
51
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,6 +43,13 @@ 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 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.
52
+
46
53
  ## Nothing is hard-wired
47
54
 
48
55
  Persistence, queueing, document/secret/SQL stores — all of them come from
@@ -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.56.9",
3
+ "version": "1.58.0",
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.56.9",
28
- "anbaric-data-store": "^1.56.9",
29
- "anbaric-state-machine": "^1.56.9",
30
- "anbaric-tsapi": "^1.56.9"
27
+ "anbaric-impl-cloud": "^1.58.0",
28
+ "anbaric-data-store": "^1.58.0",
29
+ "anbaric-state-machine": "^1.58.0",
30
+ "anbaric-tsapi": "^1.58.0"
31
31
  }
32
32
  }