@substrat-run/contract-tests 0.114.0 → 0.116.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,582 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { errorCodeOf, moduleId, permissionKey, platformActorId, principalId, scopeId, tenantId, } from '@substrat-run/contracts';
3
+ import { ulid } from '@substrat-run/kernel';
4
+ import { jobsMod } from './modules.js';
5
+ const JOBS_MODULE = moduleId.parse('@test/jobs');
6
+ /**
7
+ * The fourth driver's contract (#1577), against both adapters.
8
+ *
9
+ * What this pins is the difference between the run driver and the three that came
10
+ * before it: a run STOPS and CARRIES ON. Every assertion is written so it can only
11
+ * pass if work actually survived a boundary, never because a counter in the test
12
+ * process happened to agree with itself:
13
+ *
14
+ * - The walk's steps write through a real operation into the scope, into a table with
15
+ * no unique key, so a repeated step is a DUPLICATE ROW — loud, not absorbed.
16
+ * - The interruption is a handler that throws AFTER a step committed, which is what a
17
+ * worker eviction looks like from the driver's side: the step's effect happened and
18
+ * the pass never returned, which is the state resume has to be correct about.
19
+ * - Coalescing is asserted on the RUN ID, because "returns the live run" is a claim
20
+ * about identity; two rows sharing a key would satisfy any count-based check.
21
+ *
22
+ * **A scope per test, deliberately.** `runDueJobs` is a SCOPE-wide driver — that is
23
+ * what it is for — so runs one test leaves behind are due work for the next one, and
24
+ * assertions like "nothing is due now" would be about the suite's history rather than
25
+ * about the driver. One scope each makes every count in here absolute.
26
+ */
27
+ export function jobRunContractSuite(adapterName, makeFixture) {
28
+ describe(`resumable run driver (#1577): ${adapterName}`, () => {
29
+ let fixture;
30
+ let host;
31
+ const t = tenantId.parse(ulid());
32
+ const reader = principalId.parse(ulid());
33
+ const staff = platformActorId.parse(ulid());
34
+ /**
35
+ * The step after which the next pass "is evicted" — set by a test, consumed once.
36
+ */
37
+ let evictAfter = null;
38
+ /** Passes that have entered the walk handler — the replay counter. */
39
+ let passes = 0;
40
+ /** What the `shapes` job's step handed its handler, once per pass. */
41
+ const shapesSeen = [];
42
+ /** How many times the `doomed` job's step BODY actually ran. */
43
+ let doomedCalls = 0;
44
+ /** A scope of its own, with the module's system grant on it. */
45
+ const newScope = async () => {
46
+ const s = scopeId.parse(ulid());
47
+ await host.provisionScope(staff, { tenantId: t, scopeId: s, vertical: 'jobs-vertical' });
48
+ await host.admin.activateScope(staff, t, s);
49
+ // A job's authority is an ordinary system grant — the same tuple a schedule's
50
+ // declaration projects, written by hand because this module declares no
51
+ // schedules. Nothing about a job invents a second way in.
52
+ await host.admin.grantToSystem(staff, {
53
+ moduleId: JOBS_MODULE,
54
+ permission: permissionKey.parse('jobs:write'),
55
+ node: { tenantId: t, scopeId: s },
56
+ grantedBy: staff,
57
+ });
58
+ return s;
59
+ };
60
+ const items = async (s) => {
61
+ const stub = await host.getScope(reader, t, s);
62
+ return (await stub.invoke('jobs/items'));
63
+ };
64
+ const runOf = async (s, id) => (await host.jobRuns(t, s)).find((r) => r.id === id);
65
+ beforeAll(async () => {
66
+ fixture = await makeFixture();
67
+ host = fixture.host;
68
+ host.registerModule(jobsMod);
69
+ /**
70
+ * The walk: a bounded chunk per pass, resuming at the cursor.
71
+ *
72
+ * `pass.count` sits OUTSIDE the step and `scope.invoke` sits INSIDE it, and that
73
+ * split is the whole demonstration. On a replay the loop re-runs for every item
74
+ * in the chunk — so the counter re-accumulates and is right — while the committed
75
+ * steps return their memo and write nothing. If the memo ever stops working the
76
+ * counter still agrees with itself and the SCOPE grows a duplicate; only the
77
+ * scope can tell you.
78
+ */
79
+ host.registerJob(JOBS_MODULE, 'walk', async (pass) => {
80
+ passes += 1;
81
+ const { total, chunk } = pass.payload;
82
+ const from = pass.cursor ?? 0;
83
+ const to = Math.min(from + chunk, total);
84
+ const scope = await pass.scope();
85
+ for (let i = from; i < to; i += 1) {
86
+ const item = `item-${i}`;
87
+ await pass.step(item, () => scope.invoke('jobs/record', { item }));
88
+ pass.count('walked');
89
+ if (evictAfter === item) {
90
+ evictAfter = null;
91
+ throw new Error(`evicted after ${item}`);
92
+ }
93
+ }
94
+ return { cursor: to, done: to >= total };
95
+ },
96
+ // Immediate retries: `backoffAt` skips jitter at zero delay, so a re-drive in
97
+ // the same millisecond is due rather than racing the wall clock.
98
+ { maxAttempts: 4, baseDelayMs: 0 });
99
+ // A job whose step never succeeds — the terminal-failure half of the contract.
100
+ host.registerJob(JOBS_MODULE, 'doomed', async (pass) => {
101
+ await pass.step('always-fails', () => {
102
+ doomedCalls += 1;
103
+ throw new Error('upstream said no');
104
+ });
105
+ return { done: true };
106
+ }, { maxAttempts: 3, baseDelayMs: 0 });
107
+ // A job whose handler hands back an explicitly-undefined cursor — a SUPPLIED
108
+ // cursor, which must be refused rather than coalesced into "start over".
109
+ host.registerJob(JOBS_MODULE, 'undef-cursor', () => ({ cursor: undefined }), { maxAttempts: 2, baseDelayMs: 0 });
110
+ // A job whose step returns a value JSON cannot carry unchanged. What it returns
111
+ // is recorded by the handler so the test can compare the first pass (which ran
112
+ // the step) against the second (which replayed its memo).
113
+ host.registerJob(JOBS_MODULE, 'shapes', async (pass) => {
114
+ const got = await pass.step('shape', () => new Date('2026-01-01T00:00:00.000Z'));
115
+ shapesSeen.push({ type: typeof got, value: String(got) });
116
+ if (evictAfter === 'shape') {
117
+ evictAfter = null;
118
+ throw new Error('evicted after shape');
119
+ }
120
+ return { done: true };
121
+ }, { maxAttempts: 4, baseDelayMs: 0 });
122
+ // A job that does nothing but finish: no steps, no scope, no module table. The
123
+ // healthy half of the malformed-row test, where the subject is that a run behind
124
+ // a broken one still advances and nothing else.
125
+ host.registerJob(JOBS_MODULE, 'inert', () => ({ done: true }));
126
+ // A job that asks for one step name twice in one pass — the determinism rule's
127
+ // mechanical half. Both bodies would run on a fresh pass and only the first
128
+ // would ever be recorded, so the second is refused rather than memo-aliased.
129
+ host.registerJob(JOBS_MODULE, 'ambiguous', async (pass) => {
130
+ await pass.step('same', () => 1);
131
+ await pass.step('same', () => 2);
132
+ return { done: true };
133
+ }, { maxAttempts: 2, baseDelayMs: 0 });
134
+ await host.admin.createTenant(staff, { id: t, slug: 'jobs', name: 'Jobs' });
135
+ await host.admin.grantEntitlement(staff, t, 'jobs');
136
+ });
137
+ afterAll(async () => {
138
+ await fixture.cleanup();
139
+ });
140
+ it('joins the run already in flight instead of starting a second', async () => {
141
+ const s = await newScope();
142
+ const first = await host.startJobRun(t, s, {
143
+ moduleId: JOBS_MODULE,
144
+ job: 'walk',
145
+ instance: 'source-a',
146
+ payload: { total: 5, chunk: 2 },
147
+ });
148
+ const second = await host.startJobRun(t, s, {
149
+ moduleId: JOBS_MODULE,
150
+ job: 'walk',
151
+ instance: 'source-a',
152
+ // A DIFFERENT payload, deliberately: a join returns the live run unchanged, so
153
+ // the walk already happening is not quietly re-aimed by whoever asked second.
154
+ payload: { total: 500, chunk: 2 },
155
+ });
156
+ expect(second.id).toBe(first.id);
157
+ expect(second.payload).toEqual({ total: 5, chunk: 2 });
158
+ expect(await host.jobRuns(t, s, { job: 'walk' })).toHaveLength(1);
159
+ // A different INSTANCE is a different walk and does not join.
160
+ const other = await host.startJobRun(t, s, {
161
+ moduleId: JOBS_MODULE,
162
+ job: 'walk',
163
+ instance: 'source-b',
164
+ payload: { total: 4, chunk: 2 },
165
+ });
166
+ expect(other.id).not.toBe(first.id);
167
+ expect(await host.jobRuns(t, s, { job: 'walk' })).toHaveLength(2);
168
+ });
169
+ it('resumes from the last committed cursor, and repeats no step before it', async () => {
170
+ const s = await newScope();
171
+ const run = await host.startJobRun(t, s, {
172
+ moduleId: JOBS_MODULE,
173
+ job: 'walk',
174
+ instance: 'source-a',
175
+ payload: { total: 5, chunk: 2 },
176
+ });
177
+ // Pass 1 commits the first chunk: items 0 and 1, cursor 2.
178
+ let report = await host.runDueJobs(t, s);
179
+ expect(report).toMatchObject({ attempted: 1, advanced: 1, completed: 0, failed: 0 });
180
+ expect(await items(s)).toEqual(['item-0', 'item-1']);
181
+ expect((await runOf(s, run.id))?.cursor).toBe(2);
182
+ // Pass 2 is EVICTED after item-2: its write committed, the pass did not return.
183
+ evictAfter = 'item-2';
184
+ const before = passes;
185
+ report = await host.runDueJobs(t, s);
186
+ expect(report.retrying).toBe(1);
187
+ expect(await items(s)).toEqual(['item-0', 'item-1', 'item-2']);
188
+ // Nothing the evicted pass produced was committed: the cursor is still where
189
+ // pass 1 left it, so "resume" cannot be satisfied by the cursor having moved.
190
+ const evicted = await runOf(s, run.id);
191
+ expect(evicted?.cursor).toBe(2);
192
+ expect(evicted?.status).toBe('running');
193
+ expect(evicted?.lastError).toContain('evicted after item-2');
194
+ expect(evicted?.attempts).toBe(1);
195
+ // Pass 3 replays the SAME chunk from cursor 2. item-2's step returns its memo
196
+ // and writes nothing; item-3 runs for the first time. A broken memo shows up
197
+ // here as a second 'item-2' in the scope.
198
+ report = await host.runDueJobs(t, s);
199
+ expect(report.advanced).toBe(1);
200
+ expect(await items(s)).toEqual(['item-0', 'item-1', 'item-2', 'item-3']);
201
+ expect(passes).toBe(before + 2);
202
+ // Pass 4 finishes the walk.
203
+ report = await host.runDueJobs(t, s);
204
+ expect(report.completed).toBe(1);
205
+ expect(await items(s)).toEqual(['item-0', 'item-1', 'item-2', 'item-3', 'item-4']);
206
+ const done = await runOf(s, run.id);
207
+ expect(done?.status).toBe('done');
208
+ expect(done?.cursor).toBe(5);
209
+ expect(done?.endedAt).not.toBeNull();
210
+ expect(done?.lastError).toBeNull();
211
+ expect(done?.attempts).toBe(0);
212
+ // The counter re-accumulated across the replayed chunk and still totals 5 — an
213
+ // uncommitted pass's counts are discarded, and the replay puts them back.
214
+ expect(done?.counters).toEqual({ walked: 5 });
215
+ // Terminal: a finished run is never picked up again, and a start against the
216
+ // same key opens a FRESH run rather than joining the finished one.
217
+ expect((await host.runDueJobs(t, s)).attempted).toBe(0);
218
+ const again = await host.startJobRun(t, s, {
219
+ moduleId: JOBS_MODULE,
220
+ job: 'walk',
221
+ instance: 'source-a',
222
+ payload: { total: 0, chunk: 2 },
223
+ });
224
+ expect(again.id).not.toBe(run.id);
225
+ expect((await host.jobRuns(t, s, { job: 'walk' })).map((r) => r.status)).toEqual([
226
+ 'running',
227
+ 'done',
228
+ ]);
229
+ });
230
+ it('walks to the end in one call when the caller has a budget', async () => {
231
+ const s = await newScope();
232
+ const run = await host.startJobRun(t, s, {
233
+ moduleId: JOBS_MODULE,
234
+ job: 'walk',
235
+ instance: 'budget',
236
+ payload: { total: 5, chunk: 2 },
237
+ });
238
+ // Three passes' worth of chunks, asked for in one call: the second pass reads
239
+ // back the cursor the first one COMMITTED rather than a value carried in memory.
240
+ const report = await host.runDueJobs(t, s, { maxPasses: 5 });
241
+ expect(report).toMatchObject({ attempted: 1, advanced: 2, completed: 1 });
242
+ expect(await items(s)).toEqual(['item-0', 'item-1', 'item-2', 'item-3', 'item-4']);
243
+ expect((await runOf(s, run.id))?.status).toBe('done');
244
+ });
245
+ it('fails the run when a step exhausts its retries, and never throws at the driver', async () => {
246
+ const s = await newScope();
247
+ const run = await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'doomed' });
248
+ // maxAttempts 3: two retries, then terminal.
249
+ for (const expected of [1, 2]) {
250
+ const report = await host.runDueJobs(t, s);
251
+ expect(report.retrying).toBe(1);
252
+ expect((await runOf(s, run.id))?.attempts).toBe(expected);
253
+ }
254
+ const last = await host.runDueJobs(t, s);
255
+ expect(last.failed).toBe(1);
256
+ expect(last.errors).toHaveLength(1);
257
+ expect(last.errors[0].runId).toBe(run.id);
258
+ expect(last.errors[0].error).toContain('upstream said no');
259
+ const failed = await runOf(s, run.id);
260
+ expect(failed?.status).toBe('failed');
261
+ expect(failed?.lastError).toContain("step 'always-fails'");
262
+ expect(failed?.lastError).toContain('upstream said no');
263
+ expect(failed?.endedAt).not.toBeNull();
264
+ // Terminal: not picked up again, and the driver returned a report rather than
265
+ // throwing at whoever was holding the tick.
266
+ expect((await host.runDueJobs(t, s)).attempted).toBe(0);
267
+ });
268
+ it('refuses a payload carrying bytes, naming the path', async () => {
269
+ const s = await newScope();
270
+ const start = host.startJobRun(t, s, {
271
+ moduleId: JOBS_MODULE,
272
+ job: 'walk',
273
+ instance: 'bytes',
274
+ payload: { source: { id: 'abc', bytes: new Uint8Array([1, 2, 3]) } },
275
+ });
276
+ const err = await start.catch((e) => e);
277
+ expect(errorCodeOf(err)).toBe('validation_failed');
278
+ expect(err.message).toContain('payload.source.bytes');
279
+ expect(err.extensions?.errors?.[0]?.path).toBe('payload.source.bytes');
280
+ // Refused BEFORE a row exists — a run is never recorded carrying a value it
281
+ // could not resume from.
282
+ expect(await host.jobRuns(t, s)).toEqual([]);
283
+ });
284
+ it('refuses a payload carrying a class instance or a function, naming the path', async () => {
285
+ const s = await newScope();
286
+ await expect(host.startJobRun(t, s, {
287
+ moduleId: JOBS_MODULE,
288
+ job: 'walk',
289
+ instance: 'date',
290
+ payload: { since: new Date('2026-01-01T00:00:00.000Z') },
291
+ })).rejects.toThrow(/payload\.since is a Date/);
292
+ await expect(host.startJobRun(t, s, {
293
+ moduleId: JOBS_MODULE,
294
+ job: 'walk',
295
+ instance: 'fn',
296
+ payload: { steps: [() => 1] },
297
+ })).rejects.toThrow(/payload\.steps\.0 is a function/);
298
+ expect(await host.jobRuns(t, s)).toEqual([]);
299
+ });
300
+ /**
301
+ * F1: two starts that race must still produce ONE run.
302
+ *
303
+ * `Promise.all` is a real interleave here, not a decoration: `startJobRun` is
304
+ * `async` on both adapters, so the two calls genuinely suspend across the store
305
+ * round trips and the second reaches its lookup before the first has inserted.
306
+ * With the lookup and the insert as separate store calls both saw no live run
307
+ * and both inserted — and nothing in the schema could refuse the second, by
308
+ * design, since a crashed run has to stay restartable.
309
+ */
310
+ it('starts exactly one run when two starts race', async () => {
311
+ const s = await newScope();
312
+ const start = () => host.startJobRun(t, s, {
313
+ moduleId: JOBS_MODULE,
314
+ job: 'walk',
315
+ instance: 'racy',
316
+ payload: { total: 2, chunk: 1 },
317
+ });
318
+ const [a, b, c] = await Promise.all([start(), start(), start()]);
319
+ expect(b.id).toBe(a.id);
320
+ expect(c.id).toBe(a.id);
321
+ // The row count is the assertion that cannot be satisfied by luck: three
322
+ // concurrent callers, one run.
323
+ expect(await host.jobRuns(t, s, { instance: 'racy' })).toHaveLength(1);
324
+ });
325
+ /**
326
+ * F8: `{ cursor: undefined }` is a SUPPLIED cursor, and supplying it used to
327
+ * coalesce to null — silently restarting the walk. Refused now, by path.
328
+ */
329
+ it('refuses an explicitly undefined cursor rather than resetting the walk', async () => {
330
+ const s = await newScope();
331
+ const run = await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'undef-cursor' });
332
+ const report = await host.runDueJobs(t, s);
333
+ expect(report.retrying + report.failed).toBe(1);
334
+ const after = await runOf(s, run.id);
335
+ expect(after?.lastError).toContain('cursor is not queue-safe');
336
+ // The cursor the last commit left is untouched — the pass did not "reset" it.
337
+ expect(after?.cursor).toBeNull();
338
+ });
339
+ /** F5: a sparse array survives `forEach` and changes value through JSON. */
340
+ it('refuses a payload carrying a hole in a sparse array', async () => {
341
+ const s = await newScope();
342
+ const holed = [1, 2, 3];
343
+ // eslint-disable-next-line @typescript-eslint/no-array-delete
344
+ delete holed[1];
345
+ await expect(host.startJobRun(t, s, {
346
+ moduleId: JOBS_MODULE,
347
+ job: 'walk',
348
+ instance: 'sparse',
349
+ payload: { pages: holed },
350
+ })).rejects.toThrow(/payload\.pages\.1 is a hole in a sparse array/);
351
+ expect(await host.jobRuns(t, s)).toEqual([]);
352
+ });
353
+ /**
354
+ * F7: a step must hand the handler the SAME shape whether it just ran or was
355
+ * replayed from the memo. `walk-dates` returns a `Date` from its step; the first
356
+ * pass is evicted after it, so the second pass reads the memo — and the two
357
+ * passes must agree about what they got.
358
+ */
359
+ it('returns the same shape from a step whether it ran or was replayed', async () => {
360
+ const s = await newScope();
361
+ const run = await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'shapes' });
362
+ shapesSeen.length = 0;
363
+ evictAfter = 'shape';
364
+ await host.runDueJobs(t, s);
365
+ await host.runDueJobs(t, s);
366
+ expect(shapesSeen).toHaveLength(2);
367
+ // Ran, then replayed — and the handler could not tell the difference.
368
+ expect(shapesSeen[0]).toEqual(shapesSeen[1]);
369
+ expect((await runOf(s, run.id))?.status).toBe('done');
370
+ });
371
+ /**
372
+ * F9: runs this host cannot drive must not hold the head of the queue forever.
373
+ * Three unregistered runs are started first, so they sort ahead of the real one;
374
+ * with the budget applied to the QUERY rather than to runnable runs, a limit of
375
+ * 1 returned the same unrunnable row on every call and the real run never ran.
376
+ */
377
+ it('pages past runs it cannot drive instead of starving on them', async () => {
378
+ const s = await newScope();
379
+ for (const n of ['a', 'b', 'c']) {
380
+ await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'absent-job', instance: n });
381
+ }
382
+ const real = await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'inert' });
383
+ const report = await host.runDueJobs(t, s, { limit: 1 });
384
+ expect(report.attempted).toBe(1);
385
+ expect(report.completed).toBe(1);
386
+ expect((await runOf(s, real.id))?.status).toBe('done');
387
+ });
388
+ it('refuses two steps under one name in one pass', async () => {
389
+ const s = await newScope();
390
+ const run = await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'ambiguous' });
391
+ const report = await host.runDueJobs(t, s);
392
+ // Reported like any other pass failure — the refusal is loud, not fatal.
393
+ expect(report.retrying).toBe(1);
394
+ expect((await runOf(s, run.id))?.lastError).toContain('already run in this pass');
395
+ });
396
+ /**
397
+ * The per-run isolation, against the one input that is not a handler's fault.
398
+ *
399
+ * Every other failure in this suite arrives from inside a pass, where a `try` was
400
+ * always going to catch it. A MALFORMED ROW arrives before the pass starts, and
401
+ * decoding it used to sit above that `try` — so one bad row threw out of
402
+ * `runJobPass`, out of `runDueJobRuns` (which does not wrap the call either) and
403
+ * out of `runDueJobs` at whoever was holding the tick, taking every due run BEHIND
404
+ * it down with it. Found by re-reading the diff; no test in the suite could reach
405
+ * it, because nothing the public surface accepts produces such a row.
406
+ *
407
+ * A restore does. `importDump` replays a dump's rows verbatim
408
+ * (preview-and-snapshots.md §3), so a dump written by another world — or edited by
409
+ * hand — is the whole reproduction, and it is the same lever #1288's backfill test
410
+ * uses. The broken row's id sorts before every ULID, so the due read reaches it
411
+ * FIRST: if it is not contained, the healthy run behind it never runs.
412
+ */
413
+ it('contains a malformed run row instead of taking the drive down with it', async () => {
414
+ const s = await newScope();
415
+ await host.restoreScope(staff, t, s, {
416
+ tenantId: t,
417
+ scopeId: s,
418
+ capturedAt: '2026-09-01T00:00:00.000Z',
419
+ tables: [
420
+ {
421
+ name: '_substrat_job_runs',
422
+ // Spelled out rather than referenced: the point is that code meeting a
423
+ // FOREIGN table survives it, so this must not move when the DDL does.
424
+ ddl: 'CREATE TABLE _substrat_job_runs (id TEXT PRIMARY KEY, module_id TEXT NOT NULL, ' +
425
+ 'job TEXT NOT NULL, instance TEXT NOT NULL, payload TEXT NOT NULL, status TEXT NOT NULL, ' +
426
+ 'cursor TEXT, counters TEXT NOT NULL DEFAULT \'{}\', attempts INTEGER NOT NULL DEFAULT 0, ' +
427
+ 'last_error TEXT, started_at TEXT NOT NULL, updated_at TEXT NOT NULL, ' +
428
+ 'next_attempt_at TEXT, ended_at TEXT)',
429
+ columns: [
430
+ 'id', 'module_id', 'job', 'instance', 'payload', 'status', 'cursor', 'counters',
431
+ 'attempts', 'last_error', 'started_at', 'updated_at', 'next_attempt_at', 'ended_at',
432
+ ],
433
+ rows: [
434
+ [
435
+ // Sorts before every ULID, so this row is read first.
436
+ '00000000000000000000000000',
437
+ JOBS_MODULE,
438
+ 'walk',
439
+ 'corrupt',
440
+ '{"total":1,"chunk":1}',
441
+ 'running',
442
+ null,
443
+ 'not json at all',
444
+ 0,
445
+ null,
446
+ '2026-09-01T00:00:00.000Z',
447
+ '2026-09-01T00:00:00.000Z',
448
+ null,
449
+ null,
450
+ ],
451
+ ],
452
+ },
453
+ ],
454
+ });
455
+ // The healthy run is the INERT job, which touches no module table and needs no
456
+ // grant. That is deliberate: a restore replays only the dump, so the scope's
457
+ // module tables and its tuples came back empty, and the two adapters do not
458
+ // agree about when those are rebuilt — the pure host clears its applied-migration
459
+ // set on restore, while a warm ScopeDO keeps a memoised migration promise that
460
+ // only `retryMigrations` or a cold start clears. That asymmetry is real and
461
+ // predates this driver; making it this test's business would be asserting the
462
+ // restore path under the name of the run driver. What IS this test's business is
463
+ // that a run behind a malformed row still advances.
464
+ const healthy = await host.startJobRun(t, s, {
465
+ moduleId: JOBS_MODULE,
466
+ job: 'inert',
467
+ instance: 'healthy',
468
+ });
469
+ // Does not throw — and the run BEHIND the broken one still did its work.
470
+ const report = await host.runDueJobs(t, s);
471
+ expect(report.attempted).toBe(2);
472
+ expect(report.completed).toBe(1);
473
+ expect((await runOf(s, healthy.id))?.status).toBe('done');
474
+ // The broken row was recorded as a failed pass, not skipped and not fatal.
475
+ const broken = await runOf(s, '00000000000000000000000000');
476
+ expect(broken?.status).toBe('running');
477
+ expect(broken?.attempts).toBe(1);
478
+ expect(broken?.lastError).toContain('JSON');
479
+ expect(report.errors.map((e) => e.runId)).toEqual(['00000000000000000000000000']);
480
+ // And the READ says the row could not be decoded rather than pretending it
481
+ // could: empty counters WITH a reason, never a silent `{}` that reads as
482
+ // "nothing counted". Reading it at all is the point — the driver recorded
483
+ // evidence onto this row, and a strict decode would have made the whole list
484
+ // (including the healthy run above) unreadable because of it.
485
+ expect(broken?.decodeError).toContain('JSON');
486
+ expect(broken?.counters).toEqual({});
487
+ expect((await runOf(s, healthy.id))?.decodeError).toBeNull();
488
+ });
489
+ /**
490
+ * F6: a step whose RECORDED attempts already reached its policy must not be
491
+ * invoked one more time to discover what its own ledger row already says.
492
+ *
493
+ * The state is reachable by a stop between `recordStep` writing the final failed
494
+ * attempt and the run patch marking the run terminal, so it is restored here
495
+ * directly: a `running` run with `attempts = 0`, and beside it a step row with
496
+ * `attempts = 3` (its policy's maximum) and a NULL result. The assertion is the
497
+ * CALL COUNTER, because both the fixed and the broken code end with the run
498
+ * `failed` — the difference is only whether somebody else's API was called once
499
+ * more on the way there, which is exactly the cost being avoided.
500
+ */
501
+ it('does not re-invoke a step whose recorded attempts are already spent', async () => {
502
+ const s = await newScope();
503
+ const runId = '00000000000000000000000001';
504
+ await host.restoreScope(staff, t, s, {
505
+ tenantId: t,
506
+ scopeId: s,
507
+ capturedAt: '2026-09-01T00:00:00.000Z',
508
+ tables: [
509
+ {
510
+ name: '_substrat_job_runs',
511
+ ddl: 'CREATE TABLE _substrat_job_runs (id TEXT PRIMARY KEY, module_id TEXT NOT NULL, ' +
512
+ 'job TEXT NOT NULL, instance TEXT NOT NULL, payload TEXT NOT NULL, status TEXT NOT NULL, ' +
513
+ 'cursor TEXT, counters TEXT NOT NULL DEFAULT \'{}\', attempts INTEGER NOT NULL DEFAULT 0, ' +
514
+ 'last_error TEXT, started_at TEXT NOT NULL, updated_at TEXT NOT NULL, ' +
515
+ 'next_attempt_at TEXT, ended_at TEXT)',
516
+ columns: [
517
+ 'id', 'module_id', 'job', 'instance', 'payload', 'status', 'cursor', 'counters',
518
+ 'attempts', 'last_error', 'started_at', 'updated_at', 'next_attempt_at', 'ended_at',
519
+ ],
520
+ rows: [
521
+ [
522
+ runId, JOBS_MODULE, 'doomed', 'default', 'null', 'running', null, '{}', 0, null,
523
+ '2026-09-01T00:00:00.000Z', '2026-09-01T00:00:00.000Z', null, null,
524
+ ],
525
+ ],
526
+ },
527
+ {
528
+ name: '_substrat_job_steps',
529
+ ddl: 'CREATE TABLE _substrat_job_steps (run_id TEXT NOT NULL, step TEXT NOT NULL, ' +
530
+ 'result TEXT, attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, ' +
531
+ 'recorded_at TEXT NOT NULL, PRIMARY KEY (run_id, step))',
532
+ columns: ['run_id', 'step', 'result', 'attempts', 'last_error', 'recorded_at'],
533
+ // attempts = 3 = `doomed`'s maxAttempts, result NULL = it never succeeded.
534
+ rows: [[runId, 'always-fails', null, 3, 'upstream said no', '2026-09-01T00:00:00.000Z']],
535
+ },
536
+ ],
537
+ });
538
+ const before = doomedCalls;
539
+ const report = await host.runDueJobs(t, s);
540
+ // The step body was NOT run again — this is the whole assertion.
541
+ expect(doomedCalls).toBe(before);
542
+ expect(report.failed).toBe(1);
543
+ const settled = await runOf(s, runId);
544
+ expect(settled?.status).toBe('failed');
545
+ // Settled on the error the ledger already held, not on a freshly-produced one.
546
+ expect(settled?.lastError).toContain('upstream said no');
547
+ expect(settled?.endedAt).not.toBeNull();
548
+ });
549
+ /**
550
+ * A caller's `limit` is normalised before it reaches SQL. SQLite reads a
551
+ * NEGATIVE limit as unbounded, and finished runs are retained — so `-1` asked
552
+ * for the scope's entire history in one response, a read whose cost grows with
553
+ * retention rather than with what was asked for.
554
+ */
555
+ it('clamps the operator read to a sane row budget', async () => {
556
+ const s = await newScope();
557
+ for (const n of ['a', 'b', 'c']) {
558
+ await host.startJobRun(t, s, { moduleId: JOBS_MODULE, job: 'inert', instance: n });
559
+ }
560
+ expect(await host.jobRuns(t, s, { limit: -1 })).toHaveLength(1);
561
+ expect(await host.jobRuns(t, s, { limit: 0 })).toHaveLength(1);
562
+ // A fractional limit is a value SQLite refuses outright rather than rounds.
563
+ expect(await host.jobRuns(t, s, { limit: 2.7 })).toHaveLength(2);
564
+ expect(await host.jobRuns(t, s, { limit: 1_000_000 })).toHaveLength(3);
565
+ });
566
+ it('leaves a run whose job this host does not register untouched', async () => {
567
+ const s = await newScope();
568
+ const run = await host.startJobRun(t, s, {
569
+ moduleId: JOBS_MODULE,
570
+ job: 'not-registered-here',
571
+ payload: { any: 'thing' },
572
+ });
573
+ const report = await host.runDueJobs(t, s);
574
+ expect(report.attempted).toBe(0);
575
+ const untouched = await runOf(s, run.id);
576
+ expect(untouched?.status).toBe('running');
577
+ expect(untouched?.attempts).toBe(0);
578
+ expect(untouched?.lastError).toBeNull();
579
+ });
580
+ });
581
+ }
582
+ //# sourceMappingURL=job-run-suite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-run-suite.js","sourceRoot":"","sources":["../src/job-run-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,eAAe,EACf,WAAW,EACX,OAAO,EACP,QAAQ,GAGT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,IAAI,EAAuC,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,WAA4C;IAE5C,QAAQ,CAAC,iCAAiC,WAAW,EAAE,EAAE,GAAG,EAAE;QAC5D,IAAI,OAAyB,CAAC;QAC9B,IAAI,IAAe,CAAC;QACpB,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,MAAM,MAAM,GAAgB,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAE5C;;WAEG;QACH,IAAI,UAAU,GAAkB,IAAI,CAAC;QAErC,sEAAsE;QACtE,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,sEAAsE;QACtE,MAAM,UAAU,GAAsC,EAAE,CAAC;QAEzD,gEAAgE;QAChE,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,gEAAgE;QAChE,MAAM,QAAQ,GAAG,KAAK,IAAsB,EAAE;YAC5C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAChC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;YACzF,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5C,8EAA8E;YAC9E,wEAAwE;YACxE,0DAA0D;YAC1D,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE;gBACpC,QAAQ,EAAE,WAAW;gBACrB,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC;gBAC7C,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;gBACjC,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;QACX,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,KAAK,EAAE,CAAU,EAAqB,EAAE;YACpD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAa,CAAC;QACvD,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,KAAK,EAAE,CAAU,EAAE,EAAU,EAAE,EAAE,CAC7C,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAEtD,SAAS,CAAC,KAAK,IAAI,EAAE;YACnB,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;YAC9B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAE7B;;;;;;;;;eASG;YACH,IAAI,CAAC,WAAW,CACd,WAAW,EACX,MAAM,EACN,KAAK,EAAE,IAAoB,EAAE,EAAE;gBAC7B,MAAM,IAAI,CAAC,CAAC;gBACZ,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAA2C,CAAC;gBAC1E,MAAM,IAAI,GAAI,IAAI,CAAC,MAAwB,IAAI,CAAC,CAAC;gBACjD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;gBACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACjC,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC;oBACzB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;oBACnE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBACrB,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;wBACxB,UAAU,GAAG,IAAI,CAAC;wBAClB,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;oBAC3C,CAAC;gBACH,CAAC;gBACD,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,KAAK,EAAE,CAAC;YAC3C,CAAC;YACD,8EAA8E;YAC9E,iEAAiE;YACjE,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CACnC,CAAC;YAEF,+EAA+E;YAC/E,IAAI,CAAC,WAAW,CACd,WAAW,EACX,QAAQ,EACR,KAAK,EAAE,IAAoB,EAAE,EAAE;gBAC7B,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE;oBACnC,WAAW,IAAI,CAAC,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACtC,CAAC,CAAC,CAAC;gBACH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACxB,CAAC,EACD,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CACnC,CAAC;YAEF,6EAA6E;YAC7E,yEAAyE;YACzE,IAAI,CAAC,WAAW,CACd,WAAW,EACX,cAAc,EACd,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAC7B,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CACnC,CAAC;YAEF,gFAAgF;YAChF,+EAA+E;YAC/E,0DAA0D;YAC1D,IAAI,CAAC,WAAW,CACd,WAAW,EACX,QAAQ,EACR,KAAK,EAAE,IAAoB,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;gBACjF,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1D,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;oBAC3B,UAAU,GAAG,IAAI,CAAC;oBAClB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;gBACzC,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACxB,CAAC,EACD,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CACnC,CAAC;YAEF,+EAA+E;YAC/E,iFAAiF;YACjF,gDAAgD;YAChD,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAE/D,+EAA+E;YAC/E,4EAA4E;YAC5E,6EAA6E;YAC7E,IAAI,CAAC,WAAW,CACd,WAAW,EACX,WAAW,EACX,KAAK,EAAE,IAAoB,EAAE,EAAE;gBAC7B,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACxB,CAAC,EACD,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CACnC,CAAC;YAEF,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;YAClB,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;YAC5E,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACzC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBAC1C,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,UAAU;gBACpB,+EAA+E;gBAC/E,8EAA8E;gBAC9E,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE;aAClC,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACvD,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAElE,8DAA8D;YAC9D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACzC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YACrF,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACvC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YAEH,2DAA2D;YAC3D,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YACrF,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YACrD,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAEjD,gFAAgF;YAChF,UAAU,GAAG,QAAQ,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,CAAC;YACtB,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC/D,6EAA6E;YAC7E,8EAA8E;YAC9E,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;YAC7D,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAElC,8EAA8E;YAC9E,6EAA6E;YAC7E,0CAA0C;YAC1C,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YACzE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAEhC,4BAA4B;YAC5B,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEnF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,+EAA+E;YAC/E,0EAA0E;YAC1E,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAE9C,6EAA6E;YAC7E,mEAAmE;YACnE,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACzC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC/E,SAAS;gBACT,MAAM;aACP,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACvC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YACH,8EAA8E;YAC9E,iFAAiF;YACjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1E,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YACnF,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;YAC9F,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YACnF,6CAA6C;YAC7C,KAAK,MAAM,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5D,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;YAE5D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACvC,8EAA8E;YAC9E,4CAA4C;YAC5C,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACnC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;aACrE,CAAC,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACnD,MAAM,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;YACjE,MAAM,CAAE,GAAwD,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAClG,sBAAsB,CACvB,CAAC;YACF,4EAA4E;YAC5E,yBAAyB;YACzB,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;YAC1F,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,MAAM,CACV,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACrB,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,0BAA0B,CAAC,EAAE;aACzD,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;YAC9C,MAAM,MAAM,CACV,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACrB,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE;aAC9B,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;YACrD,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH;;;;;;;;;WASG;QACH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,GAAG,EAAE,CACjB,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACrB,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;aAChC,CAAC,CAAC;YACL,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxB,yEAAyE;YACzE,+BAA+B;YAC/B,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH;;;WAGG;QACH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YACrF,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,CAAC;YACzF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;YAC/D,8EAA8E;YAC9E,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,8DAA8D;YAC9D,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;YAChB,MAAM,MAAM,CACV,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACrB,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,MAAM;gBACX,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;aAC1B,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC;YACnE,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH;;;;;WAKG;QACH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;YACjF,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YACnF,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;YACtB,UAAU,GAAG,OAAO,CAAC;YACrB,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACnC,sEAAsE;YACtE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH;;;;;WAKG;QACH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YACnF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;YACtF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,yEAAyE;YACzE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;QAEH;;;;;;;;;;;;;;;;WAgBG;QACH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YACrF,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE;gBACnC,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,CAAC;gBACV,UAAU,EAAE,0BAA0B;gBACtC,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,oBAAoB;wBAC1B,uEAAuE;wBACvE,sEAAsE;wBACtE,GAAG,EACD,iFAAiF;4BACjF,0FAA0F;4BAC1F,2FAA2F;4BAC3F,uEAAuE;4BACvE,sCAAsC;wBACxC,OAAO,EAAE;4BACP,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU;4BAC/E,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU;yBACpF;wBACD,IAAI,EAAE;4BACJ;gCACE,sDAAsD;gCACtD,4BAA4B;gCAC5B,WAAW;gCACX,MAAM;gCACN,SAAS;gCACT,uBAAuB;gCACvB,SAAS;gCACT,IAAI;gCACJ,iBAAiB;gCACjB,CAAC;gCACD,IAAI;gCACJ,0BAA0B;gCAC1B,0BAA0B;gCAC1B,IAAI;gCACJ,IAAI;6BACL;yBACF;qBACF;iBACF;aACF,CAAC,CAAC;YACH,+EAA+E;YAC/E,6EAA6E;YAC7E,4EAA4E;YAC5E,kFAAkF;YAClF,+EAA+E;YAC/E,4EAA4E;YAC5E,8EAA8E;YAC9E,iFAAiF;YACjF,oDAAoD;YACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBAC3C,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,OAAO;gBACZ,QAAQ,EAAE,SAAS;aACpB,CAAC,CAAC;YAEH,yEAAyE;YACzE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE1D,2EAA2E;YAC3E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;YAClF,2EAA2E;YAC3E,yEAAyE;YACzE,0EAA0E;YAC1E,6EAA6E;YAC7E,8DAA8D;YAC9D,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH;;;;;;;;;;;WAWG;QACH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;YACnF,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,4BAA4B,CAAC;YAC3C,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE;gBACnC,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,CAAC;gBACV,UAAU,EAAE,0BAA0B;gBACtC,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,oBAAoB;wBAC1B,GAAG,EACD,iFAAiF;4BACjF,0FAA0F;4BAC1F,2FAA2F;4BAC3F,uEAAuE;4BACvE,sCAAsC;wBACxC,OAAO,EAAE;4BACP,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU;4BAC/E,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU;yBACpF;wBACD,IAAI,EAAE;4BACJ;gCACE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI;gCAC/E,0BAA0B,EAAE,0BAA0B,EAAE,IAAI,EAAE,IAAI;6BACnE;yBACF;qBACF;oBACD;wBACE,IAAI,EAAE,qBAAqB;wBAC3B,GAAG,EACD,8EAA8E;4BAC9E,qEAAqE;4BACrE,wDAAwD;wBAC1D,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC;wBAC9E,2EAA2E;wBAC3E,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,kBAAkB,EAAE,0BAA0B,CAAC,CAAC;qBACzF;iBACF;aACF,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,WAAW,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,iEAAiE;YACjE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACtC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvC,+EAA+E;YAC/E,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;YACzD,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH;;;;;WAKG;QACH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;YACrF,CAAC;YACD,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/D,4EAA4E;YAC5E,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;YAC5E,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE;gBACvC,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,qBAAqB;gBAC1B,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;aAC1B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}