@rise-maritime/keelson-js 0.6.0-alpha.255.dev.1 → 0.6.0-alpha.275.dev.2

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.
Files changed (48) hide show
  1. package/dist/index.d.ts +83 -8
  2. package/dist/index.js +23 -1
  3. package/dist/index.test.js +31 -0
  4. package/dist/interfaces/AlarmAck.d.ts +2 -2
  5. package/dist/interfaces/AlarmAck.js +2 -2
  6. package/dist/interfaces/ContainerControl.d.ts +239 -0
  7. package/dist/interfaces/ContainerControl.js +1053 -0
  8. package/dist/interfaces/ContainerHost.d.ts +403 -0
  9. package/dist/interfaces/ContainerHost.js +1230 -0
  10. package/dist/interfaces/ErrorResponse.d.ts +7 -2
  11. package/dist/interfaces/ErrorResponse.js +12 -2
  12. package/dist/interfaces/Route.d.ts +141 -6
  13. package/dist/interfaces/Route.js +309 -4
  14. package/dist/interfaces/RoutePlanner.d.ts +19 -1
  15. package/dist/interfaces/RoutePlanner.js +23 -1
  16. package/dist/interfaces/ScenarioControl.d.ts +132 -3
  17. package/dist/interfaces/ScenarioControl.js +439 -37
  18. package/dist/interfaces/VehicleNavigation.d.ts +55 -0
  19. package/dist/interfaces/VehicleNavigation.js +233 -1
  20. package/dist/interfaces/VehicleSimulatorControl.d.ts +0 -83
  21. package/dist/interfaces/VehicleSimulatorControl.js +1 -293
  22. package/dist/interfaces/serviceRegistry.d.ts +84 -8
  23. package/dist/interfaces/serviceRegistry.js +4 -0
  24. package/dist/interfaces.json +2 -0
  25. package/dist/payloads/AdviceDisposition.d.ts +55 -0
  26. package/dist/payloads/AdviceDisposition.js +242 -0
  27. package/dist/payloads/ChecklistHandover.d.ts +522 -0
  28. package/dist/payloads/ChecklistHandover.js +1735 -0
  29. package/dist/payloads/ContainerHost.d.ts +407 -0
  30. package/dist/payloads/ContainerHost.js +1245 -0
  31. package/dist/payloads/Encounter.d.ts +104 -0
  32. package/dist/payloads/Encounter.js +573 -0
  33. package/dist/payloads/NavigationAdvice.d.ts +94 -0
  34. package/dist/payloads/NavigationAdvice.js +596 -0
  35. package/dist/payloads/NavigationState.d.ts +90 -0
  36. package/dist/payloads/NavigationState.js +377 -0
  37. package/dist/payloads/Route.d.ts +142 -6
  38. package/dist/payloads/Route.js +318 -4
  39. package/dist/payloads/RouteExecution.d.ts +7 -0
  40. package/dist/payloads/ScenarioEvent.d.ts +14 -0
  41. package/dist/payloads/ScenarioEvent.js +16 -0
  42. package/dist/payloads/index.d.ts +1 -1
  43. package/dist/payloads/index.js +2 -2
  44. package/dist/payloads/index.keelson.d.ts +10 -4
  45. package/dist/payloads/index.keelson.js +10 -4
  46. package/dist/qos.json +6 -0
  47. package/dist/subjects.json +20 -1
  48. package/package.json +1 -1
@@ -0,0 +1,403 @@
1
+ import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
2
+ export declare const protobufPackage = "keelson";
3
+ /**
4
+ * The container lifecycle states. Values mirror the Docker Engine's state
5
+ * vocabulary, which containerd and podman also use. ContainerInfo.raw_state
6
+ * carries whatever the runtime actually said, so a state this enum does not yet
7
+ * model is preserved rather than flattened to UNSPECIFIED.
8
+ */
9
+ export declare enum ContainerState {
10
+ CONTAINER_STATE_UNSPECIFIED = 0,
11
+ CONTAINER_STATE_CREATED = 1,
12
+ CONTAINER_STATE_RUNNING = 2,
13
+ CONTAINER_STATE_PAUSED = 3,
14
+ CONTAINER_STATE_RESTARTING = 4,
15
+ CONTAINER_STATE_REMOVING = 5,
16
+ CONTAINER_STATE_EXITED = 6,
17
+ CONTAINER_STATE_DEAD = 7,
18
+ UNRECOGNIZED = -1
19
+ }
20
+ export declare function containerStateFromJSON(object: any): ContainerState;
21
+ export declare function containerStateToJSON(object: ContainerState): string;
22
+ /**
23
+ * The runtime's restart policy for a container. NO is spelled explicitly because
24
+ * the Docker API reports it as the empty string, which is indistinguishable from
25
+ * "not reported" once it reaches a UI.
26
+ */
27
+ export declare enum ContainerRestartPolicy {
28
+ CONTAINER_RESTART_POLICY_UNSPECIFIED = 0,
29
+ CONTAINER_RESTART_POLICY_NO = 1,
30
+ CONTAINER_RESTART_POLICY_ALWAYS = 2,
31
+ CONTAINER_RESTART_POLICY_UNLESS_STOPPED = 3,
32
+ CONTAINER_RESTART_POLICY_ON_FAILURE = 4,
33
+ UNRECOGNIZED = -1
34
+ }
35
+ export declare function containerRestartPolicyFromJSON(object: any): ContainerRestartPolicy;
36
+ export declare function containerRestartPolicyToJSON(object: ContainerRestartPolicy): string;
37
+ /**
38
+ * Health-check result. NONE and UNSPECIFIED are different answers: NONE means the
39
+ * container declares no health check, UNSPECIFIED means the responder could not
40
+ * determine one.
41
+ */
42
+ export declare enum ContainerHealthStatus {
43
+ CONTAINER_HEALTH_STATUS_UNSPECIFIED = 0,
44
+ CONTAINER_HEALTH_STATUS_NONE = 1,
45
+ CONTAINER_HEALTH_STATUS_STARTING = 2,
46
+ CONTAINER_HEALTH_STATUS_HEALTHY = 3,
47
+ CONTAINER_HEALTH_STATUS_UNHEALTHY = 4,
48
+ UNRECOGNIZED = -1
49
+ }
50
+ export declare function containerHealthStatusFromJSON(object: any): ContainerHealthStatus;
51
+ export declare function containerHealthStatusToJSON(object: ContainerHealthStatus): string;
52
+ export declare enum ContainerStatusTrigger {
53
+ CONTAINER_STATUS_TRIGGER_UNSPECIFIED = 0,
54
+ /** CONTAINER_STATUS_TRIGGER_CHANGE - The container set changed since the previous sample. */
55
+ CONTAINER_STATUS_TRIGGER_CHANGE = 1,
56
+ /**
57
+ * CONTAINER_STATUS_TRIGGER_HEARTBEAT - Nothing changed; this is the keep-alive. Zenoh pub/sub does not backfill,
58
+ * so without a periodic republish a subscriber that joins during a quiet
59
+ * period would wait indefinitely for its first value.
60
+ */
61
+ CONTAINER_STATUS_TRIGGER_HEARTBEAT = 2,
62
+ UNRECOGNIZED = -1
63
+ }
64
+ export declare function containerStatusTriggerFromJSON(object: any): ContainerStatusTrigger;
65
+ export declare function containerStatusTriggerToJSON(object: ContainerStatusTrigger): string;
66
+ export interface ContainerInfo {
67
+ /**
68
+ * The operator-meaningful handle. Unique per host; the value every other
69
+ * procedure here takes.
70
+ */
71
+ name: string;
72
+ /**
73
+ * The runtime's own identifier, for display and correlation only. Unstable
74
+ * across recreate -- do not address a container by it.
75
+ */
76
+ id: string;
77
+ /**
78
+ * Image reference as deployed, e.g. "ghcr.io/rise-maritime/keelson:0.6.0-pre.13".
79
+ * Falls back to the configured image reference when the running image carries
80
+ * no tag (pulled by digest, or the tag was pruned out from under it).
81
+ */
82
+ image: string;
83
+ state: ContainerState;
84
+ /** The runtime's raw state string, preserved so an unmodelled state is not lost. */
85
+ rawState: string;
86
+ createdAt: Date | undefined;
87
+ /**
88
+ * Unset if the container has never run. (The Docker API reports the zero time
89
+ * "0001-01-01T00:00:00Z" here, which renders as a year-1 date if passed through.)
90
+ */
91
+ startedAt: Date | undefined;
92
+ /** Unset while the container is running or has never run. */
93
+ finishedAt: Date | undefined;
94
+ /** Present only once the container has exited at least once. */
95
+ exitCode?: number | undefined;
96
+ restartPolicy: ContainerRestartPolicy;
97
+ /** Only meaningful for CONTAINER_RESTART_POLICY_ON_FAILURE. */
98
+ restartPolicyMaxRetries: number;
99
+ /** How many times the runtime has restarted this container under that policy. */
100
+ restartCount: number;
101
+ health: ContainerHealthStatus;
102
+ /**
103
+ * Whether THIS responder would presently permit start / stop / restart on this
104
+ * container. False for a read-only responder, for a container outside its
105
+ * allow-list, and always for the responder's own container.
106
+ */
107
+ controllable: boolean;
108
+ /**
109
+ * DELIBERATE LEAK: grouping metadata from the orchestrator that deployed the
110
+ * container (Docker Compose's `com.docker.compose.project` / `.service`
111
+ * labels, or an equivalent). Empty when the container was not deployed by one.
112
+ * Modelled because a 30-container host is unreadable without it; named after
113
+ * the concept rather than the label so another orchestrator can populate it.
114
+ */
115
+ composeProject: string;
116
+ composeService: string;
117
+ /**
118
+ * Whether THIS responder would presently permit `remove` on this container.
119
+ * NEVER IMPLIED BY controllable, and not a subset of it either: the two are
120
+ * matched against separate allow-lists, so a responder can be free with
121
+ * start/stop and remove nothing -- which is what every deployment that does
122
+ * not explicitly ask otherwise gets.
123
+ */
124
+ removable: boolean;
125
+ }
126
+ /**
127
+ * What `list` answers, published continuously instead of on request.
128
+ *
129
+ * PUBLISHED, NOT RETURNED. It is not a procedure at all, and could not be one:
130
+ * a continuous stream is pub/sub, and no `rpc` expresses it -- a caller cannot
131
+ * GET a stream. Adding this message changes no existing message's wire format.
132
+ *
133
+ * Procedures are not free, and this is why the stream is not one: a consumer
134
+ * cannot tell "this implementor predates the method" from "this implementor is
135
+ * unreachable", because zenoh returns no reply for either. container_control/v1
136
+ * is registered in messages/interfaces.yaml, so its procedure list is fixed and
137
+ * the next procedure costs a v2.
138
+ *
139
+ * Fields 1-3 deliberately carry the same numbers as ListContainersResponse.
140
+ * The two say the same three things, and a reader diffing them should see them
141
+ * line up rather than wonder whether the difference means something.
142
+ *
143
+ * ONE KEY PER HOST, never per container. A per-container key cannot express
144
+ * REMOVAL: a deleted container simply stops publishing, which is
145
+ * indistinguishable from a quiet one, a wedged publisher and a dead host, and
146
+ * recovering the difference needs tombstones plus a subscriber-side timeout. A
147
+ * whole-host snapshot expresses removal by omission, for free -- and "that
148
+ * container is gone" is exactly what an operator needs to see. `observed_at`
149
+ * and `control_enabled` are per-host facts besides, and per-container keys
150
+ * would repeat them N times and let them disagree.
151
+ *
152
+ * (logs went the other way, one key per container, because that gives Foxglove
153
+ * one channel per container to toggle. There is no equivalent value in
154
+ * splitting a state snapshot, whose whole point is that it is one instant.)
155
+ */
156
+ export interface ContainerHostStatus {
157
+ /**
158
+ * ALWAYS the complete set for this host, never a delta. A subscriber replaces
159
+ * its state wholesale; anything absent is gone.
160
+ */
161
+ containers: ContainerInfo[];
162
+ /** When the responder read the runtime's state. */
163
+ observedAt: Date | undefined;
164
+ /**
165
+ * False when this responder is read-only. Same meaning as on
166
+ * ListContainersResponse.
167
+ */
168
+ controlEnabled: boolean;
169
+ /**
170
+ * Why this sample was sent. A console showing "last change 4 minutes ago"
171
+ * next to "last heard 5 seconds ago" cannot derive that from the samples
172
+ * alone, because an unchanged heartbeat and a real change look identical.
173
+ */
174
+ trigger: ContainerStatusTrigger;
175
+ /**
176
+ * Monotonic within one process, restarting at 0. Distinguishes "the responder
177
+ * restarted" from "we missed a sample" sooner than a liveliness token flap
178
+ * does.
179
+ */
180
+ sequence: number;
181
+ /**
182
+ * False when this responder will remove nothing. Same meaning as on
183
+ * ListContainersResponse -- but NOT the same field number, and the deliberate
184
+ * 1-3 alignment above stops here: 4 and 5 were already spent on trigger and
185
+ * sequence, which the RPC response has no equivalent of. A visible break in
186
+ * the pattern beats a renumbering that changes what a shipped field means.
187
+ */
188
+ removeEnabled: boolean;
189
+ }
190
+ /**
191
+ * What one container is presently consuming, and what it was allowed.
192
+ *
193
+ * SEPARATE FROM ContainerInfo, DELIBERATELY. State is a step function -- a
194
+ * container is running or it is not -- so ContainerHostStatus publishes only
195
+ * when it changes and a subscriber can trust that a message means something
196
+ * moved. Utilisation changes on every sample by definition. Folding a CPU
197
+ * percentage into ContainerInfo would make every tick a change and collapse
198
+ * that distinction, turning a quiet subject into a fixed-rate firehose. Two
199
+ * messages on two subjects keep both properties, and let an operator subscribe
200
+ * to state on a thin link without paying for telemetry.
201
+ *
202
+ * UNSET IS NOT ZERO, throughout. Every quantity here that can fail to be
203
+ * KNOWABLE is `optional`, because "no reading" and "genuinely idle" are
204
+ * different facts and a dashboard that cannot tell them apart draws a flat
205
+ * green line over a gap. The cases that produce it are all ordinary, not
206
+ * exceptional: the first sample after a container appears has nothing to
207
+ * difference against; a restart zeroes the counters, so the tick after one has
208
+ * nothing valid to difference against either; a container sharing the host's
209
+ * network stack has no interface of its own to count; an unconstrained
210
+ * container has no limit to report; and a runtime may not account for block
211
+ * I/O at all.
212
+ *
213
+ * The converse matters as much: a value that WAS measured and came out zero is
214
+ * published as an explicit zero, and a consumer can tell the two apart. That is
215
+ * why the throttling counters below are optional despite zero being their
216
+ * normal reading.
217
+ */
218
+ export interface ContainerResourceUsage {
219
+ /** As in ContainerInfo: the name is the handle, the id is for correlation. */
220
+ name: string;
221
+ /**
222
+ * Also the responder's cache key for differencing. A recreate reuses the name
223
+ * but not the id, which is precisely why the id is the one that must be
224
+ * carried: differencing across a recreate would report a spike that never
225
+ * happened.
226
+ */
227
+ id: string;
228
+ /**
229
+ * Percent of ONE core, summed across cores: 250.0 is two and a half cores
230
+ * busy, and on a 16-core host the ceiling is 1600. Same convention as
231
+ * `docker stats` and as keelson's own process_cpu_load_pct.
232
+ *
233
+ * Unset on the first sample for a container, and unset again whenever the
234
+ * counters went backwards (a restart in place resets them). Both are the
235
+ * absence of a defensible number, not a zero.
236
+ */
237
+ cpuLoadPct?: number | undefined;
238
+ /**
239
+ * How many cores that percentage was measured against, and the divisor a
240
+ * consumer needs to normalise it to 0-100. Unset when the runtime reported
241
+ * neither a core count nor a per-core list -- a bare percentage with no
242
+ * denominator is unreadable across a mixed fleet, and guessing 0 makes the
243
+ * normalisation divide by zero rather than fail visibly.
244
+ */
245
+ onlineCpus?: number | undefined;
246
+ /**
247
+ * The working set: total usage minus reclaimable file cache, which is what
248
+ * `docker stats` shows and what an operator is asking about. Raw usage counts
249
+ * page cache the kernel would drop under pressure, and reads as a slow leak
250
+ * in any container that has touched a lot of files.
251
+ */
252
+ memoryUsedBytes: number;
253
+ /**
254
+ * The ceiling memory_used_bytes is measured against, and the memory half of
255
+ * "what it was allowed". Unset when the container is unconstrained: the
256
+ * Engine reports the host's total memory as the limit in that case, which
257
+ * would otherwise render as every container on the box using 3% "of its
258
+ * limit" -- a number that looks meaningful and is not. Read from the
259
+ * container's configuration rather than trusted from the counters, because
260
+ * "no limit configured" is the fact being reported and the counters cannot
261
+ * express it.
262
+ */
263
+ memoryLimitBytes?: number | undefined;
264
+ /**
265
+ * OF THE LIMIT WHEN THERE IS ONE, OF HOST TOTAL WHEN THERE IS NOT -- so what
266
+ * this percentage is a percentage OF depends on whether memory_limit_bytes
267
+ * above is present. That is docker's own convention and the number in its
268
+ * MEM% column, kept deliberately, but a consumer MUST label the two cases
269
+ * differently ("of limit" / "of host") or a fleet table shows two
270
+ * incomparable percentages in one column and nobody notices.
271
+ *
272
+ * The asymmetry is admitted rather than hidden: the throttling counters below
273
+ * are optional precisely so that one field's presence does not silently
274
+ * change another's meaning, and this field does exactly that. It survives
275
+ * because dropping it would lose the docker-comparable number for the
276
+ * unconstrained containers that are the overwhelming majority, and because
277
+ * the denominator is recoverable -- memory_used_bytes divided by this,
278
+ * times 100. If it is ever split, split it into two fields rather than
279
+ * adding a flag.
280
+ */
281
+ memoryUsedPct?: number | undefined;
282
+ /**
283
+ * Summed over the container's interfaces. All four unset for a container on
284
+ * the host network stack (`network_mode: host`), which has no interfaces of
285
+ * its own -- the Engine omits the counters entirely rather than zeroing them,
286
+ * and so does this.
287
+ */
288
+ networkRxBytes?: number | undefined;
289
+ networkTxBytes?: number | undefined;
290
+ networkRxBytesPerSecond?: number | undefined;
291
+ networkTxBytesPerSecond?: number | undefined;
292
+ /**
293
+ * Cumulative since the container started. Unset -- not zero -- when the
294
+ * runtime reported no block accounting at all, which some storage drivers do
295
+ * not: "this container has written nothing" and "nobody counted" are
296
+ * different facts, and the rates below already draw that distinction.
297
+ */
298
+ blockReadBytes?: number | undefined;
299
+ blockWriteBytes?: number | undefined;
300
+ blockReadBytesPerSecond?: number | undefined;
301
+ blockWriteBytesPerSecond?: number | undefined;
302
+ pidsCurrent: number;
303
+ /** Unset when no PID limit applies. */
304
+ pidsLimit?: number | undefined;
305
+ /**
306
+ * WHAT IT WAS ALLOWED, from the container's configuration rather than its
307
+ * usage, and the other half of the question "is it busy?". 90% of a half-core
308
+ * quota and 90% of sixteen cores are different emergencies, and a consumer
309
+ * cannot tell them apart from utilisation alone. Unset means unconstrained,
310
+ * which is the common case.
311
+ *
312
+ * Cores, not percent: 0.5 is half a core. Carries --cpus (NanoCpus) when set,
313
+ * otherwise the CpuQuota/CpuPeriod pair that expresses the same thing.
314
+ */
315
+ cpuAllocationCores?: number | undefined;
316
+ /**
317
+ * Relative weight under contention, not a ceiling. Only meaningful next to
318
+ * other containers' shares on the same host.
319
+ */
320
+ cpuShares?: number | undefined;
321
+ /**
322
+ * Empty when unpinned. Kept as the Engine's own spelling ("0-3,8") rather
323
+ * than expanded to a count, because "pinned to four particular cores" and
324
+ * "allowed four cores" are different constraints and the count loses which.
325
+ */
326
+ cpusetCpus: string;
327
+ /**
328
+ * Cumulative CFS throttling. This is the counter that says a CPU limit is
329
+ * actually biting: high cpu_load_pct with throttled_periods climbing is a
330
+ * container being starved, the same percentage without it is one merely
331
+ * working hard. Read next to cpu_allocation_cores, which says whether there
332
+ * is a limit for it to be biting against.
333
+ *
334
+ * OPTIONAL, and that is the whole point of them. Zero throttling is the
335
+ * normal, healthy reading, so a plain proto3 uint64 would omit these from the
336
+ * wire on exactly the containers that are fine -- and a consumer decoding a
337
+ * hard 0 could not tell "measured, never throttled" from "this runtime does
338
+ * not report throttling". The most useful field on the message would be the
339
+ * one field whose absence says nothing. Set together, whenever the runtime
340
+ * reports the counters at all.
341
+ */
342
+ cpuThrottledPeriods?: number | undefined;
343
+ /**
344
+ * Nanoseconds, cumulative. Crosses JavaScript's Number.MAX_SAFE_INTEGER after
345
+ * roughly 104 days of ACCUMULATED throttled time -- reachable only by a
346
+ * container that has been in trouble for months, but reachable. A JS consumer
347
+ * decoding this as a Number loses precision there; decoding it as a Long is
348
+ * worse, because a Long that crosses a postMessage boundary arithmetics as
349
+ * NaN. Prefer the precision loss.
350
+ */
351
+ cpuThrottledTimeNs?: number | undefined;
352
+ /**
353
+ * How wide a window the per-second rates were derived over. Not assumed from
354
+ * the configured interval: a tick the runtime was slow to answer produces a
355
+ * wider one, and a consumer averaging or integrating these needs the real
356
+ * figure. Unset exactly when the rates are.
357
+ */
358
+ sampleWindowS?: number | undefined;
359
+ }
360
+ /**
361
+ * Resource utilisation for every sampled container on one host.
362
+ *
363
+ * ONE KEY PER HOST, for the same reason ContainerHostStatus is -- see its
364
+ * comment -- with one difference worth stating: this set is the containers that
365
+ * were RUNNING and matched the responder's glob, not every container on the
366
+ * host. Absence here therefore means "not sampled", which is not the same claim
367
+ * ContainerHostStatus's absence makes ("gone"). A consumer wanting the
368
+ * authoritative set reads that subject; this one answers "what is it costing".
369
+ */
370
+ export interface ContainerHostStats {
371
+ containers: ContainerResourceUsage[];
372
+ /** When the responder finished reading the runtime's counters. */
373
+ observedAt: Date | undefined;
374
+ /**
375
+ * Monotonic within one process, restarting at 0. Same meaning as on
376
+ * ContainerHostStatus. There is no `trigger` here: every sample is new by
377
+ * definition, so there is no change/keep-alive distinction to draw.
378
+ */
379
+ sequence: number;
380
+ }
381
+ export declare const ContainerInfo: MessageFns<ContainerInfo>;
382
+ export declare const ContainerHostStatus: MessageFns<ContainerHostStatus>;
383
+ export declare const ContainerResourceUsage: MessageFns<ContainerResourceUsage>;
384
+ export declare const ContainerHostStats: MessageFns<ContainerHostStats>;
385
+ type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
386
+ export type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
387
+ [K in keyof T]?: DeepPartial<T[K]>;
388
+ } : Partial<T>;
389
+ type KeysOfUnion<T> = T extends T ? keyof T : never;
390
+ export type Exact<P, I extends P> = P extends Builtin ? P : P & {
391
+ [K in keyof P]: Exact<P[K], I[K]>;
392
+ } & {
393
+ [K in Exclude<keyof I, KeysOfUnion<P>>]: never;
394
+ };
395
+ export interface MessageFns<T> {
396
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
397
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
398
+ fromJSON(object: any): T;
399
+ toJSON(message: T): unknown;
400
+ create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
401
+ fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
402
+ }
403
+ export {};