@uns-kit/core 3.0.37 → 3.0.38
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/MIGRATIONS.md +21 -0
- package/README.md +11 -0
- package/dist/runtime/input-queue-boundary.d.ts +15 -0
- package/dist/runtime/input-queue-boundary.d.ts.map +1 -0
- package/dist/runtime/input-queue-boundary.js +33 -0
- package/dist/runtime/input-queue-boundary.js.map +1 -0
- package/dist/runtime/mqtt-handover-state.d.ts +4 -1
- package/dist/runtime/mqtt-handover-state.d.ts.map +1 -1
- package/dist/runtime/mqtt-handover-state.js +5 -3
- package/dist/runtime/mqtt-handover-state.js.map +1 -1
- package/dist/uns/handover-manager.d.ts +3 -0
- package/dist/uns/handover-manager.d.ts.map +1 -1
- package/dist/uns/handover-manager.js +61 -11
- package/dist/uns/handover-manager.js.map +1 -1
- package/dist/uns/uns-interfaces.d.ts +1 -0
- package/dist/uns/uns-interfaces.d.ts.map +1 -1
- package/dist/uns/uns-interfaces.js.map +1 -1
- package/dist/uns-mqtt/mqtt-proxy.d.ts +1 -0
- package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
- package/dist/uns-mqtt/mqtt-proxy.js +1 -0
- package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
- package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
- package/dist/uns-mqtt/mqtt-worker.js +11 -0
- package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
- package/dist/uns-mqtt/subscription-readiness.d.ts +2 -0
- package/dist/uns-mqtt/subscription-readiness.d.ts.map +1 -1
- package/dist/uns-mqtt/subscription-readiness.js +5 -1
- package/dist/uns-mqtt/subscription-readiness.js.map +1 -1
- package/dist/uns-mqtt/throttled-queue.d.ts +5 -8
- package/dist/uns-mqtt/throttled-queue.d.ts.map +1 -1
- package/dist/uns-mqtt/throttled-queue.js +25 -1
- package/dist/uns-mqtt/throttled-queue.js.map +1 -1
- package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +10 -8
- package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
- package/dist/uns-mqtt/uns-mqtt-proxy.js +43 -3
- package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
- package/package.json +1 -1
package/MIGRATIONS.md
CHANGED
|
@@ -9,6 +9,27 @@ Agents must inspect the application's existing ownership and shutdown flow
|
|
|
9
9
|
before editing it. The examples below describe the intended behavior, not a
|
|
10
10
|
mechanical search-and-replace operation.
|
|
11
11
|
|
|
12
|
+
## 3.0.38 - Bounded late-subscriber input boundary (format3)
|
|
13
|
+
|
|
14
|
+
When crossing `<3.0.38` to `>=3.0.38`, non-opt-in applications keep their existing
|
|
15
|
+
protocol. For opt-in correlated-v2 MQTT handover, both peers must use the new
|
|
16
|
+
format3 implementation. Format is bound
|
|
17
|
+
into the compatibility digest: a mixed old/new pair refuses while the source
|
|
18
|
+
remains active. Review an initial stop-before-start upgrade, then use matching
|
|
19
|
+
peers for live moves. Never relabel a business contract to bypass this check.
|
|
20
|
+
|
|
21
|
+
No application-specific migration hooks are introduced. Keep existing
|
|
22
|
+
`deferHandoverUntilReady`, semantic contracts and accepted-work/drain ownership.
|
|
23
|
+
The SDK checks fresh broker subscription acknowledgements and uniquely shared
|
|
24
|
+
ordered input before release, including sparse sources whose target missed old
|
|
25
|
+
packets. Without proof the source stays active and the target remains passive.
|
|
26
|
+
Only read-only preparation can repeat after delayed input; unknown import or
|
|
27
|
+
activation effects remain blocked. Equal values are not deduplicated.
|
|
28
|
+
|
|
29
|
+
This does not move persistent queues or guarantee replay of broker losses.
|
|
30
|
+
Use a reviewed reverse hot move for return ownership and retain existing fences.
|
|
31
|
+
See [protocol, example and limits](https://github.com/uns-openhub/uns-kit/blob/master/docs/mqtt-stateful-handover.md#sparse-input-and-late-subscribers-3038).
|
|
32
|
+
|
|
12
33
|
## 3.0.37 - Application lifecycle, HTTP ownership and bounded RAM state
|
|
13
34
|
|
|
14
35
|
When crossing `<3.0.37` to `>=3.0.37`, existing non-opt-in consumers retain their
|
package/README.md
CHANGED
|
@@ -273,6 +273,17 @@ and apply only the migrations whose version boundaries are crossed. The
|
|
|
273
273
|
`3.0.0` migration documents the MQTT table-column object contract, while the
|
|
274
274
|
`2.0.71` migration documents MQTT publish and shutdown ownership changes.
|
|
275
275
|
|
|
276
|
+
## State-preserving MQTT handover
|
|
277
|
+
|
|
278
|
+
Correlated-v2 MQTT handover in3.0.38 verifies a uniquely shared input boundary
|
|
279
|
+
before releasing the source, including a late subscriber that missed older
|
|
280
|
+
packets. Both peers need format3; mixed old/new formats refuse before freeze.
|
|
281
|
+
Retain semantic contracts, accepted-work drain and restart fences. Missing or
|
|
282
|
+
ambiguous overlap and unknown activation results stay blocked.
|
|
283
|
+
See the [protocol, example and limits](https://github.com/uns-openhub/uns-kit/blob/master/docs/mqtt-stateful-handover.md)
|
|
284
|
+
and the version-bounded3.0.38 entry in `MIGRATIONS.md`. This does not transfer
|
|
285
|
+
persistent queues or promise replay of broker losses.
|
|
286
|
+
|
|
276
287
|
## Optional configuration-owned process groups
|
|
277
288
|
|
|
278
289
|
Version3.0.29 adds optional shared contracts for immutable application-owned JSON
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const inputPreviewSchema: z.ZodArray<z.ZodString>;
|
|
3
|
+
export declare const inputCursorSchema: z.ZodObject<{
|
|
4
|
+
batchSize: z.ZodNumber;
|
|
5
|
+
referenceHash: z.ZodString;
|
|
6
|
+
processedTail: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
export type InputQueueCursor = z.infer<typeof inputCursorSchema>;
|
|
9
|
+
export declare function inputFingerprint(item: {
|
|
10
|
+
topic: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}): string;
|
|
13
|
+
/** Find an ordered common suffix. Neither a timestamp nor equal values alone authorize skipping. */
|
|
14
|
+
export declare function uniqueInputBoundary(processedTail: readonly string[], buffered: readonly string[]): number;
|
|
15
|
+
//# sourceMappingURL=input-queue-boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-queue-boundary.d.ts","sourceRoot":"","sources":["../../src/runtime/input-queue-boundary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,kBAAkB,yBAAuD,CAAC;AACvF,eAAO,MAAM,iBAAiB;;;;kBAI5B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEjF;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAezG"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const inputPreviewSchema = z.array(z.string().regex(/^[a-f0-9]{64}$/)).max(100);
|
|
4
|
+
export const inputCursorSchema = z.strictObject({
|
|
5
|
+
batchSize: z.number().int().min(0).max(100),
|
|
6
|
+
referenceHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
7
|
+
processedTail: inputPreviewSchema.optional(),
|
|
8
|
+
});
|
|
9
|
+
export function inputFingerprint(item) {
|
|
10
|
+
return createHash("sha256").update(JSON.stringify({ topic: item.topic, message: item.message })).digest("hex");
|
|
11
|
+
}
|
|
12
|
+
/** Find an ordered common suffix. Neither a timestamp nor equal values alone authorize skipping. */
|
|
13
|
+
export function uniqueInputBoundary(processedTail, buffered) {
|
|
14
|
+
if (processedTail.length === 0)
|
|
15
|
+
return 0;
|
|
16
|
+
for (let size = Math.min(processedTail.length, buffered.length); size > 0; size--) {
|
|
17
|
+
const tail = processedTail.slice(-size);
|
|
18
|
+
const matches = (items) => {
|
|
19
|
+
const ends = [];
|
|
20
|
+
for (let start = 0; start <= items.length - size; start++)
|
|
21
|
+
if (tail.every((value, offset) => items[start + offset] === value))
|
|
22
|
+
ends.push(start + size);
|
|
23
|
+
return ends;
|
|
24
|
+
};
|
|
25
|
+
if (matches(processedTail).length !== 1)
|
|
26
|
+
continue;
|
|
27
|
+
const ends = matches(buffered);
|
|
28
|
+
if (ends.length === 1)
|
|
29
|
+
return ends[0];
|
|
30
|
+
}
|
|
31
|
+
throw Error("MQTT input overlap is missing or ambiguous; successor stays passive");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=input-queue-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-queue-boundary.js","sourceRoot":"","sources":["../../src/runtime/input-queue-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;IACjD,aAAa,EAAE,kBAAkB,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC;AAEH,MAAM,UAAU,gBAAgB,CAAC,IAAwC;IACvE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjH,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,mBAAmB,CAAC,aAAgC,EAAE,QAA2B;IAC/F,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACzC,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QAClF,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,CAAC,KAAwB,EAAE,EAAE;YAC3C,MAAM,IAAI,GAAa,EAAE,CAAC;YAC1B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE;gBACvD,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,CAAC;oBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;YAC9F,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAClD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,KAAK,CAAC,qEAAqE,CAAC,CAAC;AACrF,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nexport const inputPreviewSchema = z.array(z.string().regex(/^[a-f0-9]{64}$/)).max(100);\nexport const inputCursorSchema = z.strictObject({\n batchSize: z.number().int().min(0).max(100),\n referenceHash: z.string().regex(/^[a-f0-9]{64}$/),\n processedTail: inputPreviewSchema.optional(),\n});\nexport type InputQueueCursor = z.infer<typeof inputCursorSchema>;\nexport function inputFingerprint(item: { topic: string; message: string }): string {\n return createHash(\"sha256\").update(JSON.stringify({ topic: item.topic, message: item.message })).digest(\"hex\");\n}\n\n/** Find an ordered common suffix. Neither a timestamp nor equal values alone authorize skipping. */\nexport function uniqueInputBoundary(processedTail: readonly string[], buffered: readonly string[]): number {\n if (processedTail.length === 0) return 0;\n for (let size = Math.min(processedTail.length, buffered.length); size > 0; size--) {\n const tail = processedTail.slice(-size);\n const matches = (items: readonly string[]) => {\n const ends: number[] = [];\n for (let start = 0; start <= items.length - size; start++)\n if (tail.every((value, offset) => items[start + offset] === value)) ends.push(start + size);\n return ends;\n };\n if (matches(processedTail).length !== 1) continue;\n const ends = matches(buffered);\n if (ends.length === 1) return ends[0];\n }\n throw Error(\"MQTT input overlap is missing or ambiguous; successor stays passive\");\n}\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const MQTT_HANDOVER_STATE_FORMAT =
|
|
2
|
+
export declare const MQTT_HANDOVER_STATE_FORMAT = 3;
|
|
3
3
|
export declare const MQTT_HANDOVER_STATE_MAX_BYTES: number;
|
|
4
4
|
export declare const mqttHandoverOfferSchema: z.ZodArray<z.ZodObject<{
|
|
5
5
|
instanceName: z.ZodString;
|
|
6
6
|
compatibility: z.ZodString;
|
|
7
|
+
inputPreview: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
8
|
}, z.core.$strict>>;
|
|
8
9
|
export declare const mqttProxyHandoverStateSchema: z.ZodObject<{
|
|
9
10
|
instanceName: z.ZodString;
|
|
@@ -18,6 +19,7 @@ export declare const mqttProxyHandoverStateSchema: z.ZodObject<{
|
|
|
18
19
|
cursor: z.ZodObject<{
|
|
19
20
|
batchSize: z.ZodNumber;
|
|
20
21
|
referenceHash: z.ZodString;
|
|
22
|
+
processedTail: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
23
|
}, z.core.$strict>;
|
|
22
24
|
}, z.core.$strict>;
|
|
23
25
|
export declare const mqttHandoverStateSchema: z.ZodObject<{
|
|
@@ -39,6 +41,7 @@ export declare const mqttHandoverStateSchema: z.ZodObject<{
|
|
|
39
41
|
cursor: z.ZodObject<{
|
|
40
42
|
batchSize: z.ZodNumber;
|
|
41
43
|
referenceHash: z.ZodString;
|
|
44
|
+
processedTail: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
42
45
|
}, z.core.$strict>;
|
|
43
46
|
}, z.core.$strict>>;
|
|
44
47
|
}, z.core.$strict>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mqtt-handover-state.d.ts","sourceRoot":"","sources":["../../src/runtime/mqtt-handover-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"mqtt-handover-state.d.ts","sourceRoot":"","sources":["../../src/runtime/mqtt-handover-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAC5C,eAAO,MAAM,6BAA6B,QAAc,CAAC;AAIzD,eAAO,MAAM,uBAAuB;;;;mBAI8E,CAAC;AACnH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;kBASvC,CAAC;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAMxE;AACD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAExE;AACD,eAAO,MAAM,4BAA4B;;kBAAoD,CAAC;AAC9F,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAS7E"}
|
|
@@ -3,9 +3,10 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { cloneApplicationHandoverState } from './application-handover-state.js';
|
|
6
|
+
import { inputCursorSchema, inputPreviewSchema } from './input-queue-boundary.js';
|
|
6
7
|
// Same contract for every SDK MQTT consumer. No application/profile selectors.
|
|
7
|
-
// Bound into peer compatibility so older
|
|
8
|
-
export const MQTT_HANDOVER_STATE_FORMAT =
|
|
8
|
+
// Bound into peer compatibility so older cursor/state readers reject before freeze.
|
|
9
|
+
export const MQTT_HANDOVER_STATE_FORMAT = 3;
|
|
9
10
|
export const MQTT_HANDOVER_STATE_MAX_BYTES = 1024 * 1024;
|
|
10
11
|
const text = z.string().min(1).max(65535).refine(v => !v.includes('\0'));
|
|
11
12
|
const scalar = z.union([z.number().finite(), z.string().max(65535)]);
|
|
@@ -13,13 +14,14 @@ const value = z.union([scalar, z.array(scalar).max(4096)]);
|
|
|
13
14
|
export const mqttHandoverOfferSchema = z.array(z.strictObject({
|
|
14
15
|
instanceName: z.string().min(1).max(256),
|
|
15
16
|
compatibility: z.string().regex(/^[a-f0-9]{64}$/),
|
|
17
|
+
inputPreview: inputPreviewSchema.optional(),
|
|
16
18
|
})).max(64).refine(offer => new Set(offer.map(p => p.instanceName)).size === offer.length, 'Duplicate MQTT proxy');
|
|
17
19
|
export const mqttProxyHandoverStateSchema = z.strictObject({
|
|
18
20
|
instanceName: z.string().min(1).max(256),
|
|
19
21
|
compatibility: z.string().regex(/^[a-f0-9]{64}$/),
|
|
20
22
|
values: z.array(z.strictObject({ topic: text, value, uom: z.string().max(256).optional(), timestamp: z.string().datetime() })).max(10000),
|
|
21
23
|
sequences: z.array(z.tuple([text, z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER)])).max(10000),
|
|
22
|
-
cursor:
|
|
24
|
+
cursor: inputCursorSchema,
|
|
23
25
|
}).superRefine((state, ctx) => {
|
|
24
26
|
for (const keys of [state.values.map(v => v.topic), state.sequences.map(v => v[0])])
|
|
25
27
|
if (new Set(keys).size !== keys.length)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mqtt-handover-state.js","sourceRoot":"","sources":["../../src/runtime/mqtt-handover-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"mqtt-handover-state.js","sourceRoot":"","sources":["../../src/runtime/mqtt-handover-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { cloneApplicationHandoverState } from './application-handover-state.js';\nimport { inputCursorSchema, inputPreviewSchema } from './input-queue-boundary.js';\n\n// Same contract for every SDK MQTT consumer. No application/profile selectors.\n// Bound into peer compatibility so older cursor/state readers reject before freeze.\nexport const MQTT_HANDOVER_STATE_FORMAT = 3;\nexport const MQTT_HANDOVER_STATE_MAX_BYTES = 1024 * 1024;\nconst text = z.string().min(1).max(65535).refine(v => !v.includes('\\0'));\nconst scalar = z.union([z.number().finite(), z.string().max(65535)]);\nconst value = z.union([scalar, z.array(scalar).max(4096)]);\nexport const mqttHandoverOfferSchema = z.array(z.strictObject({\n instanceName: z.string().min(1).max(256),\n compatibility: z.string().regex(/^[a-f0-9]{64}$/),\n inputPreview: inputPreviewSchema.optional(),\n})).max(64).refine(offer => new Set(offer.map(p => p.instanceName)).size === offer.length, 'Duplicate MQTT proxy');\nexport const mqttProxyHandoverStateSchema = z.strictObject({\n instanceName: z.string().min(1).max(256),\n compatibility: z.string().regex(/^[a-f0-9]{64}$/),\n values: z.array(z.strictObject({ topic: text, value, uom: z.string().max(256).optional(), timestamp: z.string().datetime() })).max(10000),\n sequences: z.array(z.tuple([text, z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER)])).max(10000),\n cursor: inputCursorSchema,\n}).superRefine((state, ctx) => {\n for (const keys of [state.values.map(v => v.topic), state.sequences.map(v => v[0])])\n if (new Set(keys).size !== keys.length) ctx.addIssue({ code: 'custom', message: 'Duplicate MQTT state key' });\n});\nexport const mqttHandoverStateSchema = z.strictObject({\n protocol: z.literal(1),\n application: z.strictObject({ contract: z.string().min(1).max(128), data: z.unknown() }).optional(),\n proxies: z.array(mqttProxyHandoverStateSchema).max(64),\n}).superRefine((state, ctx) => {\n if (new Set(state.proxies.map(p => p.instanceName)).size !== state.proxies.length)\n ctx.addIssue({ code: 'custom', message: 'Duplicate MQTT proxy' });\n});\nexport type MqttProxyHandoverState = z.infer<typeof mqttProxyHandoverStateSchema>;\nexport type MqttHandoverState = z.infer<typeof mqttHandoverStateSchema>;\nexport function parseMqttHandoverState(input: unknown): MqttHandoverState {\n const json = JSON.stringify(input);\n if (!json || Buffer.byteLength(json) > MQTT_HANDOVER_STATE_MAX_BYTES) throw Error('MQTT handover state exceeds its bound');\n const state = mqttHandoverStateSchema.parse(input);\n if (state.application) state.application.data = cloneApplicationHandoverState(state.application.data);\n return state;\n}\nexport function mqttHandoverStateDigest(state: MqttHandoverState): string {\n return createHash('sha256').update(JSON.stringify(parseMqttHandoverState(state))).digest('hex');\n}\nexport const mqttHandoverCapabilitySchema = z.strictObject({ protocolVersion: z.literal(2) });\nexport function readMqttHandoverCapability(packageRoot: string): 2 | undefined {\n const file = path.join(packageRoot, 'package.json');\n let stat: fs.Stats;\n try { stat = fs.lstatSync(file); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined; throw error; }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1048576) throw Error('Unsafe MQTT handover descriptor');\n const descriptor = JSON.parse(fs.readFileSync(file, 'utf8'))?.unsDatahub?.mqttHandover;\n if (descriptor === undefined) return undefined;\n mqttHandoverCapabilitySchema.parse(descriptor);\n return 2;\n}\n"]}
|
|
@@ -34,7 +34,10 @@ export declare class HandoverManager {
|
|
|
34
34
|
private stateDigest;
|
|
35
35
|
private acceptedStateDigest;
|
|
36
36
|
private targetPreparation;
|
|
37
|
+
private targetStateApplied;
|
|
37
38
|
private finRetry;
|
|
39
|
+
private requestRetry;
|
|
40
|
+
private sourceReviewInFlight;
|
|
38
41
|
handoverRequestEnabled: boolean;
|
|
39
42
|
handoverEnabled: boolean;
|
|
40
43
|
forceStartEnabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handover-manager.d.ts","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGxH,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAElD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQhD;;;;GAIG;AACH,qBAAa,eAAe;
|
|
1
|
+
{"version":3,"file":"handover-manager.d.ts","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGxH,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAElD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQhD;;;;GAIG;AACH,qBAAa,eAAe;IAwCxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAxC7B,KAAK,EAAE,2BAA2B,CAAC,qBAAqB,CAAC,CAA4D;IAC5H,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAClE,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,oBAAoB,CAAS;IAC9B,sBAAsB,EAAE,OAAO,CAAS;IACxC,eAAe,EAAE,OAAO,CAAQ;IAChC,iBAAiB,EAAE,OAAO,CAAS;gBAGxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,YAAY,EAAE,EAC9B,sBAAsB,EAAE,OAAO,EAC/B,eAAe,EAAE,OAAO,EACxB,iBAAiB,EAAE,OAAO,EAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,qBAAqB,EACpB,gBAAgB,UAAQ,EACxB,gBAAgB,CAAC,EAAE,wBAAwB;IAkC9D,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,oBAAoB;IAU5B;;;OAGG;IACU,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+FxE;;OAEG;IACH,OAAO,CAAC,UAAU;YAIJ,qBAAqB;YAiFrB,iBAAiB;YAOjB,mBAAmB;YA8BnB,mBAAmB;YAQnB,qBAAqB;YAWrB,oBAAoB;YA2BpB,cAAc;CA+I7B"}
|
|
@@ -40,7 +40,10 @@ export class HandoverManager {
|
|
|
40
40
|
stateDigest;
|
|
41
41
|
acceptedStateDigest;
|
|
42
42
|
targetPreparation;
|
|
43
|
+
targetStateApplied = false;
|
|
43
44
|
finRetry;
|
|
45
|
+
requestRetry;
|
|
46
|
+
sourceReviewInFlight = false;
|
|
44
47
|
handoverRequestEnabled = false;
|
|
45
48
|
handoverEnabled = true;
|
|
46
49
|
forceStartEnabled = false;
|
|
@@ -191,16 +194,35 @@ export class HandoverManager {
|
|
|
191
194
|
},
|
|
192
195
|
});
|
|
193
196
|
logger.info(`${this.processName} - Requesting handover in 10 seconds.`);
|
|
194
|
-
setTimeout(
|
|
195
|
-
logger.info(`${this.processName} - Requesting handover ${eventHandoverTopic}.`);
|
|
197
|
+
setTimeout(() => {
|
|
196
198
|
this.handoverInProgress = true;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
199
|
+
let retries = 0;
|
|
200
|
+
const request = async () => {
|
|
201
|
+
if (this.targetPreparation || this.acceptedStateDigest) {
|
|
202
|
+
clearInterval(this.requestRetry);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const offer = this.statefulHandover ? await Promise.all(this.unsMqttProxies.map(proxy => proxy.getHandoverOffer())) : undefined;
|
|
207
|
+
await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload("handover_request", undefined, this.statefulHandover ? { offer, applicationContract: this.applicationState?.contract } : {}), {
|
|
208
|
+
retain: false, properties: { responseTopic: this.topicBuilder.getHandoverTopic(), userProperties: this.getUserProperties() },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
logger.warn("MQTT input boundary preparation is not ready; successor remains passive");
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
void request();
|
|
216
|
+
if (this.statefulHandover) {
|
|
217
|
+
this.requestRetry = setInterval(() => {
|
|
218
|
+
if (++retries > 15 || this.targetPreparation || this.acceptedStateDigest) {
|
|
219
|
+
clearInterval(this.requestRetry);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
void request();
|
|
223
|
+
}, 1000);
|
|
224
|
+
this.requestRetry.unref();
|
|
225
|
+
}
|
|
204
226
|
}, 10000);
|
|
205
227
|
}
|
|
206
228
|
else {
|
|
@@ -265,7 +287,7 @@ export class HandoverManager {
|
|
|
265
287
|
data: cloneApplicationHandoverState(this.applicationState.exportState()) } : undefined;
|
|
266
288
|
if (application)
|
|
267
289
|
this.applicationState.validateState(application.data);
|
|
268
|
-
this.preparedState = parseMqttHandoverState({ protocol: 1, ...(application ? { application } : {}), proxies: this.unsMqttProxies.map((proxy, i) => proxy.exportHandoverState({ batchSize: mqttWorkerData[i].batchSize, referenceHash: mqttWorkerData[i].referenceHash })) });
|
|
290
|
+
this.preparedState = parseMqttHandoverState({ protocol: 1, ...(application ? { application } : {}), proxies: this.unsMqttProxies.map((proxy, i) => proxy.exportHandoverState({ batchSize: mqttWorkerData[i].batchSize, referenceHash: mqttWorkerData[i].referenceHash, ...(mqttWorkerData[i].processedTail ? { processedTail: mqttWorkerData[i].processedTail } : {}) })) });
|
|
269
291
|
this.stateDigest = mqttHandoverStateDigest(this.preparedState);
|
|
270
292
|
await this.sendPreparedState(event, handoverId);
|
|
271
293
|
let retries = 0;
|
|
@@ -333,6 +355,9 @@ export class HandoverManager {
|
|
|
333
355
|
// Validate every cursor before importing any state or activating any subscriber.
|
|
334
356
|
for (const snapshot of state.proxies)
|
|
335
357
|
await byName.get(snapshot.instanceName).prepareHandoverResume(snapshot.cursor);
|
|
358
|
+
// After this point an import/activation error has an unknown effect. Only
|
|
359
|
+
// read-only boundary preparation may be retried on a repeated FIN.
|
|
360
|
+
this.targetStateApplied = true;
|
|
336
361
|
for (const snapshot of state.proxies)
|
|
337
362
|
byName.get(snapshot.instanceName).importHandoverState(snapshot);
|
|
338
363
|
if (state.application)
|
|
@@ -434,6 +459,23 @@ export class HandoverManager {
|
|
|
434
459
|
const own = this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility());
|
|
435
460
|
if (offer.length !== own.length || own.some(p => !offer.some(o => o.instanceName === p.instanceName && o.compatibility === p.compatibility)))
|
|
436
461
|
throw Error("MQTT source/destination or semantic contract differs; source remains active");
|
|
462
|
+
if (this.sourceReviewInFlight)
|
|
463
|
+
return;
|
|
464
|
+
this.sourceReviewInFlight = true;
|
|
465
|
+
try {
|
|
466
|
+
// Hints only: actual frozen cursors are revalidated by every target
|
|
467
|
+
// worker before activation. A quiet/unready source keeps admission.
|
|
468
|
+
for (const proxy of this.unsMqttProxies) {
|
|
469
|
+
const preview = offer.find(candidate => candidate.instanceName === proxy.instanceName).inputPreview;
|
|
470
|
+
if (!await proxy.canPrepareHandoverInput(preview))
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
this.sourceReviewInFlight = false;
|
|
476
|
+
}
|
|
477
|
+
if (this.sourcePeerId || !this.active)
|
|
478
|
+
return;
|
|
437
479
|
}
|
|
438
480
|
this.sourcePeerId = peerId;
|
|
439
481
|
this.acceptedHandoverId = this.getHandoverId(response, event);
|
|
@@ -477,7 +519,15 @@ export class HandoverManager {
|
|
|
477
519
|
return;
|
|
478
520
|
if (this.acceptedStateDigest && response.stateDigest !== this.acceptedStateDigest)
|
|
479
521
|
return;
|
|
480
|
-
this.
|
|
522
|
+
clearInterval(this.requestRetry);
|
|
523
|
+
if (!this.targetPreparation) {
|
|
524
|
+
const preparation = this.acceptPreparedState(response);
|
|
525
|
+
this.targetPreparation = preparation;
|
|
526
|
+
void preparation.catch(() => {
|
|
527
|
+
if (!this.targetStateApplied && this.targetPreparation === preparation)
|
|
528
|
+
this.targetPreparation = undefined;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
481
531
|
await this.targetPreparation;
|
|
482
532
|
// Duplicate fin only replays the receipt; it never reapplies a baseline.
|
|
483
533
|
if (response.stateDigest !== this.acceptedStateDigest)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handover-manager.js","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAiC,MAAM,0CAA0C,CAAC;AACxH,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,uBAAuB,EAA0B,MAAM,mCAAmC,CAAC;AACrJ,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAA8B,MAAM,wBAAwB,CAAC;AAGnH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnE,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAqCP;IACA;IArCZ,KAAK,GAAuD,IAAI,2BAA2B,EAAyB,CAAC;IACpH,WAAW,CAAS;IACpB,SAAS,CAAS;IAClB,SAAS,CAAY;IACrB,cAAc,CAAiB;IAC/B,kBAAkB,GAAY,KAAK,CAAC;IACpC,kBAAkB,GAAY,KAAK,CAAC;IACpC,YAAY,CAAmB;IAC/B,aAAa,CAA6B;IACjC,UAAU,CAAqB;IACxC,kBAAkB,CAAqB;IACvC,MAAM,GAAY,KAAK,CAAC;IACxB,YAAY,CAAqB;IACjC,iBAAiB,CAA4B;IAC7C,UAAU,CAA4B;IAC7B,aAAa,CAAoC;IAC1D,gBAAgB,CAAqB;IACrC,aAAa,CAAgC;IAC7C,WAAW,CAAqB;IAChC,mBAAmB,CAAqB;IACxC,iBAAiB,CAA4B;IAC7C,QAAQ,CAA6B;IACtC,sBAAsB,GAAY,KAAK,CAAC;IACxC,eAAe,GAAY,IAAI,CAAC;IAChC,iBAAiB,GAAY,KAAK,CAAC;IAE1C,YACE,WAAmB,EACnB,SAAiB,EACjB,SAAoB,EACpB,cAA8B,EAC9B,sBAA+B,EAC/B,eAAwB,EACxB,iBAA0B,EAC1B,UAAmB,EACnB,aAAqC,EACpB,mBAAmB,KAAK,EACxB,gBAA2C;QAD3C,qBAAgB,GAAhB,gBAAgB,CAAQ;QACxB,qBAAgB,GAAhB,gBAAgB,CAA2B;QAE5D,wBAAwB,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QACrD,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAElD,iCAAiC;QACjC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC;QACtC,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,IAAI,gBAAgB,CACtC,aAAa,gBAAgB,CAAC,iBAAiB,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CACvK,CAAC;QAEF,gFAAgF;QAChF,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YACzC,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,UAAU;gBAAE,OAAO,CAAC,4CAA4C;YAClG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qFAAqF,CAAC,CAAC;YACtH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;YACpE,IAAI,CAAC;gBAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAAC,CAAC;YACzC,MAAM,CAAC;gBAAC,MAAM,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YACvF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5D,yEAAyE;YACzE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAC5E,CAAC,EAAE,cAAc,CAAC,CAAC;IACrB,CAAC;IAEO,iBAAiB,CAAC,UAAmB;QAC3C,MAAM,UAAU,GAA2B;YACzC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;QACF,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,UAAU;YAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC;QACnD,OAAO,UAAU,CAAC;IACpB,CAAC;IAEO,eAAe,CACrB,IAAY,EACZ,UAAmB,EACnB,SAAkC,EAAE;QAEpC,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,IAAI;YACJ,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,GAAG,MAAM;YACT,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;IACL,CAAC;IAEO,aAAa,CAAC,QAAiB,EAAE,KAAyB;QAChE,MAAM,WAAW,GAAG,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtF,CAAC,CAAE,QAAqC,CAAC,UAAU;YACnD,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,OAAO,WAAW,KAAK,QAAQ;YAAE,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC7E,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,CAAC;QAC5E,OAAO,OAAO,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,CAAC;IAEO,uBAAuB,CAAC,kBAAsC;QACpE,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,KAAK,kBAAkB,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qDAAqD,CAAC,CAAC;QACtF,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,kBAAkB,CAAC,KAAyB;QAClD,OAAO,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,CAAC;IAC7D,CAAC;IAEO,gBAAgB,CAAC,OAAe;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC;YAClE,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBACvE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAa;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,AAAD,EAAG,OAAO,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAC3C,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5D,MAAM,eAAe,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC9E,MAAM,eAAe,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7E,OAAO,GAAG,KAAK,eAAe,IAAI,OAAO,KAAK,eAAe,CAAC;IAChE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,iBAAiB,CAAC,KAAyB;QACtD,IAAI,CAAC;YACH,8FAA8F;YAC9F,IAAI,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;gBAC9I,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBACvD,IAAI,eAAe,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvC,OAAO;gBACT,CAAC;gBACD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACpE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;oBACzB,OAAO;gBACT,CAAC;gBACD,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,eAAe,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,UAAU,OAAO,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;gBAE/F,IAAI,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;oBAClC,uEAAuE;oBACvE,sEAAsE;oBACtE,qEAAqE;oBACrE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uEAAuE,CAAC,CAAC;oBACxG,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;oBAC9B,OAAO;gBACT,CAAC;gBAED,IAAI,IAAI,CAAC,sBAAsB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACxD,oBAAoB;oBACpB,6EAA6E;oBAC7E,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,qGAAqG;oBACvI,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;oBAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC5D,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;oBAC/B,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;oBACxC,MAAM,kBAAkB,GAAG,IAAI,gBAAgB,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;oBACnH,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE;wBACxF,MAAM,EAAE,KAAK;wBACb,UAAU,EAAE;4BACV,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE;yBACzC;qBACF,CAAC,CAAC;oBACH,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uCAAuC,CAAC,CAAC;oBACxE,UAAU,CAAC,KAAK,IAAI,EAAE;wBACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,0BAA0B,kBAAkB,GAAG,CAAC,CAAC;wBAChF,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;wBAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;4BACtQ,MAAM,EAAE,KAAK;4BACb,UAAU,EAAE;gCACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;gCACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE;6BACzC;yBACF,CAAC,CAAC;oBACL,CAAC,EAAE,KAAK,CAAC,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;wBAC3B,6DAA6D;wBAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,gCAAgC,CAAC,CAAC;wBACjE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,mFAAmF,CAAC,CAAC;wBACpH,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,qGAAqG;wBACvI,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;wBAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;4BAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;wBACpE,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;wBACjC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;wBACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;wBAC5D,8DAA8D;wBAC9D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;oBAC5E,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,6CAA6C,KAAK,CAAC,KAAK,qBAAqB,CAAC,CAAC;wBAC9G,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,wEAAwE;YACxE,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACjF,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBACvD,IAAI,eAAe,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,qCAAqC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;IACH,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,IAAY;QAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,QAAa,EAAE,KAAyB;QAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEvD,MAAM,CAAC,IAAI,CACT,GAAG,IAAI,CAAC,WAAW,qCAAqC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,uBAAuB,CACrI,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,CAAC;QAElC,yEAAyE;QACzE,MAAM,cAAc,GAA8B,EAAE,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,iCAAiC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC3F,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,sFAAsF;YACtF,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;YAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ;gBACpF,IAAI,EAAE,6BAA6B,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACzF,IAAI,WAAW;gBAAE,IAAI,CAAC,gBAAiB,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACxE,IAAI,CAAC,aAAa,GAAG,sBAAsB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAChJ,KAAK,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5H,IAAI,CAAC,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/D,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAChD,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC/B,IAAI,EAAE,OAAO,GAAG,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAAC,OAAO;gBAAC,CAAC;gBAChF,KAAK,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACrH,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtB,gFAAgF;YAChF,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qEAAqE,CAAC,CAAC;QAEtG,uFAAuF;QACvF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,UAAU,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,qBAAqB,EAAE,UAAU,EAAE;oBACtD,SAAS,EAAE,UAAU,CAAC,SAAS;oBAC/B,aAAa,EAAE,UAAU,CAAC,aAAa;oBACvC,YAAY,EAAE,UAAU,CAAC,YAAY;iBACtC,CAAC,EACF;oBACE,MAAM,EAAE,KAAK;oBACb,UAAU,EAAE;wBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;wBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;qBACnD;iBACF,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uCAAuC,CAAC,CAAC;QAExE,yDAAyD;QACzD,8CAA8C;QAC9C,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC,EAChD;YACE,MAAM,EAAE,KAAK;YACb,UAAU,EAAE;gBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;gBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;aACnD;SACF,CACF,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,CAAC,CAAC;QAEhE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,QAAsB,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1F,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,KAAyB,EAAE,UAAmB;QAC5E,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,EACxH,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE;SAC/I,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,QAAa;QAC7C,MAAM,KAAK,GAAG,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,MAAM,KAAK,QAAQ,CAAC,WAAW;YAAE,MAAM,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACxF,IAAI,KAAK,CAAC,WAAW,EAAE,QAAQ,KAAK,IAAI,CAAC,gBAAgB,EAAE,QAAQ;YAAE,MAAM,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC1H,IAAI,KAAK,CAAC,WAAW;YAAE,IAAI,CAAC,gBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,cAAc,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YAC5I,MAAM,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACjD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACzG,iFAAiF;QACjF,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtH,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACvG,IAAI,KAAK,CAAC,WAAW;YAAE,MAAM,IAAI,CAAC,gBAAiB,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;YAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;QACpE,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC,sBAAsB,EAAE,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC/B,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,EAAE,CAAC;QAAC,CAAC;QACjD,MAAM,CAAC;YACL,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YAAC,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,oDAAoD,CAAC,CAAC;QACzG,yEAAyE;QACzE,4EAA4E;QAC5E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxC,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAS,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,CAAC;IACxH,CAAC;IAEO,KAAK,CAAC,oBAAoB;QAChC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,IAAI,EAAE;YAClD,MAAM,IAAI,CAAC,iBAAiB,CAAC;YAC7B,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,uEAAuE;gBACvE,sEAAsE;gBACrE,OAAuE,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC7G,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,kBAAkB;oBAChD,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,CAAC;oBAChD,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,IAAI,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,sDAAsD,CAAC,CAAC;YACvF,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,kFAAkF;YAClF,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,4BAA4B,MAAM,kCAAkC,CAAC,CAAC;YACtG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAAyB;QACpD,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO;gBAAE,OAAO;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACtD,gFAAgF;YAChF,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,gBAAgB;gBAAE,OAAO;YAChE,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAAE,OAAO;YACpG,oBAAoB;YACpB,+FAA+F;YAC/F,IAAI,QAAQ,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAC9C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC;gBAC3D,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,IAAI,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnJ,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;oBAAC,OAAO;gBACvE,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,CAAC,UAAU;oBAAE,OAAO;gBAC1G,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAC1B,kEAAkE;oBAClE,IAAI,QAAQ,CAAC,mBAAmB,KAAK,IAAI,CAAC,gBAAgB,EAAE,QAAQ;wBAAE,MAAM,KAAK,CAAC,8DAA8D,CAAC,CAAC;oBAClJ,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBAC1B,MAAM,WAAW,GAAG,6BAA6B,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAC;wBACvF,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;oBACnD,CAAC;oBACD,MAAM,KAAK,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,CAAC;oBAC/E,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,aAAa,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC;wBAC1I,MAAM,KAAK,CAAC,6EAA6E,CAAC,CAAC;gBAC/F,CAAC;gBACD,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;gBAC3B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAC9D,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;gBAC/B,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACjC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;gBAC/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACrE,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,iBAAiB,CAAC;gBAC/B,CAAC;gBAAC,MAAM,CAAC;oBACP,sFAAsF;oBACtF,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,mEAAmE,CAAC,CAAC;oBACrG,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,gBAAgB;wBAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;YAED,oBAAoB;YACpB,iGAAiG;YACjG,qCAAqC;YACrC,IAAI,QAAQ,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBAC5C,IAAI,IAAI,CAAC,gBAAgB;oBAAE,OAAO;gBAClC,IAAI,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC9B,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAAE,OAAO;gBAC/E,2EAA2E;gBAC3E,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAsB,EAAE,EAAE;oBACrD,IAAI,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;wBACpD,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;oBAC3E,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;YAED,oBAAoB;YACpB,oFAAoF;YACpF,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACrC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAC/C,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,gBAAgB;wBAAE,OAAO;oBAChJ,IAAI,IAAI,CAAC,mBAAmB,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB;wBAAE,OAAO;oBAC1F,IAAI,CAAC,iBAAiB,KAAK,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;oBAC9D,MAAM,IAAI,CAAC,iBAAiB,CAAC;oBAC7B,yEAAyE;oBACzE,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB;wBAAE,OAAO;oBAC9D,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,EAAE,EAChH,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBACrI,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACvD,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC;oBAAE,OAAO;gBACtD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,iCAAiC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,GAAG,CAAC,CAAC;gBAE1H,2EAA2E;gBAC3E,mCAAmC;gBACnC,mCAAmC;gBAEnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC5D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,wBAAwB,CAAC,CAAC;gBACzD,8DAA8D;gBAC9D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;oBACvC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;oBAC9B,QAAQ,CAAC,mBAAmB,EAAE,CAAC;gBACjC,CAAC,CAAC,CAAC;gBAEH,2CAA2C;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC,EAChD;oBACE,MAAM,EAAE,KAAK;oBACb,UAAU,EAAE;wBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;wBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;qBACnD;iBACF,CACF,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,CAAC,CAAC;YAClE,CAAC;YAED,oBAAoB;YACpB,6EAA6E;YAC7E,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACrC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACvD,IAAI,IAAI,CAAC,kBAAkB,IAAI,UAAU,IAAI,IAAI,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;oBACpF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,oDAAoD,CAAC,CAAC;oBACrF,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,kBAAkB,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;oBAAE,OAAO;gBAChJ,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,iCAAiC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,GAAG,CAAC,CAAC;gBAC1H,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAChD,MAAM,IAAI,CAAC,UAAU,CAAC;YACxB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,0CAA0C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;CACF","sourcesContent":["import { cloneApplicationHandoverState, type ApplicationHandoverState } from \"../runtime/application-handover-state.js\";\nimport { mqttHandoverOfferSchema, parseMqttHandoverState, mqttHandoverStateDigest, type MqttHandoverState } from \"../runtime/mqtt-handover-state.js\";\nimport logger from \"../logger.js\";\nimport { runHandoverShutdown, validateHandoverShutdown, type HandoverShutdownHooks } from \"./handover-shutdown.js\";\nimport { HandoverManagerEvents } from \"../uns-mqtt/mqtt-interfaces.js\";\nimport MqttProxy from \"../uns-mqtt/mqtt-proxy.js\";\nimport { MqttTopicBuilder } from \"../uns-mqtt/mqtt-topic-builder.js\";\nimport UnsMqttProxy from \"../uns-mqtt/uns-mqtt-proxy.js\";\nimport { HandoverManagerEventEmitter } from \"./handover-manager-event-emitter.js\";\nimport { ACTIVE_TIMEOUT, PACKAGE_INFO } from \"./process-config.js\";\nimport { UnsEvents } from \"./uns-interfaces.js\";\n\nfunction normalizeHandoverId(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.trim();\n return normalized.length > 0 && normalized.length <= 128 ? normalized : undefined;\n}\n\n/**\n * HandoverManager is responsible for all handover-related logic,\n * including handling incoming MQTT messages, issuing handover requests,\n * and processing handover responses.\n */\nexport class HandoverManager {\n public event: HandoverManagerEventEmitter<HandoverManagerEvents> = new HandoverManagerEventEmitter<HandoverManagerEvents>();\n private processName: string;\n private processId: string;\n private mqttProxy: MqttProxy;\n private unsMqttProxies: UnsMqttProxy[];\n private requestingHandover: boolean = false;\n private handoverInProgress: boolean = false;\n private topicBuilder: MqttTopicBuilder;\n private activeTimeout: NodeJS.Timeout | undefined;\n private readonly handoverId: string | undefined;\n private acceptedHandoverId: string | undefined;\n private active: boolean = false;\n private sourcePeerId: string | undefined;\n private sourcePreparation: Promise<void> | undefined;\n private sourceExit: Promise<void> | undefined;\n private readonly shutdownHooks: HandoverShutdownHooks | undefined;\n private expectedSourceId: string | undefined;\n private preparedState: MqttHandoverState | undefined;\n private stateDigest: string | undefined;\n private acceptedStateDigest: string | undefined;\n private targetPreparation: Promise<void> | undefined;\n private finRetry: NodeJS.Timeout | undefined;\n public handoverRequestEnabled: boolean = false;\n public handoverEnabled: boolean = true;\n public forceStartEnabled: boolean = false;\n\n constructor(\n processName: string,\n processId: string,\n mqttProxy: MqttProxy,\n unsMqttProxies: UnsMqttProxy[],\n handoverRequestEnabled: boolean,\n handoverEnabled: boolean,\n forceStartEnabled: boolean,\n handoverId?: string,\n shutdownHooks?: HandoverShutdownHooks,\n private readonly statefulHandover = false,\n private readonly applicationState?: ApplicationHandoverState,\n ) {\n validateHandoverShutdown(shutdownHooks);\n this.shutdownHooks = shutdownHooks ? { ...shutdownHooks } : undefined;\n this.processName = processName;\n this.processId = processId;\n this.mqttProxy = mqttProxy;\n this.unsMqttProxies = unsMqttProxies;\n this.handoverRequestEnabled = handoverRequestEnabled;\n this.handoverEnabled = handoverEnabled;\n this.forceStartEnabled = forceStartEnabled;\n this.handoverId = normalizeHandoverId(handoverId);\n\n // Instantiate the topic builder.\n const packageName = PACKAGE_INFO.name;\n const version = PACKAGE_INFO.version;\n this.topicBuilder = new MqttTopicBuilder(\n `uns-infra/${MqttTopicBuilder.sanitizeTopicPart(packageName)}/${MqttTopicBuilder.sanitizeTopicPart(version)}/${MqttTopicBuilder.sanitizeTopicPart(this.processName)}/`,\n );\n\n // Set status as active after a timeout if no other active process are detected.\n this.activeTimeout = setTimeout(async () => {\n if (this.statefulHandover && this.handoverId) return; // A reviewed successor never self-promotes.\n logger.info(`${this.processName} - No active message received within timeout. Assuming no other process is running.`);\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n try { await this.activateApplication(); }\n catch { logger.error(\"Runtime activation failed; successor remains passive\"); return; }\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n // Publishers were prepared for application activation; input opens last.\n this.unsMqttProxies.forEach((unsProxy) => unsProxy.setSubscriberActive());\n }, ACTIVE_TIMEOUT);\n }\n\n private getUserProperties(handoverId?: string): Record<string, string> {\n const properties: Record<string, string> = {\n processName: this.processName,\n processId: this.processId,\n };\n const normalized = normalizeHandoverId(handoverId ?? this.handoverId);\n if (normalized) properties.handoverId = normalized;\n return properties;\n }\n\n private handoverPayload(\n type: string,\n handoverId?: string,\n fields: Record<string, unknown> = {},\n ): string {\n const normalized = normalizeHandoverId(handoverId ?? this.handoverId);\n return JSON.stringify({\n type,\n ...(this.statefulHandover ? { protocol: 2 } : {}),\n ...fields,\n ...(normalized ? { handoverId: normalized } : {}),\n });\n }\n\n private getHandoverId(response: unknown, event: UnsEvents[\"input\"]): string | undefined {\n const fromPayload = response && typeof response === \"object\" && !Array.isArray(response)\n ? (response as { handoverId?: unknown }).handoverId\n : undefined;\n if (typeof fromPayload === \"string\") return normalizeHandoverId(fromPayload);\n const fromProperties = event.packet?.properties?.userProperties?.handoverId;\n return typeof fromProperties === \"string\" ? normalizeHandoverId(fromProperties) : undefined;\n }\n\n private acceptsHandoverResponse(receivedHandoverId: string | undefined): boolean {\n if (!this.handoverId || !receivedHandoverId) {\n return true;\n }\n if (this.handoverId === receivedHandoverId) {\n return true;\n }\n logger.warn(`${this.processName} - Ignoring handover message for another migration.`);\n return false;\n }\n\n private getSourceProcessId(event: UnsEvents[\"input\"]): string | undefined {\n return event.packet?.properties?.userProperties?.processId;\n }\n\n private parseActiveValue(message: string): boolean | null {\n try {\n const parsed = JSON.parse(message);\n const value = parsed?.message?.data?.value ?? parsed?.data?.value;\n if (value === 1 || value === \"1\" || value === true || value === \"true\") {\n return true;\n }\n if (value === 0 || value === \"0\" || value === false || value === \"false\") {\n return false;\n }\n return null;\n } catch {\n return null;\n }\n }\n\n private isProcessActiveTopic(topic: string): boolean {\n const parts = topic.split(\"/\");\n if (parts.length !== 5) return false;\n const [root, pkg, , process, tail] = parts;\n if (root !== \"uns-infra\" || tail !== \"active\") return false;\n const expectedPackage = MqttTopicBuilder.sanitizeTopicPart(PACKAGE_INFO.name);\n const expectedProcess = MqttTopicBuilder.sanitizeTopicPart(this.processName);\n return pkg === expectedPackage && process === expectedProcess;\n }\n\n /**\n * Main entry point for handling incoming MQTT messages.\n * It checks the topic and delegates to the corresponding handler.\n */\n public async handleMqttMessage(event: UnsEvents[\"input\"]): Promise<void> {\n try {\n // Check if the packet is active messages from other processes and this process is not active.\n if (this.isProcessActiveTopic(event.topic) && this.requestingHandover === false && this.active === false && this.handoverInProgress === false) {\n const sourceProcessId = this.getSourceProcessId(event);\n if (sourceProcessId === this.processId) {\n return;\n }\n const activeValue = this.parseActiveValue(event.message.toString());\n if (activeValue !== true) {\n return;\n }\n const sourceInfo = sourceProcessId ? ` (processId=${sourceProcessId})` : \"\";\n logger.info(`${this.processName} - Another process is active${sourceInfo} on ${event.topic}.`);\n\n if (event.packet?.retain === true) {\n // A retained heartbeat can belong to a process that crashed before its\n // expiry. Wait for a fresh non-retained heartbeat before asking it to\n // hand over; otherwise a stale retained message would block startup.\n logger.info(`${this.processName} - Retained active heartbeat observed; waiting for a fresh heartbeat.`);\n this.activeTimeout?.refresh();\n return;\n }\n\n if (this.handoverRequestEnabled && this.handoverEnabled) {\n // Requester process\n // Publish a handover request message after 10 seconds to the handover topic.\n clearTimeout(this.activeTimeout); // Clear the active timeout if it exists - prevent this process from becoming active after a timeout.\n this.activeTimeout = undefined;\n this.event.emit(\"handoverManager\", { active: this.active });\n this.requestingHandover = true;\n this.expectedSourceId = sourceProcessId;\n const eventHandoverTopic = new MqttTopicBuilder(MqttTopicBuilder.extractBaseTopic(event.topic)).getHandoverTopic();\n await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload(\"handover_intent\"), {\n retain: false,\n properties: {\n userProperties: this.getUserProperties(),\n },\n });\n logger.info(`${this.processName} - Requesting handover in 10 seconds.`);\n setTimeout(async () => {\n logger.info(`${this.processName} - Requesting handover ${eventHandoverTopic}.`);\n this.handoverInProgress = true;\n await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload(\"handover_request\", undefined, this.statefulHandover ? { offer: this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility()), applicationContract: this.applicationState?.contract } : {}), {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(),\n },\n });\n }, 10000);\n } else {\n if (this.forceStartEnabled) {\n // Force start the process even if another process is active.\n logger.info(`${this.processName} - Force starting the process.`);\n logger.warn(`${this.processName} - Warning: Source and destination being the same may lead to duplicate messages.`);\n clearTimeout(this.activeTimeout); // Clear the active timeout if it exists - prevent this process from becoming active after a timeout.\n this.activeTimeout = undefined;\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n await this.activateApplication();\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n // Activate all UNS proxy instance publishers and subscribers.\n this.unsMqttProxies.forEach((unsProxy) => unsProxy.setSubscriberActive());\n } else {\n logger.info(`${this.processName} - Waiting for the other process on topic ${event.topic} to become passive.`);\n this.activeTimeout?.refresh();\n }\n }\n }\n\n // Check if the packet is an handover message, sent to a handover topic.\n if (event.topic === this.topicBuilder.getHandoverTopic() && this.handoverEnabled) {\n const sourceProcessId = this.getSourceProcessId(event);\n if (sourceProcessId !== this.processId) {\n await this.handleHandover(event);\n }\n }\n } catch (error) {\n logger.error(`${this.processName} - Error processing MQTT message: ${error.message}`);\n return;\n }\n }\n\n /**\n * Handles handovers.\n */\n private exitSource(code: number): void {\n process.exit(code);\n }\n\n private async prepareSourceHandover(response: any, event: UnsEvents[\"input\"]): Promise<void> {\n const handoverId = this.getHandoverId(response, event);\n\n logger.info(\n `${this.processName} - Received handover request from ${event.packet?.properties?.userProperties?.processName}. Accepting handover.`,\n );\n\n this.active = false;\n this.event.emit(\"handoverManager\", { active: false });\n this.shutdownHooks?.onRelease?.();\n\n // Set all UNS proxy instance subscribers to passive and drain the queue.\n const mqttWorkerData: UnsEvents[\"mqttWorker\"][] = [];\n for (let i = 0; i < this.unsMqttProxies.length; i++) {\n const unsProxy = this.unsMqttProxies[i];\n const workerData = await unsProxy.setSubscriberPassiveAndDrainQueue(this.statefulHandover);\n mqttWorkerData.push(workerData);\n }\n if (this.statefulHandover) {\n // Finish accepted application callbacks before taking the calculation-state boundary.\n await this.shutdownHooks?.drain();\n for (const proxy of this.unsMqttProxies) await proxy.flush();\n const application = this.applicationState ? { contract: this.applicationState.contract,\n data: cloneApplicationHandoverState(this.applicationState.exportState()) } : undefined;\n if (application) this.applicationState!.validateState(application.data);\n this.preparedState = parseMqttHandoverState({ protocol: 1, ...(application ? { application } : {}), proxies: this.unsMqttProxies.map((proxy, i) =>\n proxy.exportHandoverState({ batchSize: mqttWorkerData[i].batchSize, referenceHash: mqttWorkerData[i].referenceHash })) });\n this.stateDigest = mqttHandoverStateDigest(this.preparedState);\n await this.sendPreparedState(event, handoverId);\n let retries = 0;\n this.finRetry = setInterval(() => {\n if (++retries > 30 || this.sourceExit) { clearInterval(this.finRetry); return; }\n void this.sendPreparedState(event, handoverId).catch(() => logger.warn(\"MQTT handover receipt retry unavailable\"));\n }, 1000);\n this.finRetry.unref();\n // Keep the old owner passive until the successor acknowledges this exact state.\n return;\n }\n logger.info(`${this.processName} - Handover request accepted. Sending handover_subscriber messages.`);\n\n // Publish handover_subscriber messages for each instance that has processed some data.\n for (let i = 0; i < mqttWorkerData.length; i++) {\n const workerData = mqttWorkerData[i];\n if (workerData.batchSize > 0) {\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_subscriber\", handoverId, {\n batchSize: workerData.batchSize,\n referenceHash: workerData.referenceHash,\n instanceName: workerData.instanceName,\n }),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n }\n }\n logger.info(`${this.processName} - Handover subscriber messages sent.`);\n\n // Publish a single handover acknowledgment only when all\n // handover_subscriber messages have been sent\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_fin\", handoverId),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n logger.info(`${this.processName} - Handover fin message sent.`);\n\n await Promise.all(this.unsMqttProxies.map((unsProxy: UnsMqttProxy) => unsProxy.stop()));\n }\n\n private async sendPreparedState(event: UnsEvents[\"input\"], handoverId?: string): Promise<void> {\n await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? \"\", this.handoverPayload(\"handover_fin\", handoverId,\n { state: this.preparedState, stateDigest: this.stateDigest }), {\n retain: false, qos: 1, properties: { responseTopic: this.topicBuilder.getHandoverTopic(), userProperties: this.getUserProperties(handoverId) },\n });\n }\n\n private async acceptPreparedState(response: any): Promise<void> {\n const state = parseMqttHandoverState(response.state);\n const digest = mqttHandoverStateDigest(state);\n if (digest !== response.stateDigest) throw Error(\"MQTT handover state digest mismatch\");\n if (state.application?.contract !== this.applicationState?.contract) throw Error(\"Application handover contract differs\");\n if (state.application) this.applicationState!.validateState(state.application.data);\n const byName = new Map(this.unsMqttProxies.map(proxy => [proxy.instanceName, proxy]));\n if (byName.size !== this.unsMqttProxies.length || state.proxies.length !== byName.size || state.proxies.some(p => !byName.has(p.instanceName)))\n throw Error(\"MQTT handover proxy set differs\");\n for (const snapshot of state.proxies) byName.get(snapshot.instanceName)!.validateHandoverState(snapshot);\n // Validate every cursor before importing any state or activating any subscriber.\n for (const snapshot of state.proxies) await byName.get(snapshot.instanceName)!.prepareHandoverResume(snapshot.cursor);\n for (const snapshot of state.proxies) byName.get(snapshot.instanceName)!.importHandoverState(snapshot);\n if (state.application) await this.applicationState!.importState(state.application.data);\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n await this.activateApplication();\n try {\n for (const proxy of this.unsMqttProxies) await proxy.activateHandoverResume();\n } catch {\n await this.deactivateApplication();\n throw Error(\"MQTT input activation failed; successor remains passive\");\n }\n this.acceptedStateDigest = digest;\n this.active = true;\n this.event.emit(\"handoverManager\", { active: true });\n }\n\n private async activateApplication(): Promise<void> {\n try { await this.shutdownHooks?.onActivate?.(); }\n catch {\n await this.deactivateApplication();\n throw Error(\"Application activation failed; successor remains passive\");\n }\n }\n\n private async deactivateApplication(): Promise<void> {\n this.active = false;\n try { this.shutdownHooks?.onRelease?.(); } catch { /* Keep admission closed if cleanup itself fails. */ }\n // A failed worker may not acknowledge the stop. Request passive input on\n // every peer, close outputs, and never acknowledge ownership as successful.\n for (const proxy of this.unsMqttProxies) {\n void Promise.resolve().then(() => proxy.setSubscriberPassive(true)).catch((): void => {});\n }\n await Promise.allSettled(this.unsMqttProxies.map(proxy => Promise.resolve().then(() => proxy.setPublisherPassive())));\n }\n\n private async finishSourceHandover(): Promise<void> {\n const result = await runHandoverShutdown(async () => {\n await this.sourcePreparation;\n await this.shutdownHooks?.drain();\n if (this.statefulHandover) {\n await Promise.all(this.unsMqttProxies.map(proxy => proxy.stop()));\n // The host persists this receipt synchronously before the real process\n // exit. No receipt is emitted for a partial/failed application drain.\n (process as unknown as { emit(event: string, value: unknown): boolean }).emit(\"uns-kit:mqtt-handover-drained\", {\n protocol: 2, handoverId: this.acceptedHandoverId,\n stateDigest: this.stateDigest, pendingRecords: 0,\n outputs: this.unsMqttProxies.length,\n });\n }\n }, this.shutdownHooks?.timeoutMs ?? 30_000);\n this.handoverInProgress = false;\n this.requestingHandover = false;\n if (result === \"completed\") {\n logger.info(`${this.processName} - Handover source drain completed. Exiting process.`);\n this.exitSource(0);\n } else {\n // Do not log application error text: it may contain paths, topics or credentials.\n logger.error(`${this.processName} - Handover source drain ${result}. Exiting with incomplete drain.`);\n this.exitSource(1);\n }\n }\n\n private async handleHandover(event: UnsEvents[\"input\"]): Promise<void> {\n try {\n if (event.message.length > 1100000) return;\n const response = JSON.parse(event.message.toString());\n // No downgrade: stateful peers must agree before either relinquishes ownership.\n if ((response.protocol === 2) !== this.statefulHandover) return;\n if (this.statefulHandover && (event.packet?.retain || !this.getHandoverId(response, event))) return;\n // Responder process\n // Check if the message is a handover request and publish MULTIPLE handover_subscriber messages\n if (response.type === \"handover_request\") {\n const peerId = this.getSourceProcessId(event);\n const replyTopic = event.packet?.properties?.responseTopic;\n if (this.statefulHandover && this.sourcePeerId === peerId && this.acceptedHandoverId === this.getHandoverId(response, event) && this.preparedState) {\n await this.sendPreparedState(event, this.acceptedHandoverId); return;\n }\n if (this.sourcePeerId || !this.active || !peerId || typeof replyTopic !== \"string\" || !replyTopic) return;\n if (this.statefulHandover) {\n // Reject incompatible upgrades before freezing the current owner.\n if (response.applicationContract !== this.applicationState?.contract) throw Error(\"Application handover contract differs; source remains active\");\n if (this.applicationState) {\n const preliminary = cloneApplicationHandoverState(this.applicationState.exportState());\n this.applicationState.validateState(preliminary);\n }\n const offer = mqttHandoverOfferSchema.parse(response.offer);\n const own = this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility());\n if (offer.length !== own.length || own.some(p => !offer.some(o => o.instanceName === p.instanceName && o.compatibility === p.compatibility)))\n throw Error(\"MQTT source/destination or semantic contract differs; source remains active\");\n }\n this.sourcePeerId = peerId;\n this.acceptedHandoverId = this.getHandoverId(response, event);\n this.handoverInProgress = true;\n clearTimeout(this.activeTimeout);\n this.activeTimeout = undefined;\n this.sourcePreparation = this.prepareSourceHandover(response, event);\n try {\n await this.sourcePreparation;\n } catch {\n // Subscriber transfer or onRelease failed; never report successful source completion.\n logger.error(`${this.processName} - Handover source preparation failed; ownership requires review.`);\n if (!this.sourceExit && !this.statefulHandover) this.exitSource(1);\n }\n }\n\n // Requestor process\n // Check if the message is one of the handover_subscriber message in response to handover_request\n // and publish a handover_ack message\n if (response.type === \"handover_subscriber\") {\n if (this.statefulHandover) return;\n if (this.sourcePeerId) return;\n if (!this.acceptsHandoverResponse(this.getHandoverId(response, event))) return;\n // Find correct unsProxy instance for handover_subscriber and set it active\n this.unsMqttProxies.forEach((unsProxy: UnsMqttProxy) => {\n if (unsProxy.instanceName === response.instanceName) {\n unsProxy.setSubscriberActive(response.batchSize, response.referenceHash);\n }\n });\n }\n\n // Requestor process\n // Check if the message is a handover_fin at the end of handover_subscriber messages\n if (response.type === \"handover_fin\") {\n if (this.statefulHandover) {\n const id = this.getHandoverId(response, event);\n if (this.sourcePeerId || !this.requestingHandover || id !== this.handoverId || this.getSourceProcessId(event) !== this.expectedSourceId) return;\n if (this.acceptedStateDigest && response.stateDigest !== this.acceptedStateDigest) return;\n this.targetPreparation ??= this.acceptPreparedState(response);\n await this.targetPreparation;\n // Duplicate fin only replays the receipt; it never reapplies a baseline.\n if (response.stateDigest !== this.acceptedStateDigest) return;\n await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? \"\", this.handoverPayload(\"handover_ack\", id,\n { stateDigest: this.acceptedStateDigest }), { retain: false, qos: 1, properties: { userProperties: this.getUserProperties(id) } });\n return;\n }\n if (this.sourcePeerId) return;\n const handoverId = this.getHandoverId(response, event);\n if (!this.acceptsHandoverResponse(handoverId)) return;\n logger.info(`${this.processName} - Received handover fin from ${event.packet?.properties?.userProperties?.processName}.`);\n\n // Maybe we should count the number of requests that were allrady made TODO\n // this.handoverInProgress = false;\n // this.requestingHandover = false;\n\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n logger.info(`${this.processName} - Handover completed.`);\n // Activate all UNS proxy instance publishers and subscribers.\n this.unsMqttProxies.forEach((unsProxy) => {\n unsProxy.setPublisherActive();\n unsProxy.setSubscriberActive();\n });\n\n // Maybe we should reply with handover_ack.\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_ack\", handoverId),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n logger.info(`${this.processName} - Handover ack message sent.`);\n }\n\n // Responder process\n // Check if the message is a handover_ack at the end of handover_fin messages\n if (response.type === \"handover_ack\") {\n if (!this.sourcePreparation || this.getSourceProcessId(event) !== this.sourcePeerId) return;\n const handoverId = this.getHandoverId(response, event);\n if (this.acceptedHandoverId && handoverId && this.acceptedHandoverId !== handoverId) {\n logger.warn(`${this.processName} - Ignoring acknowledgement for another migration.`);\n return;\n }\n if (this.statefulHandover && (handoverId !== this.acceptedHandoverId || response.stateDigest !== this.stateDigest || !this.stateDigest)) return;\n clearInterval(this.finRetry);\n logger.info(`${this.processName} - Received handover ack from ${event.packet?.properties?.userProperties?.processName}.`);\n this.sourceExit ??= this.finishSourceHandover();\n await this.sourceExit;\n }\n } catch (error) {\n logger.error(`${this.processName} - Error processing handover response: ${error.message}`);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"handover-manager.js","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAiC,MAAM,0CAA0C,CAAC;AACxH,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,uBAAuB,EAA0B,MAAM,mCAAmC,CAAC;AACrJ,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAA8B,MAAM,wBAAwB,CAAC;AAGnH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnE,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAwCP;IACA;IAxCZ,KAAK,GAAuD,IAAI,2BAA2B,EAAyB,CAAC;IACpH,WAAW,CAAS;IACpB,SAAS,CAAS;IAClB,SAAS,CAAY;IACrB,cAAc,CAAiB;IAC/B,kBAAkB,GAAY,KAAK,CAAC;IACpC,kBAAkB,GAAY,KAAK,CAAC;IACpC,YAAY,CAAmB;IAC/B,aAAa,CAA6B;IACjC,UAAU,CAAqB;IACxC,kBAAkB,CAAqB;IACvC,MAAM,GAAY,KAAK,CAAC;IACxB,YAAY,CAAqB;IACjC,iBAAiB,CAA4B;IAC7C,UAAU,CAA4B;IAC7B,aAAa,CAAoC;IAC1D,gBAAgB,CAAqB;IACrC,aAAa,CAAgC;IAC7C,WAAW,CAAqB;IAChC,mBAAmB,CAAqB;IACxC,iBAAiB,CAA4B;IAC7C,kBAAkB,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAA6B;IACrC,YAAY,CAA6B;IACzC,oBAAoB,GAAG,KAAK,CAAC;IAC9B,sBAAsB,GAAY,KAAK,CAAC;IACxC,eAAe,GAAY,IAAI,CAAC;IAChC,iBAAiB,GAAY,KAAK,CAAC;IAE1C,YACE,WAAmB,EACnB,SAAiB,EACjB,SAAoB,EACpB,cAA8B,EAC9B,sBAA+B,EAC/B,eAAwB,EACxB,iBAA0B,EAC1B,UAAmB,EACnB,aAAqC,EACpB,mBAAmB,KAAK,EACxB,gBAA2C;QAD3C,qBAAgB,GAAhB,gBAAgB,CAAQ;QACxB,qBAAgB,GAAhB,gBAAgB,CAA2B;QAE5D,wBAAwB,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QACrD,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAElD,iCAAiC;QACjC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC;QACtC,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,IAAI,gBAAgB,CACtC,aAAa,gBAAgB,CAAC,iBAAiB,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CACvK,CAAC;QAEF,gFAAgF;QAChF,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YACzC,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,UAAU;gBAAE,OAAO,CAAC,4CAA4C;YAClG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qFAAqF,CAAC,CAAC;YACtH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;YACpE,IAAI,CAAC;gBAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAAC,CAAC;YACzC,MAAM,CAAC;gBAAC,MAAM,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YACvF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5D,yEAAyE;YACzE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAC5E,CAAC,EAAE,cAAc,CAAC,CAAC;IACrB,CAAC;IAEO,iBAAiB,CAAC,UAAmB;QAC3C,MAAM,UAAU,GAA2B;YACzC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;QACF,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,UAAU;YAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC;QACnD,OAAO,UAAU,CAAC;IACpB,CAAC;IAEO,eAAe,CACrB,IAAY,EACZ,UAAmB,EACnB,SAAkC,EAAE;QAEpC,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,IAAI;YACJ,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,GAAG,MAAM;YACT,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;IACL,CAAC;IAEO,aAAa,CAAC,QAAiB,EAAE,KAAyB;QAChE,MAAM,WAAW,GAAG,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtF,CAAC,CAAE,QAAqC,CAAC,UAAU;YACnD,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,OAAO,WAAW,KAAK,QAAQ;YAAE,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC7E,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,CAAC;QAC5E,OAAO,OAAO,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,CAAC;IAEO,uBAAuB,CAAC,kBAAsC;QACpE,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,KAAK,kBAAkB,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qDAAqD,CAAC,CAAC;QACtF,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,kBAAkB,CAAC,KAAyB;QAClD,OAAO,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,CAAC;IAC7D,CAAC;IAEO,gBAAgB,CAAC,OAAe;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC;YAClE,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBACvE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAa;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,AAAD,EAAG,OAAO,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAC3C,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5D,MAAM,eAAe,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC9E,MAAM,eAAe,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7E,OAAO,GAAG,KAAK,eAAe,IAAI,OAAO,KAAK,eAAe,CAAC;IAChE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,iBAAiB,CAAC,KAAyB;QACtD,IAAI,CAAC;YACH,8FAA8F;YAC9F,IAAI,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;gBAC9I,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBACvD,IAAI,eAAe,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvC,OAAO;gBACT,CAAC;gBACD,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACpE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;oBACzB,OAAO;gBACT,CAAC;gBACD,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,eAAe,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,UAAU,OAAO,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;gBAE/F,IAAI,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;oBAClC,uEAAuE;oBACvE,sEAAsE;oBACtE,qEAAqE;oBACrE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uEAAuE,CAAC,CAAC;oBACxG,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;oBAC9B,OAAO;gBACT,CAAC;gBAED,IAAI,IAAI,CAAC,sBAAsB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACxD,oBAAoB;oBACpB,6EAA6E;oBAC7E,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,qGAAqG;oBACvI,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;oBAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC5D,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;oBAC/B,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;oBACxC,MAAM,kBAAkB,GAAG,IAAI,gBAAgB,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;oBACnH,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE;wBACxF,MAAM,EAAE,KAAK;wBACb,UAAU,EAAE;4BACV,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE;yBACzC;qBACF,CAAC,CAAC;oBACH,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uCAAuC,CAAC,CAAC;oBACxE,UAAU,CAAC,GAAG,EAAE;wBACd,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;wBAC/B,IAAI,OAAO,GAAG,CAAC,CAAC;wBAChB,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;4BACzB,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gCAAC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gCAAC,OAAO;4BAAC,CAAC;4BACrG,IAAI,CAAC;gCACH,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gCAChI,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,SAAS,EACjG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;oCAC/F,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE,EAAE;iCAC7H,CAAC,CAAC;4BACL,CAAC;4BAAC,MAAM,CAAC;gCAAC,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;4BAAC,CAAC;wBACrG,CAAC,CAAC;wBACF,KAAK,OAAO,EAAE,CAAC;wBACf,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;4BAC1B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;gCACnC,IAAI,EAAE,OAAO,GAAG,EAAE,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;oCAAC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;oCAAC,OAAO;gCAAC,CAAC;gCACvH,KAAK,OAAO,EAAE,CAAC;4BACjB,CAAC,EAAE,IAAI,CAAC,CAAC;4BACT,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;wBAC5B,CAAC;oBACH,CAAC,EAAE,KAAK,CAAC,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;wBAC3B,6DAA6D;wBAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,gCAAgC,CAAC,CAAC;wBACjE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,mFAAmF,CAAC,CAAC;wBACpH,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,qGAAqG;wBACvI,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;wBAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;4BAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;wBACpE,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;wBACjC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;wBACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;wBAC5D,8DAA8D;wBAC9D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;oBAC5E,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,6CAA6C,KAAK,CAAC,KAAK,qBAAqB,CAAC,CAAC;wBAC9G,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,wEAAwE;YACxE,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACjF,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBACvD,IAAI,eAAe,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,qCAAqC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;IACH,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,IAAY;QAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,QAAa,EAAE,KAAyB;QAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEvD,MAAM,CAAC,IAAI,CACT,GAAG,IAAI,CAAC,WAAW,qCAAqC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,uBAAuB,CACrI,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,CAAC;QAElC,yEAAyE;QACzE,MAAM,cAAc,GAA8B,EAAE,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,iCAAiC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC3F,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,sFAAsF;YACtF,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;YAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ;gBACpF,IAAI,EAAE,6BAA6B,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACzF,IAAI,WAAW;gBAAE,IAAI,CAAC,gBAAiB,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACxE,IAAI,CAAC,aAAa,GAAG,sBAAsB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAChJ,KAAK,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1N,IAAI,CAAC,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/D,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAChD,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC/B,IAAI,EAAE,OAAO,GAAG,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAAC,OAAO;gBAAC,CAAC;gBAChF,KAAK,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACrH,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtB,gFAAgF;YAChF,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,qEAAqE,CAAC,CAAC;QAEtG,uFAAuF;QACvF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,UAAU,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,qBAAqB,EAAE,UAAU,EAAE;oBACtD,SAAS,EAAE,UAAU,CAAC,SAAS;oBAC/B,aAAa,EAAE,UAAU,CAAC,aAAa;oBACvC,YAAY,EAAE,UAAU,CAAC,YAAY;iBACtC,CAAC,EACF;oBACE,MAAM,EAAE,KAAK;oBACb,UAAU,EAAE;wBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;wBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;qBACnD;iBACF,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,uCAAuC,CAAC,CAAC;QAExE,yDAAyD;QACzD,8CAA8C;QAC9C,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC,EAChD;YACE,MAAM,EAAE,KAAK;YACb,UAAU,EAAE;gBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;gBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;aACnD;SACF,CACF,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,CAAC,CAAC;QAEhE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,QAAsB,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1F,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,KAAyB,EAAE,UAAmB;QAC5E,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,EACxH,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE;SAC/I,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,QAAa;QAC7C,MAAM,KAAK,GAAG,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,MAAM,KAAK,QAAQ,CAAC,WAAW;YAAE,MAAM,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACxF,IAAI,KAAK,CAAC,WAAW,EAAE,QAAQ,KAAK,IAAI,CAAC,gBAAgB,EAAE,QAAQ;YAAE,MAAM,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC1H,IAAI,KAAK,CAAC,WAAW;YAAE,IAAI,CAAC,gBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,cAAc,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YAC5I,MAAM,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACjD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACzG,iFAAiF;QACjF,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtH,0EAA0E;QAC1E,mEAAmE;QACnE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACvG,IAAI,KAAK,CAAC,WAAW;YAAE,MAAM,IAAI,CAAC,gBAAiB,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;YAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC;QACpE,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC,sBAAsB,EAAE,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC/B,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,EAAE,CAAC;QAAC,CAAC;QACjD,MAAM,CAAC;YACL,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YAAC,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,oDAAoD,CAAC,CAAC;QACzG,yEAAyE;QACzE,4EAA4E;QAC5E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxC,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAS,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,CAAC;IACxH,CAAC;IAEO,KAAK,CAAC,oBAAoB;QAChC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,IAAI,EAAE;YAClD,MAAM,IAAI,CAAC,iBAAiB,CAAC;YAC7B,MAAM,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,uEAAuE;gBACvE,sEAAsE;gBACrE,OAAuE,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC7G,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,kBAAkB;oBAChD,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,CAAC;oBAChD,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,IAAI,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,sDAAsD,CAAC,CAAC;YACvF,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,kFAAkF;YAClF,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,4BAA4B,MAAM,kCAAkC,CAAC,CAAC;YACtG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAAyB;QACpD,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO;gBAAE,OAAO;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACtD,gFAAgF;YAChF,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,gBAAgB;gBAAE,OAAO;YAChE,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAAE,OAAO;YACpG,oBAAoB;YACpB,+FAA+F;YAC/F,IAAI,QAAQ,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAC9C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC;gBAC3D,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,IAAI,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnJ,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;oBAAC,OAAO;gBACvE,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,CAAC,UAAU;oBAAE,OAAO;gBAC1G,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAC1B,kEAAkE;oBAClE,IAAI,QAAQ,CAAC,mBAAmB,KAAK,IAAI,CAAC,gBAAgB,EAAE,QAAQ;wBAAE,MAAM,KAAK,CAAC,8DAA8D,CAAC,CAAC;oBAClJ,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBAC1B,MAAM,WAAW,GAAG,6BAA6B,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAC;wBACvF,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;oBACnD,CAAC;oBACD,MAAM,KAAK,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,CAAC;oBAC/E,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,aAAa,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC;wBAC1I,MAAM,KAAK,CAAC,6EAA6E,CAAC,CAAC;oBAC7F,IAAI,IAAI,CAAC,oBAAoB;wBAAE,OAAO;oBACtC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;oBACjC,IAAI,CAAC;wBACH,oEAAoE;wBACpE,oEAAoE;wBACpE,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;4BACxC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,CAAE,CAAC,YAAY,CAAC;4BACrG,IAAI,CAAC,MAAM,KAAK,CAAC,uBAAuB,CAAC,OAAO,CAAC;gCAAE,OAAO;wBAC5D,CAAC;oBACH,CAAC;4BAAS,CAAC;wBAAC,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;oBAAC,CAAC;oBAChD,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM;wBAAE,OAAO;gBAChD,CAAC;gBACD,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;gBAC3B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAC9D,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;gBAC/B,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACjC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;gBAC/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACrE,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,iBAAiB,CAAC;gBAC/B,CAAC;gBAAC,MAAM,CAAC;oBACP,sFAAsF;oBACtF,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,mEAAmE,CAAC,CAAC;oBACrG,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,gBAAgB;wBAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;YAED,oBAAoB;YACpB,iGAAiG;YACjG,qCAAqC;YACrC,IAAI,QAAQ,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBAC5C,IAAI,IAAI,CAAC,gBAAgB;oBAAE,OAAO;gBAClC,IAAI,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC9B,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAAE,OAAO;gBAC/E,2EAA2E;gBAC3E,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAsB,EAAE,EAAE;oBACrD,IAAI,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;wBACpD,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;oBAC3E,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;YAED,oBAAoB;YACpB,oFAAoF;YACpF,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACrC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAC/C,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,gBAAgB;wBAAE,OAAO;oBAChJ,IAAI,IAAI,CAAC,mBAAmB,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB;wBAAE,OAAO;oBAC1F,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;oBACjC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;wBAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;wBACvD,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC;wBACrC,KAAK,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE;4BAC1B,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,iBAAiB,KAAK,WAAW;gCAAE,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;wBAC7G,CAAC,CAAC,CAAC;oBACL,CAAC;oBACD,MAAM,IAAI,CAAC,iBAAiB,CAAC;oBAC7B,yEAAyE;oBACzE,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB;wBAAE,OAAO;oBAC9D,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,EAAE,EAChH,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBACrI,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACvD,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC;oBAAE,OAAO;gBACtD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,iCAAiC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,GAAG,CAAC,CAAC;gBAE1H,2EAA2E;gBAC3E,mCAAmC;gBACnC,mCAAmC;gBAEnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC5D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,wBAAwB,CAAC,CAAC;gBACzD,8DAA8D;gBAC9D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;oBACvC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;oBAC9B,QAAQ,CAAC,mBAAmB,EAAE,CAAC;gBACjC,CAAC,CAAC,CAAC;gBAEH,2CAA2C;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAC1B,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAC5C,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC,EAChD;oBACE,MAAM,EAAE,KAAK;oBACb,UAAU,EAAE;wBACV,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;wBACnD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;qBACnD;iBACF,CACF,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,+BAA+B,CAAC,CAAC;YAClE,CAAC;YAED,oBAAoB;YACpB,6EAA6E;YAC7E,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACrC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,YAAY;oBAAE,OAAO;gBAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACvD,IAAI,IAAI,CAAC,kBAAkB,IAAI,UAAU,IAAI,IAAI,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;oBACpF,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,oDAAoD,CAAC,CAAC;oBACrF,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,kBAAkB,IAAI,QAAQ,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;oBAAE,OAAO;gBAChJ,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,iCAAiC,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,GAAG,CAAC,CAAC;gBAC1H,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAChD,MAAM,IAAI,CAAC,UAAU,CAAC;YACxB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,0CAA0C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;CACF","sourcesContent":["import { cloneApplicationHandoverState, type ApplicationHandoverState } from \"../runtime/application-handover-state.js\";\nimport { mqttHandoverOfferSchema, parseMqttHandoverState, mqttHandoverStateDigest, type MqttHandoverState } from \"../runtime/mqtt-handover-state.js\";\nimport logger from \"../logger.js\";\nimport { runHandoverShutdown, validateHandoverShutdown, type HandoverShutdownHooks } from \"./handover-shutdown.js\";\nimport { HandoverManagerEvents } from \"../uns-mqtt/mqtt-interfaces.js\";\nimport MqttProxy from \"../uns-mqtt/mqtt-proxy.js\";\nimport { MqttTopicBuilder } from \"../uns-mqtt/mqtt-topic-builder.js\";\nimport UnsMqttProxy from \"../uns-mqtt/uns-mqtt-proxy.js\";\nimport { HandoverManagerEventEmitter } from \"./handover-manager-event-emitter.js\";\nimport { ACTIVE_TIMEOUT, PACKAGE_INFO } from \"./process-config.js\";\nimport { UnsEvents } from \"./uns-interfaces.js\";\n\nfunction normalizeHandoverId(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.trim();\n return normalized.length > 0 && normalized.length <= 128 ? normalized : undefined;\n}\n\n/**\n * HandoverManager is responsible for all handover-related logic,\n * including handling incoming MQTT messages, issuing handover requests,\n * and processing handover responses.\n */\nexport class HandoverManager {\n public event: HandoverManagerEventEmitter<HandoverManagerEvents> = new HandoverManagerEventEmitter<HandoverManagerEvents>();\n private processName: string;\n private processId: string;\n private mqttProxy: MqttProxy;\n private unsMqttProxies: UnsMqttProxy[];\n private requestingHandover: boolean = false;\n private handoverInProgress: boolean = false;\n private topicBuilder: MqttTopicBuilder;\n private activeTimeout: NodeJS.Timeout | undefined;\n private readonly handoverId: string | undefined;\n private acceptedHandoverId: string | undefined;\n private active: boolean = false;\n private sourcePeerId: string | undefined;\n private sourcePreparation: Promise<void> | undefined;\n private sourceExit: Promise<void> | undefined;\n private readonly shutdownHooks: HandoverShutdownHooks | undefined;\n private expectedSourceId: string | undefined;\n private preparedState: MqttHandoverState | undefined;\n private stateDigest: string | undefined;\n private acceptedStateDigest: string | undefined;\n private targetPreparation: Promise<void> | undefined;\n private targetStateApplied = false;\n private finRetry: NodeJS.Timeout | undefined;\n private requestRetry: NodeJS.Timeout | undefined;\n private sourceReviewInFlight = false;\n public handoverRequestEnabled: boolean = false;\n public handoverEnabled: boolean = true;\n public forceStartEnabled: boolean = false;\n\n constructor(\n processName: string,\n processId: string,\n mqttProxy: MqttProxy,\n unsMqttProxies: UnsMqttProxy[],\n handoverRequestEnabled: boolean,\n handoverEnabled: boolean,\n forceStartEnabled: boolean,\n handoverId?: string,\n shutdownHooks?: HandoverShutdownHooks,\n private readonly statefulHandover = false,\n private readonly applicationState?: ApplicationHandoverState,\n ) {\n validateHandoverShutdown(shutdownHooks);\n this.shutdownHooks = shutdownHooks ? { ...shutdownHooks } : undefined;\n this.processName = processName;\n this.processId = processId;\n this.mqttProxy = mqttProxy;\n this.unsMqttProxies = unsMqttProxies;\n this.handoverRequestEnabled = handoverRequestEnabled;\n this.handoverEnabled = handoverEnabled;\n this.forceStartEnabled = forceStartEnabled;\n this.handoverId = normalizeHandoverId(handoverId);\n\n // Instantiate the topic builder.\n const packageName = PACKAGE_INFO.name;\n const version = PACKAGE_INFO.version;\n this.topicBuilder = new MqttTopicBuilder(\n `uns-infra/${MqttTopicBuilder.sanitizeTopicPart(packageName)}/${MqttTopicBuilder.sanitizeTopicPart(version)}/${MqttTopicBuilder.sanitizeTopicPart(this.processName)}/`,\n );\n\n // Set status as active after a timeout if no other active process are detected.\n this.activeTimeout = setTimeout(async () => {\n if (this.statefulHandover && this.handoverId) return; // A reviewed successor never self-promotes.\n logger.info(`${this.processName} - No active message received within timeout. Assuming no other process is running.`);\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n try { await this.activateApplication(); }\n catch { logger.error(\"Runtime activation failed; successor remains passive\"); return; }\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n // Publishers were prepared for application activation; input opens last.\n this.unsMqttProxies.forEach((unsProxy) => unsProxy.setSubscriberActive());\n }, ACTIVE_TIMEOUT);\n }\n\n private getUserProperties(handoverId?: string): Record<string, string> {\n const properties: Record<string, string> = {\n processName: this.processName,\n processId: this.processId,\n };\n const normalized = normalizeHandoverId(handoverId ?? this.handoverId);\n if (normalized) properties.handoverId = normalized;\n return properties;\n }\n\n private handoverPayload(\n type: string,\n handoverId?: string,\n fields: Record<string, unknown> = {},\n ): string {\n const normalized = normalizeHandoverId(handoverId ?? this.handoverId);\n return JSON.stringify({\n type,\n ...(this.statefulHandover ? { protocol: 2 } : {}),\n ...fields,\n ...(normalized ? { handoverId: normalized } : {}),\n });\n }\n\n private getHandoverId(response: unknown, event: UnsEvents[\"input\"]): string | undefined {\n const fromPayload = response && typeof response === \"object\" && !Array.isArray(response)\n ? (response as { handoverId?: unknown }).handoverId\n : undefined;\n if (typeof fromPayload === \"string\") return normalizeHandoverId(fromPayload);\n const fromProperties = event.packet?.properties?.userProperties?.handoverId;\n return typeof fromProperties === \"string\" ? normalizeHandoverId(fromProperties) : undefined;\n }\n\n private acceptsHandoverResponse(receivedHandoverId: string | undefined): boolean {\n if (!this.handoverId || !receivedHandoverId) {\n return true;\n }\n if (this.handoverId === receivedHandoverId) {\n return true;\n }\n logger.warn(`${this.processName} - Ignoring handover message for another migration.`);\n return false;\n }\n\n private getSourceProcessId(event: UnsEvents[\"input\"]): string | undefined {\n return event.packet?.properties?.userProperties?.processId;\n }\n\n private parseActiveValue(message: string): boolean | null {\n try {\n const parsed = JSON.parse(message);\n const value = parsed?.message?.data?.value ?? parsed?.data?.value;\n if (value === 1 || value === \"1\" || value === true || value === \"true\") {\n return true;\n }\n if (value === 0 || value === \"0\" || value === false || value === \"false\") {\n return false;\n }\n return null;\n } catch {\n return null;\n }\n }\n\n private isProcessActiveTopic(topic: string): boolean {\n const parts = topic.split(\"/\");\n if (parts.length !== 5) return false;\n const [root, pkg, , process, tail] = parts;\n if (root !== \"uns-infra\" || tail !== \"active\") return false;\n const expectedPackage = MqttTopicBuilder.sanitizeTopicPart(PACKAGE_INFO.name);\n const expectedProcess = MqttTopicBuilder.sanitizeTopicPart(this.processName);\n return pkg === expectedPackage && process === expectedProcess;\n }\n\n /**\n * Main entry point for handling incoming MQTT messages.\n * It checks the topic and delegates to the corresponding handler.\n */\n public async handleMqttMessage(event: UnsEvents[\"input\"]): Promise<void> {\n try {\n // Check if the packet is active messages from other processes and this process is not active.\n if (this.isProcessActiveTopic(event.topic) && this.requestingHandover === false && this.active === false && this.handoverInProgress === false) {\n const sourceProcessId = this.getSourceProcessId(event);\n if (sourceProcessId === this.processId) {\n return;\n }\n const activeValue = this.parseActiveValue(event.message.toString());\n if (activeValue !== true) {\n return;\n }\n const sourceInfo = sourceProcessId ? ` (processId=${sourceProcessId})` : \"\";\n logger.info(`${this.processName} - Another process is active${sourceInfo} on ${event.topic}.`);\n\n if (event.packet?.retain === true) {\n // A retained heartbeat can belong to a process that crashed before its\n // expiry. Wait for a fresh non-retained heartbeat before asking it to\n // hand over; otherwise a stale retained message would block startup.\n logger.info(`${this.processName} - Retained active heartbeat observed; waiting for a fresh heartbeat.`);\n this.activeTimeout?.refresh();\n return;\n }\n\n if (this.handoverRequestEnabled && this.handoverEnabled) {\n // Requester process\n // Publish a handover request message after 10 seconds to the handover topic.\n clearTimeout(this.activeTimeout); // Clear the active timeout if it exists - prevent this process from becoming active after a timeout.\n this.activeTimeout = undefined;\n this.event.emit(\"handoverManager\", { active: this.active });\n this.requestingHandover = true;\n this.expectedSourceId = sourceProcessId;\n const eventHandoverTopic = new MqttTopicBuilder(MqttTopicBuilder.extractBaseTopic(event.topic)).getHandoverTopic();\n await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload(\"handover_intent\"), {\n retain: false,\n properties: {\n userProperties: this.getUserProperties(),\n },\n });\n logger.info(`${this.processName} - Requesting handover in 10 seconds.`);\n setTimeout(() => {\n this.handoverInProgress = true;\n let retries = 0;\n const request = async () => {\n if (this.targetPreparation || this.acceptedStateDigest) { clearInterval(this.requestRetry); return; }\n try {\n const offer = this.statefulHandover ? await Promise.all(this.unsMqttProxies.map(proxy => proxy.getHandoverOffer())) : undefined;\n await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload(\"handover_request\", undefined,\n this.statefulHandover ? { offer, applicationContract: this.applicationState?.contract } : {}), {\n retain: false, properties: { responseTopic: this.topicBuilder.getHandoverTopic(), userProperties: this.getUserProperties() },\n });\n } catch { logger.warn(\"MQTT input boundary preparation is not ready; successor remains passive\"); }\n };\n void request();\n if (this.statefulHandover) {\n this.requestRetry = setInterval(() => {\n if (++retries > 15 || this.targetPreparation || this.acceptedStateDigest) { clearInterval(this.requestRetry); return; }\n void request();\n }, 1000);\n this.requestRetry.unref();\n }\n }, 10000);\n } else {\n if (this.forceStartEnabled) {\n // Force start the process even if another process is active.\n logger.info(`${this.processName} - Force starting the process.`);\n logger.warn(`${this.processName} - Warning: Source and destination being the same may lead to duplicate messages.`);\n clearTimeout(this.activeTimeout); // Clear the active timeout if it exists - prevent this process from becoming active after a timeout.\n this.activeTimeout = undefined;\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n await this.activateApplication();\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n // Activate all UNS proxy instance publishers and subscribers.\n this.unsMqttProxies.forEach((unsProxy) => unsProxy.setSubscriberActive());\n } else {\n logger.info(`${this.processName} - Waiting for the other process on topic ${event.topic} to become passive.`);\n this.activeTimeout?.refresh();\n }\n }\n }\n\n // Check if the packet is an handover message, sent to a handover topic.\n if (event.topic === this.topicBuilder.getHandoverTopic() && this.handoverEnabled) {\n const sourceProcessId = this.getSourceProcessId(event);\n if (sourceProcessId !== this.processId) {\n await this.handleHandover(event);\n }\n }\n } catch (error) {\n logger.error(`${this.processName} - Error processing MQTT message: ${error.message}`);\n return;\n }\n }\n\n /**\n * Handles handovers.\n */\n private exitSource(code: number): void {\n process.exit(code);\n }\n\n private async prepareSourceHandover(response: any, event: UnsEvents[\"input\"]): Promise<void> {\n const handoverId = this.getHandoverId(response, event);\n\n logger.info(\n `${this.processName} - Received handover request from ${event.packet?.properties?.userProperties?.processName}. Accepting handover.`,\n );\n\n this.active = false;\n this.event.emit(\"handoverManager\", { active: false });\n this.shutdownHooks?.onRelease?.();\n\n // Set all UNS proxy instance subscribers to passive and drain the queue.\n const mqttWorkerData: UnsEvents[\"mqttWorker\"][] = [];\n for (let i = 0; i < this.unsMqttProxies.length; i++) {\n const unsProxy = this.unsMqttProxies[i];\n const workerData = await unsProxy.setSubscriberPassiveAndDrainQueue(this.statefulHandover);\n mqttWorkerData.push(workerData);\n }\n if (this.statefulHandover) {\n // Finish accepted application callbacks before taking the calculation-state boundary.\n await this.shutdownHooks?.drain();\n for (const proxy of this.unsMqttProxies) await proxy.flush();\n const application = this.applicationState ? { contract: this.applicationState.contract,\n data: cloneApplicationHandoverState(this.applicationState.exportState()) } : undefined;\n if (application) this.applicationState!.validateState(application.data);\n this.preparedState = parseMqttHandoverState({ protocol: 1, ...(application ? { application } : {}), proxies: this.unsMqttProxies.map((proxy, i) =>\n proxy.exportHandoverState({ batchSize: mqttWorkerData[i].batchSize, referenceHash: mqttWorkerData[i].referenceHash, ...(mqttWorkerData[i].processedTail ? {processedTail: mqttWorkerData[i].processedTail} : {}) })) });\n this.stateDigest = mqttHandoverStateDigest(this.preparedState);\n await this.sendPreparedState(event, handoverId);\n let retries = 0;\n this.finRetry = setInterval(() => {\n if (++retries > 30 || this.sourceExit) { clearInterval(this.finRetry); return; }\n void this.sendPreparedState(event, handoverId).catch(() => logger.warn(\"MQTT handover receipt retry unavailable\"));\n }, 1000);\n this.finRetry.unref();\n // Keep the old owner passive until the successor acknowledges this exact state.\n return;\n }\n logger.info(`${this.processName} - Handover request accepted. Sending handover_subscriber messages.`);\n\n // Publish handover_subscriber messages for each instance that has processed some data.\n for (let i = 0; i < mqttWorkerData.length; i++) {\n const workerData = mqttWorkerData[i];\n if (workerData.batchSize > 0) {\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_subscriber\", handoverId, {\n batchSize: workerData.batchSize,\n referenceHash: workerData.referenceHash,\n instanceName: workerData.instanceName,\n }),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n }\n }\n logger.info(`${this.processName} - Handover subscriber messages sent.`);\n\n // Publish a single handover acknowledgment only when all\n // handover_subscriber messages have been sent\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_fin\", handoverId),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n logger.info(`${this.processName} - Handover fin message sent.`);\n\n await Promise.all(this.unsMqttProxies.map((unsProxy: UnsMqttProxy) => unsProxy.stop()));\n }\n\n private async sendPreparedState(event: UnsEvents[\"input\"], handoverId?: string): Promise<void> {\n await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? \"\", this.handoverPayload(\"handover_fin\", handoverId,\n { state: this.preparedState, stateDigest: this.stateDigest }), {\n retain: false, qos: 1, properties: { responseTopic: this.topicBuilder.getHandoverTopic(), userProperties: this.getUserProperties(handoverId) },\n });\n }\n\n private async acceptPreparedState(response: any): Promise<void> {\n const state = parseMqttHandoverState(response.state);\n const digest = mqttHandoverStateDigest(state);\n if (digest !== response.stateDigest) throw Error(\"MQTT handover state digest mismatch\");\n if (state.application?.contract !== this.applicationState?.contract) throw Error(\"Application handover contract differs\");\n if (state.application) this.applicationState!.validateState(state.application.data);\n const byName = new Map(this.unsMqttProxies.map(proxy => [proxy.instanceName, proxy]));\n if (byName.size !== this.unsMqttProxies.length || state.proxies.length !== byName.size || state.proxies.some(p => !byName.has(p.instanceName)))\n throw Error(\"MQTT handover proxy set differs\");\n for (const snapshot of state.proxies) byName.get(snapshot.instanceName)!.validateHandoverState(snapshot);\n // Validate every cursor before importing any state or activating any subscriber.\n for (const snapshot of state.proxies) await byName.get(snapshot.instanceName)!.prepareHandoverResume(snapshot.cursor);\n // After this point an import/activation error has an unknown effect. Only\n // read-only boundary preparation may be retried on a repeated FIN.\n this.targetStateApplied = true;\n for (const snapshot of state.proxies) byName.get(snapshot.instanceName)!.importHandoverState(snapshot);\n if (state.application) await this.applicationState!.importState(state.application.data);\n for (const proxy of this.unsMqttProxies) proxy.setPublisherActive();\n await this.activateApplication();\n try {\n for (const proxy of this.unsMqttProxies) await proxy.activateHandoverResume();\n } catch {\n await this.deactivateApplication();\n throw Error(\"MQTT input activation failed; successor remains passive\");\n }\n this.acceptedStateDigest = digest;\n this.active = true;\n this.event.emit(\"handoverManager\", { active: true });\n }\n\n private async activateApplication(): Promise<void> {\n try { await this.shutdownHooks?.onActivate?.(); }\n catch {\n await this.deactivateApplication();\n throw Error(\"Application activation failed; successor remains passive\");\n }\n }\n\n private async deactivateApplication(): Promise<void> {\n this.active = false;\n try { this.shutdownHooks?.onRelease?.(); } catch { /* Keep admission closed if cleanup itself fails. */ }\n // A failed worker may not acknowledge the stop. Request passive input on\n // every peer, close outputs, and never acknowledge ownership as successful.\n for (const proxy of this.unsMqttProxies) {\n void Promise.resolve().then(() => proxy.setSubscriberPassive(true)).catch((): void => {});\n }\n await Promise.allSettled(this.unsMqttProxies.map(proxy => Promise.resolve().then(() => proxy.setPublisherPassive())));\n }\n\n private async finishSourceHandover(): Promise<void> {\n const result = await runHandoverShutdown(async () => {\n await this.sourcePreparation;\n await this.shutdownHooks?.drain();\n if (this.statefulHandover) {\n await Promise.all(this.unsMqttProxies.map(proxy => proxy.stop()));\n // The host persists this receipt synchronously before the real process\n // exit. No receipt is emitted for a partial/failed application drain.\n (process as unknown as { emit(event: string, value: unknown): boolean }).emit(\"uns-kit:mqtt-handover-drained\", {\n protocol: 2, handoverId: this.acceptedHandoverId,\n stateDigest: this.stateDigest, pendingRecords: 0,\n outputs: this.unsMqttProxies.length,\n });\n }\n }, this.shutdownHooks?.timeoutMs ?? 30_000);\n this.handoverInProgress = false;\n this.requestingHandover = false;\n if (result === \"completed\") {\n logger.info(`${this.processName} - Handover source drain completed. Exiting process.`);\n this.exitSource(0);\n } else {\n // Do not log application error text: it may contain paths, topics or credentials.\n logger.error(`${this.processName} - Handover source drain ${result}. Exiting with incomplete drain.`);\n this.exitSource(1);\n }\n }\n\n private async handleHandover(event: UnsEvents[\"input\"]): Promise<void> {\n try {\n if (event.message.length > 1100000) return;\n const response = JSON.parse(event.message.toString());\n // No downgrade: stateful peers must agree before either relinquishes ownership.\n if ((response.protocol === 2) !== this.statefulHandover) return;\n if (this.statefulHandover && (event.packet?.retain || !this.getHandoverId(response, event))) return;\n // Responder process\n // Check if the message is a handover request and publish MULTIPLE handover_subscriber messages\n if (response.type === \"handover_request\") {\n const peerId = this.getSourceProcessId(event);\n const replyTopic = event.packet?.properties?.responseTopic;\n if (this.statefulHandover && this.sourcePeerId === peerId && this.acceptedHandoverId === this.getHandoverId(response, event) && this.preparedState) {\n await this.sendPreparedState(event, this.acceptedHandoverId); return;\n }\n if (this.sourcePeerId || !this.active || !peerId || typeof replyTopic !== \"string\" || !replyTopic) return;\n if (this.statefulHandover) {\n // Reject incompatible upgrades before freezing the current owner.\n if (response.applicationContract !== this.applicationState?.contract) throw Error(\"Application handover contract differs; source remains active\");\n if (this.applicationState) {\n const preliminary = cloneApplicationHandoverState(this.applicationState.exportState());\n this.applicationState.validateState(preliminary);\n }\n const offer = mqttHandoverOfferSchema.parse(response.offer);\n const own = this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility());\n if (offer.length !== own.length || own.some(p => !offer.some(o => o.instanceName === p.instanceName && o.compatibility === p.compatibility)))\n throw Error(\"MQTT source/destination or semantic contract differs; source remains active\");\n if (this.sourceReviewInFlight) return;\n this.sourceReviewInFlight = true;\n try {\n // Hints only: actual frozen cursors are revalidated by every target\n // worker before activation. A quiet/unready source keeps admission.\n for (const proxy of this.unsMqttProxies) {\n const preview = offer.find(candidate => candidate.instanceName === proxy.instanceName)!.inputPreview;\n if (!await proxy.canPrepareHandoverInput(preview)) return;\n }\n } finally { this.sourceReviewInFlight = false; }\n if (this.sourcePeerId || !this.active) return;\n }\n this.sourcePeerId = peerId;\n this.acceptedHandoverId = this.getHandoverId(response, event);\n this.handoverInProgress = true;\n clearTimeout(this.activeTimeout);\n this.activeTimeout = undefined;\n this.sourcePreparation = this.prepareSourceHandover(response, event);\n try {\n await this.sourcePreparation;\n } catch {\n // Subscriber transfer or onRelease failed; never report successful source completion.\n logger.error(`${this.processName} - Handover source preparation failed; ownership requires review.`);\n if (!this.sourceExit && !this.statefulHandover) this.exitSource(1);\n }\n }\n\n // Requestor process\n // Check if the message is one of the handover_subscriber message in response to handover_request\n // and publish a handover_ack message\n if (response.type === \"handover_subscriber\") {\n if (this.statefulHandover) return;\n if (this.sourcePeerId) return;\n if (!this.acceptsHandoverResponse(this.getHandoverId(response, event))) return;\n // Find correct unsProxy instance for handover_subscriber and set it active\n this.unsMqttProxies.forEach((unsProxy: UnsMqttProxy) => {\n if (unsProxy.instanceName === response.instanceName) {\n unsProxy.setSubscriberActive(response.batchSize, response.referenceHash);\n }\n });\n }\n\n // Requestor process\n // Check if the message is a handover_fin at the end of handover_subscriber messages\n if (response.type === \"handover_fin\") {\n if (this.statefulHandover) {\n const id = this.getHandoverId(response, event);\n if (this.sourcePeerId || !this.requestingHandover || id !== this.handoverId || this.getSourceProcessId(event) !== this.expectedSourceId) return;\n if (this.acceptedStateDigest && response.stateDigest !== this.acceptedStateDigest) return;\n clearInterval(this.requestRetry);\n if (!this.targetPreparation) {\n const preparation = this.acceptPreparedState(response);\n this.targetPreparation = preparation;\n void preparation.catch(() => {\n if (!this.targetStateApplied && this.targetPreparation === preparation) this.targetPreparation = undefined;\n });\n }\n await this.targetPreparation;\n // Duplicate fin only replays the receipt; it never reapplies a baseline.\n if (response.stateDigest !== this.acceptedStateDigest) return;\n await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? \"\", this.handoverPayload(\"handover_ack\", id,\n { stateDigest: this.acceptedStateDigest }), { retain: false, qos: 1, properties: { userProperties: this.getUserProperties(id) } });\n return;\n }\n if (this.sourcePeerId) return;\n const handoverId = this.getHandoverId(response, event);\n if (!this.acceptsHandoverResponse(handoverId)) return;\n logger.info(`${this.processName} - Received handover fin from ${event.packet?.properties?.userProperties?.processName}.`);\n\n // Maybe we should count the number of requests that were allrady made TODO\n // this.handoverInProgress = false;\n // this.requestingHandover = false;\n\n this.active = true;\n this.event.emit(\"handoverManager\", { active: this.active });\n logger.info(`${this.processName} - Handover completed.`);\n // Activate all UNS proxy instance publishers and subscribers.\n this.unsMqttProxies.forEach((unsProxy) => {\n unsProxy.setPublisherActive();\n unsProxy.setSubscriberActive();\n });\n\n // Maybe we should reply with handover_ack.\n await this.mqttProxy.publish(\n event.packet.properties?.responseTopic ?? \"\",\n this.handoverPayload(\"handover_ack\", handoverId),\n {\n retain: false,\n properties: {\n responseTopic: this.topicBuilder.getHandoverTopic(),\n userProperties: this.getUserProperties(handoverId),\n },\n },\n );\n logger.info(`${this.processName} - Handover ack message sent.`);\n }\n\n // Responder process\n // Check if the message is a handover_ack at the end of handover_fin messages\n if (response.type === \"handover_ack\") {\n if (!this.sourcePreparation || this.getSourceProcessId(event) !== this.sourcePeerId) return;\n const handoverId = this.getHandoverId(response, event);\n if (this.acceptedHandoverId && handoverId && this.acceptedHandoverId !== handoverId) {\n logger.warn(`${this.processName} - Ignoring acknowledgement for another migration.`);\n return;\n }\n if (this.statefulHandover && (handoverId !== this.acceptedHandoverId || response.stateDigest !== this.stateDigest || !this.stateDigest)) return;\n clearInterval(this.finRetry);\n logger.info(`${this.processName} - Received handover ack from ${event.packet?.properties?.userProperties?.processName}.`);\n this.sourceExit ??= this.finishSourceHandover();\n await this.sourceExit;\n }\n } catch (error) {\n logger.error(`${this.processName} - Error processing handover response: ${error.message}`);\n }\n }\n}\n"]}
|