@toa.io/extensions.cadence 1.0.0-alpha.324 → 1.0.0-alpha.326
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/cadence.metronome/operations/delay.js +4 -22
- package/package.json +5 -5
- package/readme.md +131 -9
- package/transpiled/Factory.d.ts +1 -1
- package/transpiled/Factory.js +22 -8
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Local.js +6 -1
- package/transpiled/Local.js.map +1 -1
- package/transpiled/Schedule.d.ts +73 -0
- package/transpiled/Schedule.js +149 -0
- package/transpiled/Schedule.js.map +1 -0
- package/transpiled/index.d.ts +2 -1
- package/transpiled/index.js +1 -0
- package/transpiled/index.js.map +1 -1
- package/transpiled/row.d.ts +29 -0
- package/transpiled/row.js +32 -0
- package/transpiled/row.js.map +1 -0
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { row } from '@toa.io/extensions.cadence'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A call to make later. The request carries no query, so the transition is handed a new object.
|
|
5
|
-
*
|
|
6
|
-
* The lane is random, not one this replica owns. An outbox row is written into an owned lane so
|
|
7
|
-
* that in steady state a replica settles its own rows before it ever reads them — but a delayed
|
|
8
|
-
* row has no immediate path to settle, so nothing is gained, and an even spread is what the
|
|
9
|
-
* dispatchers want.
|
|
5
|
+
* What is stored of it is what `row` says, which a schedule stores as well.
|
|
10
6
|
*/
|
|
11
7
|
export function transition(input, entry) {
|
|
12
|
-
const
|
|
8
|
+
const { endpoint, interval, overdue, request, trail } = input
|
|
13
9
|
|
|
14
|
-
entry
|
|
15
|
-
entry.due = due
|
|
16
|
-
entry.endpoint = input.endpoint
|
|
17
|
-
|
|
18
|
-
// absolute, because the bound is the caller's and a scan reads rows of many callers at once.
|
|
19
|
-
// No bound is the end of representable time rather than an absent field, so that one
|
|
20
|
-
// comparison answers for every row
|
|
21
|
-
entry.expires = input.overdue === null ? Number.MAX_SAFE_INTEGER : due + input.overdue
|
|
22
|
-
|
|
23
|
-
// a call that takes no request has none: the entity's `request` is an object where it is
|
|
24
|
-
// there at all, and a null would not fit it
|
|
25
|
-
if (input.request !== undefined) entry.request = input.request
|
|
26
|
-
|
|
27
|
-
// the chain that asked for the call, absent where the caller unchained it
|
|
28
|
-
if (input.trail !== undefined) entry.trail = input.trail
|
|
10
|
+
Object.assign(entry, row({ endpoint, due: Date.now() + interval, overdue, request, trail }))
|
|
29
11
|
|
|
30
12
|
return entry.id
|
|
31
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/extensions.cadence",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.326",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa Schedule",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
"!**/.env*"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
30
|
-
"@toa.io/definitions": "1.0.0-alpha.
|
|
29
|
+
"@toa.io/core": "1.0.0-alpha.326",
|
|
30
|
+
"@toa.io/definitions": "1.0.0-alpha.326",
|
|
31
31
|
"@toa.io/generic": "1.0.0-alpha.311",
|
|
32
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
32
|
+
"@toa.io/schemas": "1.0.0-alpha.326",
|
|
33
33
|
"openspan": "1.0.0-alpha.323"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"transpile": "tsc"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "60f11bdc805d0c39bcb06064b2218b11114a35ad"
|
|
39
39
|
}
|
package/readme.md
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Calls a component makes to itself, and calls it puts off.
|
|
4
4
|
|
|
5
|
-
A **pulse** calls an operation of its own component on a cadence its manifest states. A
|
|
6
|
-
hands one call over to be made
|
|
5
|
+
A **pulse** calls an operation of its own component on a cadence its manifest states. A
|
|
6
|
+
**schedule** calls one at the moments of a calendar. A **delay** hands one call over to be made
|
|
7
|
+
later.
|
|
7
8
|
|
|
8
|
-
A delay, and a pulse that is the component's rather than each replica's, rest on
|
|
9
|
+
A delay, a schedule, and a pulse that is the component's rather than each replica's, rest on
|
|
9
10
|
[`atomicity`](/connectors/atomicity), which is what keeps two replicas from making one call and
|
|
10
|
-
where that promise is written down. Where it is not configured,
|
|
11
|
+
where that promise is written down. Where it is not configured, none of them is made at all. A pulse
|
|
11
12
|
declared [`scope: replica`](#in-one-replica-or-in-every-one) asks nothing of it and is made
|
|
12
13
|
either way.
|
|
13
14
|
|
|
@@ -48,8 +49,8 @@ for.
|
|
|
48
49
|
|
|
49
50
|
A cycle is measured from the Unix epoch rather than from a calendar, so `86400` means UTC
|
|
50
51
|
midnight and `3600` the top of the hour. A cycle that is not a divisor of a day lands on a
|
|
51
|
-
boundary that is not any particular time of day.
|
|
52
|
-
|
|
52
|
+
boundary that is not any particular time of day. A time of day in a time zone, a day of the week
|
|
53
|
+
or of the month is what a [schedule](#schedule) states.
|
|
53
54
|
|
|
54
55
|
### In one replica, or in every one
|
|
55
56
|
|
|
@@ -88,8 +89,8 @@ what in its share is still due, rather than everything in its share, and a misse
|
|
|
88
89
|
a delay instead of a day. An operation written the other way loses a day's work to a
|
|
89
90
|
thirty-second rollout.
|
|
90
91
|
|
|
91
|
-
Where a call has to happen even if nothing was running when it came due, it
|
|
92
|
-
schedule
|
|
92
|
+
Where a call has to happen even if nothing was running when it came due, it has to be stored: a
|
|
93
|
+
[schedule](#schedule) is, and so is a [`delay`](#delay).
|
|
93
94
|
|
|
94
95
|
**An interval is called once.** Two calls for one interval need the clocks of two machines to
|
|
95
96
|
disagree and the work to change hands in the same moment, which is a window the width of that
|
|
@@ -115,6 +116,126 @@ the day's calls: the cycle is spread over _time_, not over the fleet. Replicas b
|
|
|
115
116
|
make none. How much of the fleet calls is what [`scope`](#in-one-replica-or-in-every-one) says,
|
|
116
117
|
and it is the only thing that says it.
|
|
117
118
|
|
|
119
|
+
## Schedule
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
# manifest.toa.yaml
|
|
123
|
+
cadence:
|
|
124
|
+
report: 0 12 * * 1-5 # noon UTC on weekdays
|
|
125
|
+
digest:
|
|
126
|
+
schedule: 0 9 * * 1 # nine on Monday morning
|
|
127
|
+
zone: Europe/Berlin # in Berlin
|
|
128
|
+
overdue: 3600 # seconds it may be late and still be made
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
A string is a schedule, where a number is a [pulse](#pulse). It is a cron expression of five
|
|
132
|
+
fields — minute, hour, day of the month, month, day of the week — or of six, where the first is
|
|
133
|
+
seconds.
|
|
134
|
+
|
|
135
|
+
An expression that begins with `*` is quoted, because YAML reads a leading `*` as its own:
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
# manifest.toa.yaml
|
|
139
|
+
cadence:
|
|
140
|
+
poll: '*/5 * * * *' # every five minutes
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
| | |
|
|
144
|
+
| ---------- | ----------------------------------------------------------------------------- |
|
|
145
|
+
| `schedule` | the cron expression |
|
|
146
|
+
| `zone` | the [time zone](https://www.iana.org/time-zones) it is read in, `UTC` by default |
|
|
147
|
+
| `overdue` | seconds a call may be late and still be made; until the next one by default |
|
|
148
|
+
| `region` | the region that makes it; see [Regions](#in-one-region-or-in-every-one) |
|
|
149
|
+
|
|
150
|
+
The operation is called with the moment the call was scheduled for:
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
async function report({ at }, context) {}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`at` is milliseconds since the epoch, and it is the moment of the schedule rather than of the
|
|
157
|
+
call: a report made twenty minutes late is still the report for noon. Compute the period from it
|
|
158
|
+
and not from the clock.
|
|
159
|
+
|
|
160
|
+
A schedule may name any operation a pulse may.
|
|
161
|
+
|
|
162
|
+
### What to expect
|
|
163
|
+
|
|
164
|
+
Each occurrence is a [delayed call](#delay), handed over ahead of its moment, and what a delayed
|
|
165
|
+
call [may and may not do](#what-to-expect-2) holds for it.
|
|
166
|
+
|
|
167
|
+
**A call is made late rather than lost.** An occurrence is stored as soon as the one before it
|
|
168
|
+
comes due, so a component that is down at noon is called when it is back, with the `at` of noon.
|
|
169
|
+
|
|
170
|
+
**It is not made once the next one is due.** At most one occurrence is ever owed: a component
|
|
171
|
+
that was away from Friday to Monday is called for Monday and not for Friday, and an outage of a
|
|
172
|
+
month does not come back as thirty calls. `overdue` states a tighter bound, in seconds, and is
|
|
173
|
+
subject to [`discreteness`](#discreteness) as a delay's is.
|
|
174
|
+
|
|
175
|
+
**An occurrence is lost where the component did not run at all** between the one before it and
|
|
176
|
+
its own moment. Nothing was there to store it.
|
|
177
|
+
|
|
178
|
+
**A call may be made more than once**, so where a second one would cost something the operation
|
|
179
|
+
refuses it by `at`.
|
|
180
|
+
|
|
181
|
+
**Nothing keeps a call from being made while the one before it is running.** A pulse skips an
|
|
182
|
+
interval its work did not fit in; a schedule does not.
|
|
183
|
+
|
|
184
|
+
**A schedule changed or removed may be called once more**, at the moment it had already been
|
|
185
|
+
stored for.
|
|
186
|
+
|
|
187
|
+
**Seconds are served as coarsely as delays are.** An occurrence closer than `discreteness` to the
|
|
188
|
+
one before it is made at the next pass rather than at its moment.
|
|
189
|
+
|
|
190
|
+
## Several entries
|
|
191
|
+
|
|
192
|
+
An operation takes a list where it has more than one entry, of either kind:
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
# manifest.toa.yaml
|
|
196
|
+
cadence:
|
|
197
|
+
sweep:
|
|
198
|
+
- cycle: 3600 # what is due, every hour
|
|
199
|
+
- schedule: 0 3 1 * * # and everything, on the first of the month
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The entries are independent of one another, and nothing keeps their calls apart. An operation
|
|
203
|
+
given both kinds is called with `{ n, i }` by one and `{ at }` by the other. Two schedules of one
|
|
204
|
+
operation that fall on the same moment are one call.
|
|
205
|
+
|
|
206
|
+
## In one region, or in every one
|
|
207
|
+
|
|
208
|
+
An application deployed as [several regions](/extensions/convergence) has every region make every
|
|
209
|
+
pulse and every schedule, each for itself. An entry of either kind that is work for the whole
|
|
210
|
+
application names the region that makes it, by rank:
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
# manifest.toa.yaml
|
|
214
|
+
cadence:
|
|
215
|
+
sweep:
|
|
216
|
+
cycle: 3600
|
|
217
|
+
region: 0 # a pulse only region 0 makes
|
|
218
|
+
invoice:
|
|
219
|
+
schedule: 0 6 1 * *
|
|
220
|
+
region: 0 # a schedule made once, however many regions there are
|
|
221
|
+
digest:
|
|
222
|
+
- schedule: 0 9 * * 1
|
|
223
|
+
zone: Europe/Berlin
|
|
224
|
+
region: 0
|
|
225
|
+
- schedule: 0 9 * * 1
|
|
226
|
+
zone: America/New_York
|
|
227
|
+
region: 1
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**An entry that names a rank no region has is never made**, and nothing says so: a deployment
|
|
231
|
+
reads only its own declaration. An application that is one place is rank `0`.
|
|
232
|
+
|
|
233
|
+
A region that is gone has its entries made by the one that
|
|
234
|
+
[takes its delayed calls over](#regions), by the same setting.
|
|
235
|
+
|
|
236
|
+
`region` is not stated beside `scope: replica`: what lives in a process is that process's own in
|
|
237
|
+
every region.
|
|
238
|
+
|
|
118
239
|
## Delay
|
|
119
240
|
|
|
120
241
|
```javascript
|
|
@@ -288,7 +409,8 @@ for the call, and a deployment makes the calls of the region it is. So there is
|
|
|
288
409
|
for this to be right, and a single-region application never meets it.
|
|
289
410
|
|
|
290
411
|
**`regions` is how a region that is gone is taken over.** Redeploy a surviving region naming the
|
|
291
|
-
lost one's rank beside its own, and it makes both
|
|
412
|
+
lost one's rank beside its own, and it makes both — the delayed calls of the lost region, and the
|
|
413
|
+
pulses and schedules [declared for it](#in-one-region-or-in-every-one):
|
|
292
414
|
|
|
293
415
|
```yaml
|
|
294
416
|
cadence:
|
package/transpiled/Factory.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare class Factory implements extensions.Factory {
|
|
|
7
7
|
constructor(host: Host);
|
|
8
8
|
/**
|
|
9
9
|
* What runs beside a component. Its own component gets the dispatcher; everyone else gets a
|
|
10
|
-
* timer per
|
|
10
|
+
* timer per entry they declared.
|
|
11
11
|
*
|
|
12
12
|
* A tenant is created before the components of its composition are, which is why nothing here
|
|
13
13
|
* reaches for one.
|
package/transpiled/Factory.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Connector, Locator } from '@toa.io/core';
|
|
2
2
|
import { Composition } from './Composition.js';
|
|
3
|
-
import { COMPONENT, NAMESPACE } from '@toa.io/definitions/extensions.cadence';
|
|
3
|
+
import { COMPONENT, NAMESPACE, regions } from '@toa.io/definitions/extensions.cadence';
|
|
4
4
|
import { Aspect } from './Aspect.js';
|
|
5
5
|
import { Dispatcher } from './Dispatcher.js';
|
|
6
6
|
import { Local } from './Local.js';
|
|
7
7
|
import { Pulse } from './Pulse.js';
|
|
8
|
+
import { Schedule } from './Schedule.js';
|
|
8
9
|
export class Factory {
|
|
9
10
|
host;
|
|
10
11
|
/** one per component, however much of this extension calls through it */
|
|
@@ -14,7 +15,7 @@ export class Factory {
|
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* What runs beside a component. Its own component gets the dispatcher; everyone else gets a
|
|
17
|
-
* timer per
|
|
18
|
+
* timer per entry they declared.
|
|
18
19
|
*
|
|
19
20
|
* A tenant is created before the components of its composition are, which is why nothing here
|
|
20
21
|
* reaches for one.
|
|
@@ -23,19 +24,32 @@ export class Factory {
|
|
|
23
24
|
if (mine(locator))
|
|
24
25
|
return new Dispatcher(this.metronome(), (target) => this.local(target), this.host.atom(locator.id));
|
|
25
26
|
const tenant = new Connector();
|
|
26
|
-
const
|
|
27
|
+
const declared = declaration;
|
|
27
28
|
// `cadence: ~` is a component that only delays calls
|
|
28
|
-
if (
|
|
29
|
+
if (declared === null)
|
|
30
|
+
return tenant;
|
|
31
|
+
/*
|
|
32
|
+
* An entry that names a region is made by the deployment of that rank and by no other —
|
|
33
|
+
* and by one that was told to make the work of a region that is gone. One that names none
|
|
34
|
+
* is every region's own.
|
|
35
|
+
*/
|
|
36
|
+
const ranks = regions();
|
|
37
|
+
const entries = Object.entries(declared).flatMap(([endpoint, list]) => list
|
|
38
|
+
.filter((entry) => entry.region === undefined || ranks.includes(entry.region))
|
|
39
|
+
.map((entry) => ({ endpoint, entry })));
|
|
40
|
+
if (entries.length === 0)
|
|
29
41
|
return tenant;
|
|
30
42
|
const local = this.local(locator);
|
|
31
|
-
const entries = Object.entries(pulses);
|
|
32
43
|
// a pulse every replica makes asks nobody whether it may, so a component whose pulses are
|
|
33
44
|
// all of that kind decides nothing with its replicas and needs no atom at all
|
|
34
|
-
const atom = entries.some((
|
|
45
|
+
const atom = entries.some(({ entry }) => 'cycle' in entry && entry.scope === 'group')
|
|
35
46
|
? this.host.atom(locator.id)
|
|
36
47
|
: undefined;
|
|
37
|
-
for (const
|
|
38
|
-
|
|
48
|
+
for (const { endpoint, entry } of entries)
|
|
49
|
+
if ('schedule' in entry)
|
|
50
|
+
tenant.depends(new Schedule({ locator, endpoint, ...entry }, this.metronome()));
|
|
51
|
+
else
|
|
52
|
+
tenant.depends(new Pulse({ locator, endpoint, cycle: entry.cycle, intervals: entry.intervals }, local, entry.scope === 'group' ? atom : undefined));
|
|
39
53
|
return tenant;
|
|
40
54
|
}
|
|
41
55
|
/** `context.delay` — for every component but the one that keeps the calls. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAA;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAIxC,MAAM,OAAO,OAAO;IACD,IAAI,CAAM;IAE3B,yEAAyE;IACxD,MAAM,GAA0B,EAAE,CAAA;IAEnD,YAAmB,IAAU;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,OAAgB,EAAE,WAAmB;QACjD,IAAI,IAAI,CAAC,OAAO,CAAC;YACf,OAAO,IAAI,UAAU,CACnB,IAAI,CAAC,SAAS,EAAE,EAChB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAC3B,CAAA;QAEH,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAA;QAC9B,MAAM,QAAQ,GAAG,WAAiC,CAAA;QAElD,qDAAqD;QACrD,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,MAAM,CAAA;QAEpC;;;;WAIG;QACH,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;QAEvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CACpE,IAAI;aACD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;aAC7E,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CACzC,CAAA;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAA;QAEvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAEjC,0FAA0F;QAC1F,8EAA8E;QAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC;YACnF,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,CAAC,CAAC,SAAS,CAAA;QAEb,KAAK,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,OAAO;YACvC,IAAI,UAAU,IAAI,KAAK;gBACrB,MAAM,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;;gBAE/E,MAAM,CAAC,OAAO,CACZ,IAAI,KAAK,CACP,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EACrE,KAAK,EACL,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAC3C,CACF,CAAA;QAEL,OAAO,MAAM,CAAA;IACf,CAAC;IAED,8EAA8E;IACvE,MAAM,CAAC,OAAgB;QAC5B,IAAI,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAA;QAE5B,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IACrC,CAAC;IAED,4DAA4D;IACrD,OAAO;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC/D,CAAC;IAEO,SAAS;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAA;IACtD,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAgB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAErC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ;YACvC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEzD,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC;CACF;AAED,4FAA4F;AAC5F,SAAS,IAAI,CAAC,OAAgB;IAC5B,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS,CAAA;AACxC,CAAC"}
|
package/transpiled/Local.js
CHANGED
|
@@ -16,7 +16,12 @@ export class Local extends Connector {
|
|
|
16
16
|
this.locator = locator;
|
|
17
17
|
}
|
|
18
18
|
async invoke(endpoint, request) {
|
|
19
|
-
|
|
19
|
+
// one that could not be found is looked for again by the next call, rather than kept as
|
|
20
|
+
// the answer: what hands a schedule over calls as it boots, and goes on calling
|
|
21
|
+
this.remote ??= this.locate().catch((error) => {
|
|
22
|
+
this.remote = undefined;
|
|
23
|
+
throw error;
|
|
24
|
+
});
|
|
20
25
|
return await (await this.remote).invoke(endpoint, request);
|
|
21
26
|
}
|
|
22
27
|
async locate() {
|
package/transpiled/Local.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Local.js","sourceRoot":"","sources":["../source/Local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAKxC;;;;;;GAMG;AACH,MAAM,OAAO,KAAM,SAAQ,SAAS;IACjB,IAAI,CAAM;IACV,OAAO,CAAS;IACzB,MAAM,CAAkB;IAEhC,YAAmB,IAAU,EAAE,OAAgB;QAC7C,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,OAAgB;QACpD,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"Local.js","sourceRoot":"","sources":["../source/Local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAKxC;;;;;;GAMG;AACH,MAAM,OAAO,KAAM,SAAQ,SAAS;IACjB,IAAI,CAAM;IACV,OAAO,CAAS;IACzB,MAAM,CAAkB;IAEhC,YAAmB,IAAU,EAAE,OAAgB;QAC7C,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,OAAgB;QACpD,wFAAwF;QACxF,gFAAgF;QAChF,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACrD,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;YAEvB,MAAM,KAAK,CAAA;QACb,CAAC,CAAC,CAAA;QAEF,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC5D,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAE3D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAEpB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QAEtB,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,mFAAmF;AACnF,MAAM,MAAM,GAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { Local } from './Local.ts';
|
|
3
|
+
import type { Locator } from '@toa.io/core';
|
|
4
|
+
/**
|
|
5
|
+
* A call to an operation of its own component at the moments its manifest states, as a cron
|
|
6
|
+
* expression.
|
|
7
|
+
*
|
|
8
|
+
* It makes no call. Each occurrence is a delayed call, and what runs here keeps the next one
|
|
9
|
+
* stored: at boot, and each time the one stored comes due. What makes it is what makes every
|
|
10
|
+
* delayed call, so it is made where nothing is running at its moment — which a pulse, storing
|
|
11
|
+
* nothing, cannot offer.
|
|
12
|
+
*
|
|
13
|
+
* Every replica stores every occurrence, and one row is kept: the id is derived from what the
|
|
14
|
+
* occurrence is, so nothing here asks which replica's it is. And nothing here is a chain. An
|
|
15
|
+
* occurrence is a function of the expression and the clock, so one that was not made, or not
|
|
16
|
+
* stored, has no bearing on the one after it.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Schedule extends Connector {
|
|
19
|
+
private readonly endpoint;
|
|
20
|
+
private readonly label;
|
|
21
|
+
private readonly next;
|
|
22
|
+
/** milliseconds a call may be late, or absent where it is owed until the next one is due */
|
|
23
|
+
private readonly overdue?;
|
|
24
|
+
/** whose occurrence this is: the region the entry names, or the one this deployment is */
|
|
25
|
+
private readonly region;
|
|
26
|
+
private readonly metronome;
|
|
27
|
+
/** the occurrence being waited for, which is the one last handed over */
|
|
28
|
+
private at?;
|
|
29
|
+
private timer?;
|
|
30
|
+
private retry?;
|
|
31
|
+
private closing;
|
|
32
|
+
/** Whether the process has been told to go quiet, see `stop`. */
|
|
33
|
+
private quiesced;
|
|
34
|
+
constructor(definition: Definition, metronome: Local);
|
|
35
|
+
protected open(): Promise<void>;
|
|
36
|
+
protected close(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Stops storing. What is stored stays stored, and is made when the deployment is working
|
|
39
|
+
* again as any delayed call is; an occurrence that came due unstored while the process was
|
|
40
|
+
* quiet is not made up.
|
|
41
|
+
*/
|
|
42
|
+
protected pause(): Promise<void>;
|
|
43
|
+
protected unpause(): void;
|
|
44
|
+
private disarm;
|
|
45
|
+
/**
|
|
46
|
+
* Hands over the next occurrence once the one waited for has come due, and waits again.
|
|
47
|
+
* Always the next one from now and never one already past: whether that one was made is not
|
|
48
|
+
* something this can know.
|
|
49
|
+
*/
|
|
50
|
+
private tick;
|
|
51
|
+
/**
|
|
52
|
+
* The row a delayed call is, found or made under an id that is the occurrence's own — so every
|
|
53
|
+
* replica of the component, and every boot of one, stores the same row.
|
|
54
|
+
*
|
|
55
|
+
* It begins a chain of its own, as what is called from the clock does: no trail is stored.
|
|
56
|
+
*/
|
|
57
|
+
private store;
|
|
58
|
+
/**
|
|
59
|
+
* How late the call may be. Owed until the next occurrence is due where nothing states a
|
|
60
|
+
* bound, so at most one is ever waiting and an outage does not come back as every call it
|
|
61
|
+
* covered; with no bound only where nothing ever comes after it.
|
|
62
|
+
*/
|
|
63
|
+
private late;
|
|
64
|
+
}
|
|
65
|
+
export interface Definition {
|
|
66
|
+
locator: Locator;
|
|
67
|
+
endpoint: string;
|
|
68
|
+
schedule: string;
|
|
69
|
+
zone: string;
|
|
70
|
+
/** seconds */
|
|
71
|
+
overdue?: number;
|
|
72
|
+
region?: number;
|
|
73
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { console } from 'openspan';
|
|
2
|
+
import { Connector, entities } from '@toa.io/core';
|
|
3
|
+
import { occurrences, rank } from '@toa.io/definitions/extensions.cadence';
|
|
4
|
+
import { row } from './row.js';
|
|
5
|
+
/**
|
|
6
|
+
* A call to an operation of its own component at the moments its manifest states, as a cron
|
|
7
|
+
* expression.
|
|
8
|
+
*
|
|
9
|
+
* It makes no call. Each occurrence is a delayed call, and what runs here keeps the next one
|
|
10
|
+
* stored: at boot, and each time the one stored comes due. What makes it is what makes every
|
|
11
|
+
* delayed call, so it is made where nothing is running at its moment — which a pulse, storing
|
|
12
|
+
* nothing, cannot offer.
|
|
13
|
+
*
|
|
14
|
+
* Every replica stores every occurrence, and one row is kept: the id is derived from what the
|
|
15
|
+
* occurrence is, so nothing here asks which replica's it is. And nothing here is a chain. An
|
|
16
|
+
* occurrence is a function of the expression and the clock, so one that was not made, or not
|
|
17
|
+
* stored, has no bearing on the one after it.
|
|
18
|
+
*/
|
|
19
|
+
export class Schedule extends Connector {
|
|
20
|
+
endpoint;
|
|
21
|
+
label;
|
|
22
|
+
next;
|
|
23
|
+
/** milliseconds a call may be late, or absent where it is owed until the next one is due */
|
|
24
|
+
overdue;
|
|
25
|
+
/** whose occurrence this is: the region the entry names, or the one this deployment is */
|
|
26
|
+
region;
|
|
27
|
+
metronome;
|
|
28
|
+
/** the occurrence being waited for, which is the one last handed over */
|
|
29
|
+
at;
|
|
30
|
+
timer;
|
|
31
|
+
retry;
|
|
32
|
+
closing = false;
|
|
33
|
+
/** Whether the process has been told to go quiet, see `stop`. */
|
|
34
|
+
quiesced = false;
|
|
35
|
+
constructor(definition, metronome) {
|
|
36
|
+
super();
|
|
37
|
+
const { locator, endpoint, schedule, zone, overdue, region } = definition;
|
|
38
|
+
this.endpoint = `${locator.id}.${endpoint}`;
|
|
39
|
+
this.label = `${this.endpoint} schedule`;
|
|
40
|
+
this.next = occurrences(schedule, zone);
|
|
41
|
+
this.region = region ?? rank();
|
|
42
|
+
this.metronome = metronome;
|
|
43
|
+
if (overdue !== undefined)
|
|
44
|
+
this.overdue = overdue * 1000;
|
|
45
|
+
this.depends(metronome);
|
|
46
|
+
}
|
|
47
|
+
async open() {
|
|
48
|
+
this.tick();
|
|
49
|
+
}
|
|
50
|
+
async close() {
|
|
51
|
+
this.closing = true;
|
|
52
|
+
this.disarm();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Stops storing. What is stored stays stored, and is made when the deployment is working
|
|
56
|
+
* again as any delayed call is; an occurrence that came due unstored while the process was
|
|
57
|
+
* quiet is not made up.
|
|
58
|
+
*/
|
|
59
|
+
async pause() {
|
|
60
|
+
this.quiesced = true;
|
|
61
|
+
this.disarm();
|
|
62
|
+
}
|
|
63
|
+
unpause() {
|
|
64
|
+
this.quiesced = false;
|
|
65
|
+
this.at = undefined;
|
|
66
|
+
this.tick();
|
|
67
|
+
}
|
|
68
|
+
disarm() {
|
|
69
|
+
clearTimeout(this.timer);
|
|
70
|
+
clearTimeout(this.retry);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Hands over the next occurrence once the one waited for has come due, and waits again.
|
|
74
|
+
* Always the next one from now and never one already past: whether that one was made is not
|
|
75
|
+
* something this can know.
|
|
76
|
+
*/
|
|
77
|
+
tick() {
|
|
78
|
+
if (this.closing || this.quiesced)
|
|
79
|
+
return;
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
// a timer may wake a millisecond before the moment it was set for, and a moment further
|
|
82
|
+
// out than one timer reaches is woken for several times on the way
|
|
83
|
+
let at = this.at;
|
|
84
|
+
if (at === undefined || now >= at) {
|
|
85
|
+
const next = this.next(now);
|
|
86
|
+
// an expression whose last moment has passed
|
|
87
|
+
if (next === null)
|
|
88
|
+
return;
|
|
89
|
+
at = this.at = next;
|
|
90
|
+
void this.store(at);
|
|
91
|
+
}
|
|
92
|
+
// a delay past this fires at once rather than late, so a long wait is taken in instalments
|
|
93
|
+
this.timer = setTimeout(() => this.tick(), Math.min(at - now, MAX_DELAY));
|
|
94
|
+
this.timer.unref();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The row a delayed call is, found or made under an id that is the occurrence's own — so every
|
|
98
|
+
* replica of the component, and every boot of one, stores the same row.
|
|
99
|
+
*
|
|
100
|
+
* It begins a chain of its own, as what is called from the clock does: no trail is stored.
|
|
101
|
+
*/
|
|
102
|
+
async store(at) {
|
|
103
|
+
const id = entities.derive('cadence', this.region, this.endpoint, at);
|
|
104
|
+
const entity = {
|
|
105
|
+
id,
|
|
106
|
+
...row({
|
|
107
|
+
endpoint: this.endpoint,
|
|
108
|
+
due: at,
|
|
109
|
+
overdue: this.late(at),
|
|
110
|
+
request: { input: { at } }
|
|
111
|
+
})
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
await this.metronome.invoke('ensure', { query: { id }, entity });
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.warn('Schedule could not store its next call, and will try again', {
|
|
118
|
+
schedule: this.label,
|
|
119
|
+
at,
|
|
120
|
+
error
|
|
121
|
+
});
|
|
122
|
+
/*
|
|
123
|
+
* Tried again for as long as it is the occurrence being waited for. Past that the next
|
|
124
|
+
* one is handed over instead, and this one is what the readme says may be lost.
|
|
125
|
+
*/
|
|
126
|
+
this.retry = setTimeout(() => {
|
|
127
|
+
if (!this.closing && !this.quiesced && this.at === at)
|
|
128
|
+
void this.store(at);
|
|
129
|
+
}, RETRY);
|
|
130
|
+
this.retry.unref();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* How late the call may be. Owed until the next occurrence is due where nothing states a
|
|
135
|
+
* bound, so at most one is ever waiting and an outage does not come back as every call it
|
|
136
|
+
* covered; with no bound only where nothing ever comes after it.
|
|
137
|
+
*/
|
|
138
|
+
late(at) {
|
|
139
|
+
if (this.overdue !== undefined)
|
|
140
|
+
return this.overdue;
|
|
141
|
+
const next = this.next(at);
|
|
142
|
+
return next === null ? null : next - at;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** `setTimeout` fires immediately past this, so a longer wait is taken in instalments */
|
|
146
|
+
const MAX_DELAY = 2 ** 31 - 1;
|
|
147
|
+
/** milliseconds between attempts to store an occurrence the metronome was not there to take */
|
|
148
|
+
const RETRY = 5_000;
|
|
149
|
+
//# sourceMappingURL=Schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Schedule.js","sourceRoot":"","sources":["../source/Schedule.ts"],"names":[],"mappings":"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"}
|
package/transpiled/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Factory } from './Factory.ts';
|
|
2
2
|
export { components } from './Composition.ts';
|
|
3
3
|
export { LANES } from '@toa.io/definitions/extensions.cadence';
|
|
4
|
-
export
|
|
4
|
+
export { row } from './row.ts';
|
|
5
|
+
export type { Declaration, Delay, Entry, Pulse, Schedule } from '@toa.io/definitions/extensions.cadence';
|
package/transpiled/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { Factory } from './Factory.js';
|
|
|
2
2
|
export { components } from './Composition.js';
|
|
3
3
|
// what the metronome component is written against
|
|
4
4
|
export { LANES } from '@toa.io/definitions/extensions.cadence';
|
|
5
|
+
export { row } from './row.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/transpiled/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,kDAAkD;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,kDAAkD;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAA;AAC9D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What is stored of a call to make later, as whoever hands one over states it: `context.delay`,
|
|
3
|
+
* and a schedule storing its next occurrence. Written once, so that the two store one kind of
|
|
4
|
+
* row and the dispatcher reads one.
|
|
5
|
+
*
|
|
6
|
+
* The lane is random, not one this replica owns. An outbox row is written into an owned lane so
|
|
7
|
+
* that in steady state a replica settles its own rows before it ever reads them — but a delayed
|
|
8
|
+
* row has no immediate path to settle, so nothing is gained, and an even spread is what the
|
|
9
|
+
* dispatchers want.
|
|
10
|
+
*/
|
|
11
|
+
export declare function row(call: Call): Stored;
|
|
12
|
+
export interface Call {
|
|
13
|
+
/** what to call, as `namespace.component.operation` */
|
|
14
|
+
endpoint: string;
|
|
15
|
+
/** when, in milliseconds since the epoch */
|
|
16
|
+
due: number;
|
|
17
|
+
/** milliseconds the call may be late and still be made, or `null` for no bound */
|
|
18
|
+
overdue: number | null;
|
|
19
|
+
request?: object;
|
|
20
|
+
trail?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface Stored {
|
|
23
|
+
lane: number;
|
|
24
|
+
due: number;
|
|
25
|
+
expires: number;
|
|
26
|
+
endpoint: string;
|
|
27
|
+
request?: object;
|
|
28
|
+
trail?: string[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LANES } from '@toa.io/definitions/extensions.cadence';
|
|
2
|
+
/**
|
|
3
|
+
* What is stored of a call to make later, as whoever hands one over states it: `context.delay`,
|
|
4
|
+
* and a schedule storing its next occurrence. Written once, so that the two store one kind of
|
|
5
|
+
* row and the dispatcher reads one.
|
|
6
|
+
*
|
|
7
|
+
* The lane is random, not one this replica owns. An outbox row is written into an owned lane so
|
|
8
|
+
* that in steady state a replica settles its own rows before it ever reads them — but a delayed
|
|
9
|
+
* row has no immediate path to settle, so nothing is gained, and an even spread is what the
|
|
10
|
+
* dispatchers want.
|
|
11
|
+
*/
|
|
12
|
+
export function row(call) {
|
|
13
|
+
const { endpoint, due, overdue, request, trail } = call;
|
|
14
|
+
const stored = {
|
|
15
|
+
lane: Math.floor(Math.random() * LANES),
|
|
16
|
+
due,
|
|
17
|
+
endpoint,
|
|
18
|
+
// absolute, because the bound is the caller's and a scan reads rows of many callers at once.
|
|
19
|
+
// No bound is the end of representable time rather than an absent field, so that one
|
|
20
|
+
// comparison answers for every row
|
|
21
|
+
expires: overdue === null ? Number.MAX_SAFE_INTEGER : due + overdue
|
|
22
|
+
};
|
|
23
|
+
// a call that takes no request has none: the entity's `request` is an object where it is
|
|
24
|
+
// there at all, and a null would not fit it
|
|
25
|
+
if (request !== undefined)
|
|
26
|
+
stored.request = request;
|
|
27
|
+
// the chain that asked for the call, absent where the call begins one of its own
|
|
28
|
+
if (trail !== undefined)
|
|
29
|
+
stored.trail = trail;
|
|
30
|
+
return stored;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row.js","sourceRoot":"","sources":["../source/row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAA;AAE9D;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CAAC,IAAU;IAC5B,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEvD,MAAM,MAAM,GAAW;QACrB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC;QACvC,GAAG;QACH,QAAQ;QAER,6FAA6F;QAC7F,qFAAqF;QACrF,mCAAmC;QACnC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO;KACpE,CAAA;IAED,yFAAyF;IACzF,4CAA4C;IAC5C,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAA;IAEnD,iFAAiF;IACjF,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;IAE7C,OAAO,MAAM,CAAA;AACf,CAAC"}
|