@mark1russell7/service 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,7 @@ This release has the declaration, its export, the law, the health runtime and th
14
14
  - **Keyed operations.** A keyed operation has the window that the server keeps its key for. `mayReissue` answers from the declaration of an operation. An operation without a declaration does not go again.
15
15
  - **The law.** `evaluate` and `verdictOf` decide one rule of the timer-ordering law on one binding. A rule is data: linear inequalities over `symbol@hop`, with a margin. A verdict is `holds`, `violated`, `unbounded` (it holds only because a timer is absent) or `depends` (nothing is bound).
16
16
  - **The linter.** `lint(description, scenario, rules)` applies the service rules (`SERVICE_RULES`) to a declaration. The declaration binds the symbols of the service: the polls of its checks, the windows of its policies, its drain. The scenario binds the symbols of the platform and of each dependency. A `depends` finding names the symbols that have no bound.
17
+ - **The hold.** The cap of the `fail readiness` phase binds `readiness_hold@process`, and a service without that phase holds for zero. The hold keeps the listener open after the signal, as a preStop sleep does. Thus O04 can compare the sum of the two with the propagation of the endpoints.
17
18
 
18
19
  The format of a description is `mark1russell7.service/1`.
19
20
 
package/build/index.js CHANGED
@@ -1359,6 +1359,7 @@ const verdictOf = (rule, bind, evaluated) => {
1359
1359
  //#region src/lint.ts
1360
1360
  /** These are the identifiers of the rules that each declaration gets. */
1361
1361
  const SERVICE_RULES = [
1362
+ "O04",
1362
1363
  "O09",
1363
1364
  "O10",
1364
1365
  "O11",
@@ -1405,6 +1406,8 @@ const serviceBindings = (svc, scenario) => {
1405
1406
  set("startup_budget@pod", secondsOf(svc.policies.startup), "the declared startup budget");
1406
1407
  set("process_drain_time@process", svc.shutdown.phases.reduce((total, phase) => total + secondsOf(phase.cap), 0), `the shutdown phases' caps, summed (${svc.shutdown.phases.map((phase) => phase.name).join(", ")})`);
1407
1408
  set("cooperative_window@process", boundOf(svc.shutdown.cooperativeWindow), "the declared cooperative window");
1409
+ const hold = svc.shutdown.phases.find((phase) => phase.name === "fail readiness");
1410
+ set("readiness_hold@process", hold === void 0 ? 0 : secondsOf(hold.cap), hold === void 0 ? "no fail readiness phase: the intake stops at once after the signal" : "the cap of the fail readiness phase, which holds while the endpoints update");
1408
1411
  for (const [symbol, amount] of Object.entries(scenario.platform)) set(symbol, boundOf(amount), "the scenario");
1409
1412
  return {
1410
1413
  bind,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["message","boundOf","r"],"sources":["../src/description.ts","../src/latch.ts","../src/poller.ts","../src/health.ts","../src/process.ts","../src/requests.ts","../src/lifecycle.ts","../src/watchdog.ts","../src/builder.ts","../src/law.ts","../src/lint.ts","../src/machines.ts"],"sourcesContent":["import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport type { Operation } from \"./operation.ts\";\nimport type { Poll, Timing } from \"./timing.ts\";\n\n/**\n * This is the format of a service as data: what `describe()` exports, and nothing starts. The\n * timer algebra and the ordering law read it. Each timer has a unit. Each knob comes from the\n * declaration, because the runtime has no defaults. A bound that nobody gives is \"no bound\", not\n * zero.\n */\nexport const FORMAT = \"mark1russell7.service/1\";\n\n/** A step is a step of a connection that a check examines. A check that skips a step cannot see a failure in it. */\nexport type Step =\n \"dial\" | \"local-state\" | \"round-trip\" | \"checkout\" | \"progress\" | \"topology\" | \"latency\";\n\n/** A gate is the policy that the failures of a check count toward. */\nexport type Gate = \"readiness\" | \"liveness\";\n\n/** A check is an active check: one signal, for one step of a connection. */\nexport interface Check {\n readonly name: string;\n /** This name is the dependency that it examines, or null when it reads the state of the process. */\n readonly dependency: string | null;\n readonly step: Step;\n /** This flag tells if it uses the connection of the application. A check on its own connection cannot see a failure of that connection. */\n readonly ownConnection: boolean;\n /** This gate is the policy that it controls, or null for a check that only reports. It has no default. */\n readonly gates: Gate | null;\n readonly poll: Poll;\n}\n\n/**\n * A dependency is something that the service uses, with its client. Its knobs have the names that\n * the ordering law uses: `request_timeout`, `connect_bound`, `driver_writeoff_time`,\n * `heartbeat_interval`, `client_retry_window`. Thus the law reads them without a translation.\n */\nexport interface Dependency {\n readonly name: string;\n /** This identifier is the component in the corpus (`systems.component.postgresql`), or null when the corpus does not have it. */\n readonly system: string | null;\n readonly knobs: Readonly<Record<string, Extent>>;\n /**\n * These are the operations that the service does with it, by name (`GET /v1/forecast`, `INCR`).\n * Each operation declares if it can go again. A dependency usually has the two kinds, thus a\n * dependency has no idempotency of its own. An operation without a declaration does not go again.\n */\n readonly operations: Readonly<Record<string, Operation>>;\n}\n\n/**\n * A transport is something that takes work in: a server, a consumer or a worker. Each is a\n * component with a state machine. The service is ready only while each transport takes work.\n */\nexport interface Transport {\n readonly name: string;\n readonly kind: \"server\" | \"consumer\" | \"worker\";\n /** These are its knobs, with the names of the law where the law has them: the `server_idle_close` of a server. */\n readonly knobs: Readonly<Record<string, Extent>>;\n}\n\n/**\n * A controller is a group of routes. It is ready only while the dependencies that it needs are\n * ready. Each route declares if its callers can send it again. When the shutdown stops a request,\n * an idempotent route answers 503, which a caller can retry. Other routes answer 500.\n */\nexport interface Controller {\n readonly name: string;\n readonly requires: readonly string[];\n readonly routes: Readonly<Record<string, Operation>>;\n}\n\n/** A coupling is a declared edge between two policies. The algebra checks its margin. */\nexport interface Coupling {\n readonly from: Gate;\n readonly to: Gate;\n readonly effect: \"fails\";\n}\n\n/** A phase is one step of the shutdown sequence, with a cap. Its owner adds it. */\nexport interface Phase {\n readonly name: string;\n readonly cap: Quantity;\n}\n\n/** A service description is the whole graph of a service, as data. */\nexport interface ServiceDescription {\n readonly format: typeof FORMAT;\n readonly name: string;\n /** This flag tells if a part joined after the start. That part starts tripped, and the export is partial. */\n readonly partial: boolean;\n readonly transports: readonly Transport[];\n readonly dependencies: readonly Dependency[];\n readonly controllers: readonly Controller[];\n readonly checks: readonly Check[];\n readonly policies: {\n readonly readiness: Timing;\n readonly liveness: Timing;\n /** This duration is how long the service can take to start. The startup probe must agree with it. */\n readonly startup: Quantity;\n };\n readonly couplings: readonly Coupling[];\n readonly shutdown: {\n readonly phases: readonly Phase[];\n /** This window is how long before the end of the drain the cooperative abort occurs, or no bound. */\n readonly cooperativeWindow: Extent;\n };\n}\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Timing } from \"./timing.ts\";\n\n/**\n * An outcome is the result of one poll of a check. Only `\"ok\"` passes. An `\"indeterminate\"`\n * outcome measured nothing: it does not pass, and it does not fail.\n */\nexport type Outcome = \"ok\" | \"error\" | \"indeterminate\";\n\n/** A result is one poll of a check, as the runtime records it. */\nexport interface Result {\n readonly outcome: Outcome;\n /** This time is when the result came, on the monotonic clock, in milliseconds. */\n readonly at: number;\n /** This time is how long the poll took, in milliseconds. */\n readonly took: number;\n /** This text tells why the poll did not pass, or it is null. */\n readonly cause: string | null;\n /** This flag tells if the poll failed at its timeout. */\n readonly timedOut: boolean;\n /** This flag tells if the check threw. A throw is an error with a flag. */\n readonly thrown: boolean;\n /** This flag tells if the force-fail list made the poll fail. */\n readonly forced: boolean;\n}\n\n/**\n * A latch turns the results of one check into a verdict, by the timing of its policy. A debounce\n * counts polls in sequence. A window measures time on the monotonic clock from the first failure\n * of a sequence. A tripped latch fails its policy.\n */\nexport interface Latch {\n readonly tripped: boolean;\n /** This time is when the latch last tripped or released. */\n readonly since: number;\n /** This count is the sequence of the latest results: above zero for passes, below zero for failures. */\n readonly streak: number;\n record(result: Result): void;\n /** This method stops the timers of a window. */\n stop(): void;\n}\n\n/**\n * A trace receives each transition of a machine of the runtime: the state before, the state after,\n * and the event. The machine documents of the lifecycle name the same states and events, thus a\n * test can replay a trace against its document.\n */\nexport type Trace = (from: string, to: string, on: string) => void;\n\nconst ms = (amount: { readonly value: number; readonly unit: string }): number =>\n valueIn(amount, \"ms\");\n\n/**\n * This function makes a latch. `tripped` is its state at the start: a readiness latch starts\n * tripped, and a liveness latch starts released. The latch starts `changed` each time it trips or\n * releases, and it gives each transition to `trace`. A debounce has the states `released` and\n * `tripped`. A window also has the states `failing` and `recovering`, while one of its timers runs.\n */\nexport const latch = (\n timing: Timing,\n clock: Clock,\n tripped: boolean,\n changed: () => void,\n trace?: Trace,\n): Latch => {\n let state = tripped;\n let since = clock.now();\n let streak = 0;\n let fails = 0;\n let passes = 0;\n // the timers of a window: the time that a sequence of failures or passes started, and its timer\n let failingSince: number | null = null;\n let passingSince: number | null = null;\n let failTimer: Cancel | null = null;\n let passTimer: Cancel | null = null;\n\n /** This function gives the name of the state, as the machine documents name it. */\n const named = (): string =>\n state\n ? passingSince === null\n ? \"tripped\"\n : \"recovering\"\n : failingSince === null\n ? \"released\"\n : \"failing\";\n /** This function does one step of the machine, and gives its transition to the trace. */\n const step = (on: string, change: () => void): void => {\n const from = named();\n change();\n trace?.(from, named(), on);\n };\n\n const set = (next: boolean): void => {\n if (next === state) return;\n state = next;\n since = clock.now();\n changed();\n };\n const stopFail = (): void => {\n failTimer?.();\n failTimer = null;\n failingSince = null;\n };\n const stopPass = (): void => {\n passTimer?.();\n passTimer = null;\n passingSince = null;\n };\n\n const debounce = (result: Result): void => {\n if (timing.type !== \"debounce\") return;\n if (result.outcome === \"ok\") {\n passes += 1;\n fails = 0;\n if (state && passes >= timing.recoverAfter) set(false);\n } else {\n fails += 1;\n passes = 0;\n if (!state && fails >= timing.failAfter) set(true);\n }\n };\n\n const windowed = (result: Result): void => {\n if (timing.type !== \"window\") return;\n if (result.outcome === \"ok\") {\n stopFail();\n if (!state) return;\n if (timing.recoverAfter === undefined) {\n set(false);\n return;\n }\n if (passingSince !== null) return;\n const wait = ms(timing.recoverAfter);\n if (wait <= 0) {\n stopPass();\n set(false);\n return;\n }\n passingSince = result.at;\n passTimer = clock.after(wait, () =>\n step(\"window.recovered\", () => {\n passTimer = null;\n passingSince = null;\n set(false);\n }),\n );\n return;\n }\n stopPass();\n if (state || failingSince !== null) return;\n const wait = ms(timing.failAfter);\n if (wait <= 0) {\n stopFail();\n set(true);\n return;\n }\n failingSince = result.at;\n failTimer = clock.after(wait, () =>\n step(\"window.failed\", () => {\n failTimer = null;\n failingSince = null;\n set(true);\n }),\n );\n };\n\n return {\n get tripped() {\n return state;\n },\n get since() {\n return since;\n },\n get streak() {\n return streak;\n },\n record(result) {\n // an indeterminate poll measured nothing: it moves no sequence and no streak\n if (result.outcome === \"indeterminate\") return;\n const pass = result.outcome === \"ok\";\n step(pass ? \"poll.ok\" : \"poll.fail\", () => {\n streak = pass ? Math.max(0, streak) + 1 : Math.min(0, streak) - 1;\n debounce(result);\n windowed(result);\n });\n },\n stop() {\n stopFail();\n stopPass();\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Outcome, Result, Trace } from \"./latch.ts\";\nimport type { Poll } from \"./timing.ts\";\n\n/**\n * A check function measures one step of a connection. It ends with nothing or `\"ok\"` for a pass,\n * or with `\"error\"` or `\"indeterminate\"`. A throw is an error with a flag. The runtime aborts the\n * signal at the timeout of the poll.\n */\nexport type CheckFn = (signal: AbortSignal) => Outcome | void | Promise<Outcome | void>;\n\n/** A poller starts one check on its own loop, and gives each result to `record`. */\nexport interface Poller {\n /** This result is the latest one, or null before the first poll. */\n readonly last: Result | null;\n /** This method polls one time at once, outside the loop, and gives the result. */\n pollNow(): Promise<Result>;\n /** This method starts the loop. The first poll of the loop comes after `delay` milliseconds. */\n start(delay: number): void;\n /** This method gives the next delay of the loop: the interval, with the jitter. */\n jittered(): number;\n stop(): void;\n}\n\nexport interface PollerOptions {\n readonly name: string;\n readonly poll: Poll;\n readonly run: CheckFn;\n readonly clock: Clock;\n readonly random: () => number;\n /** This function tells if the force-fail list names the check at this time. */\n readonly forced: () => boolean;\n readonly record: (result: Result) => void;\n /**\n * This function receives each transition of the poller. Its states are `idle`, `waiting`,\n * `calling`, `hung` (a call that outlived its timeout) and `stopped`.\n */\n readonly trace?: Trace | undefined;\n}\n\nconst message = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\n/**\n * This function makes a poller. The loop is on a fixed delay from the start of each poll. Thus the\n * next poll starts one interval after the current poll starts, with the jitter (±20 % is 0.2).\n * The timing algebra of the declaration uses this model.\n *\n * Each poll makes a new measurement. While a use of the check is pending, the poller does not\n * start the check again. Such a poll fails at once with the same cause, and the sequence of\n * failures continues.\n */\nexport const poller = (options: PollerOptions): Poller => {\n const { clock, poll, run } = options;\n const interval = valueIn(poll.interval, \"ms\");\n const timeout = valueIn(poll.timeout, \"ms\");\n let last: Result | null = null;\n let next: Cancel | null = null;\n let stopped = false;\n let looping = false;\n // the call that has not ended, and when it started; `late` once the call outlived its timeout\n let pending: { readonly since: number; late: boolean } | null = null;\n // the polls in flight: stop() ends each one, and its timer\n const active = new Set<() => void>();\n\n /** This function gives the name of the state, as the machine document of the poller names it. */\n const named = (): string =>\n stopped\n ? \"stopped\"\n : pending !== null\n ? pending.late\n ? \"hung\"\n : \"calling\"\n : looping\n ? \"waiting\"\n : \"idle\";\n const step = (on: string, change: () => void): void => {\n const from = named();\n change();\n options.trace?.(from, named(), on);\n };\n\n const result = (\n outcome: Outcome,\n started: number,\n fields: Partial<Pick<Result, \"cause\" | \"timedOut\" | \"thrown\" | \"forced\">> = {},\n ): Result => ({\n outcome,\n at: clock.now(),\n took: clock.now() - started,\n cause: fields.cause ?? null,\n timedOut: fields.timedOut ?? false,\n thrown: fields.thrown ?? false,\n forced: fields.forced ?? false,\n });\n\n const once = (): Promise<Result> => {\n const started = clock.now();\n if (options.forced())\n return Promise.resolve(result(\"error\", started, { cause: \"the force-fail list names it\", forced: true }));\n if (pending !== null) {\n const cause = `its call of ${Math.round(started - pending.since)} ms ago is still pending`;\n return Promise.resolve(result(\"error\", started, { cause }));\n }\n const controller = new AbortController();\n return new Promise<Result>((resolve) => {\n let done = false;\n const finish = (value: Result): void => {\n if (done) return;\n done = true;\n active.delete(abandon);\n cancel();\n resolve(value);\n };\n const abandon = (): void => {\n controller.abort();\n finish(result(\"error\", started, { cause: \"the runtime stopped\" }));\n };\n active.add(abandon);\n const call = { since: started, late: false };\n pending = call;\n const cancel = clock.after(timeout, () => {\n controller.abort();\n // the poll fails at its timeout. The call stays pending until it settles\n step(\"timeout\", () => {\n if (pending === call) call.late = true;\n });\n finish(result(\"error\", started, { cause: `no answer in ${timeout} ms`, timedOut: true }));\n });\n const settled = (): void => {\n if (pending !== call) return;\n step(call.late ? \"late-answer\" : \"answer\", () => {\n pending = null;\n });\n };\n let answer: Promise<Outcome | void>;\n try {\n answer = Promise.resolve(run(controller.signal));\n } catch (error) {\n answer = Promise.reject(error);\n }\n answer.then(\n (value) => {\n settled();\n const outcome = value ?? \"ok\";\n finish(result(outcome, started, outcome === \"ok\" ? {} : { cause: `the check gave ${outcome}` }));\n },\n (error: unknown) => {\n settled();\n finish(result(\"error\", started, { cause: message(error), thrown: true }));\n },\n );\n });\n };\n\n const handle = (value: Result): Result => {\n last = value;\n if (!stopped) options.record(value);\n return value;\n };\n\n const jittered = (): number => interval * (1 + poll.jitter * (2 * options.random() - 1));\n\n const tick = (): void => {\n next = null;\n if (stopped) return;\n let polled: Promise<Result> | undefined;\n step(\"tick\", () => {\n next = clock.after(jittered(), tick);\n polled = once();\n });\n void polled!.then(handle);\n };\n\n return {\n get last() {\n return last;\n },\n pollNow: () => {\n let polled: Promise<Result> | undefined;\n step(\"sweep\", () => {\n polled = once();\n });\n return polled!.then(handle);\n },\n start(delay) {\n if (stopped || next !== null) return;\n step(\"start\", () => {\n looping = true;\n next = clock.after(Math.max(0, delay), tick);\n });\n },\n jittered,\n stop() {\n if (stopped) return;\n step(\"stop\", () => {\n stopped = true;\n next?.();\n next = null;\n });\n for (const abandon of [...active]) abandon();\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Check, Gate, ServiceDescription } from \"./description.ts\";\nimport type { MachineName, Recorder } from \"./events.ts\";\nimport type { Latch, Result, Trace } from \"./latch.ts\";\nimport { latch } from \"./latch.ts\";\nimport type { CheckFn, Poller } from \"./poller.ts\";\nimport { poller } from \"./poller.ts\";\n\n/** These options start the runtime. Without them, it uses the real clock and `Math.random`. */\nexport interface StartOptions {\n readonly clock?: Clock | undefined;\n /** This function gives a number in [0, 1) for the jitter of each poll. */\n readonly random?: (() => number) | undefined;\n /** This list names the checks or dependencies whose polls fail at once (a seam for tests). */\n readonly forceFail?: readonly string[] | undefined;\n /** This function receives each transition of each machine of the runtime. */\n readonly record?: Recorder | undefined;\n /**\n * This function can replace the function of a check, for example to add a delay to each call\n * (a seam for tests: a degraded dependency). It gives the function to use.\n */\n readonly wrapCheck?: ((check: Check, run: CheckFn) => CheckFn) | undefined;\n}\n\n/** A check verdict is the cached state of one check, for the probes and the report. */\nexport interface CheckVerdict {\n readonly gates: Gate | null;\n readonly dependency: string | null;\n /** This flag tells if the latch of the check is tripped, or it is null for a check that only reports. */\n readonly tripped: boolean | null;\n readonly streak: number;\n /** This time is when the latch last changed, in milliseconds on the monotonic clock. */\n readonly since: number | null;\n readonly last: Result | null;\n}\n\n/** A verdict is the state of the service, from its cache. Nothing in it starts a check. */\nexport interface HealthVerdict {\n /** This flag becomes true one time, after the warm-up sweep. */\n readonly started: boolean;\n readonly draining: boolean;\n /** This flag tells if the service can take new work: started, not draining, and each readiness latch released. */\n readonly ready: boolean;\n /** This flag tells if each liveness latch is released. */\n readonly alive: boolean;\n /** These flags tell if each controller can take new work, from the dependencies that it needs. */\n readonly controllers: Readonly<Record<string, boolean>>;\n readonly dependencies: Readonly<Record<string, boolean>>;\n readonly checks: Readonly<Record<string, CheckVerdict>>;\n readonly startup: { readonly budget: number; readonly elapsed: number; readonly overBudget: boolean };\n}\n\n/** A probe kind is the question that a probe asks: the startup, the readiness, the liveness, or the whole report. */\nexport type ProbeKind = \"startup\" | \"readiness\" | \"liveness\" | \"report\";\n\n/** A probe answer is a status and a body, for any HTTP framework or none. */\nexport interface ProbeAnswer {\n readonly status: 200 | 503;\n readonly contentType: \"application/health+json\";\n readonly body: {\n readonly status: \"pass\" | \"fail\";\n readonly service: string;\n readonly probe: ProbeKind;\n readonly controller?: string | undefined;\n readonly verdict?: HealthVerdict | undefined;\n };\n}\n\n/** The health runtime of a started service. It reads its timing and its checks from the declaration. */\nexport interface Health {\n verdict(): HealthVerdict;\n /**\n * This method answers a probe from the cache, in constant time, and it starts no check. A\n * readiness probe with a controller asks about that controller. The report always answers 200.\n */\n probe(kind: ProbeKind, controller?: string): ProbeAnswer;\n /** This method gives each change of the verdict to `listener`, and gives the function that stops it. */\n subscribe(listener: (verdict: HealthVerdict) => void): () => void;\n /** This method adds a name to the force-fail list, or removes it. */\n forceFail(name: string, on: boolean): void;\n /** This method fails readiness for good. The drain of the lifecycle starts with it. */\n drain(): void;\n /** This method stops each poller and each timer of the runtime. */\n stop(): void;\n}\n\n/** The runtime parts that the builder and the lifecycle use. */\nexport interface Runtime extends Health {\n /**\n * This method opens the startup gate: one bounded warm-up sweep of each check, then the loops of\n * the pollers. It is idempotent, and it does nothing after `failStartup()` or `stop()`.\n */\n warmUp(): Promise<void>;\n /** This method moves the gate from `closed` to `starting`: the lifecycle starts its components. */\n beginStartup(): void;\n /** This method moves the gate to `failed` for good: a component did not start. */\n failStartup(cause: string): void;\n addCheck(check: Check, run: CheckFn): void;\n addDependency(name: string): void;\n}\n\ninterface Entry {\n readonly check: Check;\n readonly poller: Poller;\n readonly latch: Latch | null;\n}\n\n/**\n * This function makes the runtime of a declaration. The service has no defaults of its own: each\n * timing, poll and coupling comes from the description.\n */\nexport const healthRuntime = (\n description: ServiceDescription,\n runs: ReadonlyMap<string, CheckFn>,\n options: StartOptions = {},\n): Runtime => {\n const clock = options.clock ?? realClock;\n const random = options.random ?? Math.random;\n const forced = new Set(options.forceFail ?? []);\n const startedAt = clock.now();\n const budget = valueIn(description.policies.startup, \"ms\");\n const entries = new Map<string, Entry>();\n const dependencies = new Set(description.dependencies.map((dependency) => dependency.name));\n // a dependency that joined after the start is tripped until a check on it passes one time\n const lateDependencies = new Set<string>();\n const listeners = new Set<(verdict: HealthVerdict) => void>();\n let started = false;\n let draining = false;\n let stopped = false;\n // the startup gate: closed, starting (components start), sweeping (the warm-up), open or failed\n let gate = \"closed\";\n\n const tracer =\n (machine: MachineName, instance: string): Trace =>\n (from, to, on) =>\n options.record?.({ at: clock.now(), machine, instance, from, to, on });\n const gateTrace = tracer(\"gate\", description.name);\n const moveGate = (to: string, on: string, detail?: string): void => {\n const from = gate;\n gate = to;\n options.record?.({\n at: clock.now(),\n machine: \"gate\",\n instance: description.name,\n from,\n to,\n on,\n ...(detail === undefined ? {} : { detail }),\n });\n };\n // the startup budget: a gate that is not open at its end says so. The platform's startup probe\n // acts on it, and the runtime does not stop itself\n let budgetTimer: Cancel | null = clock.after(budget, () => {\n budgetTimer = null;\n if (gate !== \"open\" && gate !== \"failed\") gateTrace(gate, gate, \"startup.over-budget\");\n });\n\n const changed = (): void => {\n if (listeners.size === 0) return;\n const now = verdict();\n for (const listener of listeners) listener(now);\n };\n\n const add = (check: Check, given: CheckFn, late: boolean): void => {\n const run = options.wrapCheck?.(check, given) ?? given;\n const timing = check.gates === null ? null : description.policies[check.gates];\n // readiness is earned: its latch starts tripped. Liveness passes from the start (and a late\n // liveness check starts released too, or one late registration could restart the process)\n const made =\n timing === null\n ? null\n : latch(\n timing,\n clock,\n check.gates === \"readiness\",\n changed,\n tracer(timing.type === \"window\" ? \"window\" : \"latch\", check.name),\n );\n const entry: Entry = {\n check,\n latch: made,\n poller: poller({\n name: check.name,\n poll: check.poll,\n run,\n clock,\n random,\n forced: () =>\n forced.has(check.name) || (check.dependency !== null && forced.has(check.dependency)),\n record: (result) => {\n made?.record(result);\n // a late dependency is released by the first pass of a check on it\n if (result.outcome === \"ok\" && check.dependency !== null)\n if (lateDependencies.delete(check.dependency)) changed();\n },\n trace: tracer(\"poller\", check.name),\n }),\n };\n entries.set(check.name, entry);\n if (late && !stopped) {\n // a late registration pulls readiness until its first pass: it polls at once, then on its loop\n void entry.poller.pollNow();\n entry.poller.start(entry.poller.jittered());\n }\n };\n\n for (const check of description.checks) {\n const run = runs.get(check.name);\n if (run === undefined) throw new Error(`check ${check.name} has no function: the runtime has no defaults`);\n add(check, run, false);\n }\n\n const latchesOf = (gateOf: Gate, keep: (check: Check) => boolean = () => true): Latch[] =>\n [...entries.values()].flatMap((entry) =>\n entry.check.gates === gateOf && entry.latch !== null && keep(entry.check) ? [entry.latch] : [],\n );\n const released = (latches: readonly Latch[]): boolean => latches.every((each) => !each.tripped);\n /** This function tells if a policy fails through a declared coupling from another policy. */\n const coupledOff = (to: Gate, base: Record<Gate, boolean>): boolean =>\n description.couplings.some((coupling) => coupling.to === to && !base[coupling.from]);\n\n const verdict = (): HealthVerdict => {\n const base: Record<Gate, boolean> = {\n readiness: released(latchesOf(\"readiness\")) && lateDependencies.size === 0,\n liveness: released(latchesOf(\"liveness\")),\n };\n const alive = base.liveness && !coupledOff(\"liveness\", base);\n const open = started && !draining && !coupledOff(\"readiness\", base);\n const ready = open && base.readiness;\n const dependencyReady = (name: string): boolean =>\n !lateDependencies.has(name) &&\n released(latchesOf(\"readiness\", (check) => check.dependency === name));\n const own = released(latchesOf(\"readiness\", (check) => check.dependency === null));\n const dependenciesOut: Record<string, boolean> = {};\n for (const name of dependencies) dependenciesOut[name] = dependencyReady(name);\n const controllers: Record<string, boolean> = {};\n for (const controller of description.controllers)\n controllers[controller.name] = open && own && controller.requires.every(dependencyReady);\n const checks: Record<string, CheckVerdict> = {};\n for (const [name, entry] of entries) {\n checks[name] = {\n gates: entry.check.gates,\n dependency: entry.check.dependency,\n tripped: entry.latch?.tripped ?? null,\n streak: entry.latch?.streak ?? 0,\n since: entry.latch?.since ?? null,\n last: entry.poller.last,\n };\n }\n const elapsed = clock.now() - startedAt;\n return {\n started,\n draining,\n ready,\n alive,\n controllers,\n dependencies: dependenciesOut,\n checks,\n startup: { budget, elapsed, overBudget: !started && elapsed > budget },\n };\n };\n\n const answer = (kind: ProbeKind, pass: boolean, controller?: string): ProbeAnswer => ({\n status: pass || kind === \"report\" ? 200 : 503,\n contentType: \"application/health+json\",\n body: {\n status: pass ? \"pass\" : \"fail\",\n service: description.name,\n probe: kind,\n ...(controller === undefined ? {} : { controller }),\n ...(kind === \"report\" ? { verdict: verdict() } : {}),\n },\n });\n\n return {\n verdict,\n probe(kind, controller) {\n const now = verdict();\n if (kind === \"startup\") return answer(kind, now.started);\n if (kind === \"liveness\") return answer(kind, now.alive);\n if (kind === \"report\") return answer(kind, now.ready && now.alive);\n if (controller === undefined) return answer(kind, now.ready);\n const known = now.controllers[controller];\n if (known === undefined) throw new Error(`${description.name} has no controller ${controller}`);\n return answer(kind, known, controller);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n forceFail(name, on) {\n if (on) forced.add(name);\n else forced.delete(name);\n },\n drain() {\n if (draining) return;\n draining = true;\n changed();\n },\n stop() {\n stopped = true;\n budgetTimer?.();\n budgetTimer = null;\n for (const entry of entries.values()) {\n entry.poller.stop();\n entry.latch?.stop();\n }\n },\n beginStartup() {\n if (gate === \"closed\") moveGate(\"starting\", \"startup.begin\");\n },\n failStartup(cause) {\n if (gate === \"open\" || gate === \"failed\") return;\n moveGate(\"failed\", \"startup.fail\", cause);\n budgetTimer?.();\n budgetTimer = null;\n },\n async warmUp() {\n if (started || stopped || gate === \"failed\" || gate === \"sweeping\") return;\n moveGate(\"sweeping\", \"mark-ready\");\n // one bounded sweep of each check: each poll ends at its timeout at the latest\n const sweep = clock.now();\n await Promise.all([...entries.values()].map((entry) => entry.poller.pollNow()));\n if (stopped || gate !== \"sweeping\") return;\n for (const entry of entries.values())\n entry.poller.start(entry.poller.jittered() - (clock.now() - sweep));\n started = true;\n budgetTimer?.();\n budgetTimer = null;\n moveGate(\"open\", \"sweep.done\");\n changed();\n },\n addCheck(check, run) {\n if (entries.has(check.name)) throw new Error(`${description.name} already has a check named ${check.name}`);\n add(check, run, true);\n changed();\n },\n addDependency(name) {\n if (dependencies.has(name)) return;\n dependencies.add(name);\n lateDependencies.add(name);\n changed();\n },\n };\n};\n","/** An exit signal is a signal that asks the process to stop: each one starts the shutdown. */\nexport type ExitSignal = \"SIGINT\" | \"SIGTERM\" | \"SIGQUIT\";\n\n/** These are the signals that the runtime handles. */\nexport const EXIT_SIGNALS: readonly ExitSignal[] = [\"SIGINT\", \"SIGTERM\", \"SIGQUIT\"];\n\n/**\n * A process port is the part of the process that the runtime uses: its exit signals and its exit.\n * The runtime owns the handlers, because Node as PID 1 without a handler ignores SIGTERM. Only the\n * runtime ends the process, and library code does not. A test gives a fake port, sends signals,\n * and reads the exit code.\n */\nexport interface ProcessPort {\n /** This method adds a handler for a signal, and gives the function that removes it. */\n on(signal: ExitSignal, handler: () => void): () => void;\n /** This method ends the process with a code: 0 after a clean shutdown, and 1 after an error. */\n exit(code: number): void;\n}\n\ninterface NodeProcess {\n on(signal: string, handler: () => void): unknown;\n off(signal: string, handler: () => void): unknown;\n exit(code: number): never;\n}\n\n/** This port is the process of Node. */\nexport const nodeProcess: ProcessPort = {\n on(signal, handler) {\n const host = (globalThis as { process?: NodeProcess }).process;\n if (host === undefined) throw new Error(\"no process: give the runtime a process port\");\n host.on(signal, handler);\n return () => {\n host.off(signal, handler);\n };\n },\n exit(code) {\n const host = (globalThis as { process?: NodeProcess }).process;\n if (host === undefined) throw new Error(\"no process: give the runtime a process port\");\n host.exit(code);\n },\n};\n","import { quantity } from \"@mark1russell7/quantity\";\nimport type { Clock } from \"./clock.ts\";\nimport type { Controller } from \"./description.ts\";\nimport { mayReissue } from \"./operation.ts\";\n\n/**\n * A request outcome is what a request of a controller came to, for any HTTP framework or none:\n * - `done`: the work gave a value.\n * - `failed`: the work failed for a cause of its own. The adapter answers it as it answers any\n * error of the route.\n * - `refused`: the service did not admit the request. A refused request did not start, thus its\n * caller can send it again: 503, retryable.\n * - `cut`: the shutdown stopped the request. A route whose callers can send it again answers 503,\n * retryable. Each other route answers 500, not retryable, because the work can have had its\n * effect.\n */\nexport type RequestOutcome<T> =\n | { readonly kind: \"done\"; readonly value: T }\n | { readonly kind: \"failed\"; readonly error: unknown }\n | { readonly kind: \"refused\"; readonly status: 503; readonly retryable: true; readonly cause: string }\n | {\n readonly kind: \"cut\";\n readonly status: 503 | 500;\n readonly retryable: boolean;\n readonly cause: string;\n };\n\n/** A request function does the work of one request. It receives a signal that the shutdown aborts. */\nexport type RequestFn<T> = (signal: AbortSignal) => Promise<T> | T;\n\ninterface InFlight {\n readonly controller: string;\n readonly route: string;\n readonly started: number;\n readonly abort: AbortController;\n /** This function answers the request with an outcome, one time. */\n readonly answer: (outcome: RequestOutcome<never>) => void;\n}\n\n/** A tracker admits requests, counts the requests in flight, and answers what the shutdown cuts. */\nexport interface Tracker {\n request<T>(controller: string, route: string, work: RequestFn<T>): Promise<RequestOutcome<T>>;\n inFlight(controller?: string): number;\n /** This method refuses each later request. */\n stopIntake(): void;\n /** This method aborts the signal of each request in flight: the cooperative abort of the drain. */\n cooperativeAbort(): void;\n /** This method waits until no request is in flight, or until `deadline`. Then it cuts each request that is left. */\n drain(deadline: number): Promise<void>;\n}\n\n/**\n * This function makes a tracker. `refuses` gives the reason that the service refuses a new request\n * for a controller at this time, or null when it admits it.\n */\nexport const tracker = (\n controllers: readonly Controller[],\n clock: Clock,\n refuses: (controller: string) => string | null,\n): Tracker => {\n const byName = new Map(controllers.map((controller) => [controller.name, controller]));\n const flights = new Set<InFlight>();\n const idle = new Set<() => void>();\n let intake = true;\n\n const settle = (flight: InFlight): void => {\n flights.delete(flight);\n if (flights.size === 0) for (const done of [...idle]) done();\n };\n /** This function answers a request that the shutdown stopped, by the idempotency of its route. */\n const cut = (flight: InFlight, cause: string): RequestOutcome<never> => {\n const operation = byName.get(flight.controller)!.routes[flight.route]!;\n const since = quantity(Math.max(0, clock.now() - flight.started) / 1000, \"s\");\n const retryable = mayReissue(operation, since);\n return { kind: \"cut\", status: retryable ? 503 : 500, retryable, cause };\n };\n\n return {\n request<T>(controller: string, route: string, work: RequestFn<T>) {\n const held = byName.get(controller);\n if (held === undefined) throw new Error(`there is no controller ${controller}`);\n // a route that nobody declared has no idempotency: the runtime has no defaults\n if (held.routes[route] === undefined)\n throw new Error(`controller ${controller} does not declare the route ${route}`);\n const refusal = intake ? refuses(controller) : \"the service stopped its intake\";\n if (refusal !== null)\n return Promise.resolve({ kind: \"refused\", status: 503, retryable: true, cause: refusal });\n return new Promise<RequestOutcome<T>>((resolve) => {\n let answered = false;\n const flight: InFlight = {\n controller,\n route,\n started: clock.now(),\n abort: new AbortController(),\n answer: (outcome) => {\n if (answered) return;\n answered = true;\n settle(flight);\n resolve(outcome);\n },\n };\n flights.add(flight);\n let promise: Promise<T>;\n try {\n promise = Promise.resolve(work(flight.abort.signal));\n } catch (error) {\n promise = Promise.reject(error);\n }\n promise.then(\n (value) => {\n if (answered) return;\n answered = true;\n settle(flight);\n resolve({ kind: \"done\", value });\n },\n (error: unknown) => {\n if (answered) return;\n // a failure after the shutdown aborted the request is a failure that the shutdown caused\n const outcome = flight.abort.signal.aborted\n ? cut(flight, \"the shutdown aborted it, and it failed\")\n : ({ kind: \"failed\", error } as const);\n answered = true;\n settle(flight);\n resolve(outcome);\n },\n );\n });\n },\n inFlight(controller) {\n let count = 0;\n for (const flight of flights) if (controller === undefined || flight.controller === controller) count += 1;\n return count;\n },\n stopIntake() {\n intake = false;\n },\n cooperativeAbort() {\n for (const flight of flights) flight.abort.abort(new Error(\"the cooperative abort of the drain\"));\n },\n drain(deadline) {\n if (flights.size === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n let finished = false;\n const done = (): void => {\n if (finished) return;\n finished = true;\n idle.delete(done);\n cancel();\n resolve();\n };\n idle.add(done);\n const cancel = clock.after(Math.max(0, deadline - clock.now()), () => {\n for (const flight of [...flights]) {\n flight.abort.abort(new Error(\"the drain reached its deadline\"));\n flight.answer(cut(flight, \"the drain reached its deadline\"));\n }\n done();\n });\n });\n },\n };\n};\n","import type { Extent } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Phase, ServiceDescription } from \"./description.ts\";\nimport type { MachineName } from \"./events.ts\";\nimport type { Health, Runtime, StartOptions } from \"./health.ts\";\nimport type { ExitSignal, ProcessPort } from \"./process.ts\";\nimport { EXIT_SIGNALS, nodeProcess } from \"./process.ts\";\nimport type { RequestFn, RequestOutcome } from \"./requests.ts\";\nimport { tracker } from \"./requests.ts\";\n\n/**\n * A transport is a source of work: a server, a consumer or a worker. Each has one contract. It\n * starts, stops its intake, and drains to a deadline without a throw. It counts its work in flight,\n * and it closes. A second close has no effect.\n */\nexport interface TransportImpl {\n start(signal: AbortSignal): Promise<void> | void;\n stopIntake(): Promise<void> | void;\n /**\n * This method lets the work in flight end until `deadline` (milliseconds on the monotonic\n * clock). The signal is the cooperative abort, which comes before the deadline.\n */\n drain(deadline: number, signal: AbortSignal): Promise<void> | void;\n inFlight(): number;\n close(): Promise<void> | void;\n}\n\n/** A dependency handle connects to a dependency at the start, and closes the connection at the end. */\nexport interface DependencyHandle {\n connect(signal: AbortSignal): Promise<void> | void;\n close(): Promise<void> | void;\n}\n\n/**\n * A health server answers the probes, on its own port. It starts first, thus the probes answer\n * while the dependencies connect, and it closes last.\n */\nexport interface HealthServer {\n listen(health: Health): Promise<void> | void;\n close(): Promise<void> | void;\n}\n\n/** A phase context is what a phase gives its work: its deadline, its signal and the clock. */\nexport interface PhaseContext {\n /** This time is the end of the cap of the phase, in milliseconds on the monotonic clock. */\n readonly deadline: number;\n /** This signal aborts at the end of the cap of the phase. */\n readonly signal: AbortSignal;\n readonly clock: Clock;\n}\n\n/** A phase work is a step of the shutdown that an owner adds to a phase. */\nexport type PhaseWork = (context: PhaseContext) => Promise<void> | void;\n\n/**\n * These are the stages of the shutdown, in their sequence. A declared phase with one of these\n * names gets the work of the runtime for its stage. A phase with another name gets only the work of\n * its owners.\n * - `fail readiness`: readiness fails at the start of the shutdown. This phase then holds for its\n * cap, while the endpoints of the platform remove the service.\n * - `stop intake`: each transport stops its intake, and new requests get a retryable 503.\n * - `drain`: the transports and the requests in flight end, until the cap. The cooperative abort\n * comes before the end of the cap, by the cooperative window.\n * - `close transports`: each transport closes.\n * - `stop the poller`: each check and watchdog stops. Then the signal of the service aborts.\n * - `tear down dependencies`: each dependency closes, in the reverse sequence of its connection.\n * - `close the health server`: the probes stop last.\n */\nexport const STAGES = [\n \"fail readiness\",\n \"stop intake\",\n \"drain\",\n \"close transports\",\n \"stop the poller\",\n \"tear down dependencies\",\n \"close the health server\",\n] as const;\n\nexport type Stage = (typeof STAGES)[number];\n\n/** This function gives the place of a stage in the sequence, or -1 for a phase of another name. */\nexport const stageRank = (name: string): number => (STAGES as readonly string[]).indexOf(name);\n\n/** The signal of the service aborts after the poller stops, and before the dependencies close. */\nconst ABORT_AFTER = stageRank(\"stop the poller\");\n\n/**\n * This function gives the time of the cooperative abort, in milliseconds after the drain starts. It\n * is the cap less the cooperative window, and not less than half the cap. A window without a bound\n * gives half the cap.\n */\nexport const cooperativeAbortAt = (cap: number, window: Extent): number => {\n const before = window.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(window, \"ms\");\n return Math.max(cap - before, cap / 2);\n};\n\n/** These options run the lifecycle. Without them, it uses the real clock and the process of Node. */\nexport interface RunOptions extends StartOptions {\n readonly process?: ProcessPort | undefined;\n}\n\n/** A lifecycle state tells where the service is in its life. */\nexport type LifecycleState = \"starting\" | \"running\" | \"draining\" | \"exited\";\n\n/** The lifecycle of a running service. */\nexport interface Lifecycle {\n readonly health: Health;\n readonly state: LifecycleState;\n /** This code is the exit code, or null before the exit. */\n readonly exitCode: number | null;\n /** These texts are the errors that make the exit code 1. */\n readonly errors: readonly string[];\n /** This promise ends when the startup ends: the service runs, or its startup failed. */\n readonly started: Promise<void>;\n /** This promise gives the exit code when the shutdown ends. */\n readonly done: Promise<number>;\n /** This signal aborts after the poller stops: the background work of the service stops with it. */\n readonly signal: AbortSignal;\n /** This method asks for the shutdown, as an exit signal does. A second request has no effect. */\n shutdown(cause?: string): Promise<number>;\n /**\n * This method does one request of a controller, on a route that the controller declares. The\n * service refuses it while it cannot take new work, and the shutdown can cut it.\n */\n request<T>(controller: string, route: string, work: RequestFn<T>): Promise<RequestOutcome<T>>;\n inFlight(controller?: string): number;\n /**\n * This method tells if a response can keep its connection open. It is false while the service is\n * not ready, thus each response then closes its connection.\n */\n keepAlive(): boolean;\n}\n\n/** The parts that the builder gives the lifecycle, beside the declaration. */\nexport interface Components {\n readonly transports: ReadonlyMap<string, TransportImpl>;\n readonly dependencies: ReadonlyMap<string, DependencyHandle>;\n readonly healthServer: HealthServer | null;\n readonly phaseWork: ReadonlyMap<string, readonly PhaseWork[]>;\n /** This function stops the parts that the poller stage stops beside the checks: the watchdogs. */\n readonly stopPollers: () => void;\n}\n\nconst message = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\n/**\n * This function gives the problems of a declaration that the lifecycle cannot start: a stage with\n * work and no declared phase. The runtime has no defaults, thus a transport without a drain phase\n * has no time to drain.\n */\nexport const lifecycleProblems = (description: ServiceDescription, components: Components): string[] => {\n const declared = new Set(description.shutdown.phases.map((phase) => phase.name));\n const needs = (stage: Stage, why: string): string[] =>\n declared.has(stage) ? [] : [`${description.name} has ${why}, thus it declares the phase \"${stage}\"`];\n const problems: string[] = [];\n for (const transport of description.transports)\n if (!components.transports.has(transport.name))\n problems.push(`transport ${transport.name} has no implementation: the runtime has no defaults`);\n if (components.transports.size > 0 || description.controllers.length > 0) {\n problems.push(...needs(\"stop intake\", \"transports or controllers\"));\n problems.push(...needs(\"drain\", \"transports or controllers\"));\n }\n if (components.transports.size > 0) problems.push(...needs(\"close transports\", \"transports\"));\n if (description.checks.length > 0) problems.push(...needs(\"stop the poller\", \"checks\"));\n if (components.dependencies.size > 0) problems.push(...needs(\"tear down dependencies\", \"dependency handles\"));\n if (components.healthServer !== null) problems.push(...needs(\"close the health server\", \"a health server\"));\n return problems;\n};\n\n/**\n * This function starts the lifecycle of a service. The startup has a sequence: the health server\n * first, then each dependency, then each transport, and the startup gate last. A signal during the\n * startup gets its shutdown after the startup ends. The shutdown does the declared phases in their\n * sequence, each one capped. Only the lifecycle ends the process: 0 after a clean shutdown, and 1\n * after an error.\n */\nexport const lifecycle = (\n description: ServiceDescription,\n runtime: Runtime,\n components: Components,\n options: RunOptions = {},\n): Lifecycle => {\n const clock = options.clock ?? realClock;\n const port = options.process ?? nodeProcess;\n const record = (machine: MachineName, from: string, to: string, on: string, detail?: string): void =>\n options.record?.({\n at: clock.now(),\n machine,\n instance: description.name,\n from,\n to,\n on,\n ...(detail === undefined ? {} : { detail }),\n });\n const service = new AbortController();\n const errors: string[] = [];\n let state: LifecycleState = \"starting\";\n let exitCode: number | null = null;\n let intakeStopped = false;\n let shutdownStarted = false;\n let failedStartup = false;\n // the exit manager: running, stopping, exited\n let exitState = \"running\";\n const moveExit = (to: string, on: string, detail?: string): void => {\n record(\"exit\", exitState, to, on, detail);\n exitState = to;\n };\n const fail = (cause: string): void => {\n errors.push(cause);\n record(\"exit\", exitState, exitState, \"error\", cause);\n };\n\n const requests = tracker(description.controllers, clock, (controller) => {\n if (state === \"starting\") return \"the service has not started\";\n if (state === \"exited\") return \"the service has exited\";\n // while it drains and before its intake stops, the service still serves: the platform can\n // still route a request to it until its endpoints update\n if (state === \"draining\") return null;\n return runtime.verdict().controllers[controller] === true ? null : `controller ${controller} is not ready`;\n });\n\n let finish: (code: number) => void = () => {};\n const done = new Promise<number>((resolve) => {\n finish = resolve;\n });\n // what started, thus what the shutdown stops\n let healthListening = false;\n const connected: Array<[string, DependencyHandle]> = [];\n const startedTransports: Array<[string, TransportImpl]> = [];\n\n const offs = EXIT_SIGNALS.map((signal: ExitSignal) =>\n port.on(signal, () => {\n void shutdown(signal);\n }),\n );\n\n const startup = (async (): Promise<void> => {\n runtime.beginStartup();\n const started = (what: string): void => record(\"gate\", \"starting\", \"starting\", \"component.started\", what);\n try {\n if (components.healthServer !== null) {\n await components.healthServer.listen(runtime);\n healthListening = true;\n started(\"the health server\");\n }\n for (const dependency of description.dependencies) {\n const handle = components.dependencies.get(dependency.name);\n if (handle === undefined) continue;\n await handle.connect(service.signal);\n connected.push([dependency.name, handle]);\n started(`dependency ${dependency.name}`);\n }\n for (const transport of description.transports) {\n const impl = components.transports.get(transport.name)!;\n await impl.start(service.signal);\n startedTransports.push([transport.name, impl]);\n started(`${transport.kind} ${transport.name}`);\n }\n await runtime.warmUp();\n if (state === \"starting\") state = \"running\";\n } catch (error) {\n const cause = `the startup failed: ${message(error)}`;\n failedStartup = true;\n runtime.failStartup(cause);\n fail(cause);\n if (exitState === \"running\") moveExit(\"stopping\", \"startup.fail\", cause);\n else record(\"exit\", exitState, exitState, \"startup.fail\", cause);\n }\n })();\n // a failed startup shuts down what started. An exit request during the startup waits for it\n void startup.then(() => {\n if (failedStartup && !shutdownStarted) void shutdown(\"the startup failed\");\n });\n\n /** This function does the work of one phase, until its cap. It gives true when the work ended in time. */\n const runPhase = async (phase: Phase, hold: boolean, work: ReadonlyArray<(context: PhaseContext) => Promise<void> | void>): Promise<boolean> => {\n const cap = valueIn(phase.cap, \"ms\");\n const deadline = clock.now() + cap;\n const capped = new AbortController();\n const context: PhaseContext = { deadline, signal: capped.signal, clock };\n let pending = work.length;\n const all = Promise.all(\n work.map(async (each) => {\n try {\n await each(context);\n } catch (error) {\n fail(`the phase ${phase.name} failed: ${message(error)}`);\n } finally {\n pending -= 1;\n }\n }),\n );\n let cancelCap: Cancel = () => {};\n const end = new Promise<\"cap\">((resolve) => {\n cancelCap = clock.after(cap, () => resolve(\"cap\"));\n });\n const first = await Promise.race([all.then(() => \"work\" as const), end]);\n if (first === \"work\" && hold) await end;\n cancelCap();\n capped.abort(new Error(`the phase ${phase.name} reached its cap`));\n return pending === 0;\n };\n\n async function shutdown(cause = \"the service asked for it\"): Promise<number> {\n if (shutdownStarted || exitState === \"exited\") {\n if (exitState === \"stopping\") record(\"exit\", exitState, exitState, \"exit.repeat\", cause);\n return done;\n }\n shutdownStarted = true;\n if (exitState === \"running\") moveExit(\"stopping\", \"exit.request\", cause);\n // a signal during the startup gets its shutdown after the startup ends\n await startup;\n state = \"draining\";\n runtime.drain();\n let drainState = \"running\";\n const moveDrain = (to: string, on: string, detail?: string): void => {\n record(\"drain\", drainState, to, on, detail);\n drainState = to;\n };\n const abortService = (): void => {\n if (service.signal.aborted) return;\n service.abort(new Error(\"the service stopped\"));\n record(\"drain\", drainState, drainState, \"signal.abort\");\n };\n const phases = description.shutdown.phases;\n let on = \"exit.request\";\n for (const phase of phases) {\n const rank = stageRank(phase.name);\n moveDrain(phase.name, on);\n if (rank > ABORT_AFTER) abortService();\n const work: Array<(context: PhaseContext) => Promise<void> | void> = [];\n if (phase.name === \"stop intake\") {\n work.push(() => {\n intakeStopped = true;\n requests.stopIntake();\n });\n for (const [, impl] of startedTransports) work.push(() => impl.stopIntake());\n }\n // the cooperative abort of the drain: the cap less the window, and half the cap at the least.\n // It ends with the phase, thus a drain that ends before it does not abort anything\n let cancelAbort: Cancel = () => {};\n if (phase.name === \"drain\") {\n const cap = valueIn(phase.cap, \"ms\");\n const cooperative = new AbortController();\n const fireAt = cooperativeAbortAt(cap, description.shutdown.cooperativeWindow);\n cancelAbort = clock.after(fireAt, () => {\n cooperative.abort(new Error(\"the cooperative abort of the drain\"));\n requests.cooperativeAbort();\n record(\"drain\", drainState, drainState, \"drain.cooperative-abort\");\n });\n work.push(({ deadline }) => requests.drain(deadline));\n for (const [, impl] of startedTransports)\n work.push(({ deadline }) => impl.drain(deadline, cooperative.signal));\n }\n if (phase.name === \"close transports\")\n for (const [, impl] of startedTransports) work.push(() => impl.close());\n if (phase.name === \"stop the poller\")\n work.push(() => {\n runtime.stop();\n components.stopPollers();\n });\n if (phase.name === \"tear down dependencies\")\n work.push(async () => {\n for (const [, handle] of [...connected].reverse()) await handle.close();\n });\n if (phase.name === \"close the health server\" && healthListening && components.healthServer !== null) {\n const server = components.healthServer;\n work.push(() => server.close());\n }\n for (const owned of components.phaseWork.get(phase.name) ?? []) work.push(owned);\n const inTime = await runPhase(phase, phase.name === \"fail readiness\", work);\n cancelAbort();\n if (!inTime) fail(`the phase ${phase.name} did not end in its cap of ${valueIn(phase.cap, \"s\")} s`);\n on = inTime ? \"phase.done\" : \"phase.cap\";\n }\n moveDrain(\"done\", on);\n abortService();\n // a stage that the declaration does not name still stops: the checks, at the end\n runtime.stop();\n components.stopPollers();\n if (!intakeStopped) requests.stopIntake();\n for (const off of offs) off();\n state = \"exited\";\n exitCode = errors.length > 0 ? 1 : 0;\n moveExit(\"exited\", \"shutdown.done\", `exit ${exitCode}`);\n finish(exitCode);\n port.exit(exitCode);\n return exitCode;\n }\n\n return {\n health: runtime,\n get state() {\n return state;\n },\n get exitCode() {\n return exitCode;\n },\n get errors() {\n return [...errors];\n },\n started: startup,\n done,\n signal: service.signal,\n shutdown,\n request: (controller, route, work) => requests.request(controller, route, work),\n inFlight: (controller) => requests.inFlight(controller),\n keepAlive: () => state === \"running\" && runtime.verdict().ready,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Gate } from \"./description.ts\";\nimport type { Outcome, Trace } from \"./latch.ts\";\nimport type { Poll } from \"./timing.ts\";\n\n/**\n * A watchdog spec declares a watchdog of the event loop. The service declares it as a check whose\n * function the watchdog gives: the process's own wedge, which a liveness window reads.\n */\nexport interface WatchdogSpec {\n readonly name: string;\n /** This time is the interval of the tick of the watchdog. */\n readonly tick: Quantity;\n /** This time is the most that a tick can be late before the loop counts as blocked. */\n readonly threshold: Quantity;\n /** This gate is the policy that the check controls, or null for a check that only reports. */\n readonly gates: Gate | null;\n readonly poll: Poll;\n}\n\n/**\n * A watchdog measures how late its own tick comes. A blocked event loop delays each timer, thus a\n * tick that comes later than the threshold shows the block. Its states are `watching`, `blocked`\n * and `stopped`.\n */\nexport interface Watchdog {\n /** This function is the check: it fails when a tick since the last poll came late, or the loop is still blocked. */\n readonly check: () => Outcome;\n /** This time is the most that a tick was late, in milliseconds, since the start. */\n readonly worst: number;\n readonly blocked: boolean;\n stop(): void;\n}\n\n/** This function starts a watchdog on a clock. */\nexport const watchdog = (clock: Clock, spec: WatchdogSpec, trace?: Trace): Watchdog => {\n const tick = valueIn(spec.tick, \"ms\");\n const threshold = valueIn(spec.threshold, \"ms\");\n if (!(tick > 0)) throw new RangeError(`watchdog ${spec.name} ticks every ${tick} ms, not above zero`);\n let blocked = false;\n let stopped = false;\n let worst = 0;\n // the worst lateness since the last poll of the check\n let sincePoll = 0;\n let next: Cancel | null = null;\n\n const named = (): string => (stopped ? \"stopped\" : blocked ? \"blocked\" : \"watching\");\n const arm = (): void => {\n const due = clock.now() + tick;\n next = clock.after(tick, () => {\n next = null;\n if (stopped) return;\n const late = Math.max(0, clock.now() - due);\n worst = Math.max(worst, late);\n sincePoll = Math.max(sincePoll, late);\n const from = named();\n blocked = late > threshold;\n trace?.(from, named(), \"tick\");\n arm();\n });\n };\n arm();\n\n return {\n check: () => {\n const failed = blocked || sincePoll > threshold;\n sincePoll = 0;\n return failed ? \"error\" : \"ok\";\n },\n get worst() {\n return worst;\n },\n get blocked() {\n return blocked;\n },\n stop() {\n if (stopped) return;\n const from = named();\n stopped = true;\n next?.();\n next = null;\n trace?.(from, \"stopped\", \"stop\");\n },\n };\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { convertible, valueIn } from \"@mark1russell7/quantity\";\nimport type {\n Check,\n Controller,\n Coupling,\n Dependency,\n Gate,\n Phase,\n ServiceDescription,\n Transport,\n} from \"./description.ts\";\nimport { FORMAT } from \"./description.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Health, Runtime, StartOptions } from \"./health.ts\";\nimport { healthRuntime } from \"./health.ts\";\nimport type {\n Components,\n DependencyHandle,\n HealthServer,\n Lifecycle,\n PhaseWork,\n RunOptions,\n TransportImpl,\n} from \"./lifecycle.ts\";\nimport { STAGES, lifecycle, lifecycleProblems, stageRank } from \"./lifecycle.ts\";\nimport type { Operation } from \"./operation.ts\";\nimport { operationProblems } from \"./operation.ts\";\nimport type { CheckFn } from \"./poller.ts\";\nimport type { Timing } from \"./timing.ts\";\nimport type { Watchdog, WatchdogSpec } from \"./watchdog.ts\";\nimport { watchdog } from \"./watchdog.ts\";\n\n/**\n * A service is composed with plain method uses before it starts. The builder is imperative, and\n * it holds static knowledge. The service does not use an `init` function that gives registrations.\n *\n * const svc = service(\"app\");\n * svc.dependency(\"weather\", {\n * system: null,\n * knobs: { request_timeout },\n * operations: { \"GET /v1/forecast\": { idempotent: true } },\n * });\n * svc.controller(\"forecast\").requires(\"weather\").route(\"GET /api/forecast\", { idempotent: true });\n * svc.check({ name: \"weather.round-trip\", dependency: \"weather\", … }, roundTrip);\n * svc.describe(); // the whole graph as data, with nothing started\n * const health = await svc.start();\n *\n * The graph has one source, thus its parts cannot disagree. A dependency or a check can join\n * after the start. It starts tripped, and the export is partial. The runtime has no defaults of\n * its own. Thus `describe()` refuses a service that does not declare a policy, its startup budget\n * or its shutdown, and it tells which.\n *\n * `start()` starts only the health runtime. `run()` starts the whole lifecycle: the startup in its\n * sequence, the exit signals, the capped phases of the shutdown, and the exit of the process.\n */\nexport interface ControllerBuilder {\n /** This method declares the dependencies that its routes need. The controller is ready only while they are ready. */\n requires(...dependencies: string[]): ControllerBuilder;\n /** This method declares a route that the controller serves, and if its callers can send it again. */\n route(route: string, operation: Operation): ControllerBuilder;\n}\n\n/** A service builder composes a service, gives it as data, and starts it. */\nexport interface ServiceBuilder {\n readonly name: string;\n /** This method declares a dependency, and the handle that connects to it and closes it, if it has one. */\n dependency(name: string, spec: Omit<Dependency, \"name\">, handle?: DependencyHandle): ServiceBuilder;\n /** This method declares a server, and its implementation. `run()` needs the implementation. */\n server(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n consumer(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n worker(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n /** This method gives the server of the probes. It starts first and closes last. */\n healthServer(server: HealthServer): ServiceBuilder;\n /** This method adds work to a declared phase of the shutdown: the owner of the work registers it. */\n onPhase(phase: string, work: PhaseWork): ServiceBuilder;\n /** This method declares a watchdog of the event loop, as a check on the state of the process. */\n watchdog(spec: WatchdogSpec): ServiceBuilder;\n controller(name: string): ControllerBuilder;\n /** This method declares a check, and the function that measures its step. `start()` needs the function. */\n check(check: Check, run?: CheckFn): ServiceBuilder;\n readiness(timing: Timing): ServiceBuilder;\n liveness(timing: Timing): ServiceBuilder;\n startup(budget: Quantity): ServiceBuilder;\n couple(coupling: Coupling): ServiceBuilder;\n phase(name: string, cap: Quantity): ServiceBuilder;\n cooperativeWindow(window: Extent): ServiceBuilder;\n /** This method gives the service as data, and it starts nothing. */\n describe(): ServiceDescription;\n /**\n * This method closes the composition, starts the health runtime, and gives it after one warm-up\n * sweep of each check. A part that joins after it starts tripped, and it makes the export\n * partial. A second use gives the same runtime.\n */\n start(options?: StartOptions): Promise<Health>;\n /**\n * This method starts the whole lifecycle and gives it when its startup ends: the service operates,\n * or its startup failed and it shuts down. It refuses a declaration that it cannot start: a transport\n * without its implementation, or a stage with work and no declared phase.\n */\n run(options?: RunOptions): Promise<Lifecycle>;\n /**\n * This value is the health runtime from the time that `start()` begins, or null before. Thus the\n * probes can answer during the warm-up sweep: the startup probe fails until the sweep ends.\n */\n readonly health: Health | null;\n}\n\nconst isTime = (amount: Extent): boolean => convertible(amount.unit, \"s\");\nconst above = (amount: Extent): boolean =>\n amount.value === \"infinite\" || (isTime(amount) && valueIn(amount as Quantity, \"s\") > 0);\nconst whole = (count: number): boolean => Number.isInteger(count) && count >= 1;\n\n/** This function gives the problems of a timing: a zero interval or count fails decoding. */\nconst timingProblems = (policy: Gate, timing: Timing): string[] => {\n if (timing.type === \"debounce\") {\n const problems: string[] = [];\n if (!above(timing.interval)) problems.push(`the ${policy} debounce polls at an interval that is not above zero`);\n if (!whole(timing.failAfter)) problems.push(`the ${policy} debounce fails after ${timing.failAfter} polls, not a whole number above zero`);\n if (!whole(timing.recoverAfter)) problems.push(`the ${policy} debounce recovers after ${timing.recoverAfter} polls, not a whole number above zero`);\n return problems;\n }\n const negative = (amount: Quantity | undefined): boolean =>\n amount !== undefined && (!isTime(amount) || valueIn(amount, \"s\") < 0);\n return negative(timing.failAfter) || negative(timing.recoverAfter)\n ? [`the ${policy} window is not a time of zero or more`]\n : [];\n};\n\n/** This function starts the composition of a service. */\nexport const service = (name: string): ServiceBuilder => {\n if (name.trim().length === 0) throw new Error(\"a service needs a name\");\n const transports: Transport[] = [];\n const dependencies: Dependency[] = [];\n const controllers: Array<{\n name: string;\n requires: string[];\n routes: Record<string, Operation>;\n }> = [];\n const checks: Check[] = [];\n const runs = new Map<string, CheckFn>();\n const couplings: Coupling[] = [];\n const phases: Phase[] = [];\n let readiness: Timing | undefined;\n let liveness: Timing | undefined;\n let startup: Quantity | undefined;\n let cooperative: Extent | undefined;\n let started = false;\n let partial = false;\n let runtime: Promise<Health> | undefined;\n let live: Runtime | undefined;\n let running: Promise<Lifecycle> | undefined;\n const transportImpls = new Map<string, TransportImpl>();\n const handles = new Map<string, DependencyHandle>();\n const phaseWork = new Map<string, PhaseWork[]>();\n const watchdogs: WatchdogSpec[] = [];\n let probes: HealthServer | null = null;\n\n /** This function stops a change to the shape of the service after the start. */\n const composing = (what: string): void => {\n if (started) throw new Error(`${name} has started: ${what} is declared before svc.start()`);\n };\n const once = (list: ReadonlyArray<{ name: string }>, entry: string, what: string): void => {\n if (list.some((held) => held.name === entry))\n throw new Error(`${name} already has a ${what} named ${entry}`);\n };\n const transport =\n (kind: Transport[\"kind\"]) =>\n (entry: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl) => {\n composing(`a ${kind}`);\n once(transports, entry, \"transport\");\n transports.push({ name: entry, kind, knobs: { ...knobs } });\n if (impl !== undefined) transportImpls.set(entry, impl);\n return builder;\n };\n\n /** This function makes the runtime of the declaration: its checks, and a watchdog for each spec. */\n const makeRuntime = (\n options: StartOptions,\n ): { runtime: Runtime; description: ServiceDescription; stopPollers: () => void } => {\n const description = builder.describe();\n const clock = options.clock ?? realClock;\n const dogs: Watchdog[] = [];\n const all = new Map(runs);\n for (const spec of watchdogs) {\n const dog = watchdog(clock, spec, (from, to, on) =>\n options.record?.({ at: clock.now(), machine: \"watchdog\", instance: spec.name, from, to, on }),\n );\n dogs.push(dog);\n all.set(spec.name, () => dog.check());\n }\n const stopPollers = (): void => {\n for (const dog of dogs) dog.stop();\n };\n let made: Runtime;\n try {\n made = healthRuntime(description, all, options);\n } catch (error) {\n stopPollers();\n throw error;\n }\n // the stop of the health runtime also stops the watchdogs\n const runtime: Runtime = {\n ...made,\n stop: () => {\n made.stop();\n stopPollers();\n },\n };\n return { runtime, description, stopPollers };\n };\n\n const builder: ServiceBuilder = {\n name,\n get health() {\n return live ?? null;\n },\n dependency(entry, spec, handle) {\n once(dependencies, entry, \"dependency\");\n // a dependency that joins after the start has no connection that the lifecycle opens\n if (handle !== undefined && !started) handles.set(entry, handle);\n dependencies.push({\n name: entry,\n ...spec,\n knobs: { ...spec.knobs },\n operations: { ...spec.operations },\n });\n if (started) {\n partial = true;\n live?.addDependency(entry);\n }\n return builder;\n },\n server: transport(\"server\"),\n consumer: transport(\"consumer\"),\n worker: transport(\"worker\"),\n controller(entry) {\n composing(\"a controller\");\n once(controllers, entry, \"controller\");\n const held = {\n name: entry,\n requires: [] as string[],\n routes: {} as Record<string, Operation>,\n };\n controllers.push(held);\n const handle: ControllerBuilder = {\n requires(...needed) {\n composing(`what ${entry} requires`);\n held.requires.push(...needed);\n return handle;\n },\n route(served, operation) {\n composing(`${entry}'s routes`);\n if (held.routes[served] !== undefined)\n throw new Error(`${entry} already serves ${served}`);\n held.routes[served] = { ...operation };\n return handle;\n },\n };\n return handle;\n },\n check(check, run) {\n once(checks, check.name, \"check\");\n if (started && run === undefined)\n throw new Error(`check ${check.name} joins after the start, thus it needs its function`);\n checks.push({ ...check });\n if (run !== undefined) runs.set(check.name, run);\n if (started) {\n partial = true;\n live?.addCheck({ ...check, poll: { ...check.poll } }, run!);\n }\n return builder;\n },\n healthServer(server) {\n composing(\"the health server\");\n if (probes !== null) throw new Error(`${name} already has a health server`);\n probes = server;\n return builder;\n },\n onPhase(phase, work) {\n composing(`work for the phase ${phase}`);\n phaseWork.set(phase, [...(phaseWork.get(phase) ?? []), work]);\n return builder;\n },\n watchdog(spec) {\n composing(`the watchdog ${spec.name}`);\n once(checks, spec.name, \"check\");\n // the watchdog is a check on the state of the process itself, on its own connection\n checks.push({\n name: spec.name,\n dependency: null,\n step: \"local-state\",\n ownConnection: true,\n gates: spec.gates,\n poll: { ...spec.poll },\n });\n watchdogs.push(spec);\n return builder;\n },\n readiness(timing) {\n composing(\"readiness\");\n readiness = timing;\n return builder;\n },\n liveness(timing) {\n composing(\"liveness\");\n liveness = timing;\n return builder;\n },\n startup(budget) {\n composing(\"the startup budget\");\n startup = budget;\n return builder;\n },\n couple(coupling) {\n composing(\"a coupling\");\n couplings.push({ ...coupling });\n return builder;\n },\n phase(entry, cap) {\n composing(\"a shutdown phase\");\n once(phases, entry, \"shutdown phase\");\n phases.push({ name: entry, cap });\n return builder;\n },\n cooperativeWindow(window) {\n composing(\"the cooperative window\");\n cooperative = window;\n return builder;\n },\n describe() {\n const missing: string[] = [];\n if (readiness === undefined) missing.push(\"readiness (svc.readiness)\");\n if (liveness === undefined) missing.push(\"liveness (svc.liveness)\");\n if (startup === undefined) missing.push(\"a startup budget (svc.startup)\");\n if (phases.length === 0) missing.push(\"its shutdown phases (svc.phase)\");\n if (cooperative === undefined)\n missing.push(\"a cooperative window, or no bound (svc.cooperativeWindow)\");\n if (missing.length > 0)\n throw new Error(\n `${name} does not declare ${missing.join(\"; \")}: the runtime has no defaults`,\n );\n const known = new Set(dependencies.map((entry) => entry.name));\n const problems: string[] = [];\n const policies: Record<Gate, Timing> = { readiness: readiness!, liveness: liveness! };\n for (const gate of [\"readiness\", \"liveness\"] as const)\n problems.push(...timingProblems(gate, policies[gate]));\n for (const check of checks) {\n if (check.dependency !== null && !known.has(check.dependency)) {\n problems.push(\n `check ${check.name} exercises ${check.dependency}, which is not a dependency`,\n );\n }\n if (!isTime(check.poll.interval) || !isTime(check.poll.timeout)) {\n problems.push(`check ${check.name} polls in a unit that is not a time`);\n } else if (!above(check.poll.interval) || !above(check.poll.timeout)) {\n problems.push(`check ${check.name} polls with an interval or a timeout that is not above zero`);\n }\n if (!(check.poll.jitter >= 0 && check.poll.jitter < 1))\n problems.push(`check ${check.name} has a jitter of ${check.poll.jitter}, not a fraction below 1`);\n // a debounce counts the polls of each check that it gates, thus they poll at its interval\n const timing = check.gates === null ? null : policies[check.gates];\n if (\n timing?.type === \"debounce\" &&\n isTime(check.poll.interval) &&\n above(timing.interval) &&\n valueIn(check.poll.interval, \"s\") !== valueIn(timing.interval, \"s\")\n )\n problems.push(\n `check ${check.name} polls every ${valueIn(check.poll.interval, \"s\")} s, but the ${check.gates} debounce counts polls every ${valueIn(timing.interval, \"s\")} s`,\n );\n }\n for (const controller of controllers) {\n for (const needed of controller.requires) {\n if (!known.has(needed))\n problems.push(\n `controller ${controller.name} requires ${needed}, which is not a dependency`,\n );\n }\n for (const [route, operation] of Object.entries(controller.routes))\n problems.push(...operationProblems(`controller ${controller.name}`, route, operation));\n }\n for (const dependency of dependencies)\n for (const [name, operation] of Object.entries(dependency.operations))\n problems.push(...operationProblems(`dependency ${dependency.name}`, name, operation));\n for (const phase of phases)\n if (!isTime(phase.cap)) problems.push(`phase ${phase.name}'s cap is not a time`);\n // the stages of the shutdown keep their sequence: readiness fails first, the health server closes last\n const ranked = phases.map((phase) => phase.name).filter((each) => stageRank(each) >= 0);\n for (let at = 1; at < ranked.length; at += 1)\n if (stageRank(ranked[at]!) < stageRank(ranked[at - 1]!))\n problems.push(\n `the phase \"${ranked[at]!}\" comes after \"${ranked[at - 1]!}\", but the stages go ${STAGES.join(\", \")}`,\n );\n if (problems.length > 0) throw new Error(`${name}: ${problems.join(\"; \")}`);\n const controllersOut: Controller[] = controllers.map((entry) => ({\n name: entry.name,\n requires: [...entry.requires],\n routes: { ...entry.routes },\n }));\n return {\n format: FORMAT,\n name,\n partial,\n transports: transports.map((entry) => ({ ...entry, knobs: { ...entry.knobs } })),\n dependencies: dependencies.map((entry) => ({\n ...entry,\n knobs: { ...entry.knobs },\n operations: { ...entry.operations },\n })),\n controllers: controllersOut,\n checks: checks.map((entry) => ({ ...entry, poll: { ...entry.poll } })),\n policies: { readiness: readiness!, liveness: liveness!, startup: startup! },\n couplings: couplings.map((entry) => ({ ...entry })),\n shutdown: {\n phases: phases.map((entry) => ({ ...entry })),\n cooperativeWindow: cooperative!,\n },\n };\n },\n start(options = {}) {\n // the start needs the whole graph: describe() is the check, and the runtime reads it\n if (runtime !== undefined) return runtime;\n if (running !== undefined) return Promise.reject(new Error(`${name} runs its lifecycle: use its health`));\n let made: Runtime;\n try {\n made = makeRuntime(options).runtime;\n } catch (error) {\n return Promise.reject(error);\n }\n started = true;\n live = made;\n runtime = made.warmUp().then(() => made);\n return runtime;\n },\n run(options = {}) {\n if (running !== undefined) return running;\n if (runtime !== undefined) return Promise.reject(new Error(`${name} started its health only: run() starts the whole lifecycle`));\n let life: Lifecycle;\n try {\n const description = builder.describe();\n const components: Components = {\n transports: transportImpls,\n dependencies: handles,\n healthServer: probes,\n phaseWork,\n stopPollers: () => {},\n };\n const problems = lifecycleProblems(description, components);\n if (problems.length > 0) throw new Error(`${name}: ${problems.join(\"; \")}`);\n const made = makeRuntime(options);\n started = true;\n live = made.runtime;\n life = lifecycle(made.description, made.runtime, { ...components, stopPollers: made.stopPollers }, options);\n } catch (error) {\n return Promise.reject(error);\n }\n running = life.started.then(() => life);\n return running;\n },\n };\n return builder;\n};\n","/**\n * This module is the evaluator of the timer-ordering law. Its decisions are the same as the\n * decisions of the research evaluator. The rules are data, and the caller gives them. Each rule\n * is one or more linear inequalities over symbols at a hop (`readiness_detection_window@pod`),\n * with a margin. A binding maps `symbol@hop` to seconds, or to \"inf\" for a timer that is absent.\n */\n\n/** A term is a coefficient times one symbol, at a hop when it has one. */\nexport interface Term {\n readonly knob: string;\n readonly coef: number;\n readonly at?: string | undefined;\n}\n\n/** A side is a sum of terms, plus a constant. */\nexport interface Side {\n readonly terms: readonly Term[];\n readonly const?: number | undefined;\n}\n\n/** An inequality compares two sides, with a margin. */\nexport interface Inequality {\n readonly lhs: Side;\n readonly op: \"<\" | \"<=\" | \">\" | \">=\";\n readonly rhs: Side;\n /** The margin is in seconds, or it is words for a margin that no binding gives (\"a factor of several\"). Words count as 0. */\n readonly margin?: number | string | undefined;\n}\n\n/** A rule is one or more inequalities, with their statement in words. */\nexport interface Rule {\n readonly id: string;\n /** This text is the rule in words. */\n readonly rule: string;\n /** This text is the window that a violation opens. */\n readonly window?: string | undefined;\n readonly expr: Inequality;\n readonly expr_more?: readonly Inequality[] | undefined;\n}\n\n/** A bound is in seconds, or it is \"inf\" for an absent timer, or \"off\" for a disabled feature. The last two have no limit. */\nexport type Bound = number | \"inf\" | \"off\";\n\n/**\n * A binding maps `symbol@hop` to its bound. Its \"margin\" replaces the margin of each inequality.\n * Its \"margins\" replace them one by one, in sequence, as the binding of a chain can.\n */\nexport type Binding = Readonly<Record<string, Bound | readonly number[] | undefined>> & {\n readonly margin?: number | undefined;\n readonly margins?: readonly number[] | undefined;\n};\n\n/** A verdict is \"holds\", \"violated\", \"unbounded\" (it holds only because a timer is absent) or \"depends\" (nothing is bound). */\nexport type Verdict = \"holds\" | \"violated\" | \"unbounded\" | \"depends\";\n\n/** An evaluation is the value of each side of one inequality, its margin, and if it holds. */\nexport interface Evaluated {\n readonly lhs: number | null;\n readonly rhs: number | null;\n readonly margin: number;\n readonly holds: boolean | null;\n}\n\nconst value = (bound: Bound): number =>\n bound === \"inf\" || bound === \"off\" ? Infinity : Number(bound);\nconst keyOf = (term: Term): string =>\n term.at !== undefined ? `${term.knob}@${term.at}` : term.knob;\n/** This function gives the bound of a symbol, or undefined when the symbol has no bound. */\nconst boundOf = (bind: Binding, key: string): Bound | undefined => {\n const bound = bind[key];\n return typeof bound === \"number\" || bound === \"inf\" || bound === \"off\" ? bound : undefined;\n};\n\n/** This function gives the inequalities of a rule: its expr, then the others. */\nexport const inequalities = (rule: Rule): readonly Inequality[] => [\n rule.expr,\n ...(rule.expr_more ?? []),\n];\n\n/** This function gives the symbols that a rule reads, as `symbol@hop`. */\nexport const symbolsOf = (rule: Rule): string[] => {\n const out: string[] = [];\n for (const inequality of inequalities(rule)) {\n for (const term of [...inequality.lhs.terms, ...inequality.rhs.terms]) {\n const key = keyOf(term);\n if (!out.includes(key)) out.push(key);\n }\n }\n return out;\n};\n\n/** This function gives the value of a side, Σ coef × symbol + const, or null when a symbol has no bound. */\nconst sideOf = (side: Side, bind: Binding): number | null => {\n let total = side.const ?? 0;\n for (const term of side.terms) {\n const bound = boundOf(bind, keyOf(term));\n if (bound === undefined) return null;\n total += term.coef * value(bound);\n }\n return total;\n};\n\n/** This function gives the margin of an inequality: from the binding (one for all, or one each), else from the rule, else 0. */\nconst marginOf = (inequality: Inequality, index: number, bind: Binding): number => {\n if (Array.isArray(bind.margins)) return Number(bind.margins[index] ?? 0);\n if (typeof bind.margin === \"number\") return bind.margin;\n return typeof inequality.margin === \"number\" ? inequality.margin : 0;\n};\n\n/**\n * This function evaluates each inequality on one binding. \"<\" holds when lhs + margin < rhs, and\n * \">\" holds when lhs > rhs + margin. Only the inequalities with all their symbols bound apply.\n * The others are null.\n */\nexport const evaluate = (rule: Rule, bind: Binding): Evaluated[] =>\n inequalities(rule).map((inequality, index) => {\n const lhs = sideOf(inequality.lhs, bind);\n const rhs = sideOf(inequality.rhs, bind);\n const margin = marginOf(inequality, index, bind);\n if (lhs === null || rhs === null) return { lhs, rhs, margin, holds: null };\n const holds =\n inequality.op === \"<\"\n ? lhs + margin < rhs\n : inequality.op === \"<=\"\n ? lhs + margin <= rhs\n : inequality.op === \">\"\n ? lhs > rhs + margin\n : lhs >= rhs + margin;\n return { lhs, rhs, margin, holds };\n });\n\n/**\n * This function gives the verdict of an evaluation. When an applicable inequality fails, the\n * verdict is \"violated\". When all hold only because a timer is absent, the verdict is \"unbounded\",\n * because nothing then limits the window. Otherwise the verdict is \"holds\". When no inequality\n * applies, the arithmetic cannot decide, and the verdict is \"depends\".\n */\nexport const verdictOf = (rule: Rule, bind: Binding, evaluated: readonly Evaluated[]): Verdict => {\n const applicable = evaluated.flatMap((entry, index) => (entry.holds === null ? [] : [index]));\n if (applicable.length === 0) return \"depends\";\n if (applicable.some((index) => evaluated[index]?.holds === false)) return \"violated\";\n const all = inequalities(rule);\n const infinite = applicable.some((index) => {\n const inequality = all[index]!;\n return [...inequality.lhs.terms, ...inequality.rhs.terms].some((term) => {\n const bound = boundOf(bind, keyOf(term));\n return bound !== undefined && value(bound) === Infinity;\n });\n });\n return infinite ? \"unbounded\" : \"holds\";\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { convertExact, rationals as r } from \"@mark1russell7/quantity\";\nimport type { Dependency, ServiceDescription } from \"./description.ts\";\nimport type { Binding, Bound, Evaluated, Rule, Verdict } from \"./law.ts\";\nimport { evaluate, symbolsOf, verdictOf } from \"./law.ts\";\nimport { failSpan } from \"./timing.ts\";\n\n/**\n * This module applies the ordering law to a declaration. The declaration binds the symbols of the\n * service, from its primitives: the polls of its checks, the windows of its policies, its drain.\n * The scenario binds the symbols of the platform (preStop, grace, the probes of the kubelet,\n * propagation) and of each dependency (its failover time, the idle close of its server). Thus the\n * service gives the inequalities, and the scenario gives the values. A rule about a client\n * applies one time for each dependency.\n */\n\n/** A scenario gives the values of the platform and of each dependency, by the `symbol@hop` of the law. */\nexport interface Scenario {\n /** These are the values of the platform: prestop_sleep@pod, termination_grace@pod, probe_period@pod, propagation_bound@cluster. */\n readonly platform: Readonly<Record<string, Extent>>;\n /**\n * These are the probe windows of the kubelet, period × failure threshold. They add to the\n * windows of the declaration. A probe reads the cached verdict, thus detection is the latch,\n * then the probe.\n */\n readonly probes: { readonly readiness: Quantity; readonly liveness: Quantity };\n /** These are the values of each dependency, by its name: failover_time@dependency, server_idle_close@server. */\n readonly dependencies?: Readonly<Record<string, Readonly<Record<string, Extent>>>> | undefined;\n}\n\n/** A finding is the verdict of one rule for one scope: the service, or the client of a dependency. */\nexport interface Finding {\n readonly rule: string;\n readonly scope: string;\n readonly verdict: Verdict;\n readonly statement: string;\n readonly window?: string | undefined;\n readonly evaluated: readonly Evaluated[];\n /** These are the symbols that have no bound. A \"depends\" verdict waits for them. */\n readonly unbound: readonly string[];\n}\n\n/** These are the identifiers of the rules that each declaration gets. */\nexport const SERVICE_RULES: readonly string[] = [\n \"O09\",\n \"O10\",\n \"O11\",\n \"O12\",\n \"O13\",\n \"O14\",\n \"O15\",\n \"O16\",\n \"O17\",\n \"O18\",\n \"O19\",\n \"O21\",\n \"O22\",\n \"O23\",\n \"O27\",\n];\n\n/** This function gives a finite amount in seconds. */\nconst secondsOf = (amount: Quantity): number =>\n r.toNumber(convertExact(r.fromNumber(amount.value), amount.unit, \"s\"));\n/** This function gives an amount, or no bound, as the law binds it. */\nconst boundOf = (amount: Extent): Bound =>\n amount.value === \"infinite\" ? \"inf\" : secondsOf(amount);\n/** This function gives the largest of some numbers of seconds, or undefined when there are none. */\nconst most = (values: readonly number[]): number | undefined =>\n values.length === 0 ? undefined : Math.max(...values);\n\n/** This function gives what the declaration tells about the service, as symbols of the law, and the source of each. */\nexport const serviceBindings = (\n svc: ServiceDescription,\n scenario: Scenario,\n): { readonly bind: Record<string, Bound>; readonly from: Record<string, string> } => {\n const bind: Record<string, Bound> = {};\n const from: Record<string, string> = {};\n const set = (symbol: string, bound: Bound | undefined, said: string): void => {\n if (bound === undefined) return;\n bind[symbol] = bound;\n from[symbol] = said;\n };\n const gating = svc.checks.filter((check) => check.gates !== null);\n const readinessChecks = gating.filter((check) => check.gates === \"readiness\");\n const livenessChecks = gating.filter((check) => check.gates === \"liveness\");\n set(\n \"check_poll_interval@service\",\n most(gating.map((check) => secondsOf(check.poll.interval))),\n \"the slowest gating check's interval\",\n );\n set(\n \"check_timeout@service\",\n most(gating.map((check) => secondsOf(check.poll.timeout))),\n \"the longest gating check's timeout\",\n );\n set(\n \"slowest_poll@service\",\n most(\n gating.map(\n (check) =>\n secondsOf(check.poll.timeout) + secondsOf(check.poll.interval) * (1 + check.poll.jitter),\n ),\n ),\n \"a check's timeout plus one jittered interval, at its slowest\",\n );\n // the window of a policy is the time that its latch takes to fail when its slowest check times out\n const windowOf = (checks: typeof gating, policy: \"readiness\" | \"liveness\"): number | undefined =>\n most(\n checks.map((check) => secondsOf(failSpan(svc.policies[policy], check.poll, \"timeout\").max)),\n );\n const readiness = windowOf(readinessChecks, \"readiness\");\n const liveness = windowOf(livenessChecks, \"liveness\");\n set(\"readiness_detection_window@service\", readiness, \"the readiness latch, failing by timeout\");\n if (readiness !== undefined) {\n set(\n \"readiness_detection_window@pod\",\n readiness + secondsOf(scenario.probes.readiness),\n \"the readiness latch, then the kubelet's probe\",\n );\n }\n if (liveness !== undefined) {\n set(\n \"liveness_detection_window@pod\",\n liveness + secondsOf(scenario.probes.liveness),\n \"the liveness window, then the kubelet's probe\",\n );\n }\n set(\"startup_budget@pod\", secondsOf(svc.policies.startup), \"the declared startup budget\");\n set(\n \"process_drain_time@process\",\n svc.shutdown.phases.reduce((total, phase) => total + secondsOf(phase.cap), 0),\n `the shutdown phases' caps, summed (${svc.shutdown.phases.map((phase) => phase.name).join(\", \")})`,\n );\n set(\n \"cooperative_window@process\",\n boundOf(svc.shutdown.cooperativeWindow),\n \"the declared cooperative window\",\n );\n for (const [symbol, amount] of Object.entries(scenario.platform))\n set(symbol, boundOf(amount), \"the scenario\");\n return { bind, from };\n};\n\n/** This function gives the client knobs of a dependency at @client, and the values of the scenario at its own hops. */\nconst dependencyBindings = (dependency: Dependency, scenario: Scenario): Record<string, Bound> => {\n const bind: Record<string, Bound> = {};\n for (const [knob, amount] of Object.entries(dependency.knobs))\n bind[`${knob}@client`] = boundOf(amount);\n for (const [symbol, amount] of Object.entries(scenario.dependencies?.[dependency.name] ?? {})) {\n bind[symbol.includes(\"@\") ? symbol : `${symbol}@dependency`] = boundOf(amount);\n }\n return bind;\n};\n\nconst SCOPED = /@(client|dependency|server)$/;\n\n/** This function applies each service rule to a declaration and a scenario: one time for the service, and one time for each client. */\nexport const lint = (\n svc: ServiceDescription,\n scenario: Scenario,\n rules: readonly Rule[],\n): Finding[] => {\n const service = serviceBindings(svc, scenario).bind;\n const findings: Finding[] = [];\n for (const rule of rules) {\n if (!SERVICE_RULES.includes(rule.id)) continue;\n const symbols = symbolsOf(rule);\n const scopes: Array<[string, Binding]> = symbols.some((symbol) => SCOPED.test(symbol))\n ? svc.dependencies.map((dependency) => [\n dependency.name,\n { ...service, ...dependencyBindings(dependency, scenario) },\n ])\n : [[svc.name, service]];\n for (const [scope, bind] of scopes) {\n const evaluated = evaluate(rule, bind);\n findings.push({\n rule: rule.id,\n scope,\n verdict: verdictOf(rule, bind, evaluated),\n statement: rule.rule,\n ...(rule.window !== undefined ? { window: rule.window } : {}),\n evaluated,\n unbound: symbols.filter((symbol) => bind[symbol] === undefined),\n });\n }\n }\n return findings;\n};\n","import type { Machine, TransitionDef } from \"@mark1russell7/algebra\";\nimport { arr, fromStates } from \"@mark1russell7/algebra\";\nimport type { ServiceDescription } from \"./description.ts\";\nimport type { LifecycleEvent, MachineName } from \"./events.ts\";\n\n/**\n * The machines of the runtime as `mark1russell7.machine/1` documents. The documents are the\n * specification, and TypeScript implements them. A test records the transitions of the runtime\n * and replays them against each document, thus the code and the documents cannot disagree.\n */\n\nconst SOURCE = [{ ref: \"packages/service/src (the runtime implements this document)\", rung: \"implemented\" as const }];\nconst base = (id: string, title: string): Pick<Machine, \"format\" | \"id\" | \"title\" | \"layer\" | \"version\" | \"sources\"> => ({\n format: \"mark1russell7.machine/1\",\n id: `service.machine.${id}`,\n title,\n layer: \"self\",\n version: \"0.2.0\",\n sources: SOURCE,\n});\nconst t = (\n id: string,\n from: string | readonly string[],\n on: string | readonly string[],\n to: string,\n extra: Partial<TransitionDef> = {},\n): TransitionDef => ({ id, from, on, to, ...extra });\n\n/**\n * The startup gate opens one time. The lifecycle moves it from `closed` to `starting` while its\n * components start. Then one warm-up sweep of each check opens it. A component that does not start\n * fails it for good.\n */\nexport const GATE_MACHINE: Machine = {\n ...base(\"gate\", \"The startup gate\"),\n states: {\n closed: { initial: true },\n starting: { note: \"the health server, then the dependencies, then the transports\" },\n sweeping: { note: \"one bounded warm-up sweep of each check\" },\n open: { final: true },\n failed: { final: true },\n },\n events: {\n \"startup.begin\": { kind: \"local\" },\n \"component.started\": { kind: \"local\" },\n \"mark-ready\": { kind: \"local\" },\n \"sweep.done\": { kind: \"local\" },\n \"startup.fail\": { kind: \"local\" },\n \"startup.over-budget\": { kind: \"timer\" },\n },\n knobs: { startup: { key: \"policies.startup\", scope: \"service\", unit: \"s\", default: null } },\n timers: {\n startup_budget: {\n duration: { knob: \"startup\", unit: \"s\" },\n arms: [\"enter:closed\"],\n cancels: [\"sweep.done\", \"startup.fail\"],\n fires: \"startup.over-budget\",\n note: \"the gate says that it is over its budget, and the startup probe of the platform acts on it\",\n },\n },\n transitions: [\n t(\"begin\", \"closed\", \"startup.begin\", \"starting\"),\n t(\"component\", \"starting\", \"component.started\", \"starting\"),\n t(\"mark-ready\", [\"closed\", \"starting\"], \"mark-ready\", \"sweeping\"),\n t(\"open\", \"sweeping\", \"sweep.done\", \"open\"),\n t(\"fail\", [\"closed\", \"starting\", \"sweeping\"], \"startup.fail\", \"failed\"),\n // the budget timer leaves each state where it is\n t(\"over-budget-closed\", \"closed\", \"startup.over-budget\", \"closed\", { timer: \"startup_budget\" }),\n t(\"over-budget-starting\", \"starting\", \"startup.over-budget\", \"starting\", { timer: \"startup_budget\" }),\n t(\"over-budget-sweeping\", \"sweeping\", \"startup.over-budget\", \"sweeping\", { timer: \"startup_budget\" }),\n ],\n};\n\n/** A debounce latch counts the polls of its check in sequence. A readiness latch starts tripped. */\nexport const LATCH_MACHINE: Machine = {\n ...base(\"latch\", \"The latch of a policy, as a debounce\"),\n states: {\n tripped: { initial: true, note: \"a readiness latch starts here; a liveness latch starts released\" },\n released: {},\n },\n events: { \"poll.ok\": { kind: \"local\" }, \"poll.fail\": { kind: \"local\" } },\n knobs: {\n fail_after: { key: \"policies.<gate>.failAfter\", scope: \"check\", unit: \"count\", default: null },\n recover_after: { key: \"policies.<gate>.recoverAfter\", scope: \"check\", unit: \"count\", default: null },\n },\n timers: {},\n transitions: [\n t(\"pass\", \"released\", \"poll.ok\", \"released\"),\n t(\"fail-count\", \"released\", \"poll.fail\", \"released\", { guard: \"failures in sequence < fail_after\" }),\n t(\"trip\", \"released\", \"poll.fail\", \"tripped\", { guard: \"failures in sequence >= fail_after\" }),\n t(\"fail\", \"tripped\", \"poll.fail\", \"tripped\"),\n t(\"pass-count\", \"tripped\", \"poll.ok\", \"tripped\", { guard: \"passes in sequence < recover_after\" }),\n t(\"release\", \"tripped\", \"poll.ok\", \"released\", { guard: \"passes in sequence >= recover_after\" }),\n ],\n};\n\n/**\n * A window latch measures time on the monotonic clock from the first failure of a sequence, and\n * from the first pass after a trip.\n */\nexport const WINDOW_MACHINE: Machine = {\n ...base(\"window\", \"The latch of a policy, as a window\"),\n states: {\n released: { initial: true, note: \"a liveness latch starts here; a readiness latch starts tripped\" },\n failing: { note: \"the fail window runs\" },\n tripped: {},\n recovering: { note: \"the recover window runs\" },\n },\n events: {\n \"poll.ok\": { kind: \"local\" },\n \"poll.fail\": { kind: \"local\" },\n \"window.failed\": { kind: \"timer\" },\n \"window.recovered\": { kind: \"timer\" },\n },\n knobs: {\n fail_after: { key: \"policies.<gate>.failAfter\", scope: \"check\", unit: \"s\", default: null },\n recover_after: { key: \"policies.<gate>.recoverAfter\", scope: \"check\", unit: \"s\", default: null },\n },\n timers: {\n fail_window: {\n duration: { knob: \"fail_after\", unit: \"s\" },\n arms: [\"enter:failing\"],\n cancels: [\"poll.ok\"],\n fires: \"window.failed\",\n },\n recover_window: {\n duration: { knob: \"recover_after\", unit: \"s\" },\n arms: [\"enter:recovering\"],\n cancels: [\"poll.fail\"],\n fires: \"window.recovered\",\n },\n },\n transitions: [\n t(\"pass\", \"released\", \"poll.ok\", \"released\"),\n t(\"start-failing\", \"released\", \"poll.fail\", \"failing\", { guard: \"fail_after > 0\" }),\n t(\"trip-at-once\", \"released\", \"poll.fail\", \"tripped\", { guard: \"fail_after = 0\" }),\n t(\"still-failing\", \"failing\", \"poll.fail\", \"failing\"),\n t(\"stop-failing\", \"failing\", \"poll.ok\", \"released\"),\n t(\"trip\", \"failing\", \"window.failed\", \"tripped\", { timer: \"fail_window\" }),\n t(\"fail\", \"tripped\", \"poll.fail\", \"tripped\"),\n t(\"release-at-once\", \"tripped\", \"poll.ok\", \"released\", { guard: \"recover_after is absent or 0\" }),\n t(\"start-recovering\", \"tripped\", \"poll.ok\", \"recovering\", { guard: \"recover_after > 0\" }),\n t(\"still-recovering\", \"recovering\", \"poll.ok\", \"recovering\"),\n t(\"stop-recovering\", \"recovering\", \"poll.fail\", \"tripped\"),\n t(\"release\", \"recovering\", \"window.recovered\", \"released\", { timer: \"recover_window\" }),\n ],\n};\n\n/**\n * A poller starts one check on a jittered loop with a fixed delay. A call that outlives its timeout\n * stays pending (`hung`), and the poller does not start the check again until the call settles.\n */\nexport const POLLER_MACHINE: Machine = {\n ...base(\"poller\", \"The poller of a check\"),\n states: {\n idle: { initial: true, note: \"before the loop starts\" },\n waiting: { note: \"the loop timer runs, and no call is pending\" },\n calling: { note: \"a call runs, in its timeout\" },\n hung: { note: \"a call outlived its timeout, and it is still pending\" },\n stopped: { final: true },\n },\n events: {\n start: { kind: \"local\" },\n sweep: { kind: \"local\" },\n tick: { kind: \"timer\" },\n answer: { kind: \"local\" },\n timeout: { kind: \"timer\" },\n \"late-answer\": { kind: \"local\" },\n stop: { kind: \"local\" },\n },\n knobs: {\n interval: { key: \"checks.<name>.poll.interval\", scope: \"check\", unit: \"s\", default: null },\n timeout: { key: \"checks.<name>.poll.timeout\", scope: \"check\", unit: \"s\", default: null },\n },\n timers: {\n loop: {\n duration: { knob: \"interval\", unit: \"s\" },\n arms: [\"start\", \"tick\"],\n cancels: [\"stop\"],\n fires: \"tick\",\n note: \"the interval, with its jitter: a fixed delay from the start of each poll\",\n },\n poll_timeout: {\n duration: { knob: \"timeout\", unit: \"s\" },\n arms: [\"enter:calling\"],\n cancels: [\"answer\", \"stop\"],\n fires: \"timeout\",\n },\n },\n transitions: [\n t(\"sweep\", \"idle\", \"sweep\", \"calling\"),\n t(\"start\", \"idle\", \"start\", \"waiting\"),\n t(\"start-while-calling\", \"calling\", \"start\", \"calling\", { note: \"a late check polls at once, then starts its loop\" }),\n t(\"start-while-hung\", \"hung\", \"start\", \"hung\"),\n t(\"tick\", \"waiting\", \"tick\", \"calling\", { guard: \"the force-fail list does not name the check\" }),\n t(\"tick-forced\", \"waiting\", \"tick\", \"waiting\", { guard: \"the force-fail list names the check\" }),\n t(\"tick-pending\", \"calling\", \"tick\", \"calling\", { note: \"the poll fails at once: its call is pending\" }),\n t(\"tick-hung\", \"hung\", \"tick\", \"hung\", { note: \"the poll fails at once: its call is pending\" }),\n t(\"answer-loop\", \"calling\", \"answer\", \"waiting\", { guard: \"the loop runs\" }),\n t(\"answer-sweep\", \"calling\", \"answer\", \"idle\", { guard: \"the loop has not started\" }),\n t(\"timeout\", \"calling\", \"timeout\", \"hung\", { timer: \"poll_timeout\" }),\n t(\"late-answer-loop\", \"hung\", \"late-answer\", \"waiting\", { guard: \"the loop runs\" }),\n t(\"late-answer-sweep\", \"hung\", \"late-answer\", \"idle\", { guard: \"the loop has not started\" }),\n t(\"stop\", [\"idle\", \"waiting\", \"calling\", \"hung\"], \"stop\", \"stopped\"),\n t(\"settle-after-stop\", \"stopped\", [\"answer\", \"late-answer\", \"timeout\"], \"stopped\"),\n ],\n};\n\n/**\n * The exit manager is the only part that ends the process. The first exit request starts the\n * shutdown, and a later one has no effect. An error makes the exit code 1.\n */\nexport const EXIT_MACHINE: Machine = {\n ...base(\"exit\", \"The exit manager\"),\n states: { running: { initial: true }, stopping: {}, exited: { final: true } },\n events: {\n \"exit.request\": { kind: \"app\", note: \"SIGINT, SIGTERM, SIGQUIT or shutdown()\" },\n \"exit.repeat\": { kind: \"app\" },\n \"startup.fail\": { kind: \"local\" },\n error: { kind: \"local\" },\n \"shutdown.done\": { kind: \"local\" },\n },\n knobs: {},\n timers: {},\n transitions: [\n t(\"request\", \"running\", \"exit.request\", \"stopping\"),\n t(\"startup-fail\", \"running\", \"startup.fail\", \"stopping\"),\n t(\"error-running\", \"running\", \"error\", \"running\"),\n t(\"repeat\", \"stopping\", \"exit.repeat\", \"stopping\"),\n t(\"startup-fail-stopping\", \"stopping\", \"startup.fail\", \"stopping\"),\n t(\"error\", \"stopping\", \"error\", \"stopping\"),\n t(\"exit\", \"stopping\", \"shutdown.done\", \"exited\", { note: \"the exit code is 1 after an error, else 0\" }),\n ],\n};\n\n/** A watchdog measures how late its own tick comes, and a late tick shows a blocked event loop. */\nexport const WATCHDOG_MACHINE: Machine = {\n ...base(\"watchdog\", \"The watchdog of the event loop\"),\n states: { watching: { initial: true }, blocked: {}, stopped: { final: true } },\n events: { tick: { kind: \"timer\" }, stop: { kind: \"local\" } },\n knobs: {\n tick: { key: \"watchdog.tick\", scope: \"process\", unit: \"s\", default: null },\n threshold: { key: \"watchdog.threshold\", scope: \"process\", unit: \"s\", default: null },\n },\n timers: {\n tick: { duration: { knob: \"tick\", unit: \"s\" }, arms: [\"enter:watching\", \"tick\"], cancels: [\"stop\"], fires: \"tick\" },\n },\n transitions: [\n t(\"on-time\", \"watching\", \"tick\", \"watching\", { guard: \"lateness <= threshold\" }),\n t(\"block\", \"watching\", \"tick\", \"blocked\", { guard: \"lateness > threshold\" }),\n t(\"still-blocked\", \"blocked\", \"tick\", \"blocked\", { guard: \"lateness > threshold\" }),\n t(\"unblock\", \"blocked\", \"tick\", \"watching\", { guard: \"lateness <= threshold\" }),\n t(\"stop\", [\"watching\", \"blocked\"], \"stop\", \"stopped\"),\n ],\n};\n\n/** This function gives the name of a knob for the cap of a phase: `drain` gives `drain_cap`. */\nexport const capKnob = (phase: string): string => `${phase.replace(/[^A-Za-z0-9]+/g, \"_\")}_cap`;\n\n/**\n * This function gives the drain of a declaration as a machine. It has one state for each declared\n * phase, in its sequence, and each state has a timer at its cap. The drain also has the cooperative abort: the\n * cap of the drain less the cooperative window, and half that cap at the least.\n */\nexport const drainMachine = (description: ServiceDescription): Machine => {\n const phases = description.shutdown.phases;\n const states: Record<string, { initial?: boolean; final?: boolean; note?: string }> = {\n running: { initial: true },\n };\n for (const phase of phases) states[phase.name] = {};\n states[\"done\"] = { final: true };\n const knobs: Machine[\"knobs\"] = Object.fromEntries([\n ...phases.map((phase) => [\n capKnob(phase.name),\n { key: `shutdown.phases.${phase.name}.cap`, scope: \"service\", unit: \"s\", default: null },\n ]),\n [\"cooperative_window\", { key: \"shutdown.cooperativeWindow\", scope: \"service\", unit: \"s\", default: null }],\n ]);\n const timers: Record<string, Machine[\"timers\"][string]> = {};\n for (const phase of phases)\n timers[`${capKnob(phase.name)}_timer`] = {\n duration: { knob: capKnob(phase.name), unit: \"s\" },\n arms: [`enter:${phase.name}`],\n cancels: [\"phase.done\"],\n fires: \"phase.cap\",\n };\n if (phases.some((phase) => phase.name === \"drain\"))\n timers[\"cooperative_abort\"] = {\n duration: { expr: \"max(drain_cap - cooperative_window, drain_cap / 2)\", unit: \"s\" },\n arms: [\"enter:drain\"],\n cancels: [\"phase.done\", \"phase.cap\"],\n fires: \"drain.cooperative-abort\",\n };\n const transitions: TransitionDef[] = [];\n const order = [\"running\", ...phases.map((phase) => phase.name), \"done\"];\n for (let at = 0; at + 1 < order.length; at += 1) {\n const from = order[at]!;\n const to = order[at + 1]!;\n transitions.push(\n at === 0\n ? t(`begin`, from, \"exit.request\", to)\n : t(`after-${from}`, from, [\"phase.done\", \"phase.cap\"], to, {\n timer: `${capKnob(from)}_timer`,\n note: \"phase.cap: the work of the phase did not end in its cap, and the exit code is 1\",\n }),\n );\n }\n if (phases.some((phase) => phase.name === \"drain\"))\n transitions.push(t(\"cooperative-abort\", \"drain\", \"drain.cooperative-abort\", \"drain\", { timer: \"cooperative_abort\" }));\n // the signal of the service aborts when the sequence passes the poller, or at the end: it leaves\n // the drain where it is\n for (const state of [...phases.map((phase) => phase.name), \"done\"])\n transitions.push(t(`signal-abort-${state}`, state, \"signal.abort\", state));\n return {\n ...base(\"drain\", `The drain of ${description.name}`),\n states,\n events: {\n \"exit.request\": { kind: \"app\" },\n \"phase.done\": { kind: \"local\" },\n \"phase.cap\": { kind: \"timer\" },\n \"drain.cooperative-abort\": { kind: \"timer\" },\n \"signal.abort\": { kind: \"local\" },\n },\n knobs,\n timers,\n transitions,\n };\n};\n\n/** These are the static machines of the runtime, by the name that their events carry. */\nexport const MACHINES: Readonly<Record<Exclude<MachineName, \"drain\" | \"client\">, Machine>> = {\n gate: GATE_MACHINE,\n latch: LATCH_MACHINE,\n window: WINDOW_MACHINE,\n poller: POLLER_MACHINE,\n exit: EXIT_MACHINE,\n watchdog: WATCHDOG_MACHINE,\n};\n\n/**\n * This function replays the transitions of one machine of the runtime against its document. Each\n * instance starts in the state that its first transition leaves. Each transition must leave the\n * state where the replay is. The document must have a transition from that state, on that event,\n * to that state. The function gives each disagreement.\n */\nexport const replay = (doc: Machine, events: readonly LifecycleEvent[]): string[] => {\n const problems: string[] = [];\n const at = new Map<string, string>();\n for (const event of events) {\n const where = at.get(event.instance) ?? event.from;\n if (doc.states[where] === undefined) problems.push(`${event.instance}: the state ${where} is not in ${doc.id}`);\n if (event.from !== where)\n problems.push(`${event.instance}: the runtime left ${event.from} on ${event.on}, but the replay is in ${where}`);\n const allowed = doc.transitions.some(\n (each) => fromStates(doc, each.from).includes(event.from) && arr(each.on).includes(event.on) && each.to === event.to,\n );\n if (!allowed) problems.push(`${event.instance}: ${doc.id} has no transition from ${event.from} on ${event.on} to ${event.to}`);\n at.set(event.instance, event.to);\n }\n return problems;\n};\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,SAAS;;;ACwCtB,MAAM,MAAM,WACV,QAAQ,QAAQ,IAAI;;;;;;;AAQtB,MAAa,SACX,QACA,OACA,SACA,SACA,UACU;CACV,IAAI,QAAQ;CACZ,IAAI,QAAQ,MAAM,IAAI;CACtB,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,IAAI,SAAS;CAEb,IAAI,eAA8B;CAClC,IAAI,eAA8B;CAClC,IAAI,YAA2B;CAC/B,IAAI,YAA2B;;CAG/B,MAAM,cACJ,QACI,iBAAiB,OACf,YACA,eACF,iBAAiB,OACf,aACA;;CAER,MAAM,QAAQ,IAAY,WAA6B;EACrD,MAAM,OAAO,MAAM;EACnB,OAAO;EACP,QAAQ,MAAM,MAAM,GAAG,EAAE;CAC3B;CAEA,MAAM,OAAO,SAAwB;EACnC,IAAI,SAAS,OAAO;EACpB,QAAQ;EACR,QAAQ,MAAM,IAAI;EAClB,QAAQ;CACV;CACA,MAAM,iBAAuB;EAC3B,YAAY;EACZ,YAAY;EACZ,eAAe;CACjB;CACA,MAAM,iBAAuB;EAC3B,YAAY;EACZ,YAAY;EACZ,eAAe;CACjB;CAEA,MAAM,YAAY,WAAyB;EACzC,IAAI,OAAO,SAAS,YAAY;EAChC,IAAI,OAAO,YAAY,MAAM;GAC3B,UAAU;GACV,QAAQ;GACR,IAAI,SAAS,UAAU,OAAO,cAAc,IAAI,KAAK;EACvD,OAAO;GACL,SAAS;GACT,SAAS;GACT,IAAI,CAAC,SAAS,SAAS,OAAO,WAAW,IAAI,IAAI;EACnD;CACF;CAEA,MAAM,YAAY,WAAyB;EACzC,IAAI,OAAO,SAAS,UAAU;EAC9B,IAAI,OAAO,YAAY,MAAM;GAC3B,SAAS;GACT,IAAI,CAAC,OAAO;GACZ,IAAI,OAAO,iBAAiB,KAAA,GAAW;IACrC,IAAI,KAAK;IACT;GACF;GACA,IAAI,iBAAiB,MAAM;GAC3B,MAAM,OAAO,GAAG,OAAO,YAAY;GACnC,IAAI,QAAQ,GAAG;IACb,SAAS;IACT,IAAI,KAAK;IACT;GACF;GACA,eAAe,OAAO;GACtB,YAAY,MAAM,MAAM,YACtB,KAAK,0BAA0B;IAC7B,YAAY;IACZ,eAAe;IACf,IAAI,KAAK;GACX,CAAC,CACH;GACA;EACF;EACA,SAAS;EACT,IAAI,SAAS,iBAAiB,MAAM;EACpC,MAAM,OAAO,GAAG,OAAO,SAAS;EAChC,IAAI,QAAQ,GAAG;GACb,SAAS;GACT,IAAI,IAAI;GACR;EACF;EACA,eAAe,OAAO;EACtB,YAAY,MAAM,MAAM,YACtB,KAAK,uBAAuB;GAC1B,YAAY;GACZ,eAAe;GACf,IAAI,IAAI;EACV,CAAC,CACH;CACF;CAEA,OAAO;EACL,IAAI,UAAU;GACZ,OAAO;EACT;EACA,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,SAAS;GACX,OAAO;EACT;EACA,OAAO,QAAQ;GAEb,IAAI,OAAO,YAAY,iBAAiB;GACxC,MAAM,OAAO,OAAO,YAAY;GAChC,KAAK,OAAO,YAAY,mBAAmB;IACzC,SAAS,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAChE,SAAS,MAAM;IACf,SAAS,MAAM;GACjB,CAAC;EACH;EACA,OAAO;GACL,SAAS;GACT,SAAS;EACX;CACF;AACF;;;ACvJA,MAAMA,aAAW,UACf,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;;;;;AAWvD,MAAa,UAAU,YAAmC;CACxD,MAAM,EAAE,OAAO,MAAM,QAAQ;CAC7B,MAAM,WAAW,QAAQ,KAAK,UAAU,IAAI;CAC5C,MAAM,UAAU,QAAQ,KAAK,SAAS,IAAI;CAC1C,IAAI,OAAsB;CAC1B,IAAI,OAAsB;CAC1B,IAAI,UAAU;CACd,IAAI,UAAU;CAEd,IAAI,UAA4D;CAEhE,MAAM,yBAAS,IAAI,IAAgB;;CAGnC,MAAM,cACJ,UACI,YACA,YAAY,OACV,QAAQ,OACN,SACA,YACF,UACE,YACA;CACV,MAAM,QAAQ,IAAY,WAA6B;EACrD,MAAM,OAAO,MAAM;EACnB,OAAO;EACP,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE;CACnC;CAEA,MAAM,UACJ,SACA,SACA,SAA4E,CAAC,OACjE;EACZ;EACA,IAAI,MAAM,IAAI;EACd,MAAM,MAAM,IAAI,IAAI;EACpB,OAAO,OAAO,SAAS;EACvB,UAAU,OAAO,YAAY;EAC7B,QAAQ,OAAO,UAAU;EACzB,QAAQ,OAAO,UAAU;CAC3B;CAEA,MAAM,aAA8B;EAClC,MAAM,UAAU,MAAM,IAAI;EAC1B,IAAI,QAAQ,OAAO,GACjB,OAAO,QAAQ,QAAQ,OAAO,SAAS,SAAS;GAAE,OAAO;GAAgC,QAAQ;EAAK,CAAC,CAAC;EAC1G,IAAI,YAAY,MAAM;GACpB,MAAM,QAAQ,eAAe,KAAK,MAAM,UAAU,QAAQ,KAAK,EAAE;GACjE,OAAO,QAAQ,QAAQ,OAAO,SAAS,SAAS,EAAE,MAAM,CAAC,CAAC;EAC5D;EACA,MAAM,aAAa,IAAI,gBAAgB;EACvC,OAAO,IAAI,SAAiB,YAAY;GACtC,IAAI,OAAO;GACX,MAAM,UAAU,UAAwB;IACtC,IAAI,MAAM;IACV,OAAO;IACP,OAAO,OAAO,OAAO;IACrB,OAAO;IACP,QAAQ,KAAK;GACf;GACA,MAAM,gBAAsB;IAC1B,WAAW,MAAM;IACjB,OAAO,OAAO,SAAS,SAAS,EAAE,OAAO,sBAAsB,CAAC,CAAC;GACnE;GACA,OAAO,IAAI,OAAO;GAClB,MAAM,OAAO;IAAE,OAAO;IAAS,MAAM;GAAM;GAC3C,UAAU;GACV,MAAM,SAAS,MAAM,MAAM,eAAe;IACxC,WAAW,MAAM;IAEjB,KAAK,iBAAiB;KACpB,IAAI,YAAY,MAAM,KAAK,OAAO;IACpC,CAAC;IACD,OAAO,OAAO,SAAS,SAAS;KAAE,OAAO,gBAAgB,QAAQ;KAAM,UAAU;IAAK,CAAC,CAAC;GAC1F,CAAC;GACD,MAAM,gBAAsB;IAC1B,IAAI,YAAY,MAAM;IACtB,KAAK,KAAK,OAAO,gBAAgB,gBAAgB;KAC/C,UAAU;IACZ,CAAC;GACH;GACA,IAAI;GACJ,IAAI;IACF,SAAS,QAAQ,QAAQ,IAAI,WAAW,MAAM,CAAC;GACjD,SAAS,OAAO;IACd,SAAS,QAAQ,OAAO,KAAK;GAC/B;GACA,OAAO,MACJ,UAAU;IACT,QAAQ;IACR,MAAM,UAAU,SAAS;IACzB,OAAO,OAAO,SAAS,SAAS,YAAY,OAAO,CAAC,IAAI,EAAE,OAAO,kBAAkB,UAAU,CAAC,CAAC;GACjG,IACC,UAAmB;IAClB,QAAQ;IACR,OAAO,OAAO,SAAS,SAAS;KAAE,OAAOA,UAAQ,KAAK;KAAG,QAAQ;IAAK,CAAC,CAAC;GAC1E,CACF;EACF,CAAC;CACH;CAEA,MAAM,UAAU,UAA0B;EACxC,OAAO;EACP,IAAI,CAAC,SAAS,QAAQ,OAAO,KAAK;EAClC,OAAO;CACT;CAEA,MAAM,iBAAyB,YAAY,IAAI,KAAK,UAAU,IAAI,QAAQ,OAAO,IAAI;CAErF,MAAM,aAAmB;EACvB,OAAO;EACP,IAAI,SAAS;EACb,IAAI;EACJ,KAAK,cAAc;GACjB,OAAO,MAAM,MAAM,SAAS,GAAG,IAAI;GACnC,SAAS,KAAK;EAChB,CAAC;EACD,OAAa,KAAK,MAAM;CAC1B;CAEA,OAAO;EACL,IAAI,OAAO;GACT,OAAO;EACT;EACA,eAAe;GACb,IAAI;GACJ,KAAK,eAAe;IAClB,SAAS,KAAK;GAChB,CAAC;GACD,OAAO,OAAQ,KAAK,MAAM;EAC5B;EACA,MAAM,OAAO;GACX,IAAI,WAAW,SAAS,MAAM;GAC9B,KAAK,eAAe;IAClB,UAAU;IACV,OAAO,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI;GAC7C,CAAC;EACH;EACA;EACA,OAAO;GACL,IAAI,SAAS;GACb,KAAK,cAAc;IACjB,UAAU;IACV,OAAO;IACP,OAAO;GACT,CAAC;GACD,KAAK,MAAM,WAAW,CAAC,GAAG,MAAM,GAAG,QAAQ;EAC7C;CACF;AACF;;;;;;;AC3FA,MAAa,iBACX,aACA,MACA,UAAwB,CAAC,MACb;CACZ,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,SAAS,QAAQ,UAAU,KAAK;CACtC,MAAM,SAAS,IAAI,IAAI,QAAQ,aAAa,CAAC,CAAC;CAC9C,MAAM,YAAY,MAAM,IAAI;CAC5B,MAAM,SAAS,QAAQ,YAAY,SAAS,SAAS,IAAI;CACzD,MAAM,0BAAU,IAAI,IAAmB;CACvC,MAAM,eAAe,IAAI,IAAI,YAAY,aAAa,KAAK,eAAe,WAAW,IAAI,CAAC;CAE1F,MAAM,mCAAmB,IAAI,IAAY;CACzC,MAAM,4BAAY,IAAI,IAAsC;CAC5D,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,IAAI,OAAO;CAEX,MAAM,UACH,SAAsB,cACtB,MAAM,IAAI,OACT,QAAQ,SAAS;EAAE,IAAI,MAAM,IAAI;EAAG;EAAS;EAAU;EAAM;EAAI;CAAG,CAAC;CACzE,MAAM,YAAY,OAAO,QAAQ,YAAY,IAAI;CACjD,MAAM,YAAY,IAAY,IAAY,WAA0B;EAClE,MAAM,OAAO;EACb,OAAO;EACP,QAAQ,SAAS;GACf,IAAI,MAAM,IAAI;GACd,SAAS;GACT,UAAU,YAAY;GACtB;GACA;GACA;GACA,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EAC3C,CAAC;CACH;CAGA,IAAI,cAA6B,MAAM,MAAM,cAAc;EACzD,cAAc;EACd,IAAI,SAAS,UAAU,SAAS,UAAU,UAAU,MAAM,MAAM,qBAAqB;CACvF,CAAC;CAED,MAAM,gBAAsB;EAC1B,IAAI,UAAU,SAAS,GAAG;EAC1B,MAAM,MAAM,QAAQ;EACpB,KAAK,MAAM,YAAY,WAAW,SAAS,GAAG;CAChD;CAEA,MAAM,OAAO,OAAc,OAAgB,SAAwB;EACjE,MAAM,MAAM,QAAQ,YAAY,OAAO,KAAK,KAAK;EACjD,MAAM,SAAS,MAAM,UAAU,OAAO,OAAO,YAAY,SAAS,MAAM;EAGxE,MAAM,OACJ,WAAW,OACP,OACA,MACE,QACA,OACA,MAAM,UAAU,aAChB,SACA,OAAO,OAAO,SAAS,WAAW,WAAW,SAAS,MAAM,IAAI,CAClE;EACN,MAAM,QAAe;GACnB;GACA,OAAO;GACP,QAAQ,OAAO;IACb,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ;IACA;IACA;IACA,cACE,OAAO,IAAI,MAAM,IAAI,KAAM,MAAM,eAAe,QAAQ,OAAO,IAAI,MAAM,UAAU;IACrF,SAAS,WAAW;KAClB,MAAM,OAAO,MAAM;KAEnB,IAAI,OAAO,YAAY,QAAQ,MAAM,eAAe,MAC9C;UAAA,iBAAiB,OAAO,MAAM,UAAU,GAAG,QAAQ;KAAA;IAC3D;IACA,OAAO,OAAO,UAAU,MAAM,IAAI;GACpC,CAAC;EACH;EACA,QAAQ,IAAI,MAAM,MAAM,KAAK;EAC7B,IAAI,QAAQ,CAAC,SAAS;GAEpB,MAAW,OAAO,QAAQ;GAC1B,MAAM,OAAO,MAAM,MAAM,OAAO,SAAS,CAAC;EAC5C;CACF;CAEA,KAAK,MAAM,SAAS,YAAY,QAAQ;EACtC,MAAM,MAAM,KAAK,IAAI,MAAM,IAAI;EAC/B,IAAI,QAAQ,KAAA,GAAW,MAAM,IAAI,MAAM,SAAS,MAAM,KAAK,8CAA8C;EACzG,IAAI,OAAO,KAAK,KAAK;CACvB;CAEA,MAAM,aAAa,QAAc,aAAwC,SACvE,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,SAAS,UAC7B,MAAM,MAAM,UAAU,UAAU,MAAM,UAAU,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAC/F;CACF,MAAM,YAAY,YAAuC,QAAQ,OAAO,SAAS,CAAC,KAAK,OAAO;;CAE9F,MAAM,cAAc,IAAU,SAC5B,YAAY,UAAU,MAAM,aAAa,SAAS,OAAO,MAAM,CAAC,KAAK,SAAS,KAAK;CAErF,MAAM,gBAA+B;EACnC,MAAM,OAA8B;GAClC,WAAW,SAAS,UAAU,WAAW,CAAC,KAAK,iBAAiB,SAAS;GACzE,UAAU,SAAS,UAAU,UAAU,CAAC;EAC1C;EACA,MAAM,QAAQ,KAAK,YAAY,CAAC,WAAW,YAAY,IAAI;EAC3D,MAAM,OAAO,WAAW,CAAC,YAAY,CAAC,WAAW,aAAa,IAAI;EAClE,MAAM,QAAQ,QAAQ,KAAK;EAC3B,MAAM,mBAAmB,SACvB,CAAC,iBAAiB,IAAI,IAAI,KAC1B,SAAS,UAAU,cAAc,UAAU,MAAM,eAAe,IAAI,CAAC;EACvE,MAAM,MAAM,SAAS,UAAU,cAAc,UAAU,MAAM,eAAe,IAAI,CAAC;EACjF,MAAM,kBAA2C,CAAC;EAClD,KAAK,MAAM,QAAQ,cAAc,gBAAgB,QAAQ,gBAAgB,IAAI;EAC7E,MAAM,cAAuC,CAAC;EAC9C,KAAK,MAAM,cAAc,YAAY,aACnC,YAAY,WAAW,QAAQ,QAAQ,OAAO,WAAW,SAAS,MAAM,eAAe;EACzF,MAAM,SAAuC,CAAC;EAC9C,KAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,OAAO,QAAQ;GACb,OAAO,MAAM,MAAM;GACnB,YAAY,MAAM,MAAM;GACxB,SAAS,MAAM,OAAO,WAAW;GACjC,QAAQ,MAAM,OAAO,UAAU;GAC/B,OAAO,MAAM,OAAO,SAAS;GAC7B,MAAM,MAAM,OAAO;EACrB;EAEF,MAAM,UAAU,MAAM,IAAI,IAAI;EAC9B,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,cAAc;GACd;GACA,SAAS;IAAE;IAAQ;IAAS,YAAY,CAAC,WAAW,UAAU;GAAO;EACvE;CACF;CAEA,MAAM,UAAU,MAAiB,MAAe,gBAAsC;EACpF,QAAQ,QAAQ,SAAS,WAAW,MAAM;EAC1C,aAAa;EACb,MAAM;GACJ,QAAQ,OAAO,SAAS;GACxB,SAAS,YAAY;GACrB,OAAO;GACP,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,GAAI,SAAS,WAAW,EAAE,SAAS,QAAQ,EAAE,IAAI,CAAC;EACpD;CACF;CAEA,OAAO;EACL;EACA,MAAM,MAAM,YAAY;GACtB,MAAM,MAAM,QAAQ;GACpB,IAAI,SAAS,WAAW,OAAO,OAAO,MAAM,IAAI,OAAO;GACvD,IAAI,SAAS,YAAY,OAAO,OAAO,MAAM,IAAI,KAAK;GACtD,IAAI,SAAS,UAAU,OAAO,OAAO,MAAM,IAAI,SAAS,IAAI,KAAK;GACjE,IAAI,eAAe,KAAA,GAAW,OAAO,OAAO,MAAM,IAAI,KAAK;GAC3D,MAAM,QAAQ,IAAI,YAAY;GAC9B,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,GAAG,YAAY,KAAK,qBAAqB,YAAY;GAC9F,OAAO,OAAO,MAAM,OAAO,UAAU;EACvC;EACA,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;EACA,UAAU,MAAM,IAAI;GAClB,IAAI,IAAI,OAAO,IAAI,IAAI;QAClB,OAAO,OAAO,IAAI;EACzB;EACA,QAAQ;GACN,IAAI,UAAU;GACd,WAAW;GACX,QAAQ;EACV;EACA,OAAO;GACL,UAAU;GACV,cAAc;GACd,cAAc;GACd,KAAK,MAAM,SAAS,QAAQ,OAAO,GAAG;IACpC,MAAM,OAAO,KAAK;IAClB,MAAM,OAAO,KAAK;GACpB;EACF;EACA,eAAe;GACb,IAAI,SAAS,UAAU,SAAS,YAAY,eAAe;EAC7D;EACA,YAAY,OAAO;GACjB,IAAI,SAAS,UAAU,SAAS,UAAU;GAC1C,SAAS,UAAU,gBAAgB,KAAK;GACxC,cAAc;GACd,cAAc;EAChB;EACA,MAAM,SAAS;GACb,IAAI,WAAW,WAAW,SAAS,YAAY,SAAS,YAAY;GACpE,SAAS,YAAY,YAAY;GAEjC,MAAM,QAAQ,MAAM,IAAI;GACxB,MAAM,QAAQ,IAAI,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,OAAO,QAAQ,CAAC,CAAC;GAC9E,IAAI,WAAW,SAAS,YAAY;GACpC,KAAK,MAAM,SAAS,QAAQ,OAAO,GACjC,MAAM,OAAO,MAAM,MAAM,OAAO,SAAS,KAAK,MAAM,IAAI,IAAI,MAAM;GACpE,UAAU;GACV,cAAc;GACd,cAAc;GACd,SAAS,QAAQ,YAAY;GAC7B,QAAQ;EACV;EACA,SAAS,OAAO,KAAK;GACnB,IAAI,QAAQ,IAAI,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,YAAY,KAAK,6BAA6B,MAAM,MAAM;GAC1G,IAAI,OAAO,KAAK,IAAI;GACpB,QAAQ;EACV;EACA,cAAc,MAAM;GAClB,IAAI,aAAa,IAAI,IAAI,GAAG;GAC5B,aAAa,IAAI,IAAI;GACrB,iBAAiB,IAAI,IAAI;GACzB,QAAQ;EACV;CACF;AACF;;;;ACtVA,MAAa,eAAsC;CAAC;CAAU;CAAW;AAAS;;AAsBlF,MAAa,cAA2B;CACtC,GAAG,QAAQ,SAAS;EAClB,MAAM,OAAQ,WAAyC;EACvD,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,6CAA6C;EACrF,KAAK,GAAG,QAAQ,OAAO;EACvB,aAAa;GACX,KAAK,IAAI,QAAQ,OAAO;EAC1B;CACF;CACA,KAAK,MAAM;EACT,MAAM,OAAQ,WAAyC;EACvD,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,6CAA6C;EACrF,KAAK,KAAK,IAAI;CAChB;AACF;;;;;;;ACeA,MAAa,WACX,aACA,OACA,YACY;CACZ,MAAM,SAAS,IAAI,IAAI,YAAY,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC;CACrF,MAAM,0BAAU,IAAI,IAAc;CAClC,MAAM,uBAAO,IAAI,IAAgB;CACjC,IAAI,SAAS;CAEb,MAAM,UAAU,WAA2B;EACzC,QAAQ,OAAO,MAAM;EACrB,IAAI,QAAQ,SAAS,GAAG,KAAK,MAAM,QAAQ,CAAC,GAAG,IAAI,GAAG,KAAK;CAC7D;;CAEA,MAAM,OAAO,QAAkB,UAAyC;EACtE,MAAM,YAAY,OAAO,IAAI,OAAO,UAAU,CAAC,CAAE,OAAO,OAAO;EAC/D,MAAM,QAAQ,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,OAAO,IAAI,KAAM,GAAG;EAC5E,MAAM,YAAY,WAAW,WAAW,KAAK;EAC7C,OAAO;GAAE,MAAM;GAAO,QAAQ,YAAY,MAAM;GAAK;GAAW;EAAM;CACxE;CAEA,OAAO;EACL,QAAW,YAAoB,OAAe,MAAoB;GAChE,MAAM,OAAO,OAAO,IAAI,UAAU;GAClC,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,0BAA0B,YAAY;GAE9E,IAAI,KAAK,OAAO,WAAW,KAAA,GACzB,MAAM,IAAI,MAAM,cAAc,WAAW,8BAA8B,OAAO;GAChF,MAAM,UAAU,SAAS,QAAQ,UAAU,IAAI;GAC/C,IAAI,YAAY,MACd,OAAO,QAAQ,QAAQ;IAAE,MAAM;IAAW,QAAQ;IAAK,WAAW;IAAM,OAAO;GAAQ,CAAC;GAC1F,OAAO,IAAI,SAA4B,YAAY;IACjD,IAAI,WAAW;IACf,MAAM,SAAmB;KACvB;KACA;KACA,SAAS,MAAM,IAAI;KACnB,OAAO,IAAI,gBAAgB;KAC3B,SAAS,YAAY;MACnB,IAAI,UAAU;MACd,WAAW;MACX,OAAO,MAAM;MACb,QAAQ,OAAO;KACjB;IACF;IACA,QAAQ,IAAI,MAAM;IAClB,IAAI;IACJ,IAAI;KACF,UAAU,QAAQ,QAAQ,KAAK,OAAO,MAAM,MAAM,CAAC;IACrD,SAAS,OAAO;KACd,UAAU,QAAQ,OAAO,KAAK;IAChC;IACA,QAAQ,MACL,UAAU;KACT,IAAI,UAAU;KACd,WAAW;KACX,OAAO,MAAM;KACb,QAAQ;MAAE,MAAM;MAAQ;KAAM,CAAC;IACjC,IACC,UAAmB;KAClB,IAAI,UAAU;KAEd,MAAM,UAAU,OAAO,MAAM,OAAO,UAChC,IAAI,QAAQ,wCAAwC,IACnD;MAAE,MAAM;MAAU;KAAM;KAC7B,WAAW;KACX,OAAO,MAAM;KACb,QAAQ,OAAO;IACjB,CACF;GACF,CAAC;EACH;EACA,SAAS,YAAY;GACnB,IAAI,QAAQ;GACZ,KAAK,MAAM,UAAU,SAAS,IAAI,eAAe,KAAA,KAAa,OAAO,eAAe,YAAY,SAAS;GACzG,OAAO;EACT;EACA,aAAa;GACX,SAAS;EACX;EACA,mBAAmB;GACjB,KAAK,MAAM,UAAU,SAAS,OAAO,MAAM,sBAAM,IAAI,MAAM,oCAAoC,CAAC;EAClG;EACA,MAAM,UAAU;GACd,IAAI,QAAQ,SAAS,GAAG,OAAO,QAAQ,QAAQ;GAC/C,OAAO,IAAI,SAAe,YAAY;IACpC,IAAI,WAAW;IACf,MAAM,aAAmB;KACvB,IAAI,UAAU;KACd,WAAW;KACX,KAAK,OAAO,IAAI;KAChB,OAAO;KACP,QAAQ;IACV;IACA,KAAK,IAAI,IAAI;IACb,MAAM,SAAS,MAAM,MAAM,KAAK,IAAI,GAAG,WAAW,MAAM,IAAI,CAAC,SAAS;KACpE,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG;MACjC,OAAO,MAAM,sBAAM,IAAI,MAAM,gCAAgC,CAAC;MAC9D,OAAO,OAAO,IAAI,QAAQ,gCAAgC,CAAC;KAC7D;KACA,KAAK;IACP,CAAC;GACH,CAAC;EACH;CACF;AACF;;;;;;;;;;;;;;;;;AC3FA,MAAa,SAAS;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAKA,MAAa,aAAa,SAA0B,OAA6B,QAAQ,IAAI;;AAG7F,MAAM,cAAc,UAAU,iBAAiB;;;;;;AAO/C,MAAa,sBAAsB,KAAa,WAA2B;CACzE,MAAM,SAAS,OAAO,UAAU,aAAa,OAAO,oBAAoB,QAAQ,QAAQ,IAAI;CAC5F,OAAO,KAAK,IAAI,MAAM,QAAQ,MAAM,CAAC;AACvC;AAiDA,MAAM,WAAW,UACf,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;AAOvD,MAAa,qBAAqB,aAAiC,eAAqC;CACtG,MAAM,WAAW,IAAI,IAAI,YAAY,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;CAC/E,MAAM,SAAS,OAAc,QAC3B,SAAS,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,OAAO,IAAI,gCAAgC,MAAM,EAAE;CACrG,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,aAAa,YAAY,YAClC,IAAI,CAAC,WAAW,WAAW,IAAI,UAAU,IAAI,GAC3C,SAAS,KAAK,aAAa,UAAU,KAAK,oDAAoD;CAClG,IAAI,WAAW,WAAW,OAAO,KAAK,YAAY,YAAY,SAAS,GAAG;EACxE,SAAS,KAAK,GAAG,MAAM,eAAe,2BAA2B,CAAC;EAClE,SAAS,KAAK,GAAG,MAAM,SAAS,2BAA2B,CAAC;CAC9D;CACA,IAAI,WAAW,WAAW,OAAO,GAAG,SAAS,KAAK,GAAG,MAAM,oBAAoB,YAAY,CAAC;CAC5F,IAAI,YAAY,OAAO,SAAS,GAAG,SAAS,KAAK,GAAG,MAAM,mBAAmB,QAAQ,CAAC;CACtF,IAAI,WAAW,aAAa,OAAO,GAAG,SAAS,KAAK,GAAG,MAAM,0BAA0B,oBAAoB,CAAC;CAC5G,IAAI,WAAW,iBAAiB,MAAM,SAAS,KAAK,GAAG,MAAM,2BAA2B,iBAAiB,CAAC;CAC1G,OAAO;AACT;;;;;;;;AASA,MAAa,aACX,aACA,SACA,YACA,UAAsB,CAAC,MACT;CACd,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,OAAO,QAAQ,WAAW;CAChC,MAAM,UAAU,SAAsB,MAAc,IAAY,IAAY,WAC1E,QAAQ,SAAS;EACf,IAAI,MAAM,IAAI;EACd;EACA,UAAU,YAAY;EACtB;EACA;EACA;EACA,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;CAC3C,CAAC;CACH,MAAM,UAAU,IAAI,gBAAgB;CACpC,MAAM,SAAmB,CAAC;CAC1B,IAAI,QAAwB;CAC5B,IAAI,WAA0B;CAC9B,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CACtB,IAAI,gBAAgB;CAEpB,IAAI,YAAY;CAChB,MAAM,YAAY,IAAY,IAAY,WAA0B;EAClE,OAAO,QAAQ,WAAW,IAAI,IAAI,MAAM;EACxC,YAAY;CACd;CACA,MAAM,QAAQ,UAAwB;EACpC,OAAO,KAAK,KAAK;EACjB,OAAO,QAAQ,WAAW,WAAW,SAAS,KAAK;CACrD;CAEA,MAAM,WAAW,QAAQ,YAAY,aAAa,QAAQ,eAAe;EACvE,IAAI,UAAU,YAAY,OAAO;EACjC,IAAI,UAAU,UAAU,OAAO;EAG/B,IAAI,UAAU,YAAY,OAAO;EACjC,OAAO,QAAQ,QAAQ,CAAC,CAAC,YAAY,gBAAgB,OAAO,OAAO,cAAc,WAAW;CAC9F,CAAC;CAED,IAAI,eAAuC,CAAC;CAC5C,MAAM,OAAO,IAAI,SAAiB,YAAY;EAC5C,SAAS;CACX,CAAC;CAED,IAAI,kBAAkB;CACtB,MAAM,YAA+C,CAAC;CACtD,MAAM,oBAAoD,CAAC;CAE3D,MAAM,OAAO,aAAa,KAAK,WAC7B,KAAK,GAAG,cAAc;EACpB,SAAc,MAAM;CACtB,CAAC,CACH;CAEA,MAAM,WAAW,YAA2B;EAC1C,QAAQ,aAAa;EACrB,MAAM,WAAW,SAAuB,OAAO,QAAQ,YAAY,YAAY,qBAAqB,IAAI;EACxG,IAAI;GACF,IAAI,WAAW,iBAAiB,MAAM;IACpC,MAAM,WAAW,aAAa,OAAO,OAAO;IAC5C,kBAAkB;IAClB,QAAQ,mBAAmB;GAC7B;GACA,KAAK,MAAM,cAAc,YAAY,cAAc;IACjD,MAAM,SAAS,WAAW,aAAa,IAAI,WAAW,IAAI;IAC1D,IAAI,WAAW,KAAA,GAAW;IAC1B,MAAM,OAAO,QAAQ,QAAQ,MAAM;IACnC,UAAU,KAAK,CAAC,WAAW,MAAM,MAAM,CAAC;IACxC,QAAQ,cAAc,WAAW,MAAM;GACzC;GACA,KAAK,MAAM,aAAa,YAAY,YAAY;IAC9C,MAAM,OAAO,WAAW,WAAW,IAAI,UAAU,IAAI;IACrD,MAAM,KAAK,MAAM,QAAQ,MAAM;IAC/B,kBAAkB,KAAK,CAAC,UAAU,MAAM,IAAI,CAAC;IAC7C,QAAQ,GAAG,UAAU,KAAK,GAAG,UAAU,MAAM;GAC/C;GACA,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,YAAY,QAAQ;EACpC,SAAS,OAAO;GACd,MAAM,QAAQ,uBAAuB,QAAQ,KAAK;GAClD,gBAAgB;GAChB,QAAQ,YAAY,KAAK;GACzB,KAAK,KAAK;GACV,IAAI,cAAc,WAAW,SAAS,YAAY,gBAAgB,KAAK;QAClE,OAAO,QAAQ,WAAW,WAAW,gBAAgB,KAAK;EACjE;CACF,EAAA,CAAG;CAEH,QAAa,WAAW;EACtB,IAAI,iBAAiB,CAAC,iBAAiB,SAAc,oBAAoB;CAC3E,CAAC;;CAGD,MAAM,WAAW,OAAO,OAAc,MAAe,SAA2F;EAC9I,MAAM,MAAM,QAAQ,MAAM,KAAK,IAAI;EACnC,MAAM,WAAW,MAAM,IAAI,IAAI;EAC/B,MAAM,SAAS,IAAI,gBAAgB;EACnC,MAAM,UAAwB;GAAE;GAAU,QAAQ,OAAO;GAAQ;EAAM;EACvE,IAAI,UAAU,KAAK;EACnB,MAAM,MAAM,QAAQ,IAClB,KAAK,IAAI,OAAO,SAAS;GACvB,IAAI;IACF,MAAM,KAAK,OAAO;GACpB,SAAS,OAAO;IACd,KAAK,aAAa,MAAM,KAAK,WAAW,QAAQ,KAAK,GAAG;GAC1D,UAAU;IACR,WAAW;GACb;EACF,CAAC,CACH;EACA,IAAI,kBAA0B,CAAC;EAC/B,MAAM,MAAM,IAAI,SAAgB,YAAY;GAC1C,YAAY,MAAM,MAAM,WAAW,QAAQ,KAAK,CAAC;EACnD,CAAC;EAED,IAAI,MADgB,QAAQ,KAAK,CAAC,IAAI,WAAW,MAAe,GAAG,GAAG,CAAC,MACzD,UAAU,MAAM,MAAM;EACpC,UAAU;EACV,OAAO,sBAAM,IAAI,MAAM,aAAa,MAAM,KAAK,iBAAiB,CAAC;EACjE,OAAO,YAAY;CACrB;CAEA,eAAe,SAAS,QAAQ,4BAA6C;EAC3E,IAAI,mBAAmB,cAAc,UAAU;GAC7C,IAAI,cAAc,YAAY,OAAO,QAAQ,WAAW,WAAW,eAAe,KAAK;GACvF,OAAO;EACT;EACA,kBAAkB;EAClB,IAAI,cAAc,WAAW,SAAS,YAAY,gBAAgB,KAAK;EAEvE,MAAM;EACN,QAAQ;EACR,QAAQ,MAAM;EACd,IAAI,aAAa;EACjB,MAAM,aAAa,IAAY,IAAY,WAA0B;GACnE,OAAO,SAAS,YAAY,IAAI,IAAI,MAAM;GAC1C,aAAa;EACf;EACA,MAAM,qBAA2B;GAC/B,IAAI,QAAQ,OAAO,SAAS;GAC5B,QAAQ,sBAAM,IAAI,MAAM,qBAAqB,CAAC;GAC9C,OAAO,SAAS,YAAY,YAAY,cAAc;EACxD;EACA,MAAM,SAAS,YAAY,SAAS;EACpC,IAAI,KAAK;EACT,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,OAAO,UAAU,MAAM,IAAI;GACjC,UAAU,MAAM,MAAM,EAAE;GACxB,IAAI,OAAO,aAAa,aAAa;GACrC,MAAM,OAA+D,CAAC;GACtE,IAAI,MAAM,SAAS,eAAe;IAChC,KAAK,WAAW;KACd,gBAAgB;KAChB,SAAS,WAAW;IACtB,CAAC;IACD,KAAK,MAAM,GAAG,SAAS,mBAAmB,KAAK,WAAW,KAAK,WAAW,CAAC;GAC7E;GAGA,IAAI,oBAA4B,CAAC;GACjC,IAAI,MAAM,SAAS,SAAS;IAC1B,MAAM,MAAM,QAAQ,MAAM,KAAK,IAAI;IACnC,MAAM,cAAc,IAAI,gBAAgB;IACxC,MAAM,SAAS,mBAAmB,KAAK,YAAY,SAAS,iBAAiB;IAC7E,cAAc,MAAM,MAAM,cAAc;KACtC,YAAY,sBAAM,IAAI,MAAM,oCAAoC,CAAC;KACjE,SAAS,iBAAiB;KAC1B,OAAO,SAAS,YAAY,YAAY,yBAAyB;IACnE,CAAC;IACD,KAAK,MAAM,EAAE,eAAe,SAAS,MAAM,QAAQ,CAAC;IACpD,KAAK,MAAM,GAAG,SAAS,mBACrB,KAAK,MAAM,EAAE,eAAe,KAAK,MAAM,UAAU,YAAY,MAAM,CAAC;GACxE;GACA,IAAI,MAAM,SAAS,oBACjB,KAAK,MAAM,GAAG,SAAS,mBAAmB,KAAK,WAAW,KAAK,MAAM,CAAC;GACxE,IAAI,MAAM,SAAS,mBACjB,KAAK,WAAW;IACd,QAAQ,KAAK;IACb,WAAW,YAAY;GACzB,CAAC;GACH,IAAI,MAAM,SAAS,0BACjB,KAAK,KAAK,YAAY;IACpB,KAAK,MAAM,GAAG,WAAW,CAAC,GAAG,SAAS,CAAC,CAAC,QAAQ,GAAG,MAAM,OAAO,MAAM;GACxE,CAAC;GACH,IAAI,MAAM,SAAS,6BAA6B,mBAAmB,WAAW,iBAAiB,MAAM;IACnG,MAAM,SAAS,WAAW;IAC1B,KAAK,WAAW,OAAO,MAAM,CAAC;GAChC;GACA,KAAK,MAAM,SAAS,WAAW,UAAU,IAAI,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,KAAK;GAC/E,MAAM,SAAS,MAAM,SAAS,OAAO,MAAM,SAAS,kBAAkB,IAAI;GAC1E,YAAY;GACZ,IAAI,CAAC,QAAQ,KAAK,aAAa,MAAM,KAAK,6BAA6B,QAAQ,MAAM,KAAK,GAAG,EAAE,GAAG;GAClG,KAAK,SAAS,eAAe;EAC/B;EACA,UAAU,QAAQ,EAAE;EACpB,aAAa;EAEb,QAAQ,KAAK;EACb,WAAW,YAAY;EACvB,IAAI,CAAC,eAAe,SAAS,WAAW;EACxC,KAAK,MAAM,OAAO,MAAM,IAAI;EAC5B,QAAQ;EACR,WAAW,OAAO,SAAS,IAAI,IAAI;EACnC,SAAS,UAAU,iBAAiB,QAAQ,UAAU;EACtD,OAAO,QAAQ;EACf,KAAK,KAAK,QAAQ;EAClB,OAAO;CACT;CAEA,OAAO;EACL,QAAQ;EACR,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,WAAW;GACb,OAAO;EACT;EACA,IAAI,SAAS;GACX,OAAO,CAAC,GAAG,MAAM;EACnB;EACA,SAAS;EACT;EACA,QAAQ,QAAQ;EAChB;EACA,UAAU,YAAY,OAAO,SAAS,SAAS,QAAQ,YAAY,OAAO,IAAI;EAC9E,WAAW,eAAe,SAAS,SAAS,UAAU;EACtD,iBAAiB,UAAU,aAAa,QAAQ,QAAQ,CAAC,CAAC;CAC5D;AACF;;;;ACvXA,MAAa,YAAY,OAAc,MAAoB,UAA4B;CACrF,MAAM,OAAO,QAAQ,KAAK,MAAM,IAAI;CACpC,MAAM,YAAY,QAAQ,KAAK,WAAW,IAAI;CAC9C,IAAI,EAAE,OAAO,IAAI,MAAM,IAAI,WAAW,YAAY,KAAK,KAAK,eAAe,KAAK,oBAAoB;CACpG,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,QAAQ;CAEZ,IAAI,YAAY;CAChB,IAAI,OAAsB;CAE1B,MAAM,cAAuB,UAAU,YAAY,UAAU,YAAY;CACzE,MAAM,YAAkB;EACtB,MAAM,MAAM,MAAM,IAAI,IAAI;EAC1B,OAAO,MAAM,MAAM,YAAY;GAC7B,OAAO;GACP,IAAI,SAAS;GACb,MAAM,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,GAAG;GAC1C,QAAQ,KAAK,IAAI,OAAO,IAAI;GAC5B,YAAY,KAAK,IAAI,WAAW,IAAI;GACpC,MAAM,OAAO,MAAM;GACnB,UAAU,OAAO;GACjB,QAAQ,MAAM,MAAM,GAAG,MAAM;GAC7B,IAAI;EACN,CAAC;CACH;CACA,IAAI;CAEJ,OAAO;EACL,aAAa;GACX,MAAM,SAAS,WAAW,YAAY;GACtC,YAAY;GACZ,OAAO,SAAS,UAAU;EAC5B;EACA,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,UAAU;GACZ,OAAO;EACT;EACA,OAAO;GACL,IAAI,SAAS;GACb,MAAM,OAAO,MAAM;GACnB,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ,MAAM,WAAW,MAAM;EACjC;CACF;AACF;;;ACsBA,MAAM,UAAU,WAA4B,YAAY,OAAO,MAAM,GAAG;AACxE,MAAM,SAAS,WACb,OAAO,UAAU,cAAe,OAAO,MAAM,KAAK,QAAQ,QAAoB,GAAG,IAAI;AACvF,MAAM,SAAS,UAA2B,OAAO,UAAU,KAAK,KAAK,SAAS;;AAG9E,MAAM,kBAAkB,QAAc,WAA6B;CACjE,IAAI,OAAO,SAAS,YAAY;EAC9B,MAAM,WAAqB,CAAC;EAC5B,IAAI,CAAC,MAAM,OAAO,QAAQ,GAAG,SAAS,KAAK,OAAO,OAAO,sDAAsD;EAC/G,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,SAAS,KAAK,OAAO,OAAO,wBAAwB,OAAO,UAAU,sCAAsC;EACzI,IAAI,CAAC,MAAM,OAAO,YAAY,GAAG,SAAS,KAAK,OAAO,OAAO,2BAA2B,OAAO,aAAa,sCAAsC;EAClJ,OAAO;CACT;CACA,MAAM,YAAY,WAChB,WAAW,KAAA,MAAc,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG,IAAI;CACrE,OAAO,SAAS,OAAO,SAAS,KAAK,SAAS,OAAO,YAAY,IAC7D,CAAC,OAAO,OAAO,sCAAsC,IACrD,CAAC;AACP;;AAGA,MAAa,WAAW,SAAiC;CACvD,IAAI,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG,MAAM,IAAI,MAAM,wBAAwB;CACtE,MAAM,aAA0B,CAAC;CACjC,MAAM,eAA6B,CAAC;CACpC,MAAM,cAID,CAAC;CACN,MAAM,SAAkB,CAAC;CACzB,MAAM,uBAAO,IAAI,IAAqB;CACtC,MAAM,YAAwB,CAAC;CAC/B,MAAM,SAAkB,CAAC;CACzB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,iCAAiB,IAAI,IAA2B;CACtD,MAAM,0BAAU,IAAI,IAA8B;CAClD,MAAM,4BAAY,IAAI,IAAyB;CAC/C,MAAM,YAA4B,CAAC;CACnC,IAAI,SAA8B;;CAGlC,MAAM,aAAa,SAAuB;EACxC,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,KAAK,gBAAgB,KAAK,gCAAgC;CAC5F;CACA,MAAM,QAAQ,MAAuC,OAAe,SAAuB;EACzF,IAAI,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GACzC,MAAM,IAAI,MAAM,GAAG,KAAK,iBAAiB,KAAK,SAAS,OAAO;CAClE;CACA,MAAM,aACH,UACA,OAAe,OAAyC,SAAyB;EAChF,UAAU,KAAK,MAAM;EACrB,KAAK,YAAY,OAAO,WAAW;EACnC,WAAW,KAAK;GAAE,MAAM;GAAO;GAAM,OAAO,EAAE,GAAG,MAAM;EAAE,CAAC;EAC1D,IAAI,SAAS,KAAA,GAAW,eAAe,IAAI,OAAO,IAAI;EACtD,OAAO;CACT;;CAGF,MAAM,eACJ,YACmF;EACnF,MAAM,cAAc,QAAQ,SAAS;EACrC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,MAAM,OAAmB,CAAC;EAC1B,MAAM,MAAM,IAAI,IAAI,IAAI;EACxB,KAAK,MAAM,QAAQ,WAAW;GAC5B,MAAM,MAAM,SAAS,OAAO,OAAO,MAAM,IAAI,OAC3C,QAAQ,SAAS;IAAE,IAAI,MAAM,IAAI;IAAG,SAAS;IAAY,UAAU,KAAK;IAAM;IAAM;IAAI;GAAG,CAAC,CAC9F;GACA,KAAK,KAAK,GAAG;GACb,IAAI,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC;EACtC;EACA,MAAM,oBAA0B;GAC9B,KAAK,MAAM,OAAO,MAAM,IAAI,KAAK;EACnC;EACA,IAAI;EACJ,IAAI;GACF,OAAO,cAAc,aAAa,KAAK,OAAO;EAChD,SAAS,OAAO;GACd,YAAY;GACZ,MAAM;EACR;EASA,OAAO;GAAE,SAAA;IANP,GAAG;IACH,YAAY;KACV,KAAK,KAAK;KACV,YAAY;IACd;GAEa;GAAG;GAAa;EAAY;CAC7C;CAEA,MAAM,UAA0B;EAC9B;EACA,IAAI,SAAS;GACX,OAAO,QAAQ;EACjB;EACA,WAAW,OAAO,MAAM,QAAQ;GAC9B,KAAK,cAAc,OAAO,YAAY;GAEtC,IAAI,WAAW,KAAA,KAAa,CAAC,SAAS,QAAQ,IAAI,OAAO,MAAM;GAC/D,aAAa,KAAK;IAChB,MAAM;IACN,GAAG;IACH,OAAO,EAAE,GAAG,KAAK,MAAM;IACvB,YAAY,EAAE,GAAG,KAAK,WAAW;GACnC,CAAC;GACD,IAAI,SAAS;IACX,UAAU;IACV,MAAM,cAAc,KAAK;GAC3B;GACA,OAAO;EACT;EACA,QAAQ,UAAU,QAAQ;EAC1B,UAAU,UAAU,UAAU;EAC9B,QAAQ,UAAU,QAAQ;EAC1B,WAAW,OAAO;GAChB,UAAU,cAAc;GACxB,KAAK,aAAa,OAAO,YAAY;GACrC,MAAM,OAAO;IACX,MAAM;IACN,UAAU,CAAC;IACX,QAAQ,CAAC;GACX;GACA,YAAY,KAAK,IAAI;GACrB,MAAM,SAA4B;IAChC,SAAS,GAAG,QAAQ;KAClB,UAAU,QAAQ,MAAM,UAAU;KAClC,KAAK,SAAS,KAAK,GAAG,MAAM;KAC5B,OAAO;IACT;IACA,MAAM,QAAQ,WAAW;KACvB,UAAU,GAAG,MAAM,UAAU;KAC7B,IAAI,KAAK,OAAO,YAAY,KAAA,GAC1B,MAAM,IAAI,MAAM,GAAG,MAAM,kBAAkB,QAAQ;KACrD,KAAK,OAAO,UAAU,EAAE,GAAG,UAAU;KACrC,OAAO;IACT;GACF;GACA,OAAO;EACT;EACA,MAAM,OAAO,KAAK;GAChB,KAAK,QAAQ,MAAM,MAAM,OAAO;GAChC,IAAI,WAAW,QAAQ,KAAA,GACrB,MAAM,IAAI,MAAM,SAAS,MAAM,KAAK,mDAAmD;GACzF,OAAO,KAAK,EAAE,GAAG,MAAM,CAAC;GACxB,IAAI,QAAQ,KAAA,GAAW,KAAK,IAAI,MAAM,MAAM,GAAG;GAC/C,IAAI,SAAS;IACX,UAAU;IACV,MAAM,SAAS;KAAE,GAAG;KAAO,MAAM,EAAE,GAAG,MAAM,KAAK;IAAE,GAAG,GAAI;GAC5D;GACA,OAAO;EACT;EACA,aAAa,QAAQ;GACnB,UAAU,mBAAmB;GAC7B,IAAI,WAAW,MAAM,MAAM,IAAI,MAAM,GAAG,KAAK,6BAA6B;GAC1E,SAAS;GACT,OAAO;EACT;EACA,QAAQ,OAAO,MAAM;GACnB,UAAU,sBAAsB,OAAO;GACvC,UAAU,IAAI,OAAO,CAAC,GAAI,UAAU,IAAI,KAAK,KAAK,CAAC,GAAI,IAAI,CAAC;GAC5D,OAAO;EACT;EACA,SAAS,MAAM;GACb,UAAU,gBAAgB,KAAK,MAAM;GACrC,KAAK,QAAQ,KAAK,MAAM,OAAO;GAE/B,OAAO,KAAK;IACV,MAAM,KAAK;IACX,YAAY;IACZ,MAAM;IACN,eAAe;IACf,OAAO,KAAK;IACZ,MAAM,EAAE,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,UAAU,KAAK,IAAI;GACnB,OAAO;EACT;EACA,UAAU,QAAQ;GAChB,UAAU,WAAW;GACrB,YAAY;GACZ,OAAO;EACT;EACA,SAAS,QAAQ;GACf,UAAU,UAAU;GACpB,WAAW;GACX,OAAO;EACT;EACA,QAAQ,QAAQ;GACd,UAAU,oBAAoB;GAC9B,UAAU;GACV,OAAO;EACT;EACA,OAAO,UAAU;GACf,UAAU,YAAY;GACtB,UAAU,KAAK,EAAE,GAAG,SAAS,CAAC;GAC9B,OAAO;EACT;EACA,MAAM,OAAO,KAAK;GAChB,UAAU,kBAAkB;GAC5B,KAAK,QAAQ,OAAO,gBAAgB;GACpC,OAAO,KAAK;IAAE,MAAM;IAAO;GAAI,CAAC;GAChC,OAAO;EACT;EACA,kBAAkB,QAAQ;GACxB,UAAU,wBAAwB;GAClC,cAAc;GACd,OAAO;EACT;EACA,WAAW;GACT,MAAM,UAAoB,CAAC;GAC3B,IAAI,cAAc,KAAA,GAAW,QAAQ,KAAK,2BAA2B;GACrE,IAAI,aAAa,KAAA,GAAW,QAAQ,KAAK,yBAAyB;GAClE,IAAI,YAAY,KAAA,GAAW,QAAQ,KAAK,gCAAgC;GACxE,IAAI,OAAO,WAAW,GAAG,QAAQ,KAAK,iCAAiC;GACvE,IAAI,gBAAgB,KAAA,GAClB,QAAQ,KAAK,2DAA2D;GAC1E,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,GAAG,KAAK,oBAAoB,QAAQ,KAAK,IAAI,EAAE,8BACjD;GACF,MAAM,QAAQ,IAAI,IAAI,aAAa,KAAK,UAAU,MAAM,IAAI,CAAC;GAC7D,MAAM,WAAqB,CAAC;GAC5B,MAAM,WAAiC;IAAa;IAAsB;GAAU;GACpF,KAAK,MAAM,QAAQ,CAAC,aAAa,UAAU,GACzC,SAAS,KAAK,GAAG,eAAe,MAAM,SAAS,KAAK,CAAC;GACvD,KAAK,MAAM,SAAS,QAAQ;IAC1B,IAAI,MAAM,eAAe,QAAQ,CAAC,MAAM,IAAI,MAAM,UAAU,GAC1D,SAAS,KACP,SAAS,MAAM,KAAK,aAAa,MAAM,WAAW,4BACpD;IAEF,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,KAAK,CAAC,OAAO,MAAM,KAAK,OAAO,GAC5D,SAAS,KAAK,SAAS,MAAM,KAAK,oCAAoC;SACjE,IAAI,CAAC,MAAM,MAAM,KAAK,QAAQ,KAAK,CAAC,MAAM,MAAM,KAAK,OAAO,GACjE,SAAS,KAAK,SAAS,MAAM,KAAK,4DAA4D;IAEhG,IAAI,EAAE,MAAM,KAAK,UAAU,KAAK,MAAM,KAAK,SAAS,IAClD,SAAS,KAAK,SAAS,MAAM,KAAK,mBAAmB,MAAM,KAAK,OAAO,yBAAyB;IAElG,MAAM,SAAS,MAAM,UAAU,OAAO,OAAO,SAAS,MAAM;IAC5D,IACE,QAAQ,SAAS,cACjB,OAAO,MAAM,KAAK,QAAQ,KAC1B,MAAM,OAAO,QAAQ,KACrB,QAAQ,MAAM,KAAK,UAAU,GAAG,MAAM,QAAQ,OAAO,UAAU,GAAG,GAElE,SAAS,KACP,SAAS,MAAM,KAAK,eAAe,QAAQ,MAAM,KAAK,UAAU,GAAG,EAAE,cAAc,MAAM,MAAM,+BAA+B,QAAQ,OAAO,UAAU,GAAG,EAAE,GAC9J;GACJ;GACA,KAAK,MAAM,cAAc,aAAa;IACpC,KAAK,MAAM,UAAU,WAAW,UAC9B,IAAI,CAAC,MAAM,IAAI,MAAM,GACnB,SAAS,KACP,cAAc,WAAW,KAAK,YAAY,OAAO,4BACnD;IAEJ,KAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,WAAW,MAAM,GAC/D,SAAS,KAAK,GAAG,kBAAkB,cAAc,WAAW,QAAQ,OAAO,SAAS,CAAC;GACzF;GACA,KAAK,MAAM,cAAc,cACvB,KAAK,MAAM,CAAC,MAAM,cAAc,OAAO,QAAQ,WAAW,UAAU,GAClE,SAAS,KAAK,GAAG,kBAAkB,cAAc,WAAW,QAAQ,MAAM,SAAS,CAAC;GACxF,KAAK,MAAM,SAAS,QAClB,IAAI,CAAC,OAAO,MAAM,GAAG,GAAG,SAAS,KAAK,SAAS,MAAM,KAAK,qBAAqB;GAEjF,MAAM,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,QAAQ,SAAS,UAAU,IAAI,KAAK,CAAC;GACtF,KAAK,IAAI,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM,GACzC,IAAI,UAAU,OAAO,GAAI,IAAI,UAAU,OAAO,KAAK,EAAG,GACpD,SAAS,KACP,cAAc,OAAO,IAAK,iBAAiB,OAAO,KAAK,GAAI,uBAAuB,OAAO,KAAK,IAAI,GACpG;GACJ,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG;GAC1E,MAAM,iBAA+B,YAAY,KAAK,WAAW;IAC/D,MAAM,MAAM;IACZ,UAAU,CAAC,GAAG,MAAM,QAAQ;IAC5B,QAAQ,EAAE,GAAG,MAAM,OAAO;GAC5B,EAAE;GACF,OAAO;IACL,QAAQ;IACR;IACA;IACA,YAAY,WAAW,KAAK,WAAW;KAAE,GAAG;KAAO,OAAO,EAAE,GAAG,MAAM,MAAM;IAAE,EAAE;IAC/E,cAAc,aAAa,KAAK,WAAW;KACzC,GAAG;KACH,OAAO,EAAE,GAAG,MAAM,MAAM;KACxB,YAAY,EAAE,GAAG,MAAM,WAAW;IACpC,EAAE;IACF,aAAa;IACb,QAAQ,OAAO,KAAK,WAAW;KAAE,GAAG;KAAO,MAAM,EAAE,GAAG,MAAM,KAAK;IAAE,EAAE;IACrE,UAAU;KAAa;KAAsB;KAAoB;IAAS;IAC1E,WAAW,UAAU,KAAK,WAAW,EAAE,GAAG,MAAM,EAAE;IAClD,UAAU;KACR,QAAQ,OAAO,KAAK,WAAW,EAAE,GAAG,MAAM,EAAE;KAC5C,mBAAmB;IACrB;GACF;EACF;EACA,MAAM,UAAU,CAAC,GAAG;GAElB,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,uBAAO,IAAI,MAAM,GAAG,KAAK,oCAAoC,CAAC;GACxG,IAAI;GACJ,IAAI;IACF,OAAO,YAAY,OAAO,CAAC,CAAC;GAC9B,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,KAAK;GAC7B;GACA,UAAU;GACV,OAAO;GACP,UAAU,KAAK,OAAO,CAAC,CAAC,WAAW,IAAI;GACvC,OAAO;EACT;EACA,IAAI,UAAU,CAAC,GAAG;GAChB,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,uBAAO,IAAI,MAAM,GAAG,KAAK,2DAA2D,CAAC;GAC/H,IAAI;GACJ,IAAI;IACF,MAAM,cAAc,QAAQ,SAAS;IACrC,MAAM,aAAyB;KAC7B,YAAY;KACZ,cAAc;KACd,cAAc;KACd;KACA,mBAAmB,CAAC;IACtB;IACA,MAAM,WAAW,kBAAkB,aAAa,UAAU;IAC1D,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG;IAC1E,MAAM,OAAO,YAAY,OAAO;IAChC,UAAU;IACV,OAAO,KAAK;IACZ,OAAO,UAAU,KAAK,aAAa,KAAK,SAAS;KAAE,GAAG;KAAY,aAAa,KAAK;IAAY,GAAG,OAAO;GAC5G,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,KAAK;GAC7B;GACA,UAAU,KAAK,QAAQ,WAAW,IAAI;GACtC,OAAO;EACT;CACF;CACA,OAAO;AACT;;;AC/YA,MAAM,SAAS,UACb,UAAU,SAAS,UAAU,QAAQ,WAAW,OAAO,KAAK;AAC9D,MAAM,SAAS,SACb,KAAK,OAAO,KAAA,IAAY,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,KAAK;;AAE3D,MAAMC,aAAW,MAAe,QAAmC;CACjE,MAAM,QAAQ,KAAK;CACnB,OAAO,OAAO,UAAU,YAAY,UAAU,SAAS,UAAU,QAAQ,QAAQ,KAAA;AACnF;;AAGA,MAAa,gBAAgB,SAAsC,CACjE,KAAK,MACL,GAAI,KAAK,aAAa,CAAC,CACzB;;AAGA,MAAa,aAAa,SAAyB;CACjD,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,cAAc,aAAa,IAAI,GACxC,KAAK,MAAM,QAAQ,CAAC,GAAG,WAAW,IAAI,OAAO,GAAG,WAAW,IAAI,KAAK,GAAG;EACrE,MAAM,MAAM,MAAM,IAAI;EACtB,IAAI,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,KAAK,GAAG;CACtC;CAEF,OAAO;AACT;;AAGA,MAAM,UAAU,MAAY,SAAiC;CAC3D,IAAI,QAAQ,KAAK,SAAS;CAC1B,KAAK,MAAM,QAAQ,KAAK,OAAO;EAC7B,MAAM,QAAQA,UAAQ,MAAM,MAAM,IAAI,CAAC;EACvC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,SAAS,KAAK,OAAO,MAAM,KAAK;CAClC;CACA,OAAO;AACT;;AAGA,MAAM,YAAY,YAAwB,OAAe,SAA0B;CACjF,IAAI,MAAM,QAAQ,KAAK,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,UAAU,CAAC;CACvE,IAAI,OAAO,KAAK,WAAW,UAAU,OAAO,KAAK;CACjD,OAAO,OAAO,WAAW,WAAW,WAAW,WAAW,SAAS;AACrE;;;;;;AAOA,MAAa,YAAY,MAAY,SACnC,aAAa,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU;CAC5C,MAAM,MAAM,OAAO,WAAW,KAAK,IAAI;CACvC,MAAM,MAAM,OAAO,WAAW,KAAK,IAAI;CACvC,MAAM,SAAS,SAAS,YAAY,OAAO,IAAI;CAC/C,IAAI,QAAQ,QAAQ,QAAQ,MAAM,OAAO;EAAE;EAAK;EAAK;EAAQ,OAAO;CAAK;CASzE,OAAO;EAAE;EAAK;EAAK;EAAQ,OAPzB,WAAW,OAAO,MACd,MAAM,SAAS,MACf,WAAW,OAAO,OAChB,MAAM,UAAU,MAChB,WAAW,OAAO,MAChB,MAAM,MAAM,SACZ,OAAO,MAAM;CACU;AACnC,CAAC;;;;;;;AAQH,MAAa,aAAa,MAAY,MAAe,cAA6C;CAChG,MAAM,aAAa,UAAU,SAAS,OAAO,UAAW,MAAM,UAAU,OAAO,CAAC,IAAI,CAAC,KAAK,CAAE;CAC5F,IAAI,WAAW,WAAW,GAAG,OAAO;CACpC,IAAI,WAAW,MAAM,UAAU,UAAU,MAAM,EAAE,UAAU,KAAK,GAAG,OAAO;CAC1E,MAAM,MAAM,aAAa,IAAI;CAQ7B,OAPiB,WAAW,MAAM,UAAU;EAC1C,MAAM,aAAa,IAAI;EACvB,OAAO,CAAC,GAAG,WAAW,IAAI,OAAO,GAAG,WAAW,IAAI,KAAK,CAAC,CAAC,MAAM,SAAS;GACvE,MAAM,QAAQA,UAAQ,MAAM,MAAM,IAAI,CAAC;GACvC,OAAO,UAAU,KAAA,KAAa,MAAM,KAAK,MAAM;EACjD,CAAC;CACH,CACc,IAAI,cAAc;AAClC;;;;AC3GA,MAAa,gBAAmC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,aAAa,WACjBC,UAAE,SAAS,aAAaA,UAAE,WAAW,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,CAAC;;AAEvE,MAAM,WAAW,WACf,OAAO,UAAU,aAAa,QAAQ,UAAU,MAAM;;AAExD,MAAM,QAAQ,WACZ,OAAO,WAAW,IAAI,KAAA,IAAY,KAAK,IAAI,GAAG,MAAM;;AAGtD,MAAa,mBACX,KACA,aACoF;CACpF,MAAM,OAA8B,CAAC;CACrC,MAAM,OAA+B,CAAC;CACtC,MAAM,OAAO,QAAgB,OAA0B,SAAuB;EAC5E,IAAI,UAAU,KAAA,GAAW;EACzB,KAAK,UAAU;EACf,KAAK,UAAU;CACjB;CACA,MAAM,SAAS,IAAI,OAAO,QAAQ,UAAU,MAAM,UAAU,IAAI;CAChE,MAAM,kBAAkB,OAAO,QAAQ,UAAU,MAAM,UAAU,WAAW;CAC5E,MAAM,iBAAiB,OAAO,QAAQ,UAAU,MAAM,UAAU,UAAU;CAC1E,IACE,+BACA,KAAK,OAAO,KAAK,UAAU,UAAU,MAAM,KAAK,QAAQ,CAAC,CAAC,GAC1D,qCACF;CACA,IACE,yBACA,KAAK,OAAO,KAAK,UAAU,UAAU,MAAM,KAAK,OAAO,CAAC,CAAC,GACzD,oCACF;CACA,IACE,wBACA,KACE,OAAO,KACJ,UACC,UAAU,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM,KAAK,QAAQ,KAAK,IAAI,MAAM,KAAK,OACrF,CACF,GACA,8DACF;CAEA,MAAM,YAAY,QAAuB,WACvC,KACE,OAAO,KAAK,UAAU,UAAU,SAAS,IAAI,SAAS,SAAS,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,CAAC,CAC5F;CACF,MAAM,YAAY,SAAS,iBAAiB,WAAW;CACvD,MAAM,WAAW,SAAS,gBAAgB,UAAU;CACpD,IAAI,sCAAsC,WAAW,yCAAyC;CAC9F,IAAI,cAAc,KAAA,GAChB,IACE,kCACA,YAAY,UAAU,SAAS,OAAO,SAAS,GAC/C,+CACF;CAEF,IAAI,aAAa,KAAA,GACf,IACE,iCACA,WAAW,UAAU,SAAS,OAAO,QAAQ,GAC7C,+CACF;CAEF,IAAI,sBAAsB,UAAU,IAAI,SAAS,OAAO,GAAG,6BAA6B;CACxF,IACE,8BACA,IAAI,SAAS,OAAO,QAAQ,OAAO,UAAU,QAAQ,UAAU,MAAM,GAAG,GAAG,CAAC,GAC5E,sCAAsC,IAAI,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAClG;CACA,IACE,8BACA,QAAQ,IAAI,SAAS,iBAAiB,GACtC,iCACF;CACA,KAAK,MAAM,CAAC,QAAQ,WAAW,OAAO,QAAQ,SAAS,QAAQ,GAC7D,IAAI,QAAQ,QAAQ,MAAM,GAAG,cAAc;CAC7C,OAAO;EAAE;EAAM;CAAK;AACtB;;AAGA,MAAM,sBAAsB,YAAwB,aAA8C;CAChG,MAAM,OAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,WAAW,KAAK,GAC1D,KAAK,GAAG,KAAK,YAAY,QAAQ,MAAM;CACzC,KAAK,MAAM,CAAC,QAAQ,WAAW,OAAO,QAAQ,SAAS,eAAe,WAAW,SAAS,CAAC,CAAC,GAC1F,KAAK,OAAO,SAAS,GAAG,IAAI,SAAS,GAAG,OAAO,gBAAgB,QAAQ,MAAM;CAE/E,OAAO;AACT;AAEA,MAAM,SAAS;;AAGf,MAAa,QACX,KACA,UACA,UACc;CACd,MAAM,UAAU,gBAAgB,KAAK,QAAQ,CAAC,CAAC;CAC/C,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,cAAc,SAAS,KAAK,EAAE,GAAG;EACtC,MAAM,UAAU,UAAU,IAAI;EAC9B,MAAM,SAAmC,QAAQ,MAAM,WAAW,OAAO,KAAK,MAAM,CAAC,IACjF,IAAI,aAAa,KAAK,eAAe,CACnC,WAAW,MACX;GAAE,GAAG;GAAS,GAAG,mBAAmB,YAAY,QAAQ;EAAE,CAC5D,CAAC,IACD,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC;EACxB,KAAK,MAAM,CAAC,OAAO,SAAS,QAAQ;GAClC,MAAM,YAAY,SAAS,MAAM,IAAI;GACrC,SAAS,KAAK;IACZ,MAAM,KAAK;IACX;IACA,SAAS,UAAU,MAAM,MAAM,SAAS;IACxC,WAAW,KAAK;IAChB,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;IAC3D;IACA,SAAS,QAAQ,QAAQ,WAAW,KAAK,YAAY,KAAA,CAAS;GAChE,CAAC;EACH;CACF;CACA,OAAO;AACT;;;;;;;;ACjLA,MAAM,SAAS,CAAC;CAAE,KAAK;CAA+D,MAAM;AAAuB,CAAC;AACpH,MAAM,QAAQ,IAAY,WAA+F;CACvH,QAAQ;CACR,IAAI,mBAAmB;CACvB;CACA,OAAO;CACP,SAAS;CACT,SAAS;AACX;AACA,MAAM,KACJ,IACA,MACA,IACA,IACA,QAAgC,CAAC,OACd;CAAE;CAAI;CAAM;CAAI;CAAI,GAAG;AAAM;;;;;;AAOlD,MAAa,eAAwB;CACnC,GAAG,KAAK,QAAQ,kBAAkB;CAClC,QAAQ;EACN,QAAQ,EAAE,SAAS,KAAK;EACxB,UAAU,EAAE,MAAM,gEAAgE;EAClF,UAAU,EAAE,MAAM,0CAA0C;EAC5D,MAAM,EAAE,OAAO,KAAK;EACpB,QAAQ,EAAE,OAAO,KAAK;CACxB;CACA,QAAQ;EACN,iBAAiB,EAAE,MAAM,QAAQ;EACjC,qBAAqB,EAAE,MAAM,QAAQ;EACrC,cAAc,EAAE,MAAM,QAAQ;EAC9B,cAAc,EAAE,MAAM,QAAQ;EAC9B,gBAAgB,EAAE,MAAM,QAAQ;EAChC,uBAAuB,EAAE,MAAM,QAAQ;CACzC;CACA,OAAO,EAAE,SAAS;EAAE,KAAK;EAAoB,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,EAAE;CAC1F,QAAQ,EACN,gBAAgB;EACd,UAAU;GAAE,MAAM;GAAW,MAAM;EAAI;EACvC,MAAM,CAAC,cAAc;EACrB,SAAS,CAAC,cAAc,cAAc;EACtC,OAAO;EACP,MAAM;CACR,EACF;CACA,aAAa;EACX,EAAE,SAAS,UAAU,iBAAiB,UAAU;EAChD,EAAE,aAAa,YAAY,qBAAqB,UAAU;EAC1D,EAAE,cAAc,CAAC,UAAU,UAAU,GAAG,cAAc,UAAU;EAChE,EAAE,QAAQ,YAAY,cAAc,MAAM;EAC1C,EAAE,QAAQ;GAAC;GAAU;GAAY;EAAU,GAAG,gBAAgB,QAAQ;EAEtE,EAAE,sBAAsB,UAAU,uBAAuB,UAAU,EAAE,OAAO,iBAAiB,CAAC;EAC9F,EAAE,wBAAwB,YAAY,uBAAuB,YAAY,EAAE,OAAO,iBAAiB,CAAC;EACpG,EAAE,wBAAwB,YAAY,uBAAuB,YAAY,EAAE,OAAO,iBAAiB,CAAC;CACtG;AACF;;AAGA,MAAa,gBAAyB;CACpC,GAAG,KAAK,SAAS,sCAAsC;CACvD,QAAQ;EACN,SAAS;GAAE,SAAS;GAAM,MAAM;EAAkE;EAClG,UAAU,CAAC;CACb;CACA,QAAQ;EAAE,WAAW,EAAE,MAAM,QAAQ;EAAG,aAAa,EAAE,MAAM,QAAQ;CAAE;CACvE,OAAO;EACL,YAAY;GAAE,KAAK;GAA6B,OAAO;GAAS,MAAM;GAAS,SAAS;EAAK;EAC7F,eAAe;GAAE,KAAK;GAAgC,OAAO;GAAS,MAAM;GAAS,SAAS;EAAK;CACrG;CACA,QAAQ,CAAC;CACT,aAAa;EACX,EAAE,QAAQ,YAAY,WAAW,UAAU;EAC3C,EAAE,cAAc,YAAY,aAAa,YAAY,EAAE,OAAO,oCAAoC,CAAC;EACnG,EAAE,QAAQ,YAAY,aAAa,WAAW,EAAE,OAAO,qCAAqC,CAAC;EAC7F,EAAE,QAAQ,WAAW,aAAa,SAAS;EAC3C,EAAE,cAAc,WAAW,WAAW,WAAW,EAAE,OAAO,qCAAqC,CAAC;EAChG,EAAE,WAAW,WAAW,WAAW,YAAY,EAAE,OAAO,sCAAsC,CAAC;CACjG;AACF;;;;;AAMA,MAAa,iBAA0B;CACrC,GAAG,KAAK,UAAU,oCAAoC;CACtD,QAAQ;EACN,UAAU;GAAE,SAAS;GAAM,MAAM;EAAiE;EAClG,SAAS,EAAE,MAAM,uBAAuB;EACxC,SAAS,CAAC;EACV,YAAY,EAAE,MAAM,0BAA0B;CAChD;CACA,QAAQ;EACN,WAAW,EAAE,MAAM,QAAQ;EAC3B,aAAa,EAAE,MAAM,QAAQ;EAC7B,iBAAiB,EAAE,MAAM,QAAQ;EACjC,oBAAoB,EAAE,MAAM,QAAQ;CACtC;CACA,OAAO;EACL,YAAY;GAAE,KAAK;GAA6B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;EACzF,eAAe;GAAE,KAAK;GAAgC,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;CACjG;CACA,QAAQ;EACN,aAAa;GACX,UAAU;IAAE,MAAM;IAAc,MAAM;GAAI;GAC1C,MAAM,CAAC,eAAe;GACtB,SAAS,CAAC,SAAS;GACnB,OAAO;EACT;EACA,gBAAgB;GACd,UAAU;IAAE,MAAM;IAAiB,MAAM;GAAI;GAC7C,MAAM,CAAC,kBAAkB;GACzB,SAAS,CAAC,WAAW;GACrB,OAAO;EACT;CACF;CACA,aAAa;EACX,EAAE,QAAQ,YAAY,WAAW,UAAU;EAC3C,EAAE,iBAAiB,YAAY,aAAa,WAAW,EAAE,OAAO,iBAAiB,CAAC;EAClF,EAAE,gBAAgB,YAAY,aAAa,WAAW,EAAE,OAAO,iBAAiB,CAAC;EACjF,EAAE,iBAAiB,WAAW,aAAa,SAAS;EACpD,EAAE,gBAAgB,WAAW,WAAW,UAAU;EAClD,EAAE,QAAQ,WAAW,iBAAiB,WAAW,EAAE,OAAO,cAAc,CAAC;EACzE,EAAE,QAAQ,WAAW,aAAa,SAAS;EAC3C,EAAE,mBAAmB,WAAW,WAAW,YAAY,EAAE,OAAO,+BAA+B,CAAC;EAChG,EAAE,oBAAoB,WAAW,WAAW,cAAc,EAAE,OAAO,oBAAoB,CAAC;EACxF,EAAE,oBAAoB,cAAc,WAAW,YAAY;EAC3D,EAAE,mBAAmB,cAAc,aAAa,SAAS;EACzD,EAAE,WAAW,cAAc,oBAAoB,YAAY,EAAE,OAAO,iBAAiB,CAAC;CACxF;AACF;;;;;AAMA,MAAa,iBAA0B;CACrC,GAAG,KAAK,UAAU,uBAAuB;CACzC,QAAQ;EACN,MAAM;GAAE,SAAS;GAAM,MAAM;EAAyB;EACtD,SAAS,EAAE,MAAM,8CAA8C;EAC/D,SAAS,EAAE,MAAM,8BAA8B;EAC/C,MAAM,EAAE,MAAM,uDAAuD;EACrE,SAAS,EAAE,OAAO,KAAK;CACzB;CACA,QAAQ;EACN,OAAO,EAAE,MAAM,QAAQ;EACvB,OAAO,EAAE,MAAM,QAAQ;EACvB,MAAM,EAAE,MAAM,QAAQ;EACtB,QAAQ,EAAE,MAAM,QAAQ;EACxB,SAAS,EAAE,MAAM,QAAQ;EACzB,eAAe,EAAE,MAAM,QAAQ;EAC/B,MAAM,EAAE,MAAM,QAAQ;CACxB;CACA,OAAO;EACL,UAAU;GAAE,KAAK;GAA+B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;EACzF,SAAS;GAAE,KAAK;GAA8B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;CACzF;CACA,QAAQ;EACN,MAAM;GACJ,UAAU;IAAE,MAAM;IAAY,MAAM;GAAI;GACxC,MAAM,CAAC,SAAS,MAAM;GACtB,SAAS,CAAC,MAAM;GAChB,OAAO;GACP,MAAM;EACR;EACA,cAAc;GACZ,UAAU;IAAE,MAAM;IAAW,MAAM;GAAI;GACvC,MAAM,CAAC,eAAe;GACtB,SAAS,CAAC,UAAU,MAAM;GAC1B,OAAO;EACT;CACF;CACA,aAAa;EACX,EAAE,SAAS,QAAQ,SAAS,SAAS;EACrC,EAAE,SAAS,QAAQ,SAAS,SAAS;EACrC,EAAE,uBAAuB,WAAW,SAAS,WAAW,EAAE,MAAM,mDAAmD,CAAC;EACpH,EAAE,oBAAoB,QAAQ,SAAS,MAAM;EAC7C,EAAE,QAAQ,WAAW,QAAQ,WAAW,EAAE,OAAO,8CAA8C,CAAC;EAChG,EAAE,eAAe,WAAW,QAAQ,WAAW,EAAE,OAAO,sCAAsC,CAAC;EAC/F,EAAE,gBAAgB,WAAW,QAAQ,WAAW,EAAE,MAAM,8CAA8C,CAAC;EACvG,EAAE,aAAa,QAAQ,QAAQ,QAAQ,EAAE,MAAM,8CAA8C,CAAC;EAC9F,EAAE,eAAe,WAAW,UAAU,WAAW,EAAE,OAAO,gBAAgB,CAAC;EAC3E,EAAE,gBAAgB,WAAW,UAAU,QAAQ,EAAE,OAAO,2BAA2B,CAAC;EACpF,EAAE,WAAW,WAAW,WAAW,QAAQ,EAAE,OAAO,eAAe,CAAC;EACpE,EAAE,oBAAoB,QAAQ,eAAe,WAAW,EAAE,OAAO,gBAAgB,CAAC;EAClF,EAAE,qBAAqB,QAAQ,eAAe,QAAQ,EAAE,OAAO,2BAA2B,CAAC;EAC3F,EAAE,QAAQ;GAAC;GAAQ;GAAW;GAAW;EAAM,GAAG,QAAQ,SAAS;EACnE,EAAE,qBAAqB,WAAW;GAAC;GAAU;GAAe;EAAS,GAAG,SAAS;CACnF;AACF;;;;;AAMA,MAAa,eAAwB;CACnC,GAAG,KAAK,QAAQ,kBAAkB;CAClC,QAAQ;EAAE,SAAS,EAAE,SAAS,KAAK;EAAG,UAAU,CAAC;EAAG,QAAQ,EAAE,OAAO,KAAK;CAAE;CAC5E,QAAQ;EACN,gBAAgB;GAAE,MAAM;GAAO,MAAM;EAAyC;EAC9E,eAAe,EAAE,MAAM,MAAM;EAC7B,gBAAgB,EAAE,MAAM,QAAQ;EAChC,OAAO,EAAE,MAAM,QAAQ;EACvB,iBAAiB,EAAE,MAAM,QAAQ;CACnC;CACA,OAAO,CAAC;CACR,QAAQ,CAAC;CACT,aAAa;EACX,EAAE,WAAW,WAAW,gBAAgB,UAAU;EAClD,EAAE,gBAAgB,WAAW,gBAAgB,UAAU;EACvD,EAAE,iBAAiB,WAAW,SAAS,SAAS;EAChD,EAAE,UAAU,YAAY,eAAe,UAAU;EACjD,EAAE,yBAAyB,YAAY,gBAAgB,UAAU;EACjE,EAAE,SAAS,YAAY,SAAS,UAAU;EAC1C,EAAE,QAAQ,YAAY,iBAAiB,UAAU,EAAE,MAAM,4CAA4C,CAAC;CACxG;AACF;;AAGA,MAAa,mBAA4B;CACvC,GAAG,KAAK,YAAY,gCAAgC;CACpD,QAAQ;EAAE,UAAU,EAAE,SAAS,KAAK;EAAG,SAAS,CAAC;EAAG,SAAS,EAAE,OAAO,KAAK;CAAE;CAC7E,QAAQ;EAAE,MAAM,EAAE,MAAM,QAAQ;EAAG,MAAM,EAAE,MAAM,QAAQ;CAAE;CAC3D,OAAO;EACL,MAAM;GAAE,KAAK;GAAiB,OAAO;GAAW,MAAM;GAAK,SAAS;EAAK;EACzE,WAAW;GAAE,KAAK;GAAsB,OAAO;GAAW,MAAM;GAAK,SAAS;EAAK;CACrF;CACA,QAAQ,EACN,MAAM;EAAE,UAAU;GAAE,MAAM;GAAQ,MAAM;EAAI;EAAG,MAAM,CAAC,kBAAkB,MAAM;EAAG,SAAS,CAAC,MAAM;EAAG,OAAO;CAAO,EACpH;CACA,aAAa;EACX,EAAE,WAAW,YAAY,QAAQ,YAAY,EAAE,OAAO,wBAAwB,CAAC;EAC/E,EAAE,SAAS,YAAY,QAAQ,WAAW,EAAE,OAAO,uBAAuB,CAAC;EAC3E,EAAE,iBAAiB,WAAW,QAAQ,WAAW,EAAE,OAAO,uBAAuB,CAAC;EAClF,EAAE,WAAW,WAAW,QAAQ,YAAY,EAAE,OAAO,wBAAwB,CAAC;EAC9E,EAAE,QAAQ,CAAC,YAAY,SAAS,GAAG,QAAQ,SAAS;CACtD;AACF;;AAGA,MAAa,WAAW,UAA0B,GAAG,MAAM,QAAQ,kBAAkB,GAAG,EAAE;;;;;;AAO1F,MAAa,gBAAgB,gBAA6C;CACxE,MAAM,SAAS,YAAY,SAAS;CACpC,MAAM,SAAgF,EACpF,SAAS,EAAE,SAAS,KAAK,EAC3B;CACA,KAAK,MAAM,SAAS,QAAQ,OAAO,MAAM,QAAQ,CAAC;CAClD,OAAO,UAAU,EAAE,OAAO,KAAK;CAC/B,MAAM,QAA0B,OAAO,YAAY,CACjD,GAAG,OAAO,KAAK,UAAU,CACvB,QAAQ,MAAM,IAAI,GAClB;EAAE,KAAK,mBAAmB,MAAM,KAAK;EAAO,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,CACzF,CAAC,GACD,CAAC,sBAAsB;EAAE,KAAK;EAA8B,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,CAAC,CAC1G,CAAC;CACD,MAAM,SAAoD,CAAC;CAC3D,KAAK,MAAM,SAAS,QAClB,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE,WAAW;EACvC,UAAU;GAAE,MAAM,QAAQ,MAAM,IAAI;GAAG,MAAM;EAAI;EACjD,MAAM,CAAC,SAAS,MAAM,MAAM;EAC5B,SAAS,CAAC,YAAY;EACtB,OAAO;CACT;CACF,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,OAAO,GAC/C,OAAO,uBAAuB;EAC5B,UAAU;GAAE,MAAM;GAAsD,MAAM;EAAI;EAClF,MAAM,CAAC,aAAa;EACpB,SAAS,CAAC,cAAc,WAAW;EACnC,OAAO;CACT;CACF,MAAM,cAA+B,CAAC;CACtC,MAAM,QAAQ;EAAC;EAAW,GAAG,OAAO,KAAK,UAAU,MAAM,IAAI;EAAG;CAAM;CACtE,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG;EAC/C,MAAM,OAAO,MAAM;EACnB,MAAM,KAAK,MAAM,KAAK;EACtB,YAAY,KACV,OAAO,IACH,EAAE,SAAS,MAAM,gBAAgB,EAAE,IACnC,EAAE,SAAS,QAAQ,MAAM,CAAC,cAAc,WAAW,GAAG,IAAI;GACxD,OAAO,GAAG,QAAQ,IAAI,EAAE;GACxB,MAAM;EACR,CAAC,CACP;CACF;CACA,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,OAAO,GAC/C,YAAY,KAAK,EAAE,qBAAqB,SAAS,2BAA2B,SAAS,EAAE,OAAO,oBAAoB,CAAC,CAAC;CAGtH,KAAK,MAAM,SAAS,CAAC,GAAG,OAAO,KAAK,UAAU,MAAM,IAAI,GAAG,MAAM,GAC/D,YAAY,KAAK,EAAE,gBAAgB,SAAS,OAAO,gBAAgB,KAAK,CAAC;CAC3E,OAAO;EACL,GAAG,KAAK,SAAS,gBAAgB,YAAY,MAAM;EACnD;EACA,QAAQ;GACN,gBAAgB,EAAE,MAAM,MAAM;GAC9B,cAAc,EAAE,MAAM,QAAQ;GAC9B,aAAa,EAAE,MAAM,QAAQ;GAC7B,2BAA2B,EAAE,MAAM,QAAQ;GAC3C,gBAAgB,EAAE,MAAM,QAAQ;EAClC;EACA;EACA;EACA;CACF;AACF;;AAGA,MAAa,WAAgF;CAC3F,MAAM;CACN,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,UAAU;AACZ;;;;;;;AAQA,MAAa,UAAU,KAAc,WAAgD;CACnF,MAAM,WAAqB,CAAC;CAC5B,MAAM,qBAAK,IAAI,IAAoB;CACnC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK,MAAM;EAC9C,IAAI,IAAI,OAAO,WAAW,KAAA,GAAW,SAAS,KAAK,GAAG,MAAM,SAAS,cAAc,MAAM,aAAa,IAAI,IAAI;EAC9G,IAAI,MAAM,SAAS,OACjB,SAAS,KAAK,GAAG,MAAM,SAAS,qBAAqB,MAAM,KAAK,MAAM,MAAM,GAAG,yBAAyB,OAAO;EAIjH,IAAI,CAHY,IAAI,YAAY,MAC7B,SAAS,WAAW,KAAK,KAAK,IAAI,CAAC,CAAC,SAAS,MAAM,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,KAAK,OAAO,MAAM,EAEzG,GAAG,SAAS,KAAK,GAAG,MAAM,SAAS,IAAI,IAAI,GAAG,0BAA0B,MAAM,KAAK,MAAM,MAAM,GAAG,MAAM,MAAM,IAAI;EAC7H,GAAG,IAAI,MAAM,UAAU,MAAM,EAAE;CACjC;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":["message","boundOf","r"],"sources":["../src/description.ts","../src/latch.ts","../src/poller.ts","../src/health.ts","../src/process.ts","../src/requests.ts","../src/lifecycle.ts","../src/watchdog.ts","../src/builder.ts","../src/law.ts","../src/lint.ts","../src/machines.ts"],"sourcesContent":["import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport type { Operation } from \"./operation.ts\";\nimport type { Poll, Timing } from \"./timing.ts\";\n\n/**\n * This is the format of a service as data: what `describe()` exports, and nothing starts. The\n * timer algebra and the ordering law read it. Each timer has a unit. Each knob comes from the\n * declaration, because the runtime has no defaults. A bound that nobody gives is \"no bound\", not\n * zero.\n */\nexport const FORMAT = \"mark1russell7.service/1\";\n\n/** A step is a step of a connection that a check examines. A check that skips a step cannot see a failure in it. */\nexport type Step =\n \"dial\" | \"local-state\" | \"round-trip\" | \"checkout\" | \"progress\" | \"topology\" | \"latency\";\n\n/** A gate is the policy that the failures of a check count toward. */\nexport type Gate = \"readiness\" | \"liveness\";\n\n/** A check is an active check: one signal, for one step of a connection. */\nexport interface Check {\n readonly name: string;\n /** This name is the dependency that it examines, or null when it reads the state of the process. */\n readonly dependency: string | null;\n readonly step: Step;\n /** This flag tells if it uses the connection of the application. A check on its own connection cannot see a failure of that connection. */\n readonly ownConnection: boolean;\n /** This gate is the policy that it controls, or null for a check that only reports. It has no default. */\n readonly gates: Gate | null;\n readonly poll: Poll;\n}\n\n/**\n * A dependency is something that the service uses, with its client. Its knobs have the names that\n * the ordering law uses: `request_timeout`, `connect_bound`, `driver_writeoff_time`,\n * `heartbeat_interval`, `client_retry_window`. Thus the law reads them without a translation.\n */\nexport interface Dependency {\n readonly name: string;\n /** This identifier is the component in the corpus (`systems.component.postgresql`), or null when the corpus does not have it. */\n readonly system: string | null;\n readonly knobs: Readonly<Record<string, Extent>>;\n /**\n * These are the operations that the service does with it, by name (`GET /v1/forecast`, `INCR`).\n * Each operation declares if it can go again. A dependency usually has the two kinds, thus a\n * dependency has no idempotency of its own. An operation without a declaration does not go again.\n */\n readonly operations: Readonly<Record<string, Operation>>;\n}\n\n/**\n * A transport is something that takes work in: a server, a consumer or a worker. Each is a\n * component with a state machine. The service is ready only while each transport takes work.\n */\nexport interface Transport {\n readonly name: string;\n readonly kind: \"server\" | \"consumer\" | \"worker\";\n /** These are its knobs, with the names of the law where the law has them: the `server_idle_close` of a server. */\n readonly knobs: Readonly<Record<string, Extent>>;\n}\n\n/**\n * A controller is a group of routes. It is ready only while the dependencies that it needs are\n * ready. Each route declares if its callers can send it again. When the shutdown stops a request,\n * an idempotent route answers 503, which a caller can retry. Other routes answer 500.\n */\nexport interface Controller {\n readonly name: string;\n readonly requires: readonly string[];\n readonly routes: Readonly<Record<string, Operation>>;\n}\n\n/** A coupling is a declared edge between two policies. The algebra checks its margin. */\nexport interface Coupling {\n readonly from: Gate;\n readonly to: Gate;\n readonly effect: \"fails\";\n}\n\n/** A phase is one step of the shutdown sequence, with a cap. Its owner adds it. */\nexport interface Phase {\n readonly name: string;\n readonly cap: Quantity;\n}\n\n/** A service description is the whole graph of a service, as data. */\nexport interface ServiceDescription {\n readonly format: typeof FORMAT;\n readonly name: string;\n /** This flag tells if a part joined after the start. That part starts tripped, and the export is partial. */\n readonly partial: boolean;\n readonly transports: readonly Transport[];\n readonly dependencies: readonly Dependency[];\n readonly controllers: readonly Controller[];\n readonly checks: readonly Check[];\n readonly policies: {\n readonly readiness: Timing;\n readonly liveness: Timing;\n /** This duration is how long the service can take to start. The startup probe must agree with it. */\n readonly startup: Quantity;\n };\n readonly couplings: readonly Coupling[];\n readonly shutdown: {\n readonly phases: readonly Phase[];\n /** This window is how long before the end of the drain the cooperative abort occurs, or no bound. */\n readonly cooperativeWindow: Extent;\n };\n}\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Timing } from \"./timing.ts\";\n\n/**\n * An outcome is the result of one poll of a check. Only `\"ok\"` passes. An `\"indeterminate\"`\n * outcome measured nothing: it does not pass, and it does not fail.\n */\nexport type Outcome = \"ok\" | \"error\" | \"indeterminate\";\n\n/** A result is one poll of a check, as the runtime records it. */\nexport interface Result {\n readonly outcome: Outcome;\n /** This time is when the result came, on the monotonic clock, in milliseconds. */\n readonly at: number;\n /** This time is how long the poll took, in milliseconds. */\n readonly took: number;\n /** This text tells why the poll did not pass, or it is null. */\n readonly cause: string | null;\n /** This flag tells if the poll failed at its timeout. */\n readonly timedOut: boolean;\n /** This flag tells if the check threw. A throw is an error with a flag. */\n readonly thrown: boolean;\n /** This flag tells if the force-fail list made the poll fail. */\n readonly forced: boolean;\n}\n\n/**\n * A latch turns the results of one check into a verdict, by the timing of its policy. A debounce\n * counts polls in sequence. A window measures time on the monotonic clock from the first failure\n * of a sequence. A tripped latch fails its policy.\n */\nexport interface Latch {\n readonly tripped: boolean;\n /** This time is when the latch last tripped or released. */\n readonly since: number;\n /** This count is the sequence of the latest results: above zero for passes, below zero for failures. */\n readonly streak: number;\n record(result: Result): void;\n /** This method stops the timers of a window. */\n stop(): void;\n}\n\n/**\n * A trace receives each transition of a machine of the runtime: the state before, the state after,\n * and the event. The machine documents of the lifecycle name the same states and events, thus a\n * test can replay a trace against its document.\n */\nexport type Trace = (from: string, to: string, on: string) => void;\n\nconst ms = (amount: { readonly value: number; readonly unit: string }): number =>\n valueIn(amount, \"ms\");\n\n/**\n * This function makes a latch. `tripped` is its state at the start: a readiness latch starts\n * tripped, and a liveness latch starts released. The latch starts `changed` each time it trips or\n * releases, and it gives each transition to `trace`. A debounce has the states `released` and\n * `tripped`. A window also has the states `failing` and `recovering`, while one of its timers runs.\n */\nexport const latch = (\n timing: Timing,\n clock: Clock,\n tripped: boolean,\n changed: () => void,\n trace?: Trace,\n): Latch => {\n let state = tripped;\n let since = clock.now();\n let streak = 0;\n let fails = 0;\n let passes = 0;\n // the timers of a window: the time that a sequence of failures or passes started, and its timer\n let failingSince: number | null = null;\n let passingSince: number | null = null;\n let failTimer: Cancel | null = null;\n let passTimer: Cancel | null = null;\n\n /** This function gives the name of the state, as the machine documents name it. */\n const named = (): string =>\n state\n ? passingSince === null\n ? \"tripped\"\n : \"recovering\"\n : failingSince === null\n ? \"released\"\n : \"failing\";\n /** This function does one step of the machine, and gives its transition to the trace. */\n const step = (on: string, change: () => void): void => {\n const from = named();\n change();\n trace?.(from, named(), on);\n };\n\n const set = (next: boolean): void => {\n if (next === state) return;\n state = next;\n since = clock.now();\n changed();\n };\n const stopFail = (): void => {\n failTimer?.();\n failTimer = null;\n failingSince = null;\n };\n const stopPass = (): void => {\n passTimer?.();\n passTimer = null;\n passingSince = null;\n };\n\n const debounce = (result: Result): void => {\n if (timing.type !== \"debounce\") return;\n if (result.outcome === \"ok\") {\n passes += 1;\n fails = 0;\n if (state && passes >= timing.recoverAfter) set(false);\n } else {\n fails += 1;\n passes = 0;\n if (!state && fails >= timing.failAfter) set(true);\n }\n };\n\n const windowed = (result: Result): void => {\n if (timing.type !== \"window\") return;\n if (result.outcome === \"ok\") {\n stopFail();\n if (!state) return;\n if (timing.recoverAfter === undefined) {\n set(false);\n return;\n }\n if (passingSince !== null) return;\n const wait = ms(timing.recoverAfter);\n if (wait <= 0) {\n stopPass();\n set(false);\n return;\n }\n passingSince = result.at;\n passTimer = clock.after(wait, () =>\n step(\"window.recovered\", () => {\n passTimer = null;\n passingSince = null;\n set(false);\n }),\n );\n return;\n }\n stopPass();\n if (state || failingSince !== null) return;\n const wait = ms(timing.failAfter);\n if (wait <= 0) {\n stopFail();\n set(true);\n return;\n }\n failingSince = result.at;\n failTimer = clock.after(wait, () =>\n step(\"window.failed\", () => {\n failTimer = null;\n failingSince = null;\n set(true);\n }),\n );\n };\n\n return {\n get tripped() {\n return state;\n },\n get since() {\n return since;\n },\n get streak() {\n return streak;\n },\n record(result) {\n // an indeterminate poll measured nothing: it moves no sequence and no streak\n if (result.outcome === \"indeterminate\") return;\n const pass = result.outcome === \"ok\";\n step(pass ? \"poll.ok\" : \"poll.fail\", () => {\n streak = pass ? Math.max(0, streak) + 1 : Math.min(0, streak) - 1;\n debounce(result);\n windowed(result);\n });\n },\n stop() {\n stopFail();\n stopPass();\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Outcome, Result, Trace } from \"./latch.ts\";\nimport type { Poll } from \"./timing.ts\";\n\n/**\n * A check function measures one step of a connection. It ends with nothing or `\"ok\"` for a pass,\n * or with `\"error\"` or `\"indeterminate\"`. A throw is an error with a flag. The runtime aborts the\n * signal at the timeout of the poll.\n */\nexport type CheckFn = (signal: AbortSignal) => Outcome | void | Promise<Outcome | void>;\n\n/** A poller starts one check on its own loop, and gives each result to `record`. */\nexport interface Poller {\n /** This result is the latest one, or null before the first poll. */\n readonly last: Result | null;\n /** This method polls one time at once, outside the loop, and gives the result. */\n pollNow(): Promise<Result>;\n /** This method starts the loop. The first poll of the loop comes after `delay` milliseconds. */\n start(delay: number): void;\n /** This method gives the next delay of the loop: the interval, with the jitter. */\n jittered(): number;\n stop(): void;\n}\n\nexport interface PollerOptions {\n readonly name: string;\n readonly poll: Poll;\n readonly run: CheckFn;\n readonly clock: Clock;\n readonly random: () => number;\n /** This function tells if the force-fail list names the check at this time. */\n readonly forced: () => boolean;\n readonly record: (result: Result) => void;\n /**\n * This function receives each transition of the poller. Its states are `idle`, `waiting`,\n * `calling`, `hung` (a call that outlived its timeout) and `stopped`.\n */\n readonly trace?: Trace | undefined;\n}\n\nconst message = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\n/**\n * This function makes a poller. The loop is on a fixed delay from the start of each poll. Thus the\n * next poll starts one interval after the current poll starts, with the jitter (±20 % is 0.2).\n * The timing algebra of the declaration uses this model.\n *\n * Each poll makes a new measurement. While a use of the check is pending, the poller does not\n * start the check again. Such a poll fails at once with the same cause, and the sequence of\n * failures continues.\n */\nexport const poller = (options: PollerOptions): Poller => {\n const { clock, poll, run } = options;\n const interval = valueIn(poll.interval, \"ms\");\n const timeout = valueIn(poll.timeout, \"ms\");\n let last: Result | null = null;\n let next: Cancel | null = null;\n let stopped = false;\n let looping = false;\n // the call that has not ended, and when it started; `late` once the call outlived its timeout\n let pending: { readonly since: number; late: boolean } | null = null;\n // the polls in flight: stop() ends each one, and its timer\n const active = new Set<() => void>();\n\n /** This function gives the name of the state, as the machine document of the poller names it. */\n const named = (): string =>\n stopped\n ? \"stopped\"\n : pending !== null\n ? pending.late\n ? \"hung\"\n : \"calling\"\n : looping\n ? \"waiting\"\n : \"idle\";\n const step = (on: string, change: () => void): void => {\n const from = named();\n change();\n options.trace?.(from, named(), on);\n };\n\n const result = (\n outcome: Outcome,\n started: number,\n fields: Partial<Pick<Result, \"cause\" | \"timedOut\" | \"thrown\" | \"forced\">> = {},\n ): Result => ({\n outcome,\n at: clock.now(),\n took: clock.now() - started,\n cause: fields.cause ?? null,\n timedOut: fields.timedOut ?? false,\n thrown: fields.thrown ?? false,\n forced: fields.forced ?? false,\n });\n\n const once = (): Promise<Result> => {\n const started = clock.now();\n if (options.forced())\n return Promise.resolve(result(\"error\", started, { cause: \"the force-fail list names it\", forced: true }));\n if (pending !== null) {\n const cause = `its call of ${Math.round(started - pending.since)} ms ago is still pending`;\n return Promise.resolve(result(\"error\", started, { cause }));\n }\n const controller = new AbortController();\n return new Promise<Result>((resolve) => {\n let done = false;\n const finish = (value: Result): void => {\n if (done) return;\n done = true;\n active.delete(abandon);\n cancel();\n resolve(value);\n };\n const abandon = (): void => {\n controller.abort();\n finish(result(\"error\", started, { cause: \"the runtime stopped\" }));\n };\n active.add(abandon);\n const call = { since: started, late: false };\n pending = call;\n const cancel = clock.after(timeout, () => {\n controller.abort();\n // the poll fails at its timeout. The call stays pending until it settles\n step(\"timeout\", () => {\n if (pending === call) call.late = true;\n });\n finish(result(\"error\", started, { cause: `no answer in ${timeout} ms`, timedOut: true }));\n });\n const settled = (): void => {\n if (pending !== call) return;\n step(call.late ? \"late-answer\" : \"answer\", () => {\n pending = null;\n });\n };\n let answer: Promise<Outcome | void>;\n try {\n answer = Promise.resolve(run(controller.signal));\n } catch (error) {\n answer = Promise.reject(error);\n }\n answer.then(\n (value) => {\n settled();\n const outcome = value ?? \"ok\";\n finish(result(outcome, started, outcome === \"ok\" ? {} : { cause: `the check gave ${outcome}` }));\n },\n (error: unknown) => {\n settled();\n finish(result(\"error\", started, { cause: message(error), thrown: true }));\n },\n );\n });\n };\n\n const handle = (value: Result): Result => {\n last = value;\n if (!stopped) options.record(value);\n return value;\n };\n\n const jittered = (): number => interval * (1 + poll.jitter * (2 * options.random() - 1));\n\n const tick = (): void => {\n next = null;\n if (stopped) return;\n let polled: Promise<Result> | undefined;\n step(\"tick\", () => {\n next = clock.after(jittered(), tick);\n polled = once();\n });\n void polled!.then(handle);\n };\n\n return {\n get last() {\n return last;\n },\n pollNow: () => {\n let polled: Promise<Result> | undefined;\n step(\"sweep\", () => {\n polled = once();\n });\n return polled!.then(handle);\n },\n start(delay) {\n if (stopped || next !== null) return;\n step(\"start\", () => {\n looping = true;\n next = clock.after(Math.max(0, delay), tick);\n });\n },\n jittered,\n stop() {\n if (stopped) return;\n step(\"stop\", () => {\n stopped = true;\n next?.();\n next = null;\n });\n for (const abandon of [...active]) abandon();\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Check, Gate, ServiceDescription } from \"./description.ts\";\nimport type { MachineName, Recorder } from \"./events.ts\";\nimport type { Latch, Result, Trace } from \"./latch.ts\";\nimport { latch } from \"./latch.ts\";\nimport type { CheckFn, Poller } from \"./poller.ts\";\nimport { poller } from \"./poller.ts\";\n\n/** These options start the runtime. Without them, it uses the real clock and `Math.random`. */\nexport interface StartOptions {\n readonly clock?: Clock | undefined;\n /** This function gives a number in [0, 1) for the jitter of each poll. */\n readonly random?: (() => number) | undefined;\n /** This list names the checks or dependencies whose polls fail at once (a seam for tests). */\n readonly forceFail?: readonly string[] | undefined;\n /** This function receives each transition of each machine of the runtime. */\n readonly record?: Recorder | undefined;\n /**\n * This function can replace the function of a check, for example to add a delay to each call\n * (a seam for tests: a degraded dependency). It gives the function to use.\n */\n readonly wrapCheck?: ((check: Check, run: CheckFn) => CheckFn) | undefined;\n}\n\n/** A check verdict is the cached state of one check, for the probes and the report. */\nexport interface CheckVerdict {\n readonly gates: Gate | null;\n readonly dependency: string | null;\n /** This flag tells if the latch of the check is tripped, or it is null for a check that only reports. */\n readonly tripped: boolean | null;\n readonly streak: number;\n /** This time is when the latch last changed, in milliseconds on the monotonic clock. */\n readonly since: number | null;\n readonly last: Result | null;\n}\n\n/** A verdict is the state of the service, from its cache. Nothing in it starts a check. */\nexport interface HealthVerdict {\n /** This flag becomes true one time, after the warm-up sweep. */\n readonly started: boolean;\n readonly draining: boolean;\n /** This flag tells if the service can take new work: started, not draining, and each readiness latch released. */\n readonly ready: boolean;\n /** This flag tells if each liveness latch is released. */\n readonly alive: boolean;\n /** These flags tell if each controller can take new work, from the dependencies that it needs. */\n readonly controllers: Readonly<Record<string, boolean>>;\n readonly dependencies: Readonly<Record<string, boolean>>;\n readonly checks: Readonly<Record<string, CheckVerdict>>;\n readonly startup: { readonly budget: number; readonly elapsed: number; readonly overBudget: boolean };\n}\n\n/** A probe kind is the question that a probe asks: the startup, the readiness, the liveness, or the whole report. */\nexport type ProbeKind = \"startup\" | \"readiness\" | \"liveness\" | \"report\";\n\n/** A probe answer is a status and a body, for any HTTP framework or none. */\nexport interface ProbeAnswer {\n readonly status: 200 | 503;\n readonly contentType: \"application/health+json\";\n readonly body: {\n readonly status: \"pass\" | \"fail\";\n readonly service: string;\n readonly probe: ProbeKind;\n readonly controller?: string | undefined;\n readonly verdict?: HealthVerdict | undefined;\n };\n}\n\n/** The health runtime of a started service. It reads its timing and its checks from the declaration. */\nexport interface Health {\n verdict(): HealthVerdict;\n /**\n * This method answers a probe from the cache, in constant time, and it starts no check. A\n * readiness probe with a controller asks about that controller. The report always answers 200.\n */\n probe(kind: ProbeKind, controller?: string): ProbeAnswer;\n /** This method gives each change of the verdict to `listener`, and gives the function that stops it. */\n subscribe(listener: (verdict: HealthVerdict) => void): () => void;\n /** This method adds a name to the force-fail list, or removes it. */\n forceFail(name: string, on: boolean): void;\n /** This method fails readiness for good. The drain of the lifecycle starts with it. */\n drain(): void;\n /** This method stops each poller and each timer of the runtime. */\n stop(): void;\n}\n\n/** The runtime parts that the builder and the lifecycle use. */\nexport interface Runtime extends Health {\n /**\n * This method opens the startup gate: one bounded warm-up sweep of each check, then the loops of\n * the pollers. It is idempotent, and it does nothing after `failStartup()` or `stop()`.\n */\n warmUp(): Promise<void>;\n /** This method moves the gate from `closed` to `starting`: the lifecycle starts its components. */\n beginStartup(): void;\n /** This method moves the gate to `failed` for good: a component did not start. */\n failStartup(cause: string): void;\n addCheck(check: Check, run: CheckFn): void;\n addDependency(name: string): void;\n}\n\ninterface Entry {\n readonly check: Check;\n readonly poller: Poller;\n readonly latch: Latch | null;\n}\n\n/**\n * This function makes the runtime of a declaration. The service has no defaults of its own: each\n * timing, poll and coupling comes from the description.\n */\nexport const healthRuntime = (\n description: ServiceDescription,\n runs: ReadonlyMap<string, CheckFn>,\n options: StartOptions = {},\n): Runtime => {\n const clock = options.clock ?? realClock;\n const random = options.random ?? Math.random;\n const forced = new Set(options.forceFail ?? []);\n const startedAt = clock.now();\n const budget = valueIn(description.policies.startup, \"ms\");\n const entries = new Map<string, Entry>();\n const dependencies = new Set(description.dependencies.map((dependency) => dependency.name));\n // a dependency that joined after the start is tripped until a check on it passes one time\n const lateDependencies = new Set<string>();\n const listeners = new Set<(verdict: HealthVerdict) => void>();\n let started = false;\n let draining = false;\n let stopped = false;\n // the startup gate: closed, starting (components start), sweeping (the warm-up), open or failed\n let gate = \"closed\";\n\n const tracer =\n (machine: MachineName, instance: string): Trace =>\n (from, to, on) =>\n options.record?.({ at: clock.now(), machine, instance, from, to, on });\n const gateTrace = tracer(\"gate\", description.name);\n const moveGate = (to: string, on: string, detail?: string): void => {\n const from = gate;\n gate = to;\n options.record?.({\n at: clock.now(),\n machine: \"gate\",\n instance: description.name,\n from,\n to,\n on,\n ...(detail === undefined ? {} : { detail }),\n });\n };\n // the startup budget: a gate that is not open at its end says so. The platform's startup probe\n // acts on it, and the runtime does not stop itself\n let budgetTimer: Cancel | null = clock.after(budget, () => {\n budgetTimer = null;\n if (gate !== \"open\" && gate !== \"failed\") gateTrace(gate, gate, \"startup.over-budget\");\n });\n\n const changed = (): void => {\n if (listeners.size === 0) return;\n const now = verdict();\n for (const listener of listeners) listener(now);\n };\n\n const add = (check: Check, given: CheckFn, late: boolean): void => {\n const run = options.wrapCheck?.(check, given) ?? given;\n const timing = check.gates === null ? null : description.policies[check.gates];\n // readiness is earned: its latch starts tripped. Liveness passes from the start (and a late\n // liveness check starts released too, or one late registration could restart the process)\n const made =\n timing === null\n ? null\n : latch(\n timing,\n clock,\n check.gates === \"readiness\",\n changed,\n tracer(timing.type === \"window\" ? \"window\" : \"latch\", check.name),\n );\n const entry: Entry = {\n check,\n latch: made,\n poller: poller({\n name: check.name,\n poll: check.poll,\n run,\n clock,\n random,\n forced: () =>\n forced.has(check.name) || (check.dependency !== null && forced.has(check.dependency)),\n record: (result) => {\n made?.record(result);\n // a late dependency is released by the first pass of a check on it\n if (result.outcome === \"ok\" && check.dependency !== null)\n if (lateDependencies.delete(check.dependency)) changed();\n },\n trace: tracer(\"poller\", check.name),\n }),\n };\n entries.set(check.name, entry);\n if (late && !stopped) {\n // a late registration pulls readiness until its first pass: it polls at once, then on its loop\n void entry.poller.pollNow();\n entry.poller.start(entry.poller.jittered());\n }\n };\n\n for (const check of description.checks) {\n const run = runs.get(check.name);\n if (run === undefined) throw new Error(`check ${check.name} has no function: the runtime has no defaults`);\n add(check, run, false);\n }\n\n const latchesOf = (gateOf: Gate, keep: (check: Check) => boolean = () => true): Latch[] =>\n [...entries.values()].flatMap((entry) =>\n entry.check.gates === gateOf && entry.latch !== null && keep(entry.check) ? [entry.latch] : [],\n );\n const released = (latches: readonly Latch[]): boolean => latches.every((each) => !each.tripped);\n /** This function tells if a policy fails through a declared coupling from another policy. */\n const coupledOff = (to: Gate, base: Record<Gate, boolean>): boolean =>\n description.couplings.some((coupling) => coupling.to === to && !base[coupling.from]);\n\n const verdict = (): HealthVerdict => {\n const base: Record<Gate, boolean> = {\n readiness: released(latchesOf(\"readiness\")) && lateDependencies.size === 0,\n liveness: released(latchesOf(\"liveness\")),\n };\n const alive = base.liveness && !coupledOff(\"liveness\", base);\n const open = started && !draining && !coupledOff(\"readiness\", base);\n const ready = open && base.readiness;\n const dependencyReady = (name: string): boolean =>\n !lateDependencies.has(name) &&\n released(latchesOf(\"readiness\", (check) => check.dependency === name));\n const own = released(latchesOf(\"readiness\", (check) => check.dependency === null));\n const dependenciesOut: Record<string, boolean> = {};\n for (const name of dependencies) dependenciesOut[name] = dependencyReady(name);\n const controllers: Record<string, boolean> = {};\n for (const controller of description.controllers)\n controllers[controller.name] = open && own && controller.requires.every(dependencyReady);\n const checks: Record<string, CheckVerdict> = {};\n for (const [name, entry] of entries) {\n checks[name] = {\n gates: entry.check.gates,\n dependency: entry.check.dependency,\n tripped: entry.latch?.tripped ?? null,\n streak: entry.latch?.streak ?? 0,\n since: entry.latch?.since ?? null,\n last: entry.poller.last,\n };\n }\n const elapsed = clock.now() - startedAt;\n return {\n started,\n draining,\n ready,\n alive,\n controllers,\n dependencies: dependenciesOut,\n checks,\n startup: { budget, elapsed, overBudget: !started && elapsed > budget },\n };\n };\n\n const answer = (kind: ProbeKind, pass: boolean, controller?: string): ProbeAnswer => ({\n status: pass || kind === \"report\" ? 200 : 503,\n contentType: \"application/health+json\",\n body: {\n status: pass ? \"pass\" : \"fail\",\n service: description.name,\n probe: kind,\n ...(controller === undefined ? {} : { controller }),\n ...(kind === \"report\" ? { verdict: verdict() } : {}),\n },\n });\n\n return {\n verdict,\n probe(kind, controller) {\n const now = verdict();\n if (kind === \"startup\") return answer(kind, now.started);\n if (kind === \"liveness\") return answer(kind, now.alive);\n if (kind === \"report\") return answer(kind, now.ready && now.alive);\n if (controller === undefined) return answer(kind, now.ready);\n const known = now.controllers[controller];\n if (known === undefined) throw new Error(`${description.name} has no controller ${controller}`);\n return answer(kind, known, controller);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n forceFail(name, on) {\n if (on) forced.add(name);\n else forced.delete(name);\n },\n drain() {\n if (draining) return;\n draining = true;\n changed();\n },\n stop() {\n stopped = true;\n budgetTimer?.();\n budgetTimer = null;\n for (const entry of entries.values()) {\n entry.poller.stop();\n entry.latch?.stop();\n }\n },\n beginStartup() {\n if (gate === \"closed\") moveGate(\"starting\", \"startup.begin\");\n },\n failStartup(cause) {\n if (gate === \"open\" || gate === \"failed\") return;\n moveGate(\"failed\", \"startup.fail\", cause);\n budgetTimer?.();\n budgetTimer = null;\n },\n async warmUp() {\n if (started || stopped || gate === \"failed\" || gate === \"sweeping\") return;\n moveGate(\"sweeping\", \"mark-ready\");\n // one bounded sweep of each check: each poll ends at its timeout at the latest\n const sweep = clock.now();\n await Promise.all([...entries.values()].map((entry) => entry.poller.pollNow()));\n if (stopped || gate !== \"sweeping\") return;\n for (const entry of entries.values())\n entry.poller.start(entry.poller.jittered() - (clock.now() - sweep));\n started = true;\n budgetTimer?.();\n budgetTimer = null;\n moveGate(\"open\", \"sweep.done\");\n changed();\n },\n addCheck(check, run) {\n if (entries.has(check.name)) throw new Error(`${description.name} already has a check named ${check.name}`);\n add(check, run, true);\n changed();\n },\n addDependency(name) {\n if (dependencies.has(name)) return;\n dependencies.add(name);\n lateDependencies.add(name);\n changed();\n },\n };\n};\n","/** An exit signal is a signal that asks the process to stop: each one starts the shutdown. */\nexport type ExitSignal = \"SIGINT\" | \"SIGTERM\" | \"SIGQUIT\";\n\n/** These are the signals that the runtime handles. */\nexport const EXIT_SIGNALS: readonly ExitSignal[] = [\"SIGINT\", \"SIGTERM\", \"SIGQUIT\"];\n\n/**\n * A process port is the part of the process that the runtime uses: its exit signals and its exit.\n * The runtime owns the handlers, because Node as PID 1 without a handler ignores SIGTERM. Only the\n * runtime ends the process, and library code does not. A test gives a fake port, sends signals,\n * and reads the exit code.\n */\nexport interface ProcessPort {\n /** This method adds a handler for a signal, and gives the function that removes it. */\n on(signal: ExitSignal, handler: () => void): () => void;\n /** This method ends the process with a code: 0 after a clean shutdown, and 1 after an error. */\n exit(code: number): void;\n}\n\ninterface NodeProcess {\n on(signal: string, handler: () => void): unknown;\n off(signal: string, handler: () => void): unknown;\n exit(code: number): never;\n}\n\n/** This port is the process of Node. */\nexport const nodeProcess: ProcessPort = {\n on(signal, handler) {\n const host = (globalThis as { process?: NodeProcess }).process;\n if (host === undefined) throw new Error(\"no process: give the runtime a process port\");\n host.on(signal, handler);\n return () => {\n host.off(signal, handler);\n };\n },\n exit(code) {\n const host = (globalThis as { process?: NodeProcess }).process;\n if (host === undefined) throw new Error(\"no process: give the runtime a process port\");\n host.exit(code);\n },\n};\n","import { quantity } from \"@mark1russell7/quantity\";\nimport type { Clock } from \"./clock.ts\";\nimport type { Controller } from \"./description.ts\";\nimport { mayReissue } from \"./operation.ts\";\n\n/**\n * A request outcome is what a request of a controller came to, for any HTTP framework or none:\n * - `done`: the work gave a value.\n * - `failed`: the work failed for a cause of its own. The adapter answers it as it answers any\n * error of the route.\n * - `refused`: the service did not admit the request. A refused request did not start, thus its\n * caller can send it again: 503, retryable.\n * - `cut`: the shutdown stopped the request. A route whose callers can send it again answers 503,\n * retryable. Each other route answers 500, not retryable, because the work can have had its\n * effect.\n */\nexport type RequestOutcome<T> =\n | { readonly kind: \"done\"; readonly value: T }\n | { readonly kind: \"failed\"; readonly error: unknown }\n | { readonly kind: \"refused\"; readonly status: 503; readonly retryable: true; readonly cause: string }\n | {\n readonly kind: \"cut\";\n readonly status: 503 | 500;\n readonly retryable: boolean;\n readonly cause: string;\n };\n\n/** A request function does the work of one request. It receives a signal that the shutdown aborts. */\nexport type RequestFn<T> = (signal: AbortSignal) => Promise<T> | T;\n\ninterface InFlight {\n readonly controller: string;\n readonly route: string;\n readonly started: number;\n readonly abort: AbortController;\n /** This function answers the request with an outcome, one time. */\n readonly answer: (outcome: RequestOutcome<never>) => void;\n}\n\n/** A tracker admits requests, counts the requests in flight, and answers what the shutdown cuts. */\nexport interface Tracker {\n request<T>(controller: string, route: string, work: RequestFn<T>): Promise<RequestOutcome<T>>;\n inFlight(controller?: string): number;\n /** This method refuses each later request. */\n stopIntake(): void;\n /** This method aborts the signal of each request in flight: the cooperative abort of the drain. */\n cooperativeAbort(): void;\n /** This method waits until no request is in flight, or until `deadline`. Then it cuts each request that is left. */\n drain(deadline: number): Promise<void>;\n}\n\n/**\n * This function makes a tracker. `refuses` gives the reason that the service refuses a new request\n * for a controller at this time, or null when it admits it.\n */\nexport const tracker = (\n controllers: readonly Controller[],\n clock: Clock,\n refuses: (controller: string) => string | null,\n): Tracker => {\n const byName = new Map(controllers.map((controller) => [controller.name, controller]));\n const flights = new Set<InFlight>();\n const idle = new Set<() => void>();\n let intake = true;\n\n const settle = (flight: InFlight): void => {\n flights.delete(flight);\n if (flights.size === 0) for (const done of [...idle]) done();\n };\n /** This function answers a request that the shutdown stopped, by the idempotency of its route. */\n const cut = (flight: InFlight, cause: string): RequestOutcome<never> => {\n const operation = byName.get(flight.controller)!.routes[flight.route]!;\n const since = quantity(Math.max(0, clock.now() - flight.started) / 1000, \"s\");\n const retryable = mayReissue(operation, since);\n return { kind: \"cut\", status: retryable ? 503 : 500, retryable, cause };\n };\n\n return {\n request<T>(controller: string, route: string, work: RequestFn<T>) {\n const held = byName.get(controller);\n if (held === undefined) throw new Error(`there is no controller ${controller}`);\n // a route that nobody declared has no idempotency: the runtime has no defaults\n if (held.routes[route] === undefined)\n throw new Error(`controller ${controller} does not declare the route ${route}`);\n const refusal = intake ? refuses(controller) : \"the service stopped its intake\";\n if (refusal !== null)\n return Promise.resolve({ kind: \"refused\", status: 503, retryable: true, cause: refusal });\n return new Promise<RequestOutcome<T>>((resolve) => {\n let answered = false;\n const flight: InFlight = {\n controller,\n route,\n started: clock.now(),\n abort: new AbortController(),\n answer: (outcome) => {\n if (answered) return;\n answered = true;\n settle(flight);\n resolve(outcome);\n },\n };\n flights.add(flight);\n let promise: Promise<T>;\n try {\n promise = Promise.resolve(work(flight.abort.signal));\n } catch (error) {\n promise = Promise.reject(error);\n }\n promise.then(\n (value) => {\n if (answered) return;\n answered = true;\n settle(flight);\n resolve({ kind: \"done\", value });\n },\n (error: unknown) => {\n if (answered) return;\n // a failure after the shutdown aborted the request is a failure that the shutdown caused\n const outcome = flight.abort.signal.aborted\n ? cut(flight, \"the shutdown aborted it, and it failed\")\n : ({ kind: \"failed\", error } as const);\n answered = true;\n settle(flight);\n resolve(outcome);\n },\n );\n });\n },\n inFlight(controller) {\n let count = 0;\n for (const flight of flights) if (controller === undefined || flight.controller === controller) count += 1;\n return count;\n },\n stopIntake() {\n intake = false;\n },\n cooperativeAbort() {\n for (const flight of flights) flight.abort.abort(new Error(\"the cooperative abort of the drain\"));\n },\n drain(deadline) {\n if (flights.size === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n let finished = false;\n const done = (): void => {\n if (finished) return;\n finished = true;\n idle.delete(done);\n cancel();\n resolve();\n };\n idle.add(done);\n const cancel = clock.after(Math.max(0, deadline - clock.now()), () => {\n for (const flight of [...flights]) {\n flight.abort.abort(new Error(\"the drain reached its deadline\"));\n flight.answer(cut(flight, \"the drain reached its deadline\"));\n }\n done();\n });\n });\n },\n };\n};\n","import type { Extent } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Phase, ServiceDescription } from \"./description.ts\";\nimport type { MachineName } from \"./events.ts\";\nimport type { Health, Runtime, StartOptions } from \"./health.ts\";\nimport type { ExitSignal, ProcessPort } from \"./process.ts\";\nimport { EXIT_SIGNALS, nodeProcess } from \"./process.ts\";\nimport type { RequestFn, RequestOutcome } from \"./requests.ts\";\nimport { tracker } from \"./requests.ts\";\n\n/**\n * A transport is a source of work: a server, a consumer or a worker. Each has one contract. It\n * starts, stops its intake, and drains to a deadline without a throw. It counts its work in flight,\n * and it closes. A second close has no effect.\n */\nexport interface TransportImpl {\n start(signal: AbortSignal): Promise<void> | void;\n stopIntake(): Promise<void> | void;\n /**\n * This method lets the work in flight end until `deadline` (milliseconds on the monotonic\n * clock). The signal is the cooperative abort, which comes before the deadline.\n */\n drain(deadline: number, signal: AbortSignal): Promise<void> | void;\n inFlight(): number;\n close(): Promise<void> | void;\n}\n\n/** A dependency handle connects to a dependency at the start, and closes the connection at the end. */\nexport interface DependencyHandle {\n connect(signal: AbortSignal): Promise<void> | void;\n close(): Promise<void> | void;\n}\n\n/**\n * A health server answers the probes, on its own port. It starts first, thus the probes answer\n * while the dependencies connect, and it closes last.\n */\nexport interface HealthServer {\n listen(health: Health): Promise<void> | void;\n close(): Promise<void> | void;\n}\n\n/** A phase context is what a phase gives its work: its deadline, its signal and the clock. */\nexport interface PhaseContext {\n /** This time is the end of the cap of the phase, in milliseconds on the monotonic clock. */\n readonly deadline: number;\n /** This signal aborts at the end of the cap of the phase. */\n readonly signal: AbortSignal;\n readonly clock: Clock;\n}\n\n/** A phase work is a step of the shutdown that an owner adds to a phase. */\nexport type PhaseWork = (context: PhaseContext) => Promise<void> | void;\n\n/**\n * These are the stages of the shutdown, in their sequence. A declared phase with one of these\n * names gets the work of the runtime for its stage. A phase with another name gets only the work of\n * its owners.\n * - `fail readiness`: readiness fails at the start of the shutdown. This phase then holds for its\n * cap, while the endpoints of the platform remove the service.\n * - `stop intake`: each transport stops its intake, and new requests get a retryable 503.\n * - `drain`: the transports and the requests in flight end, until the cap. The cooperative abort\n * comes before the end of the cap, by the cooperative window.\n * - `close transports`: each transport closes.\n * - `stop the poller`: each check and watchdog stops. Then the signal of the service aborts.\n * - `tear down dependencies`: each dependency closes, in the reverse sequence of its connection.\n * - `close the health server`: the probes stop last.\n */\nexport const STAGES = [\n \"fail readiness\",\n \"stop intake\",\n \"drain\",\n \"close transports\",\n \"stop the poller\",\n \"tear down dependencies\",\n \"close the health server\",\n] as const;\n\nexport type Stage = (typeof STAGES)[number];\n\n/** This function gives the place of a stage in the sequence, or -1 for a phase of another name. */\nexport const stageRank = (name: string): number => (STAGES as readonly string[]).indexOf(name);\n\n/** The signal of the service aborts after the poller stops, and before the dependencies close. */\nconst ABORT_AFTER = stageRank(\"stop the poller\");\n\n/**\n * This function gives the time of the cooperative abort, in milliseconds after the drain starts. It\n * is the cap less the cooperative window, and not less than half the cap. A window without a bound\n * gives half the cap.\n */\nexport const cooperativeAbortAt = (cap: number, window: Extent): number => {\n const before = window.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(window, \"ms\");\n return Math.max(cap - before, cap / 2);\n};\n\n/** These options run the lifecycle. Without them, it uses the real clock and the process of Node. */\nexport interface RunOptions extends StartOptions {\n readonly process?: ProcessPort | undefined;\n}\n\n/** A lifecycle state tells where the service is in its life. */\nexport type LifecycleState = \"starting\" | \"running\" | \"draining\" | \"exited\";\n\n/** The lifecycle of a running service. */\nexport interface Lifecycle {\n readonly health: Health;\n readonly state: LifecycleState;\n /** This code is the exit code, or null before the exit. */\n readonly exitCode: number | null;\n /** These texts are the errors that make the exit code 1. */\n readonly errors: readonly string[];\n /** This promise ends when the startup ends: the service runs, or its startup failed. */\n readonly started: Promise<void>;\n /** This promise gives the exit code when the shutdown ends. */\n readonly done: Promise<number>;\n /** This signal aborts after the poller stops: the background work of the service stops with it. */\n readonly signal: AbortSignal;\n /** This method asks for the shutdown, as an exit signal does. A second request has no effect. */\n shutdown(cause?: string): Promise<number>;\n /**\n * This method does one request of a controller, on a route that the controller declares. The\n * service refuses it while it cannot take new work, and the shutdown can cut it.\n */\n request<T>(controller: string, route: string, work: RequestFn<T>): Promise<RequestOutcome<T>>;\n inFlight(controller?: string): number;\n /**\n * This method tells if a response can keep its connection open. It is false while the service is\n * not ready, thus each response then closes its connection.\n */\n keepAlive(): boolean;\n}\n\n/** The parts that the builder gives the lifecycle, beside the declaration. */\nexport interface Components {\n readonly transports: ReadonlyMap<string, TransportImpl>;\n readonly dependencies: ReadonlyMap<string, DependencyHandle>;\n readonly healthServer: HealthServer | null;\n readonly phaseWork: ReadonlyMap<string, readonly PhaseWork[]>;\n /** This function stops the parts that the poller stage stops beside the checks: the watchdogs. */\n readonly stopPollers: () => void;\n}\n\nconst message = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\n/**\n * This function gives the problems of a declaration that the lifecycle cannot start: a stage with\n * work and no declared phase. The runtime has no defaults, thus a transport without a drain phase\n * has no time to drain.\n */\nexport const lifecycleProblems = (description: ServiceDescription, components: Components): string[] => {\n const declared = new Set(description.shutdown.phases.map((phase) => phase.name));\n const needs = (stage: Stage, why: string): string[] =>\n declared.has(stage) ? [] : [`${description.name} has ${why}, thus it declares the phase \"${stage}\"`];\n const problems: string[] = [];\n for (const transport of description.transports)\n if (!components.transports.has(transport.name))\n problems.push(`transport ${transport.name} has no implementation: the runtime has no defaults`);\n if (components.transports.size > 0 || description.controllers.length > 0) {\n problems.push(...needs(\"stop intake\", \"transports or controllers\"));\n problems.push(...needs(\"drain\", \"transports or controllers\"));\n }\n if (components.transports.size > 0) problems.push(...needs(\"close transports\", \"transports\"));\n if (description.checks.length > 0) problems.push(...needs(\"stop the poller\", \"checks\"));\n if (components.dependencies.size > 0) problems.push(...needs(\"tear down dependencies\", \"dependency handles\"));\n if (components.healthServer !== null) problems.push(...needs(\"close the health server\", \"a health server\"));\n return problems;\n};\n\n/**\n * This function starts the lifecycle of a service. The startup has a sequence: the health server\n * first, then each dependency, then each transport, and the startup gate last. A signal during the\n * startup gets its shutdown after the startup ends. The shutdown does the declared phases in their\n * sequence, each one capped. Only the lifecycle ends the process: 0 after a clean shutdown, and 1\n * after an error.\n */\nexport const lifecycle = (\n description: ServiceDescription,\n runtime: Runtime,\n components: Components,\n options: RunOptions = {},\n): Lifecycle => {\n const clock = options.clock ?? realClock;\n const port = options.process ?? nodeProcess;\n const record = (machine: MachineName, from: string, to: string, on: string, detail?: string): void =>\n options.record?.({\n at: clock.now(),\n machine,\n instance: description.name,\n from,\n to,\n on,\n ...(detail === undefined ? {} : { detail }),\n });\n const service = new AbortController();\n const errors: string[] = [];\n let state: LifecycleState = \"starting\";\n let exitCode: number | null = null;\n let intakeStopped = false;\n let shutdownStarted = false;\n let failedStartup = false;\n // the exit manager: running, stopping, exited\n let exitState = \"running\";\n const moveExit = (to: string, on: string, detail?: string): void => {\n record(\"exit\", exitState, to, on, detail);\n exitState = to;\n };\n const fail = (cause: string): void => {\n errors.push(cause);\n record(\"exit\", exitState, exitState, \"error\", cause);\n };\n\n const requests = tracker(description.controllers, clock, (controller) => {\n if (state === \"starting\") return \"the service has not started\";\n if (state === \"exited\") return \"the service has exited\";\n // while it drains and before its intake stops, the service still serves: the platform can\n // still route a request to it until its endpoints update\n if (state === \"draining\") return null;\n return runtime.verdict().controllers[controller] === true ? null : `controller ${controller} is not ready`;\n });\n\n let finish: (code: number) => void = () => {};\n const done = new Promise<number>((resolve) => {\n finish = resolve;\n });\n // what started, thus what the shutdown stops\n let healthListening = false;\n const connected: Array<[string, DependencyHandle]> = [];\n const startedTransports: Array<[string, TransportImpl]> = [];\n\n const offs = EXIT_SIGNALS.map((signal: ExitSignal) =>\n port.on(signal, () => {\n void shutdown(signal);\n }),\n );\n\n const startup = (async (): Promise<void> => {\n runtime.beginStartup();\n const started = (what: string): void => record(\"gate\", \"starting\", \"starting\", \"component.started\", what);\n try {\n if (components.healthServer !== null) {\n await components.healthServer.listen(runtime);\n healthListening = true;\n started(\"the health server\");\n }\n for (const dependency of description.dependencies) {\n const handle = components.dependencies.get(dependency.name);\n if (handle === undefined) continue;\n await handle.connect(service.signal);\n connected.push([dependency.name, handle]);\n started(`dependency ${dependency.name}`);\n }\n for (const transport of description.transports) {\n const impl = components.transports.get(transport.name)!;\n await impl.start(service.signal);\n startedTransports.push([transport.name, impl]);\n started(`${transport.kind} ${transport.name}`);\n }\n await runtime.warmUp();\n if (state === \"starting\") state = \"running\";\n } catch (error) {\n const cause = `the startup failed: ${message(error)}`;\n failedStartup = true;\n runtime.failStartup(cause);\n fail(cause);\n if (exitState === \"running\") moveExit(\"stopping\", \"startup.fail\", cause);\n else record(\"exit\", exitState, exitState, \"startup.fail\", cause);\n }\n })();\n // a failed startup shuts down what started. An exit request during the startup waits for it\n void startup.then(() => {\n if (failedStartup && !shutdownStarted) void shutdown(\"the startup failed\");\n });\n\n /** This function does the work of one phase, until its cap. It gives true when the work ended in time. */\n const runPhase = async (phase: Phase, hold: boolean, work: ReadonlyArray<(context: PhaseContext) => Promise<void> | void>): Promise<boolean> => {\n const cap = valueIn(phase.cap, \"ms\");\n const deadline = clock.now() + cap;\n const capped = new AbortController();\n const context: PhaseContext = { deadline, signal: capped.signal, clock };\n let pending = work.length;\n const all = Promise.all(\n work.map(async (each) => {\n try {\n await each(context);\n } catch (error) {\n fail(`the phase ${phase.name} failed: ${message(error)}`);\n } finally {\n pending -= 1;\n }\n }),\n );\n let cancelCap: Cancel = () => {};\n const end = new Promise<\"cap\">((resolve) => {\n cancelCap = clock.after(cap, () => resolve(\"cap\"));\n });\n const first = await Promise.race([all.then(() => \"work\" as const), end]);\n if (first === \"work\" && hold) await end;\n cancelCap();\n capped.abort(new Error(`the phase ${phase.name} reached its cap`));\n return pending === 0;\n };\n\n async function shutdown(cause = \"the service asked for it\"): Promise<number> {\n if (shutdownStarted || exitState === \"exited\") {\n if (exitState === \"stopping\") record(\"exit\", exitState, exitState, \"exit.repeat\", cause);\n return done;\n }\n shutdownStarted = true;\n if (exitState === \"running\") moveExit(\"stopping\", \"exit.request\", cause);\n // a signal during the startup gets its shutdown after the startup ends\n await startup;\n state = \"draining\";\n runtime.drain();\n let drainState = \"running\";\n const moveDrain = (to: string, on: string, detail?: string): void => {\n record(\"drain\", drainState, to, on, detail);\n drainState = to;\n };\n const abortService = (): void => {\n if (service.signal.aborted) return;\n service.abort(new Error(\"the service stopped\"));\n record(\"drain\", drainState, drainState, \"signal.abort\");\n };\n const phases = description.shutdown.phases;\n let on = \"exit.request\";\n for (const phase of phases) {\n const rank = stageRank(phase.name);\n moveDrain(phase.name, on);\n if (rank > ABORT_AFTER) abortService();\n const work: Array<(context: PhaseContext) => Promise<void> | void> = [];\n if (phase.name === \"stop intake\") {\n work.push(() => {\n intakeStopped = true;\n requests.stopIntake();\n });\n for (const [, impl] of startedTransports) work.push(() => impl.stopIntake());\n }\n // the cooperative abort of the drain: the cap less the window, and half the cap at the least.\n // It ends with the phase, thus a drain that ends before it does not abort anything\n let cancelAbort: Cancel = () => {};\n if (phase.name === \"drain\") {\n const cap = valueIn(phase.cap, \"ms\");\n const cooperative = new AbortController();\n const fireAt = cooperativeAbortAt(cap, description.shutdown.cooperativeWindow);\n cancelAbort = clock.after(fireAt, () => {\n cooperative.abort(new Error(\"the cooperative abort of the drain\"));\n requests.cooperativeAbort();\n record(\"drain\", drainState, drainState, \"drain.cooperative-abort\");\n });\n work.push(({ deadline }) => requests.drain(deadline));\n for (const [, impl] of startedTransports)\n work.push(({ deadline }) => impl.drain(deadline, cooperative.signal));\n }\n if (phase.name === \"close transports\")\n for (const [, impl] of startedTransports) work.push(() => impl.close());\n if (phase.name === \"stop the poller\")\n work.push(() => {\n runtime.stop();\n components.stopPollers();\n });\n if (phase.name === \"tear down dependencies\")\n work.push(async () => {\n for (const [, handle] of [...connected].reverse()) await handle.close();\n });\n if (phase.name === \"close the health server\" && healthListening && components.healthServer !== null) {\n const server = components.healthServer;\n work.push(() => server.close());\n }\n for (const owned of components.phaseWork.get(phase.name) ?? []) work.push(owned);\n const inTime = await runPhase(phase, phase.name === \"fail readiness\", work);\n cancelAbort();\n if (!inTime) fail(`the phase ${phase.name} did not end in its cap of ${valueIn(phase.cap, \"s\")} s`);\n on = inTime ? \"phase.done\" : \"phase.cap\";\n }\n moveDrain(\"done\", on);\n abortService();\n // a stage that the declaration does not name still stops: the checks, at the end\n runtime.stop();\n components.stopPollers();\n if (!intakeStopped) requests.stopIntake();\n for (const off of offs) off();\n state = \"exited\";\n exitCode = errors.length > 0 ? 1 : 0;\n moveExit(\"exited\", \"shutdown.done\", `exit ${exitCode}`);\n finish(exitCode);\n port.exit(exitCode);\n return exitCode;\n }\n\n return {\n health: runtime,\n get state() {\n return state;\n },\n get exitCode() {\n return exitCode;\n },\n get errors() {\n return [...errors];\n },\n started: startup,\n done,\n signal: service.signal,\n shutdown,\n request: (controller, route, work) => requests.request(controller, route, work),\n inFlight: (controller) => requests.inFlight(controller),\n keepAlive: () => state === \"running\" && runtime.verdict().ready,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { Gate } from \"./description.ts\";\nimport type { Outcome, Trace } from \"./latch.ts\";\nimport type { Poll } from \"./timing.ts\";\n\n/**\n * A watchdog spec declares a watchdog of the event loop. The service declares it as a check whose\n * function the watchdog gives: the process's own wedge, which a liveness window reads.\n */\nexport interface WatchdogSpec {\n readonly name: string;\n /** This time is the interval of the tick of the watchdog. */\n readonly tick: Quantity;\n /** This time is the most that a tick can be late before the loop counts as blocked. */\n readonly threshold: Quantity;\n /** This gate is the policy that the check controls, or null for a check that only reports. */\n readonly gates: Gate | null;\n readonly poll: Poll;\n}\n\n/**\n * A watchdog measures how late its own tick comes. A blocked event loop delays each timer, thus a\n * tick that comes later than the threshold shows the block. Its states are `watching`, `blocked`\n * and `stopped`.\n */\nexport interface Watchdog {\n /** This function is the check: it fails when a tick since the last poll came late, or the loop is still blocked. */\n readonly check: () => Outcome;\n /** This time is the most that a tick was late, in milliseconds, since the start. */\n readonly worst: number;\n readonly blocked: boolean;\n stop(): void;\n}\n\n/** This function starts a watchdog on a clock. */\nexport const watchdog = (clock: Clock, spec: WatchdogSpec, trace?: Trace): Watchdog => {\n const tick = valueIn(spec.tick, \"ms\");\n const threshold = valueIn(spec.threshold, \"ms\");\n if (!(tick > 0)) throw new RangeError(`watchdog ${spec.name} ticks every ${tick} ms, not above zero`);\n let blocked = false;\n let stopped = false;\n let worst = 0;\n // the worst lateness since the last poll of the check\n let sincePoll = 0;\n let next: Cancel | null = null;\n\n const named = (): string => (stopped ? \"stopped\" : blocked ? \"blocked\" : \"watching\");\n const arm = (): void => {\n const due = clock.now() + tick;\n next = clock.after(tick, () => {\n next = null;\n if (stopped) return;\n const late = Math.max(0, clock.now() - due);\n worst = Math.max(worst, late);\n sincePoll = Math.max(sincePoll, late);\n const from = named();\n blocked = late > threshold;\n trace?.(from, named(), \"tick\");\n arm();\n });\n };\n arm();\n\n return {\n check: () => {\n const failed = blocked || sincePoll > threshold;\n sincePoll = 0;\n return failed ? \"error\" : \"ok\";\n },\n get worst() {\n return worst;\n },\n get blocked() {\n return blocked;\n },\n stop() {\n if (stopped) return;\n const from = named();\n stopped = true;\n next?.();\n next = null;\n trace?.(from, \"stopped\", \"stop\");\n },\n };\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { convertible, valueIn } from \"@mark1russell7/quantity\";\nimport type {\n Check,\n Controller,\n Coupling,\n Dependency,\n Gate,\n Phase,\n ServiceDescription,\n Transport,\n} from \"./description.ts\";\nimport { FORMAT } from \"./description.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { Health, Runtime, StartOptions } from \"./health.ts\";\nimport { healthRuntime } from \"./health.ts\";\nimport type {\n Components,\n DependencyHandle,\n HealthServer,\n Lifecycle,\n PhaseWork,\n RunOptions,\n TransportImpl,\n} from \"./lifecycle.ts\";\nimport { STAGES, lifecycle, lifecycleProblems, stageRank } from \"./lifecycle.ts\";\nimport type { Operation } from \"./operation.ts\";\nimport { operationProblems } from \"./operation.ts\";\nimport type { CheckFn } from \"./poller.ts\";\nimport type { Timing } from \"./timing.ts\";\nimport type { Watchdog, WatchdogSpec } from \"./watchdog.ts\";\nimport { watchdog } from \"./watchdog.ts\";\n\n/**\n * A service is composed with plain method uses before it starts. The builder is imperative, and\n * it holds static knowledge. The service does not use an `init` function that gives registrations.\n *\n * const svc = service(\"app\");\n * svc.dependency(\"weather\", {\n * system: null,\n * knobs: { request_timeout },\n * operations: { \"GET /v1/forecast\": { idempotent: true } },\n * });\n * svc.controller(\"forecast\").requires(\"weather\").route(\"GET /api/forecast\", { idempotent: true });\n * svc.check({ name: \"weather.round-trip\", dependency: \"weather\", … }, roundTrip);\n * svc.describe(); // the whole graph as data, with nothing started\n * const health = await svc.start();\n *\n * The graph has one source, thus its parts cannot disagree. A dependency or a check can join\n * after the start. It starts tripped, and the export is partial. The runtime has no defaults of\n * its own. Thus `describe()` refuses a service that does not declare a policy, its startup budget\n * or its shutdown, and it tells which.\n *\n * `start()` starts only the health runtime. `run()` starts the whole lifecycle: the startup in its\n * sequence, the exit signals, the capped phases of the shutdown, and the exit of the process.\n */\nexport interface ControllerBuilder {\n /** This method declares the dependencies that its routes need. The controller is ready only while they are ready. */\n requires(...dependencies: string[]): ControllerBuilder;\n /** This method declares a route that the controller serves, and if its callers can send it again. */\n route(route: string, operation: Operation): ControllerBuilder;\n}\n\n/** A service builder composes a service, gives it as data, and starts it. */\nexport interface ServiceBuilder {\n readonly name: string;\n /** This method declares a dependency, and the handle that connects to it and closes it, if it has one. */\n dependency(name: string, spec: Omit<Dependency, \"name\">, handle?: DependencyHandle): ServiceBuilder;\n /** This method declares a server, and its implementation. `run()` needs the implementation. */\n server(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n consumer(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n worker(name: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl): ServiceBuilder;\n /** This method gives the server of the probes. It starts first and closes last. */\n healthServer(server: HealthServer): ServiceBuilder;\n /** This method adds work to a declared phase of the shutdown: the owner of the work registers it. */\n onPhase(phase: string, work: PhaseWork): ServiceBuilder;\n /** This method declares a watchdog of the event loop, as a check on the state of the process. */\n watchdog(spec: WatchdogSpec): ServiceBuilder;\n controller(name: string): ControllerBuilder;\n /** This method declares a check, and the function that measures its step. `start()` needs the function. */\n check(check: Check, run?: CheckFn): ServiceBuilder;\n readiness(timing: Timing): ServiceBuilder;\n liveness(timing: Timing): ServiceBuilder;\n startup(budget: Quantity): ServiceBuilder;\n couple(coupling: Coupling): ServiceBuilder;\n phase(name: string, cap: Quantity): ServiceBuilder;\n cooperativeWindow(window: Extent): ServiceBuilder;\n /** This method gives the service as data, and it starts nothing. */\n describe(): ServiceDescription;\n /**\n * This method closes the composition, starts the health runtime, and gives it after one warm-up\n * sweep of each check. A part that joins after it starts tripped, and it makes the export\n * partial. A second use gives the same runtime.\n */\n start(options?: StartOptions): Promise<Health>;\n /**\n * This method starts the whole lifecycle and gives it when its startup ends: the service operates,\n * or its startup failed and it shuts down. It refuses a declaration that it cannot start: a transport\n * without its implementation, or a stage with work and no declared phase.\n */\n run(options?: RunOptions): Promise<Lifecycle>;\n /**\n * This value is the health runtime from the time that `start()` begins, or null before. Thus the\n * probes can answer during the warm-up sweep: the startup probe fails until the sweep ends.\n */\n readonly health: Health | null;\n}\n\nconst isTime = (amount: Extent): boolean => convertible(amount.unit, \"s\");\nconst above = (amount: Extent): boolean =>\n amount.value === \"infinite\" || (isTime(amount) && valueIn(amount as Quantity, \"s\") > 0);\nconst whole = (count: number): boolean => Number.isInteger(count) && count >= 1;\n\n/** This function gives the problems of a timing: a zero interval or count fails decoding. */\nconst timingProblems = (policy: Gate, timing: Timing): string[] => {\n if (timing.type === \"debounce\") {\n const problems: string[] = [];\n if (!above(timing.interval)) problems.push(`the ${policy} debounce polls at an interval that is not above zero`);\n if (!whole(timing.failAfter)) problems.push(`the ${policy} debounce fails after ${timing.failAfter} polls, not a whole number above zero`);\n if (!whole(timing.recoverAfter)) problems.push(`the ${policy} debounce recovers after ${timing.recoverAfter} polls, not a whole number above zero`);\n return problems;\n }\n const negative = (amount: Quantity | undefined): boolean =>\n amount !== undefined && (!isTime(amount) || valueIn(amount, \"s\") < 0);\n return negative(timing.failAfter) || negative(timing.recoverAfter)\n ? [`the ${policy} window is not a time of zero or more`]\n : [];\n};\n\n/** This function starts the composition of a service. */\nexport const service = (name: string): ServiceBuilder => {\n if (name.trim().length === 0) throw new Error(\"a service needs a name\");\n const transports: Transport[] = [];\n const dependencies: Dependency[] = [];\n const controllers: Array<{\n name: string;\n requires: string[];\n routes: Record<string, Operation>;\n }> = [];\n const checks: Check[] = [];\n const runs = new Map<string, CheckFn>();\n const couplings: Coupling[] = [];\n const phases: Phase[] = [];\n let readiness: Timing | undefined;\n let liveness: Timing | undefined;\n let startup: Quantity | undefined;\n let cooperative: Extent | undefined;\n let started = false;\n let partial = false;\n let runtime: Promise<Health> | undefined;\n let live: Runtime | undefined;\n let running: Promise<Lifecycle> | undefined;\n const transportImpls = new Map<string, TransportImpl>();\n const handles = new Map<string, DependencyHandle>();\n const phaseWork = new Map<string, PhaseWork[]>();\n const watchdogs: WatchdogSpec[] = [];\n let probes: HealthServer | null = null;\n\n /** This function stops a change to the shape of the service after the start. */\n const composing = (what: string): void => {\n if (started) throw new Error(`${name} has started: ${what} is declared before svc.start()`);\n };\n const once = (list: ReadonlyArray<{ name: string }>, entry: string, what: string): void => {\n if (list.some((held) => held.name === entry))\n throw new Error(`${name} already has a ${what} named ${entry}`);\n };\n const transport =\n (kind: Transport[\"kind\"]) =>\n (entry: string, knobs: Readonly<Record<string, Extent>>, impl?: TransportImpl) => {\n composing(`a ${kind}`);\n once(transports, entry, \"transport\");\n transports.push({ name: entry, kind, knobs: { ...knobs } });\n if (impl !== undefined) transportImpls.set(entry, impl);\n return builder;\n };\n\n /** This function makes the runtime of the declaration: its checks, and a watchdog for each spec. */\n const makeRuntime = (\n options: StartOptions,\n ): { runtime: Runtime; description: ServiceDescription; stopPollers: () => void } => {\n const description = builder.describe();\n const clock = options.clock ?? realClock;\n const dogs: Watchdog[] = [];\n const all = new Map(runs);\n for (const spec of watchdogs) {\n const dog = watchdog(clock, spec, (from, to, on) =>\n options.record?.({ at: clock.now(), machine: \"watchdog\", instance: spec.name, from, to, on }),\n );\n dogs.push(dog);\n all.set(spec.name, () => dog.check());\n }\n const stopPollers = (): void => {\n for (const dog of dogs) dog.stop();\n };\n let made: Runtime;\n try {\n made = healthRuntime(description, all, options);\n } catch (error) {\n stopPollers();\n throw error;\n }\n // the stop of the health runtime also stops the watchdogs\n const runtime: Runtime = {\n ...made,\n stop: () => {\n made.stop();\n stopPollers();\n },\n };\n return { runtime, description, stopPollers };\n };\n\n const builder: ServiceBuilder = {\n name,\n get health() {\n return live ?? null;\n },\n dependency(entry, spec, handle) {\n once(dependencies, entry, \"dependency\");\n // a dependency that joins after the start has no connection that the lifecycle opens\n if (handle !== undefined && !started) handles.set(entry, handle);\n dependencies.push({\n name: entry,\n ...spec,\n knobs: { ...spec.knobs },\n operations: { ...spec.operations },\n });\n if (started) {\n partial = true;\n live?.addDependency(entry);\n }\n return builder;\n },\n server: transport(\"server\"),\n consumer: transport(\"consumer\"),\n worker: transport(\"worker\"),\n controller(entry) {\n composing(\"a controller\");\n once(controllers, entry, \"controller\");\n const held = {\n name: entry,\n requires: [] as string[],\n routes: {} as Record<string, Operation>,\n };\n controllers.push(held);\n const handle: ControllerBuilder = {\n requires(...needed) {\n composing(`what ${entry} requires`);\n held.requires.push(...needed);\n return handle;\n },\n route(served, operation) {\n composing(`${entry}'s routes`);\n if (held.routes[served] !== undefined)\n throw new Error(`${entry} already serves ${served}`);\n held.routes[served] = { ...operation };\n return handle;\n },\n };\n return handle;\n },\n check(check, run) {\n once(checks, check.name, \"check\");\n if (started && run === undefined)\n throw new Error(`check ${check.name} joins after the start, thus it needs its function`);\n checks.push({ ...check });\n if (run !== undefined) runs.set(check.name, run);\n if (started) {\n partial = true;\n live?.addCheck({ ...check, poll: { ...check.poll } }, run!);\n }\n return builder;\n },\n healthServer(server) {\n composing(\"the health server\");\n if (probes !== null) throw new Error(`${name} already has a health server`);\n probes = server;\n return builder;\n },\n onPhase(phase, work) {\n composing(`work for the phase ${phase}`);\n phaseWork.set(phase, [...(phaseWork.get(phase) ?? []), work]);\n return builder;\n },\n watchdog(spec) {\n composing(`the watchdog ${spec.name}`);\n once(checks, spec.name, \"check\");\n // the watchdog is a check on the state of the process itself, on its own connection\n checks.push({\n name: spec.name,\n dependency: null,\n step: \"local-state\",\n ownConnection: true,\n gates: spec.gates,\n poll: { ...spec.poll },\n });\n watchdogs.push(spec);\n return builder;\n },\n readiness(timing) {\n composing(\"readiness\");\n readiness = timing;\n return builder;\n },\n liveness(timing) {\n composing(\"liveness\");\n liveness = timing;\n return builder;\n },\n startup(budget) {\n composing(\"the startup budget\");\n startup = budget;\n return builder;\n },\n couple(coupling) {\n composing(\"a coupling\");\n couplings.push({ ...coupling });\n return builder;\n },\n phase(entry, cap) {\n composing(\"a shutdown phase\");\n once(phases, entry, \"shutdown phase\");\n phases.push({ name: entry, cap });\n return builder;\n },\n cooperativeWindow(window) {\n composing(\"the cooperative window\");\n cooperative = window;\n return builder;\n },\n describe() {\n const missing: string[] = [];\n if (readiness === undefined) missing.push(\"readiness (svc.readiness)\");\n if (liveness === undefined) missing.push(\"liveness (svc.liveness)\");\n if (startup === undefined) missing.push(\"a startup budget (svc.startup)\");\n if (phases.length === 0) missing.push(\"its shutdown phases (svc.phase)\");\n if (cooperative === undefined)\n missing.push(\"a cooperative window, or no bound (svc.cooperativeWindow)\");\n if (missing.length > 0)\n throw new Error(\n `${name} does not declare ${missing.join(\"; \")}: the runtime has no defaults`,\n );\n const known = new Set(dependencies.map((entry) => entry.name));\n const problems: string[] = [];\n const policies: Record<Gate, Timing> = { readiness: readiness!, liveness: liveness! };\n for (const gate of [\"readiness\", \"liveness\"] as const)\n problems.push(...timingProblems(gate, policies[gate]));\n for (const check of checks) {\n if (check.dependency !== null && !known.has(check.dependency)) {\n problems.push(\n `check ${check.name} exercises ${check.dependency}, which is not a dependency`,\n );\n }\n if (!isTime(check.poll.interval) || !isTime(check.poll.timeout)) {\n problems.push(`check ${check.name} polls in a unit that is not a time`);\n } else if (!above(check.poll.interval) || !above(check.poll.timeout)) {\n problems.push(`check ${check.name} polls with an interval or a timeout that is not above zero`);\n }\n if (!(check.poll.jitter >= 0 && check.poll.jitter < 1))\n problems.push(`check ${check.name} has a jitter of ${check.poll.jitter}, not a fraction below 1`);\n // a debounce counts the polls of each check that it gates, thus they poll at its interval\n const timing = check.gates === null ? null : policies[check.gates];\n if (\n timing?.type === \"debounce\" &&\n isTime(check.poll.interval) &&\n above(timing.interval) &&\n valueIn(check.poll.interval, \"s\") !== valueIn(timing.interval, \"s\")\n )\n problems.push(\n `check ${check.name} polls every ${valueIn(check.poll.interval, \"s\")} s, but the ${check.gates} debounce counts polls every ${valueIn(timing.interval, \"s\")} s`,\n );\n }\n for (const controller of controllers) {\n for (const needed of controller.requires) {\n if (!known.has(needed))\n problems.push(\n `controller ${controller.name} requires ${needed}, which is not a dependency`,\n );\n }\n for (const [route, operation] of Object.entries(controller.routes))\n problems.push(...operationProblems(`controller ${controller.name}`, route, operation));\n }\n for (const dependency of dependencies)\n for (const [name, operation] of Object.entries(dependency.operations))\n problems.push(...operationProblems(`dependency ${dependency.name}`, name, operation));\n for (const phase of phases)\n if (!isTime(phase.cap)) problems.push(`phase ${phase.name}'s cap is not a time`);\n // the stages of the shutdown keep their sequence: readiness fails first, the health server closes last\n const ranked = phases.map((phase) => phase.name).filter((each) => stageRank(each) >= 0);\n for (let at = 1; at < ranked.length; at += 1)\n if (stageRank(ranked[at]!) < stageRank(ranked[at - 1]!))\n problems.push(\n `the phase \"${ranked[at]!}\" comes after \"${ranked[at - 1]!}\", but the stages go ${STAGES.join(\", \")}`,\n );\n if (problems.length > 0) throw new Error(`${name}: ${problems.join(\"; \")}`);\n const controllersOut: Controller[] = controllers.map((entry) => ({\n name: entry.name,\n requires: [...entry.requires],\n routes: { ...entry.routes },\n }));\n return {\n format: FORMAT,\n name,\n partial,\n transports: transports.map((entry) => ({ ...entry, knobs: { ...entry.knobs } })),\n dependencies: dependencies.map((entry) => ({\n ...entry,\n knobs: { ...entry.knobs },\n operations: { ...entry.operations },\n })),\n controllers: controllersOut,\n checks: checks.map((entry) => ({ ...entry, poll: { ...entry.poll } })),\n policies: { readiness: readiness!, liveness: liveness!, startup: startup! },\n couplings: couplings.map((entry) => ({ ...entry })),\n shutdown: {\n phases: phases.map((entry) => ({ ...entry })),\n cooperativeWindow: cooperative!,\n },\n };\n },\n start(options = {}) {\n // the start needs the whole graph: describe() is the check, and the runtime reads it\n if (runtime !== undefined) return runtime;\n if (running !== undefined) return Promise.reject(new Error(`${name} runs its lifecycle: use its health`));\n let made: Runtime;\n try {\n made = makeRuntime(options).runtime;\n } catch (error) {\n return Promise.reject(error);\n }\n started = true;\n live = made;\n runtime = made.warmUp().then(() => made);\n return runtime;\n },\n run(options = {}) {\n if (running !== undefined) return running;\n if (runtime !== undefined) return Promise.reject(new Error(`${name} started its health only: run() starts the whole lifecycle`));\n let life: Lifecycle;\n try {\n const description = builder.describe();\n const components: Components = {\n transports: transportImpls,\n dependencies: handles,\n healthServer: probes,\n phaseWork,\n stopPollers: () => {},\n };\n const problems = lifecycleProblems(description, components);\n if (problems.length > 0) throw new Error(`${name}: ${problems.join(\"; \")}`);\n const made = makeRuntime(options);\n started = true;\n live = made.runtime;\n life = lifecycle(made.description, made.runtime, { ...components, stopPollers: made.stopPollers }, options);\n } catch (error) {\n return Promise.reject(error);\n }\n running = life.started.then(() => life);\n return running;\n },\n };\n return builder;\n};\n","/**\n * This module is the evaluator of the timer-ordering law. Its decisions are the same as the\n * decisions of the research evaluator. The rules are data, and the caller gives them. Each rule\n * is one or more linear inequalities over symbols at a hop (`readiness_detection_window@pod`),\n * with a margin. A binding maps `symbol@hop` to seconds, or to \"inf\" for a timer that is absent.\n */\n\n/** A term is a coefficient times one symbol, at a hop when it has one. */\nexport interface Term {\n readonly knob: string;\n readonly coef: number;\n readonly at?: string | undefined;\n}\n\n/** A side is a sum of terms, plus a constant. */\nexport interface Side {\n readonly terms: readonly Term[];\n readonly const?: number | undefined;\n}\n\n/** An inequality compares two sides, with a margin. */\nexport interface Inequality {\n readonly lhs: Side;\n readonly op: \"<\" | \"<=\" | \">\" | \">=\";\n readonly rhs: Side;\n /** The margin is in seconds, or it is words for a margin that no binding gives (\"a factor of several\"). Words count as 0. */\n readonly margin?: number | string | undefined;\n}\n\n/** A rule is one or more inequalities, with their statement in words. */\nexport interface Rule {\n readonly id: string;\n /** This text is the rule in words. */\n readonly rule: string;\n /** This text is the window that a violation opens. */\n readonly window?: string | undefined;\n readonly expr: Inequality;\n readonly expr_more?: readonly Inequality[] | undefined;\n}\n\n/** A bound is in seconds, or it is \"inf\" for an absent timer, or \"off\" for a disabled feature. The last two have no limit. */\nexport type Bound = number | \"inf\" | \"off\";\n\n/**\n * A binding maps `symbol@hop` to its bound. Its \"margin\" replaces the margin of each inequality.\n * Its \"margins\" replace them one by one, in sequence, as the binding of a chain can.\n */\nexport type Binding = Readonly<Record<string, Bound | readonly number[] | undefined>> & {\n readonly margin?: number | undefined;\n readonly margins?: readonly number[] | undefined;\n};\n\n/** A verdict is \"holds\", \"violated\", \"unbounded\" (it holds only because a timer is absent) or \"depends\" (nothing is bound). */\nexport type Verdict = \"holds\" | \"violated\" | \"unbounded\" | \"depends\";\n\n/** An evaluation is the value of each side of one inequality, its margin, and if it holds. */\nexport interface Evaluated {\n readonly lhs: number | null;\n readonly rhs: number | null;\n readonly margin: number;\n readonly holds: boolean | null;\n}\n\nconst value = (bound: Bound): number =>\n bound === \"inf\" || bound === \"off\" ? Infinity : Number(bound);\nconst keyOf = (term: Term): string =>\n term.at !== undefined ? `${term.knob}@${term.at}` : term.knob;\n/** This function gives the bound of a symbol, or undefined when the symbol has no bound. */\nconst boundOf = (bind: Binding, key: string): Bound | undefined => {\n const bound = bind[key];\n return typeof bound === \"number\" || bound === \"inf\" || bound === \"off\" ? bound : undefined;\n};\n\n/** This function gives the inequalities of a rule: its expr, then the others. */\nexport const inequalities = (rule: Rule): readonly Inequality[] => [\n rule.expr,\n ...(rule.expr_more ?? []),\n];\n\n/** This function gives the symbols that a rule reads, as `symbol@hop`. */\nexport const symbolsOf = (rule: Rule): string[] => {\n const out: string[] = [];\n for (const inequality of inequalities(rule)) {\n for (const term of [...inequality.lhs.terms, ...inequality.rhs.terms]) {\n const key = keyOf(term);\n if (!out.includes(key)) out.push(key);\n }\n }\n return out;\n};\n\n/** This function gives the value of a side, Σ coef × symbol + const, or null when a symbol has no bound. */\nconst sideOf = (side: Side, bind: Binding): number | null => {\n let total = side.const ?? 0;\n for (const term of side.terms) {\n const bound = boundOf(bind, keyOf(term));\n if (bound === undefined) return null;\n total += term.coef * value(bound);\n }\n return total;\n};\n\n/** This function gives the margin of an inequality: from the binding (one for all, or one each), else from the rule, else 0. */\nconst marginOf = (inequality: Inequality, index: number, bind: Binding): number => {\n if (Array.isArray(bind.margins)) return Number(bind.margins[index] ?? 0);\n if (typeof bind.margin === \"number\") return bind.margin;\n return typeof inequality.margin === \"number\" ? inequality.margin : 0;\n};\n\n/**\n * This function evaluates each inequality on one binding. \"<\" holds when lhs + margin < rhs, and\n * \">\" holds when lhs > rhs + margin. Only the inequalities with all their symbols bound apply.\n * The others are null.\n */\nexport const evaluate = (rule: Rule, bind: Binding): Evaluated[] =>\n inequalities(rule).map((inequality, index) => {\n const lhs = sideOf(inequality.lhs, bind);\n const rhs = sideOf(inequality.rhs, bind);\n const margin = marginOf(inequality, index, bind);\n if (lhs === null || rhs === null) return { lhs, rhs, margin, holds: null };\n const holds =\n inequality.op === \"<\"\n ? lhs + margin < rhs\n : inequality.op === \"<=\"\n ? lhs + margin <= rhs\n : inequality.op === \">\"\n ? lhs > rhs + margin\n : lhs >= rhs + margin;\n return { lhs, rhs, margin, holds };\n });\n\n/**\n * This function gives the verdict of an evaluation. When an applicable inequality fails, the\n * verdict is \"violated\". When all hold only because a timer is absent, the verdict is \"unbounded\",\n * because nothing then limits the window. Otherwise the verdict is \"holds\". When no inequality\n * applies, the arithmetic cannot decide, and the verdict is \"depends\".\n */\nexport const verdictOf = (rule: Rule, bind: Binding, evaluated: readonly Evaluated[]): Verdict => {\n const applicable = evaluated.flatMap((entry, index) => (entry.holds === null ? [] : [index]));\n if (applicable.length === 0) return \"depends\";\n if (applicable.some((index) => evaluated[index]?.holds === false)) return \"violated\";\n const all = inequalities(rule);\n const infinite = applicable.some((index) => {\n const inequality = all[index]!;\n return [...inequality.lhs.terms, ...inequality.rhs.terms].some((term) => {\n const bound = boundOf(bind, keyOf(term));\n return bound !== undefined && value(bound) === Infinity;\n });\n });\n return infinite ? \"unbounded\" : \"holds\";\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { convertExact, rationals as r } from \"@mark1russell7/quantity\";\nimport type { Dependency, ServiceDescription } from \"./description.ts\";\nimport type { Binding, Bound, Evaluated, Rule, Verdict } from \"./law.ts\";\nimport { evaluate, symbolsOf, verdictOf } from \"./law.ts\";\nimport { failSpan } from \"./timing.ts\";\n\n/**\n * This module applies the ordering law to a declaration. The declaration binds the symbols of the\n * service, from its primitives: the polls of its checks, the windows of its policies, its drain.\n * The scenario binds the symbols of the platform (preStop, grace, the probes of the kubelet,\n * propagation) and of each dependency (its failover time, the idle close of its server). Thus the\n * service gives the inequalities, and the scenario gives the values. A rule about a client\n * applies one time for each dependency.\n */\n\n/** A scenario gives the values of the platform and of each dependency, by the `symbol@hop` of the law. */\nexport interface Scenario {\n /** These are the values of the platform: prestop_sleep@pod, termination_grace@pod, probe_period@pod, propagation_bound@cluster. */\n readonly platform: Readonly<Record<string, Extent>>;\n /**\n * These are the probe windows of the kubelet, period × failure threshold. They add to the\n * windows of the declaration. A probe reads the cached verdict, thus detection is the latch,\n * then the probe.\n */\n readonly probes: { readonly readiness: Quantity; readonly liveness: Quantity };\n /** These are the values of each dependency, by its name: failover_time@dependency, server_idle_close@server. */\n readonly dependencies?: Readonly<Record<string, Readonly<Record<string, Extent>>>> | undefined;\n}\n\n/** A finding is the verdict of one rule for one scope: the service, or the client of a dependency. */\nexport interface Finding {\n readonly rule: string;\n readonly scope: string;\n readonly verdict: Verdict;\n readonly statement: string;\n readonly window?: string | undefined;\n readonly evaluated: readonly Evaluated[];\n /** These are the symbols that have no bound. A \"depends\" verdict waits for them. */\n readonly unbound: readonly string[];\n}\n\n/** These are the identifiers of the rules that each declaration gets. */\nexport const SERVICE_RULES: readonly string[] = [\n \"O04\",\n \"O09\",\n \"O10\",\n \"O11\",\n \"O12\",\n \"O13\",\n \"O14\",\n \"O15\",\n \"O16\",\n \"O17\",\n \"O18\",\n \"O19\",\n \"O21\",\n \"O22\",\n \"O23\",\n \"O27\",\n];\n\n/** This function gives a finite amount in seconds. */\nconst secondsOf = (amount: Quantity): number =>\n r.toNumber(convertExact(r.fromNumber(amount.value), amount.unit, \"s\"));\n/** This function gives an amount, or no bound, as the law binds it. */\nconst boundOf = (amount: Extent): Bound =>\n amount.value === \"infinite\" ? \"inf\" : secondsOf(amount);\n/** This function gives the largest of some numbers of seconds, or undefined when there are none. */\nconst most = (values: readonly number[]): number | undefined =>\n values.length === 0 ? undefined : Math.max(...values);\n\n/** This function gives what the declaration tells about the service, as symbols of the law, and the source of each. */\nexport const serviceBindings = (\n svc: ServiceDescription,\n scenario: Scenario,\n): { readonly bind: Record<string, Bound>; readonly from: Record<string, string> } => {\n const bind: Record<string, Bound> = {};\n const from: Record<string, string> = {};\n const set = (symbol: string, bound: Bound | undefined, said: string): void => {\n if (bound === undefined) return;\n bind[symbol] = bound;\n from[symbol] = said;\n };\n const gating = svc.checks.filter((check) => check.gates !== null);\n const readinessChecks = gating.filter((check) => check.gates === \"readiness\");\n const livenessChecks = gating.filter((check) => check.gates === \"liveness\");\n set(\n \"check_poll_interval@service\",\n most(gating.map((check) => secondsOf(check.poll.interval))),\n \"the slowest gating check's interval\",\n );\n set(\n \"check_timeout@service\",\n most(gating.map((check) => secondsOf(check.poll.timeout))),\n \"the longest gating check's timeout\",\n );\n set(\n \"slowest_poll@service\",\n most(\n gating.map(\n (check) =>\n secondsOf(check.poll.timeout) + secondsOf(check.poll.interval) * (1 + check.poll.jitter),\n ),\n ),\n \"a check's timeout plus one jittered interval, at its slowest\",\n );\n // the window of a policy is the time that its latch takes to fail when its slowest check times out\n const windowOf = (checks: typeof gating, policy: \"readiness\" | \"liveness\"): number | undefined =>\n most(\n checks.map((check) => secondsOf(failSpan(svc.policies[policy], check.poll, \"timeout\").max)),\n );\n const readiness = windowOf(readinessChecks, \"readiness\");\n const liveness = windowOf(livenessChecks, \"liveness\");\n set(\"readiness_detection_window@service\", readiness, \"the readiness latch, failing by timeout\");\n if (readiness !== undefined) {\n set(\n \"readiness_detection_window@pod\",\n readiness + secondsOf(scenario.probes.readiness),\n \"the readiness latch, then the kubelet's probe\",\n );\n }\n if (liveness !== undefined) {\n set(\n \"liveness_detection_window@pod\",\n liveness + secondsOf(scenario.probes.liveness),\n \"the liveness window, then the kubelet's probe\",\n );\n }\n set(\"startup_budget@pod\", secondsOf(svc.policies.startup), \"the declared startup budget\");\n set(\n \"process_drain_time@process\",\n svc.shutdown.phases.reduce((total, phase) => total + secondsOf(phase.cap), 0),\n `the shutdown phases' caps, summed (${svc.shutdown.phases.map((phase) => phase.name).join(\", \")})`,\n );\n set(\n \"cooperative_window@process\",\n boundOf(svc.shutdown.cooperativeWindow),\n \"the declared cooperative window\",\n );\n // the hold of \"fail readiness\" keeps the listener open after the signal, as a preStop sleep does\n const hold = svc.shutdown.phases.find((phase) => phase.name === \"fail readiness\");\n set(\n \"readiness_hold@process\",\n hold === undefined ? 0 : secondsOf(hold.cap),\n hold === undefined\n ? \"no fail readiness phase: the intake stops at once after the signal\"\n : \"the cap of the fail readiness phase, which holds while the endpoints update\",\n );\n for (const [symbol, amount] of Object.entries(scenario.platform))\n set(symbol, boundOf(amount), \"the scenario\");\n return { bind, from };\n};\n\n/** This function gives the client knobs of a dependency at @client, and the values of the scenario at its own hops. */\nconst dependencyBindings = (dependency: Dependency, scenario: Scenario): Record<string, Bound> => {\n const bind: Record<string, Bound> = {};\n for (const [knob, amount] of Object.entries(dependency.knobs))\n bind[`${knob}@client`] = boundOf(amount);\n for (const [symbol, amount] of Object.entries(scenario.dependencies?.[dependency.name] ?? {})) {\n bind[symbol.includes(\"@\") ? symbol : `${symbol}@dependency`] = boundOf(amount);\n }\n return bind;\n};\n\nconst SCOPED = /@(client|dependency|server)$/;\n\n/** This function applies each service rule to a declaration and a scenario: one time for the service, and one time for each client. */\nexport const lint = (\n svc: ServiceDescription,\n scenario: Scenario,\n rules: readonly Rule[],\n): Finding[] => {\n const service = serviceBindings(svc, scenario).bind;\n const findings: Finding[] = [];\n for (const rule of rules) {\n if (!SERVICE_RULES.includes(rule.id)) continue;\n const symbols = symbolsOf(rule);\n const scopes: Array<[string, Binding]> = symbols.some((symbol) => SCOPED.test(symbol))\n ? svc.dependencies.map((dependency) => [\n dependency.name,\n { ...service, ...dependencyBindings(dependency, scenario) },\n ])\n : [[svc.name, service]];\n for (const [scope, bind] of scopes) {\n const evaluated = evaluate(rule, bind);\n findings.push({\n rule: rule.id,\n scope,\n verdict: verdictOf(rule, bind, evaluated),\n statement: rule.rule,\n ...(rule.window !== undefined ? { window: rule.window } : {}),\n evaluated,\n unbound: symbols.filter((symbol) => bind[symbol] === undefined),\n });\n }\n }\n return findings;\n};\n","import type { Machine, TransitionDef } from \"@mark1russell7/algebra\";\nimport { arr, fromStates } from \"@mark1russell7/algebra\";\nimport type { ServiceDescription } from \"./description.ts\";\nimport type { LifecycleEvent, MachineName } from \"./events.ts\";\n\n/**\n * The machines of the runtime as `mark1russell7.machine/1` documents. The documents are the\n * specification, and TypeScript implements them. A test records the transitions of the runtime\n * and replays them against each document, thus the code and the documents cannot disagree.\n */\n\nconst SOURCE = [{ ref: \"packages/service/src (the runtime implements this document)\", rung: \"implemented\" as const }];\nconst base = (id: string, title: string): Pick<Machine, \"format\" | \"id\" | \"title\" | \"layer\" | \"version\" | \"sources\"> => ({\n format: \"mark1russell7.machine/1\",\n id: `service.machine.${id}`,\n title,\n layer: \"self\",\n version: \"0.2.0\",\n sources: SOURCE,\n});\nconst t = (\n id: string,\n from: string | readonly string[],\n on: string | readonly string[],\n to: string,\n extra: Partial<TransitionDef> = {},\n): TransitionDef => ({ id, from, on, to, ...extra });\n\n/**\n * The startup gate opens one time. The lifecycle moves it from `closed` to `starting` while its\n * components start. Then one warm-up sweep of each check opens it. A component that does not start\n * fails it for good.\n */\nexport const GATE_MACHINE: Machine = {\n ...base(\"gate\", \"The startup gate\"),\n states: {\n closed: { initial: true },\n starting: { note: \"the health server, then the dependencies, then the transports\" },\n sweeping: { note: \"one bounded warm-up sweep of each check\" },\n open: { final: true },\n failed: { final: true },\n },\n events: {\n \"startup.begin\": { kind: \"local\" },\n \"component.started\": { kind: \"local\" },\n \"mark-ready\": { kind: \"local\" },\n \"sweep.done\": { kind: \"local\" },\n \"startup.fail\": { kind: \"local\" },\n \"startup.over-budget\": { kind: \"timer\" },\n },\n knobs: { startup: { key: \"policies.startup\", scope: \"service\", unit: \"s\", default: null } },\n timers: {\n startup_budget: {\n duration: { knob: \"startup\", unit: \"s\" },\n arms: [\"enter:closed\"],\n cancels: [\"sweep.done\", \"startup.fail\"],\n fires: \"startup.over-budget\",\n note: \"the gate says that it is over its budget, and the startup probe of the platform acts on it\",\n },\n },\n transitions: [\n t(\"begin\", \"closed\", \"startup.begin\", \"starting\"),\n t(\"component\", \"starting\", \"component.started\", \"starting\"),\n t(\"mark-ready\", [\"closed\", \"starting\"], \"mark-ready\", \"sweeping\"),\n t(\"open\", \"sweeping\", \"sweep.done\", \"open\"),\n t(\"fail\", [\"closed\", \"starting\", \"sweeping\"], \"startup.fail\", \"failed\"),\n // the budget timer leaves each state where it is\n t(\"over-budget-closed\", \"closed\", \"startup.over-budget\", \"closed\", { timer: \"startup_budget\" }),\n t(\"over-budget-starting\", \"starting\", \"startup.over-budget\", \"starting\", { timer: \"startup_budget\" }),\n t(\"over-budget-sweeping\", \"sweeping\", \"startup.over-budget\", \"sweeping\", { timer: \"startup_budget\" }),\n ],\n};\n\n/** A debounce latch counts the polls of its check in sequence. A readiness latch starts tripped. */\nexport const LATCH_MACHINE: Machine = {\n ...base(\"latch\", \"The latch of a policy, as a debounce\"),\n states: {\n tripped: { initial: true, note: \"a readiness latch starts here; a liveness latch starts released\" },\n released: {},\n },\n events: { \"poll.ok\": { kind: \"local\" }, \"poll.fail\": { kind: \"local\" } },\n knobs: {\n fail_after: { key: \"policies.<gate>.failAfter\", scope: \"check\", unit: \"count\", default: null },\n recover_after: { key: \"policies.<gate>.recoverAfter\", scope: \"check\", unit: \"count\", default: null },\n },\n timers: {},\n transitions: [\n t(\"pass\", \"released\", \"poll.ok\", \"released\"),\n t(\"fail-count\", \"released\", \"poll.fail\", \"released\", { guard: \"failures in sequence < fail_after\" }),\n t(\"trip\", \"released\", \"poll.fail\", \"tripped\", { guard: \"failures in sequence >= fail_after\" }),\n t(\"fail\", \"tripped\", \"poll.fail\", \"tripped\"),\n t(\"pass-count\", \"tripped\", \"poll.ok\", \"tripped\", { guard: \"passes in sequence < recover_after\" }),\n t(\"release\", \"tripped\", \"poll.ok\", \"released\", { guard: \"passes in sequence >= recover_after\" }),\n ],\n};\n\n/**\n * A window latch measures time on the monotonic clock from the first failure of a sequence, and\n * from the first pass after a trip.\n */\nexport const WINDOW_MACHINE: Machine = {\n ...base(\"window\", \"The latch of a policy, as a window\"),\n states: {\n released: { initial: true, note: \"a liveness latch starts here; a readiness latch starts tripped\" },\n failing: { note: \"the fail window runs\" },\n tripped: {},\n recovering: { note: \"the recover window runs\" },\n },\n events: {\n \"poll.ok\": { kind: \"local\" },\n \"poll.fail\": { kind: \"local\" },\n \"window.failed\": { kind: \"timer\" },\n \"window.recovered\": { kind: \"timer\" },\n },\n knobs: {\n fail_after: { key: \"policies.<gate>.failAfter\", scope: \"check\", unit: \"s\", default: null },\n recover_after: { key: \"policies.<gate>.recoverAfter\", scope: \"check\", unit: \"s\", default: null },\n },\n timers: {\n fail_window: {\n duration: { knob: \"fail_after\", unit: \"s\" },\n arms: [\"enter:failing\"],\n cancels: [\"poll.ok\"],\n fires: \"window.failed\",\n },\n recover_window: {\n duration: { knob: \"recover_after\", unit: \"s\" },\n arms: [\"enter:recovering\"],\n cancels: [\"poll.fail\"],\n fires: \"window.recovered\",\n },\n },\n transitions: [\n t(\"pass\", \"released\", \"poll.ok\", \"released\"),\n t(\"start-failing\", \"released\", \"poll.fail\", \"failing\", { guard: \"fail_after > 0\" }),\n t(\"trip-at-once\", \"released\", \"poll.fail\", \"tripped\", { guard: \"fail_after = 0\" }),\n t(\"still-failing\", \"failing\", \"poll.fail\", \"failing\"),\n t(\"stop-failing\", \"failing\", \"poll.ok\", \"released\"),\n t(\"trip\", \"failing\", \"window.failed\", \"tripped\", { timer: \"fail_window\" }),\n t(\"fail\", \"tripped\", \"poll.fail\", \"tripped\"),\n t(\"release-at-once\", \"tripped\", \"poll.ok\", \"released\", { guard: \"recover_after is absent or 0\" }),\n t(\"start-recovering\", \"tripped\", \"poll.ok\", \"recovering\", { guard: \"recover_after > 0\" }),\n t(\"still-recovering\", \"recovering\", \"poll.ok\", \"recovering\"),\n t(\"stop-recovering\", \"recovering\", \"poll.fail\", \"tripped\"),\n t(\"release\", \"recovering\", \"window.recovered\", \"released\", { timer: \"recover_window\" }),\n ],\n};\n\n/**\n * A poller starts one check on a jittered loop with a fixed delay. A call that outlives its timeout\n * stays pending (`hung`), and the poller does not start the check again until the call settles.\n */\nexport const POLLER_MACHINE: Machine = {\n ...base(\"poller\", \"The poller of a check\"),\n states: {\n idle: { initial: true, note: \"before the loop starts\" },\n waiting: { note: \"the loop timer runs, and no call is pending\" },\n calling: { note: \"a call runs, in its timeout\" },\n hung: { note: \"a call outlived its timeout, and it is still pending\" },\n stopped: { final: true },\n },\n events: {\n start: { kind: \"local\" },\n sweep: { kind: \"local\" },\n tick: { kind: \"timer\" },\n answer: { kind: \"local\" },\n timeout: { kind: \"timer\" },\n \"late-answer\": { kind: \"local\" },\n stop: { kind: \"local\" },\n },\n knobs: {\n interval: { key: \"checks.<name>.poll.interval\", scope: \"check\", unit: \"s\", default: null },\n timeout: { key: \"checks.<name>.poll.timeout\", scope: \"check\", unit: \"s\", default: null },\n },\n timers: {\n loop: {\n duration: { knob: \"interval\", unit: \"s\" },\n arms: [\"start\", \"tick\"],\n cancels: [\"stop\"],\n fires: \"tick\",\n note: \"the interval, with its jitter: a fixed delay from the start of each poll\",\n },\n poll_timeout: {\n duration: { knob: \"timeout\", unit: \"s\" },\n arms: [\"enter:calling\"],\n cancels: [\"answer\", \"stop\"],\n fires: \"timeout\",\n },\n },\n transitions: [\n t(\"sweep\", \"idle\", \"sweep\", \"calling\"),\n t(\"start\", \"idle\", \"start\", \"waiting\"),\n t(\"start-while-calling\", \"calling\", \"start\", \"calling\", { note: \"a late check polls at once, then starts its loop\" }),\n t(\"start-while-hung\", \"hung\", \"start\", \"hung\"),\n t(\"tick\", \"waiting\", \"tick\", \"calling\", { guard: \"the force-fail list does not name the check\" }),\n t(\"tick-forced\", \"waiting\", \"tick\", \"waiting\", { guard: \"the force-fail list names the check\" }),\n t(\"tick-pending\", \"calling\", \"tick\", \"calling\", { note: \"the poll fails at once: its call is pending\" }),\n t(\"tick-hung\", \"hung\", \"tick\", \"hung\", { note: \"the poll fails at once: its call is pending\" }),\n t(\"answer-loop\", \"calling\", \"answer\", \"waiting\", { guard: \"the loop runs\" }),\n t(\"answer-sweep\", \"calling\", \"answer\", \"idle\", { guard: \"the loop has not started\" }),\n t(\"timeout\", \"calling\", \"timeout\", \"hung\", { timer: \"poll_timeout\" }),\n t(\"late-answer-loop\", \"hung\", \"late-answer\", \"waiting\", { guard: \"the loop runs\" }),\n t(\"late-answer-sweep\", \"hung\", \"late-answer\", \"idle\", { guard: \"the loop has not started\" }),\n t(\"stop\", [\"idle\", \"waiting\", \"calling\", \"hung\"], \"stop\", \"stopped\"),\n t(\"settle-after-stop\", \"stopped\", [\"answer\", \"late-answer\", \"timeout\"], \"stopped\"),\n ],\n};\n\n/**\n * The exit manager is the only part that ends the process. The first exit request starts the\n * shutdown, and a later one has no effect. An error makes the exit code 1.\n */\nexport const EXIT_MACHINE: Machine = {\n ...base(\"exit\", \"The exit manager\"),\n states: { running: { initial: true }, stopping: {}, exited: { final: true } },\n events: {\n \"exit.request\": { kind: \"app\", note: \"SIGINT, SIGTERM, SIGQUIT or shutdown()\" },\n \"exit.repeat\": { kind: \"app\" },\n \"startup.fail\": { kind: \"local\" },\n error: { kind: \"local\" },\n \"shutdown.done\": { kind: \"local\" },\n },\n knobs: {},\n timers: {},\n transitions: [\n t(\"request\", \"running\", \"exit.request\", \"stopping\"),\n t(\"startup-fail\", \"running\", \"startup.fail\", \"stopping\"),\n t(\"error-running\", \"running\", \"error\", \"running\"),\n t(\"repeat\", \"stopping\", \"exit.repeat\", \"stopping\"),\n t(\"startup-fail-stopping\", \"stopping\", \"startup.fail\", \"stopping\"),\n t(\"error\", \"stopping\", \"error\", \"stopping\"),\n t(\"exit\", \"stopping\", \"shutdown.done\", \"exited\", { note: \"the exit code is 1 after an error, else 0\" }),\n ],\n};\n\n/** A watchdog measures how late its own tick comes, and a late tick shows a blocked event loop. */\nexport const WATCHDOG_MACHINE: Machine = {\n ...base(\"watchdog\", \"The watchdog of the event loop\"),\n states: { watching: { initial: true }, blocked: {}, stopped: { final: true } },\n events: { tick: { kind: \"timer\" }, stop: { kind: \"local\" } },\n knobs: {\n tick: { key: \"watchdog.tick\", scope: \"process\", unit: \"s\", default: null },\n threshold: { key: \"watchdog.threshold\", scope: \"process\", unit: \"s\", default: null },\n },\n timers: {\n tick: { duration: { knob: \"tick\", unit: \"s\" }, arms: [\"enter:watching\", \"tick\"], cancels: [\"stop\"], fires: \"tick\" },\n },\n transitions: [\n t(\"on-time\", \"watching\", \"tick\", \"watching\", { guard: \"lateness <= threshold\" }),\n t(\"block\", \"watching\", \"tick\", \"blocked\", { guard: \"lateness > threshold\" }),\n t(\"still-blocked\", \"blocked\", \"tick\", \"blocked\", { guard: \"lateness > threshold\" }),\n t(\"unblock\", \"blocked\", \"tick\", \"watching\", { guard: \"lateness <= threshold\" }),\n t(\"stop\", [\"watching\", \"blocked\"], \"stop\", \"stopped\"),\n ],\n};\n\n/** This function gives the name of a knob for the cap of a phase: `drain` gives `drain_cap`. */\nexport const capKnob = (phase: string): string => `${phase.replace(/[^A-Za-z0-9]+/g, \"_\")}_cap`;\n\n/**\n * This function gives the drain of a declaration as a machine. It has one state for each declared\n * phase, in its sequence, and each state has a timer at its cap. The drain also has the cooperative abort: the\n * cap of the drain less the cooperative window, and half that cap at the least.\n */\nexport const drainMachine = (description: ServiceDescription): Machine => {\n const phases = description.shutdown.phases;\n const states: Record<string, { initial?: boolean; final?: boolean; note?: string }> = {\n running: { initial: true },\n };\n for (const phase of phases) states[phase.name] = {};\n states[\"done\"] = { final: true };\n const knobs: Machine[\"knobs\"] = Object.fromEntries([\n ...phases.map((phase) => [\n capKnob(phase.name),\n { key: `shutdown.phases.${phase.name}.cap`, scope: \"service\", unit: \"s\", default: null },\n ]),\n [\"cooperative_window\", { key: \"shutdown.cooperativeWindow\", scope: \"service\", unit: \"s\", default: null }],\n ]);\n const timers: Record<string, Machine[\"timers\"][string]> = {};\n for (const phase of phases)\n timers[`${capKnob(phase.name)}_timer`] = {\n duration: { knob: capKnob(phase.name), unit: \"s\" },\n arms: [`enter:${phase.name}`],\n cancels: [\"phase.done\"],\n fires: \"phase.cap\",\n };\n if (phases.some((phase) => phase.name === \"drain\"))\n timers[\"cooperative_abort\"] = {\n duration: { expr: \"max(drain_cap - cooperative_window, drain_cap / 2)\", unit: \"s\" },\n arms: [\"enter:drain\"],\n cancels: [\"phase.done\", \"phase.cap\"],\n fires: \"drain.cooperative-abort\",\n };\n const transitions: TransitionDef[] = [];\n const order = [\"running\", ...phases.map((phase) => phase.name), \"done\"];\n for (let at = 0; at + 1 < order.length; at += 1) {\n const from = order[at]!;\n const to = order[at + 1]!;\n transitions.push(\n at === 0\n ? t(`begin`, from, \"exit.request\", to)\n : t(`after-${from}`, from, [\"phase.done\", \"phase.cap\"], to, {\n timer: `${capKnob(from)}_timer`,\n note: \"phase.cap: the work of the phase did not end in its cap, and the exit code is 1\",\n }),\n );\n }\n if (phases.some((phase) => phase.name === \"drain\"))\n transitions.push(t(\"cooperative-abort\", \"drain\", \"drain.cooperative-abort\", \"drain\", { timer: \"cooperative_abort\" }));\n // the signal of the service aborts when the sequence passes the poller, or at the end: it leaves\n // the drain where it is\n for (const state of [...phases.map((phase) => phase.name), \"done\"])\n transitions.push(t(`signal-abort-${state}`, state, \"signal.abort\", state));\n return {\n ...base(\"drain\", `The drain of ${description.name}`),\n states,\n events: {\n \"exit.request\": { kind: \"app\" },\n \"phase.done\": { kind: \"local\" },\n \"phase.cap\": { kind: \"timer\" },\n \"drain.cooperative-abort\": { kind: \"timer\" },\n \"signal.abort\": { kind: \"local\" },\n },\n knobs,\n timers,\n transitions,\n };\n};\n\n/** These are the static machines of the runtime, by the name that their events carry. */\nexport const MACHINES: Readonly<Record<Exclude<MachineName, \"drain\" | \"client\">, Machine>> = {\n gate: GATE_MACHINE,\n latch: LATCH_MACHINE,\n window: WINDOW_MACHINE,\n poller: POLLER_MACHINE,\n exit: EXIT_MACHINE,\n watchdog: WATCHDOG_MACHINE,\n};\n\n/**\n * This function replays the transitions of one machine of the runtime against its document. Each\n * instance starts in the state that its first transition leaves. Each transition must leave the\n * state where the replay is. The document must have a transition from that state, on that event,\n * to that state. The function gives each disagreement.\n */\nexport const replay = (doc: Machine, events: readonly LifecycleEvent[]): string[] => {\n const problems: string[] = [];\n const at = new Map<string, string>();\n for (const event of events) {\n const where = at.get(event.instance) ?? event.from;\n if (doc.states[where] === undefined) problems.push(`${event.instance}: the state ${where} is not in ${doc.id}`);\n if (event.from !== where)\n problems.push(`${event.instance}: the runtime left ${event.from} on ${event.on}, but the replay is in ${where}`);\n const allowed = doc.transitions.some(\n (each) => fromStates(doc, each.from).includes(event.from) && arr(each.on).includes(event.on) && each.to === event.to,\n );\n if (!allowed) problems.push(`${event.instance}: ${doc.id} has no transition from ${event.from} on ${event.on} to ${event.to}`);\n at.set(event.instance, event.to);\n }\n return problems;\n};\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,SAAS;;;ACwCtB,MAAM,MAAM,WACV,QAAQ,QAAQ,IAAI;;;;;;;AAQtB,MAAa,SACX,QACA,OACA,SACA,SACA,UACU;CACV,IAAI,QAAQ;CACZ,IAAI,QAAQ,MAAM,IAAI;CACtB,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,IAAI,SAAS;CAEb,IAAI,eAA8B;CAClC,IAAI,eAA8B;CAClC,IAAI,YAA2B;CAC/B,IAAI,YAA2B;;CAG/B,MAAM,cACJ,QACI,iBAAiB,OACf,YACA,eACF,iBAAiB,OACf,aACA;;CAER,MAAM,QAAQ,IAAY,WAA6B;EACrD,MAAM,OAAO,MAAM;EACnB,OAAO;EACP,QAAQ,MAAM,MAAM,GAAG,EAAE;CAC3B;CAEA,MAAM,OAAO,SAAwB;EACnC,IAAI,SAAS,OAAO;EACpB,QAAQ;EACR,QAAQ,MAAM,IAAI;EAClB,QAAQ;CACV;CACA,MAAM,iBAAuB;EAC3B,YAAY;EACZ,YAAY;EACZ,eAAe;CACjB;CACA,MAAM,iBAAuB;EAC3B,YAAY;EACZ,YAAY;EACZ,eAAe;CACjB;CAEA,MAAM,YAAY,WAAyB;EACzC,IAAI,OAAO,SAAS,YAAY;EAChC,IAAI,OAAO,YAAY,MAAM;GAC3B,UAAU;GACV,QAAQ;GACR,IAAI,SAAS,UAAU,OAAO,cAAc,IAAI,KAAK;EACvD,OAAO;GACL,SAAS;GACT,SAAS;GACT,IAAI,CAAC,SAAS,SAAS,OAAO,WAAW,IAAI,IAAI;EACnD;CACF;CAEA,MAAM,YAAY,WAAyB;EACzC,IAAI,OAAO,SAAS,UAAU;EAC9B,IAAI,OAAO,YAAY,MAAM;GAC3B,SAAS;GACT,IAAI,CAAC,OAAO;GACZ,IAAI,OAAO,iBAAiB,KAAA,GAAW;IACrC,IAAI,KAAK;IACT;GACF;GACA,IAAI,iBAAiB,MAAM;GAC3B,MAAM,OAAO,GAAG,OAAO,YAAY;GACnC,IAAI,QAAQ,GAAG;IACb,SAAS;IACT,IAAI,KAAK;IACT;GACF;GACA,eAAe,OAAO;GACtB,YAAY,MAAM,MAAM,YACtB,KAAK,0BAA0B;IAC7B,YAAY;IACZ,eAAe;IACf,IAAI,KAAK;GACX,CAAC,CACH;GACA;EACF;EACA,SAAS;EACT,IAAI,SAAS,iBAAiB,MAAM;EACpC,MAAM,OAAO,GAAG,OAAO,SAAS;EAChC,IAAI,QAAQ,GAAG;GACb,SAAS;GACT,IAAI,IAAI;GACR;EACF;EACA,eAAe,OAAO;EACtB,YAAY,MAAM,MAAM,YACtB,KAAK,uBAAuB;GAC1B,YAAY;GACZ,eAAe;GACf,IAAI,IAAI;EACV,CAAC,CACH;CACF;CAEA,OAAO;EACL,IAAI,UAAU;GACZ,OAAO;EACT;EACA,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,SAAS;GACX,OAAO;EACT;EACA,OAAO,QAAQ;GAEb,IAAI,OAAO,YAAY,iBAAiB;GACxC,MAAM,OAAO,OAAO,YAAY;GAChC,KAAK,OAAO,YAAY,mBAAmB;IACzC,SAAS,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAChE,SAAS,MAAM;IACf,SAAS,MAAM;GACjB,CAAC;EACH;EACA,OAAO;GACL,SAAS;GACT,SAAS;EACX;CACF;AACF;;;ACvJA,MAAMA,aAAW,UACf,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;;;;;AAWvD,MAAa,UAAU,YAAmC;CACxD,MAAM,EAAE,OAAO,MAAM,QAAQ;CAC7B,MAAM,WAAW,QAAQ,KAAK,UAAU,IAAI;CAC5C,MAAM,UAAU,QAAQ,KAAK,SAAS,IAAI;CAC1C,IAAI,OAAsB;CAC1B,IAAI,OAAsB;CAC1B,IAAI,UAAU;CACd,IAAI,UAAU;CAEd,IAAI,UAA4D;CAEhE,MAAM,yBAAS,IAAI,IAAgB;;CAGnC,MAAM,cACJ,UACI,YACA,YAAY,OACV,QAAQ,OACN,SACA,YACF,UACE,YACA;CACV,MAAM,QAAQ,IAAY,WAA6B;EACrD,MAAM,OAAO,MAAM;EACnB,OAAO;EACP,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE;CACnC;CAEA,MAAM,UACJ,SACA,SACA,SAA4E,CAAC,OACjE;EACZ;EACA,IAAI,MAAM,IAAI;EACd,MAAM,MAAM,IAAI,IAAI;EACpB,OAAO,OAAO,SAAS;EACvB,UAAU,OAAO,YAAY;EAC7B,QAAQ,OAAO,UAAU;EACzB,QAAQ,OAAO,UAAU;CAC3B;CAEA,MAAM,aAA8B;EAClC,MAAM,UAAU,MAAM,IAAI;EAC1B,IAAI,QAAQ,OAAO,GACjB,OAAO,QAAQ,QAAQ,OAAO,SAAS,SAAS;GAAE,OAAO;GAAgC,QAAQ;EAAK,CAAC,CAAC;EAC1G,IAAI,YAAY,MAAM;GACpB,MAAM,QAAQ,eAAe,KAAK,MAAM,UAAU,QAAQ,KAAK,EAAE;GACjE,OAAO,QAAQ,QAAQ,OAAO,SAAS,SAAS,EAAE,MAAM,CAAC,CAAC;EAC5D;EACA,MAAM,aAAa,IAAI,gBAAgB;EACvC,OAAO,IAAI,SAAiB,YAAY;GACtC,IAAI,OAAO;GACX,MAAM,UAAU,UAAwB;IACtC,IAAI,MAAM;IACV,OAAO;IACP,OAAO,OAAO,OAAO;IACrB,OAAO;IACP,QAAQ,KAAK;GACf;GACA,MAAM,gBAAsB;IAC1B,WAAW,MAAM;IACjB,OAAO,OAAO,SAAS,SAAS,EAAE,OAAO,sBAAsB,CAAC,CAAC;GACnE;GACA,OAAO,IAAI,OAAO;GAClB,MAAM,OAAO;IAAE,OAAO;IAAS,MAAM;GAAM;GAC3C,UAAU;GACV,MAAM,SAAS,MAAM,MAAM,eAAe;IACxC,WAAW,MAAM;IAEjB,KAAK,iBAAiB;KACpB,IAAI,YAAY,MAAM,KAAK,OAAO;IACpC,CAAC;IACD,OAAO,OAAO,SAAS,SAAS;KAAE,OAAO,gBAAgB,QAAQ;KAAM,UAAU;IAAK,CAAC,CAAC;GAC1F,CAAC;GACD,MAAM,gBAAsB;IAC1B,IAAI,YAAY,MAAM;IACtB,KAAK,KAAK,OAAO,gBAAgB,gBAAgB;KAC/C,UAAU;IACZ,CAAC;GACH;GACA,IAAI;GACJ,IAAI;IACF,SAAS,QAAQ,QAAQ,IAAI,WAAW,MAAM,CAAC;GACjD,SAAS,OAAO;IACd,SAAS,QAAQ,OAAO,KAAK;GAC/B;GACA,OAAO,MACJ,UAAU;IACT,QAAQ;IACR,MAAM,UAAU,SAAS;IACzB,OAAO,OAAO,SAAS,SAAS,YAAY,OAAO,CAAC,IAAI,EAAE,OAAO,kBAAkB,UAAU,CAAC,CAAC;GACjG,IACC,UAAmB;IAClB,QAAQ;IACR,OAAO,OAAO,SAAS,SAAS;KAAE,OAAOA,UAAQ,KAAK;KAAG,QAAQ;IAAK,CAAC,CAAC;GAC1E,CACF;EACF,CAAC;CACH;CAEA,MAAM,UAAU,UAA0B;EACxC,OAAO;EACP,IAAI,CAAC,SAAS,QAAQ,OAAO,KAAK;EAClC,OAAO;CACT;CAEA,MAAM,iBAAyB,YAAY,IAAI,KAAK,UAAU,IAAI,QAAQ,OAAO,IAAI;CAErF,MAAM,aAAmB;EACvB,OAAO;EACP,IAAI,SAAS;EACb,IAAI;EACJ,KAAK,cAAc;GACjB,OAAO,MAAM,MAAM,SAAS,GAAG,IAAI;GACnC,SAAS,KAAK;EAChB,CAAC;EACD,OAAa,KAAK,MAAM;CAC1B;CAEA,OAAO;EACL,IAAI,OAAO;GACT,OAAO;EACT;EACA,eAAe;GACb,IAAI;GACJ,KAAK,eAAe;IAClB,SAAS,KAAK;GAChB,CAAC;GACD,OAAO,OAAQ,KAAK,MAAM;EAC5B;EACA,MAAM,OAAO;GACX,IAAI,WAAW,SAAS,MAAM;GAC9B,KAAK,eAAe;IAClB,UAAU;IACV,OAAO,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI;GAC7C,CAAC;EACH;EACA;EACA,OAAO;GACL,IAAI,SAAS;GACb,KAAK,cAAc;IACjB,UAAU;IACV,OAAO;IACP,OAAO;GACT,CAAC;GACD,KAAK,MAAM,WAAW,CAAC,GAAG,MAAM,GAAG,QAAQ;EAC7C;CACF;AACF;;;;;;;AC3FA,MAAa,iBACX,aACA,MACA,UAAwB,CAAC,MACb;CACZ,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,SAAS,QAAQ,UAAU,KAAK;CACtC,MAAM,SAAS,IAAI,IAAI,QAAQ,aAAa,CAAC,CAAC;CAC9C,MAAM,YAAY,MAAM,IAAI;CAC5B,MAAM,SAAS,QAAQ,YAAY,SAAS,SAAS,IAAI;CACzD,MAAM,0BAAU,IAAI,IAAmB;CACvC,MAAM,eAAe,IAAI,IAAI,YAAY,aAAa,KAAK,eAAe,WAAW,IAAI,CAAC;CAE1F,MAAM,mCAAmB,IAAI,IAAY;CACzC,MAAM,4BAAY,IAAI,IAAsC;CAC5D,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,IAAI,OAAO;CAEX,MAAM,UACH,SAAsB,cACtB,MAAM,IAAI,OACT,QAAQ,SAAS;EAAE,IAAI,MAAM,IAAI;EAAG;EAAS;EAAU;EAAM;EAAI;CAAG,CAAC;CACzE,MAAM,YAAY,OAAO,QAAQ,YAAY,IAAI;CACjD,MAAM,YAAY,IAAY,IAAY,WAA0B;EAClE,MAAM,OAAO;EACb,OAAO;EACP,QAAQ,SAAS;GACf,IAAI,MAAM,IAAI;GACd,SAAS;GACT,UAAU,YAAY;GACtB;GACA;GACA;GACA,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EAC3C,CAAC;CACH;CAGA,IAAI,cAA6B,MAAM,MAAM,cAAc;EACzD,cAAc;EACd,IAAI,SAAS,UAAU,SAAS,UAAU,UAAU,MAAM,MAAM,qBAAqB;CACvF,CAAC;CAED,MAAM,gBAAsB;EAC1B,IAAI,UAAU,SAAS,GAAG;EAC1B,MAAM,MAAM,QAAQ;EACpB,KAAK,MAAM,YAAY,WAAW,SAAS,GAAG;CAChD;CAEA,MAAM,OAAO,OAAc,OAAgB,SAAwB;EACjE,MAAM,MAAM,QAAQ,YAAY,OAAO,KAAK,KAAK;EACjD,MAAM,SAAS,MAAM,UAAU,OAAO,OAAO,YAAY,SAAS,MAAM;EAGxE,MAAM,OACJ,WAAW,OACP,OACA,MACE,QACA,OACA,MAAM,UAAU,aAChB,SACA,OAAO,OAAO,SAAS,WAAW,WAAW,SAAS,MAAM,IAAI,CAClE;EACN,MAAM,QAAe;GACnB;GACA,OAAO;GACP,QAAQ,OAAO;IACb,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ;IACA;IACA;IACA,cACE,OAAO,IAAI,MAAM,IAAI,KAAM,MAAM,eAAe,QAAQ,OAAO,IAAI,MAAM,UAAU;IACrF,SAAS,WAAW;KAClB,MAAM,OAAO,MAAM;KAEnB,IAAI,OAAO,YAAY,QAAQ,MAAM,eAAe,MAC9C;UAAA,iBAAiB,OAAO,MAAM,UAAU,GAAG,QAAQ;KAAA;IAC3D;IACA,OAAO,OAAO,UAAU,MAAM,IAAI;GACpC,CAAC;EACH;EACA,QAAQ,IAAI,MAAM,MAAM,KAAK;EAC7B,IAAI,QAAQ,CAAC,SAAS;GAEpB,MAAW,OAAO,QAAQ;GAC1B,MAAM,OAAO,MAAM,MAAM,OAAO,SAAS,CAAC;EAC5C;CACF;CAEA,KAAK,MAAM,SAAS,YAAY,QAAQ;EACtC,MAAM,MAAM,KAAK,IAAI,MAAM,IAAI;EAC/B,IAAI,QAAQ,KAAA,GAAW,MAAM,IAAI,MAAM,SAAS,MAAM,KAAK,8CAA8C;EACzG,IAAI,OAAO,KAAK,KAAK;CACvB;CAEA,MAAM,aAAa,QAAc,aAAwC,SACvE,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,SAAS,UAC7B,MAAM,MAAM,UAAU,UAAU,MAAM,UAAU,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAC/F;CACF,MAAM,YAAY,YAAuC,QAAQ,OAAO,SAAS,CAAC,KAAK,OAAO;;CAE9F,MAAM,cAAc,IAAU,SAC5B,YAAY,UAAU,MAAM,aAAa,SAAS,OAAO,MAAM,CAAC,KAAK,SAAS,KAAK;CAErF,MAAM,gBAA+B;EACnC,MAAM,OAA8B;GAClC,WAAW,SAAS,UAAU,WAAW,CAAC,KAAK,iBAAiB,SAAS;GACzE,UAAU,SAAS,UAAU,UAAU,CAAC;EAC1C;EACA,MAAM,QAAQ,KAAK,YAAY,CAAC,WAAW,YAAY,IAAI;EAC3D,MAAM,OAAO,WAAW,CAAC,YAAY,CAAC,WAAW,aAAa,IAAI;EAClE,MAAM,QAAQ,QAAQ,KAAK;EAC3B,MAAM,mBAAmB,SACvB,CAAC,iBAAiB,IAAI,IAAI,KAC1B,SAAS,UAAU,cAAc,UAAU,MAAM,eAAe,IAAI,CAAC;EACvE,MAAM,MAAM,SAAS,UAAU,cAAc,UAAU,MAAM,eAAe,IAAI,CAAC;EACjF,MAAM,kBAA2C,CAAC;EAClD,KAAK,MAAM,QAAQ,cAAc,gBAAgB,QAAQ,gBAAgB,IAAI;EAC7E,MAAM,cAAuC,CAAC;EAC9C,KAAK,MAAM,cAAc,YAAY,aACnC,YAAY,WAAW,QAAQ,QAAQ,OAAO,WAAW,SAAS,MAAM,eAAe;EACzF,MAAM,SAAuC,CAAC;EAC9C,KAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,OAAO,QAAQ;GACb,OAAO,MAAM,MAAM;GACnB,YAAY,MAAM,MAAM;GACxB,SAAS,MAAM,OAAO,WAAW;GACjC,QAAQ,MAAM,OAAO,UAAU;GAC/B,OAAO,MAAM,OAAO,SAAS;GAC7B,MAAM,MAAM,OAAO;EACrB;EAEF,MAAM,UAAU,MAAM,IAAI,IAAI;EAC9B,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,cAAc;GACd;GACA,SAAS;IAAE;IAAQ;IAAS,YAAY,CAAC,WAAW,UAAU;GAAO;EACvE;CACF;CAEA,MAAM,UAAU,MAAiB,MAAe,gBAAsC;EACpF,QAAQ,QAAQ,SAAS,WAAW,MAAM;EAC1C,aAAa;EACb,MAAM;GACJ,QAAQ,OAAO,SAAS;GACxB,SAAS,YAAY;GACrB,OAAO;GACP,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,GAAI,SAAS,WAAW,EAAE,SAAS,QAAQ,EAAE,IAAI,CAAC;EACpD;CACF;CAEA,OAAO;EACL;EACA,MAAM,MAAM,YAAY;GACtB,MAAM,MAAM,QAAQ;GACpB,IAAI,SAAS,WAAW,OAAO,OAAO,MAAM,IAAI,OAAO;GACvD,IAAI,SAAS,YAAY,OAAO,OAAO,MAAM,IAAI,KAAK;GACtD,IAAI,SAAS,UAAU,OAAO,OAAO,MAAM,IAAI,SAAS,IAAI,KAAK;GACjE,IAAI,eAAe,KAAA,GAAW,OAAO,OAAO,MAAM,IAAI,KAAK;GAC3D,MAAM,QAAQ,IAAI,YAAY;GAC9B,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,GAAG,YAAY,KAAK,qBAAqB,YAAY;GAC9F,OAAO,OAAO,MAAM,OAAO,UAAU;EACvC;EACA,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;EACA,UAAU,MAAM,IAAI;GAClB,IAAI,IAAI,OAAO,IAAI,IAAI;QAClB,OAAO,OAAO,IAAI;EACzB;EACA,QAAQ;GACN,IAAI,UAAU;GACd,WAAW;GACX,QAAQ;EACV;EACA,OAAO;GACL,UAAU;GACV,cAAc;GACd,cAAc;GACd,KAAK,MAAM,SAAS,QAAQ,OAAO,GAAG;IACpC,MAAM,OAAO,KAAK;IAClB,MAAM,OAAO,KAAK;GACpB;EACF;EACA,eAAe;GACb,IAAI,SAAS,UAAU,SAAS,YAAY,eAAe;EAC7D;EACA,YAAY,OAAO;GACjB,IAAI,SAAS,UAAU,SAAS,UAAU;GAC1C,SAAS,UAAU,gBAAgB,KAAK;GACxC,cAAc;GACd,cAAc;EAChB;EACA,MAAM,SAAS;GACb,IAAI,WAAW,WAAW,SAAS,YAAY,SAAS,YAAY;GACpE,SAAS,YAAY,YAAY;GAEjC,MAAM,QAAQ,MAAM,IAAI;GACxB,MAAM,QAAQ,IAAI,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,OAAO,QAAQ,CAAC,CAAC;GAC9E,IAAI,WAAW,SAAS,YAAY;GACpC,KAAK,MAAM,SAAS,QAAQ,OAAO,GACjC,MAAM,OAAO,MAAM,MAAM,OAAO,SAAS,KAAK,MAAM,IAAI,IAAI,MAAM;GACpE,UAAU;GACV,cAAc;GACd,cAAc;GACd,SAAS,QAAQ,YAAY;GAC7B,QAAQ;EACV;EACA,SAAS,OAAO,KAAK;GACnB,IAAI,QAAQ,IAAI,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM,GAAG,YAAY,KAAK,6BAA6B,MAAM,MAAM;GAC1G,IAAI,OAAO,KAAK,IAAI;GACpB,QAAQ;EACV;EACA,cAAc,MAAM;GAClB,IAAI,aAAa,IAAI,IAAI,GAAG;GAC5B,aAAa,IAAI,IAAI;GACrB,iBAAiB,IAAI,IAAI;GACzB,QAAQ;EACV;CACF;AACF;;;;ACtVA,MAAa,eAAsC;CAAC;CAAU;CAAW;AAAS;;AAsBlF,MAAa,cAA2B;CACtC,GAAG,QAAQ,SAAS;EAClB,MAAM,OAAQ,WAAyC;EACvD,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,6CAA6C;EACrF,KAAK,GAAG,QAAQ,OAAO;EACvB,aAAa;GACX,KAAK,IAAI,QAAQ,OAAO;EAC1B;CACF;CACA,KAAK,MAAM;EACT,MAAM,OAAQ,WAAyC;EACvD,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,6CAA6C;EACrF,KAAK,KAAK,IAAI;CAChB;AACF;;;;;;;ACeA,MAAa,WACX,aACA,OACA,YACY;CACZ,MAAM,SAAS,IAAI,IAAI,YAAY,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC;CACrF,MAAM,0BAAU,IAAI,IAAc;CAClC,MAAM,uBAAO,IAAI,IAAgB;CACjC,IAAI,SAAS;CAEb,MAAM,UAAU,WAA2B;EACzC,QAAQ,OAAO,MAAM;EACrB,IAAI,QAAQ,SAAS,GAAG,KAAK,MAAM,QAAQ,CAAC,GAAG,IAAI,GAAG,KAAK;CAC7D;;CAEA,MAAM,OAAO,QAAkB,UAAyC;EACtE,MAAM,YAAY,OAAO,IAAI,OAAO,UAAU,CAAC,CAAE,OAAO,OAAO;EAC/D,MAAM,QAAQ,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,OAAO,IAAI,KAAM,GAAG;EAC5E,MAAM,YAAY,WAAW,WAAW,KAAK;EAC7C,OAAO;GAAE,MAAM;GAAO,QAAQ,YAAY,MAAM;GAAK;GAAW;EAAM;CACxE;CAEA,OAAO;EACL,QAAW,YAAoB,OAAe,MAAoB;GAChE,MAAM,OAAO,OAAO,IAAI,UAAU;GAClC,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,0BAA0B,YAAY;GAE9E,IAAI,KAAK,OAAO,WAAW,KAAA,GACzB,MAAM,IAAI,MAAM,cAAc,WAAW,8BAA8B,OAAO;GAChF,MAAM,UAAU,SAAS,QAAQ,UAAU,IAAI;GAC/C,IAAI,YAAY,MACd,OAAO,QAAQ,QAAQ;IAAE,MAAM;IAAW,QAAQ;IAAK,WAAW;IAAM,OAAO;GAAQ,CAAC;GAC1F,OAAO,IAAI,SAA4B,YAAY;IACjD,IAAI,WAAW;IACf,MAAM,SAAmB;KACvB;KACA;KACA,SAAS,MAAM,IAAI;KACnB,OAAO,IAAI,gBAAgB;KAC3B,SAAS,YAAY;MACnB,IAAI,UAAU;MACd,WAAW;MACX,OAAO,MAAM;MACb,QAAQ,OAAO;KACjB;IACF;IACA,QAAQ,IAAI,MAAM;IAClB,IAAI;IACJ,IAAI;KACF,UAAU,QAAQ,QAAQ,KAAK,OAAO,MAAM,MAAM,CAAC;IACrD,SAAS,OAAO;KACd,UAAU,QAAQ,OAAO,KAAK;IAChC;IACA,QAAQ,MACL,UAAU;KACT,IAAI,UAAU;KACd,WAAW;KACX,OAAO,MAAM;KACb,QAAQ;MAAE,MAAM;MAAQ;KAAM,CAAC;IACjC,IACC,UAAmB;KAClB,IAAI,UAAU;KAEd,MAAM,UAAU,OAAO,MAAM,OAAO,UAChC,IAAI,QAAQ,wCAAwC,IACnD;MAAE,MAAM;MAAU;KAAM;KAC7B,WAAW;KACX,OAAO,MAAM;KACb,QAAQ,OAAO;IACjB,CACF;GACF,CAAC;EACH;EACA,SAAS,YAAY;GACnB,IAAI,QAAQ;GACZ,KAAK,MAAM,UAAU,SAAS,IAAI,eAAe,KAAA,KAAa,OAAO,eAAe,YAAY,SAAS;GACzG,OAAO;EACT;EACA,aAAa;GACX,SAAS;EACX;EACA,mBAAmB;GACjB,KAAK,MAAM,UAAU,SAAS,OAAO,MAAM,sBAAM,IAAI,MAAM,oCAAoC,CAAC;EAClG;EACA,MAAM,UAAU;GACd,IAAI,QAAQ,SAAS,GAAG,OAAO,QAAQ,QAAQ;GAC/C,OAAO,IAAI,SAAe,YAAY;IACpC,IAAI,WAAW;IACf,MAAM,aAAmB;KACvB,IAAI,UAAU;KACd,WAAW;KACX,KAAK,OAAO,IAAI;KAChB,OAAO;KACP,QAAQ;IACV;IACA,KAAK,IAAI,IAAI;IACb,MAAM,SAAS,MAAM,MAAM,KAAK,IAAI,GAAG,WAAW,MAAM,IAAI,CAAC,SAAS;KACpE,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG;MACjC,OAAO,MAAM,sBAAM,IAAI,MAAM,gCAAgC,CAAC;MAC9D,OAAO,OAAO,IAAI,QAAQ,gCAAgC,CAAC;KAC7D;KACA,KAAK;IACP,CAAC;GACH,CAAC;EACH;CACF;AACF;;;;;;;;;;;;;;;;;AC3FA,MAAa,SAAS;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAKA,MAAa,aAAa,SAA0B,OAA6B,QAAQ,IAAI;;AAG7F,MAAM,cAAc,UAAU,iBAAiB;;;;;;AAO/C,MAAa,sBAAsB,KAAa,WAA2B;CACzE,MAAM,SAAS,OAAO,UAAU,aAAa,OAAO,oBAAoB,QAAQ,QAAQ,IAAI;CAC5F,OAAO,KAAK,IAAI,MAAM,QAAQ,MAAM,CAAC;AACvC;AAiDA,MAAM,WAAW,UACf,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;AAOvD,MAAa,qBAAqB,aAAiC,eAAqC;CACtG,MAAM,WAAW,IAAI,IAAI,YAAY,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;CAC/E,MAAM,SAAS,OAAc,QAC3B,SAAS,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,OAAO,IAAI,gCAAgC,MAAM,EAAE;CACrG,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,aAAa,YAAY,YAClC,IAAI,CAAC,WAAW,WAAW,IAAI,UAAU,IAAI,GAC3C,SAAS,KAAK,aAAa,UAAU,KAAK,oDAAoD;CAClG,IAAI,WAAW,WAAW,OAAO,KAAK,YAAY,YAAY,SAAS,GAAG;EACxE,SAAS,KAAK,GAAG,MAAM,eAAe,2BAA2B,CAAC;EAClE,SAAS,KAAK,GAAG,MAAM,SAAS,2BAA2B,CAAC;CAC9D;CACA,IAAI,WAAW,WAAW,OAAO,GAAG,SAAS,KAAK,GAAG,MAAM,oBAAoB,YAAY,CAAC;CAC5F,IAAI,YAAY,OAAO,SAAS,GAAG,SAAS,KAAK,GAAG,MAAM,mBAAmB,QAAQ,CAAC;CACtF,IAAI,WAAW,aAAa,OAAO,GAAG,SAAS,KAAK,GAAG,MAAM,0BAA0B,oBAAoB,CAAC;CAC5G,IAAI,WAAW,iBAAiB,MAAM,SAAS,KAAK,GAAG,MAAM,2BAA2B,iBAAiB,CAAC;CAC1G,OAAO;AACT;;;;;;;;AASA,MAAa,aACX,aACA,SACA,YACA,UAAsB,CAAC,MACT;CACd,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,OAAO,QAAQ,WAAW;CAChC,MAAM,UAAU,SAAsB,MAAc,IAAY,IAAY,WAC1E,QAAQ,SAAS;EACf,IAAI,MAAM,IAAI;EACd;EACA,UAAU,YAAY;EACtB;EACA;EACA;EACA,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;CAC3C,CAAC;CACH,MAAM,UAAU,IAAI,gBAAgB;CACpC,MAAM,SAAmB,CAAC;CAC1B,IAAI,QAAwB;CAC5B,IAAI,WAA0B;CAC9B,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CACtB,IAAI,gBAAgB;CAEpB,IAAI,YAAY;CAChB,MAAM,YAAY,IAAY,IAAY,WAA0B;EAClE,OAAO,QAAQ,WAAW,IAAI,IAAI,MAAM;EACxC,YAAY;CACd;CACA,MAAM,QAAQ,UAAwB;EACpC,OAAO,KAAK,KAAK;EACjB,OAAO,QAAQ,WAAW,WAAW,SAAS,KAAK;CACrD;CAEA,MAAM,WAAW,QAAQ,YAAY,aAAa,QAAQ,eAAe;EACvE,IAAI,UAAU,YAAY,OAAO;EACjC,IAAI,UAAU,UAAU,OAAO;EAG/B,IAAI,UAAU,YAAY,OAAO;EACjC,OAAO,QAAQ,QAAQ,CAAC,CAAC,YAAY,gBAAgB,OAAO,OAAO,cAAc,WAAW;CAC9F,CAAC;CAED,IAAI,eAAuC,CAAC;CAC5C,MAAM,OAAO,IAAI,SAAiB,YAAY;EAC5C,SAAS;CACX,CAAC;CAED,IAAI,kBAAkB;CACtB,MAAM,YAA+C,CAAC;CACtD,MAAM,oBAAoD,CAAC;CAE3D,MAAM,OAAO,aAAa,KAAK,WAC7B,KAAK,GAAG,cAAc;EACpB,SAAc,MAAM;CACtB,CAAC,CACH;CAEA,MAAM,WAAW,YAA2B;EAC1C,QAAQ,aAAa;EACrB,MAAM,WAAW,SAAuB,OAAO,QAAQ,YAAY,YAAY,qBAAqB,IAAI;EACxG,IAAI;GACF,IAAI,WAAW,iBAAiB,MAAM;IACpC,MAAM,WAAW,aAAa,OAAO,OAAO;IAC5C,kBAAkB;IAClB,QAAQ,mBAAmB;GAC7B;GACA,KAAK,MAAM,cAAc,YAAY,cAAc;IACjD,MAAM,SAAS,WAAW,aAAa,IAAI,WAAW,IAAI;IAC1D,IAAI,WAAW,KAAA,GAAW;IAC1B,MAAM,OAAO,QAAQ,QAAQ,MAAM;IACnC,UAAU,KAAK,CAAC,WAAW,MAAM,MAAM,CAAC;IACxC,QAAQ,cAAc,WAAW,MAAM;GACzC;GACA,KAAK,MAAM,aAAa,YAAY,YAAY;IAC9C,MAAM,OAAO,WAAW,WAAW,IAAI,UAAU,IAAI;IACrD,MAAM,KAAK,MAAM,QAAQ,MAAM;IAC/B,kBAAkB,KAAK,CAAC,UAAU,MAAM,IAAI,CAAC;IAC7C,QAAQ,GAAG,UAAU,KAAK,GAAG,UAAU,MAAM;GAC/C;GACA,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,YAAY,QAAQ;EACpC,SAAS,OAAO;GACd,MAAM,QAAQ,uBAAuB,QAAQ,KAAK;GAClD,gBAAgB;GAChB,QAAQ,YAAY,KAAK;GACzB,KAAK,KAAK;GACV,IAAI,cAAc,WAAW,SAAS,YAAY,gBAAgB,KAAK;QAClE,OAAO,QAAQ,WAAW,WAAW,gBAAgB,KAAK;EACjE;CACF,EAAA,CAAG;CAEH,QAAa,WAAW;EACtB,IAAI,iBAAiB,CAAC,iBAAiB,SAAc,oBAAoB;CAC3E,CAAC;;CAGD,MAAM,WAAW,OAAO,OAAc,MAAe,SAA2F;EAC9I,MAAM,MAAM,QAAQ,MAAM,KAAK,IAAI;EACnC,MAAM,WAAW,MAAM,IAAI,IAAI;EAC/B,MAAM,SAAS,IAAI,gBAAgB;EACnC,MAAM,UAAwB;GAAE;GAAU,QAAQ,OAAO;GAAQ;EAAM;EACvE,IAAI,UAAU,KAAK;EACnB,MAAM,MAAM,QAAQ,IAClB,KAAK,IAAI,OAAO,SAAS;GACvB,IAAI;IACF,MAAM,KAAK,OAAO;GACpB,SAAS,OAAO;IACd,KAAK,aAAa,MAAM,KAAK,WAAW,QAAQ,KAAK,GAAG;GAC1D,UAAU;IACR,WAAW;GACb;EACF,CAAC,CACH;EACA,IAAI,kBAA0B,CAAC;EAC/B,MAAM,MAAM,IAAI,SAAgB,YAAY;GAC1C,YAAY,MAAM,MAAM,WAAW,QAAQ,KAAK,CAAC;EACnD,CAAC;EAED,IAAI,MADgB,QAAQ,KAAK,CAAC,IAAI,WAAW,MAAe,GAAG,GAAG,CAAC,MACzD,UAAU,MAAM,MAAM;EACpC,UAAU;EACV,OAAO,sBAAM,IAAI,MAAM,aAAa,MAAM,KAAK,iBAAiB,CAAC;EACjE,OAAO,YAAY;CACrB;CAEA,eAAe,SAAS,QAAQ,4BAA6C;EAC3E,IAAI,mBAAmB,cAAc,UAAU;GAC7C,IAAI,cAAc,YAAY,OAAO,QAAQ,WAAW,WAAW,eAAe,KAAK;GACvF,OAAO;EACT;EACA,kBAAkB;EAClB,IAAI,cAAc,WAAW,SAAS,YAAY,gBAAgB,KAAK;EAEvE,MAAM;EACN,QAAQ;EACR,QAAQ,MAAM;EACd,IAAI,aAAa;EACjB,MAAM,aAAa,IAAY,IAAY,WAA0B;GACnE,OAAO,SAAS,YAAY,IAAI,IAAI,MAAM;GAC1C,aAAa;EACf;EACA,MAAM,qBAA2B;GAC/B,IAAI,QAAQ,OAAO,SAAS;GAC5B,QAAQ,sBAAM,IAAI,MAAM,qBAAqB,CAAC;GAC9C,OAAO,SAAS,YAAY,YAAY,cAAc;EACxD;EACA,MAAM,SAAS,YAAY,SAAS;EACpC,IAAI,KAAK;EACT,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,OAAO,UAAU,MAAM,IAAI;GACjC,UAAU,MAAM,MAAM,EAAE;GACxB,IAAI,OAAO,aAAa,aAAa;GACrC,MAAM,OAA+D,CAAC;GACtE,IAAI,MAAM,SAAS,eAAe;IAChC,KAAK,WAAW;KACd,gBAAgB;KAChB,SAAS,WAAW;IACtB,CAAC;IACD,KAAK,MAAM,GAAG,SAAS,mBAAmB,KAAK,WAAW,KAAK,WAAW,CAAC;GAC7E;GAGA,IAAI,oBAA4B,CAAC;GACjC,IAAI,MAAM,SAAS,SAAS;IAC1B,MAAM,MAAM,QAAQ,MAAM,KAAK,IAAI;IACnC,MAAM,cAAc,IAAI,gBAAgB;IACxC,MAAM,SAAS,mBAAmB,KAAK,YAAY,SAAS,iBAAiB;IAC7E,cAAc,MAAM,MAAM,cAAc;KACtC,YAAY,sBAAM,IAAI,MAAM,oCAAoC,CAAC;KACjE,SAAS,iBAAiB;KAC1B,OAAO,SAAS,YAAY,YAAY,yBAAyB;IACnE,CAAC;IACD,KAAK,MAAM,EAAE,eAAe,SAAS,MAAM,QAAQ,CAAC;IACpD,KAAK,MAAM,GAAG,SAAS,mBACrB,KAAK,MAAM,EAAE,eAAe,KAAK,MAAM,UAAU,YAAY,MAAM,CAAC;GACxE;GACA,IAAI,MAAM,SAAS,oBACjB,KAAK,MAAM,GAAG,SAAS,mBAAmB,KAAK,WAAW,KAAK,MAAM,CAAC;GACxE,IAAI,MAAM,SAAS,mBACjB,KAAK,WAAW;IACd,QAAQ,KAAK;IACb,WAAW,YAAY;GACzB,CAAC;GACH,IAAI,MAAM,SAAS,0BACjB,KAAK,KAAK,YAAY;IACpB,KAAK,MAAM,GAAG,WAAW,CAAC,GAAG,SAAS,CAAC,CAAC,QAAQ,GAAG,MAAM,OAAO,MAAM;GACxE,CAAC;GACH,IAAI,MAAM,SAAS,6BAA6B,mBAAmB,WAAW,iBAAiB,MAAM;IACnG,MAAM,SAAS,WAAW;IAC1B,KAAK,WAAW,OAAO,MAAM,CAAC;GAChC;GACA,KAAK,MAAM,SAAS,WAAW,UAAU,IAAI,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,KAAK;GAC/E,MAAM,SAAS,MAAM,SAAS,OAAO,MAAM,SAAS,kBAAkB,IAAI;GAC1E,YAAY;GACZ,IAAI,CAAC,QAAQ,KAAK,aAAa,MAAM,KAAK,6BAA6B,QAAQ,MAAM,KAAK,GAAG,EAAE,GAAG;GAClG,KAAK,SAAS,eAAe;EAC/B;EACA,UAAU,QAAQ,EAAE;EACpB,aAAa;EAEb,QAAQ,KAAK;EACb,WAAW,YAAY;EACvB,IAAI,CAAC,eAAe,SAAS,WAAW;EACxC,KAAK,MAAM,OAAO,MAAM,IAAI;EAC5B,QAAQ;EACR,WAAW,OAAO,SAAS,IAAI,IAAI;EACnC,SAAS,UAAU,iBAAiB,QAAQ,UAAU;EACtD,OAAO,QAAQ;EACf,KAAK,KAAK,QAAQ;EAClB,OAAO;CACT;CAEA,OAAO;EACL,QAAQ;EACR,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,WAAW;GACb,OAAO;EACT;EACA,IAAI,SAAS;GACX,OAAO,CAAC,GAAG,MAAM;EACnB;EACA,SAAS;EACT;EACA,QAAQ,QAAQ;EAChB;EACA,UAAU,YAAY,OAAO,SAAS,SAAS,QAAQ,YAAY,OAAO,IAAI;EAC9E,WAAW,eAAe,SAAS,SAAS,UAAU;EACtD,iBAAiB,UAAU,aAAa,QAAQ,QAAQ,CAAC,CAAC;CAC5D;AACF;;;;ACvXA,MAAa,YAAY,OAAc,MAAoB,UAA4B;CACrF,MAAM,OAAO,QAAQ,KAAK,MAAM,IAAI;CACpC,MAAM,YAAY,QAAQ,KAAK,WAAW,IAAI;CAC9C,IAAI,EAAE,OAAO,IAAI,MAAM,IAAI,WAAW,YAAY,KAAK,KAAK,eAAe,KAAK,oBAAoB;CACpG,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,QAAQ;CAEZ,IAAI,YAAY;CAChB,IAAI,OAAsB;CAE1B,MAAM,cAAuB,UAAU,YAAY,UAAU,YAAY;CACzE,MAAM,YAAkB;EACtB,MAAM,MAAM,MAAM,IAAI,IAAI;EAC1B,OAAO,MAAM,MAAM,YAAY;GAC7B,OAAO;GACP,IAAI,SAAS;GACb,MAAM,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,GAAG;GAC1C,QAAQ,KAAK,IAAI,OAAO,IAAI;GAC5B,YAAY,KAAK,IAAI,WAAW,IAAI;GACpC,MAAM,OAAO,MAAM;GACnB,UAAU,OAAO;GACjB,QAAQ,MAAM,MAAM,GAAG,MAAM;GAC7B,IAAI;EACN,CAAC;CACH;CACA,IAAI;CAEJ,OAAO;EACL,aAAa;GACX,MAAM,SAAS,WAAW,YAAY;GACtC,YAAY;GACZ,OAAO,SAAS,UAAU;EAC5B;EACA,IAAI,QAAQ;GACV,OAAO;EACT;EACA,IAAI,UAAU;GACZ,OAAO;EACT;EACA,OAAO;GACL,IAAI,SAAS;GACb,MAAM,OAAO,MAAM;GACnB,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ,MAAM,WAAW,MAAM;EACjC;CACF;AACF;;;ACsBA,MAAM,UAAU,WAA4B,YAAY,OAAO,MAAM,GAAG;AACxE,MAAM,SAAS,WACb,OAAO,UAAU,cAAe,OAAO,MAAM,KAAK,QAAQ,QAAoB,GAAG,IAAI;AACvF,MAAM,SAAS,UAA2B,OAAO,UAAU,KAAK,KAAK,SAAS;;AAG9E,MAAM,kBAAkB,QAAc,WAA6B;CACjE,IAAI,OAAO,SAAS,YAAY;EAC9B,MAAM,WAAqB,CAAC;EAC5B,IAAI,CAAC,MAAM,OAAO,QAAQ,GAAG,SAAS,KAAK,OAAO,OAAO,sDAAsD;EAC/G,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,SAAS,KAAK,OAAO,OAAO,wBAAwB,OAAO,UAAU,sCAAsC;EACzI,IAAI,CAAC,MAAM,OAAO,YAAY,GAAG,SAAS,KAAK,OAAO,OAAO,2BAA2B,OAAO,aAAa,sCAAsC;EAClJ,OAAO;CACT;CACA,MAAM,YAAY,WAChB,WAAW,KAAA,MAAc,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG,IAAI;CACrE,OAAO,SAAS,OAAO,SAAS,KAAK,SAAS,OAAO,YAAY,IAC7D,CAAC,OAAO,OAAO,sCAAsC,IACrD,CAAC;AACP;;AAGA,MAAa,WAAW,SAAiC;CACvD,IAAI,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG,MAAM,IAAI,MAAM,wBAAwB;CACtE,MAAM,aAA0B,CAAC;CACjC,MAAM,eAA6B,CAAC;CACpC,MAAM,cAID,CAAC;CACN,MAAM,SAAkB,CAAC;CACzB,MAAM,uBAAO,IAAI,IAAqB;CACtC,MAAM,YAAwB,CAAC;CAC/B,MAAM,SAAkB,CAAC;CACzB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,iCAAiB,IAAI,IAA2B;CACtD,MAAM,0BAAU,IAAI,IAA8B;CAClD,MAAM,4BAAY,IAAI,IAAyB;CAC/C,MAAM,YAA4B,CAAC;CACnC,IAAI,SAA8B;;CAGlC,MAAM,aAAa,SAAuB;EACxC,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,KAAK,gBAAgB,KAAK,gCAAgC;CAC5F;CACA,MAAM,QAAQ,MAAuC,OAAe,SAAuB;EACzF,IAAI,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GACzC,MAAM,IAAI,MAAM,GAAG,KAAK,iBAAiB,KAAK,SAAS,OAAO;CAClE;CACA,MAAM,aACH,UACA,OAAe,OAAyC,SAAyB;EAChF,UAAU,KAAK,MAAM;EACrB,KAAK,YAAY,OAAO,WAAW;EACnC,WAAW,KAAK;GAAE,MAAM;GAAO;GAAM,OAAO,EAAE,GAAG,MAAM;EAAE,CAAC;EAC1D,IAAI,SAAS,KAAA,GAAW,eAAe,IAAI,OAAO,IAAI;EACtD,OAAO;CACT;;CAGF,MAAM,eACJ,YACmF;EACnF,MAAM,cAAc,QAAQ,SAAS;EACrC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,MAAM,OAAmB,CAAC;EAC1B,MAAM,MAAM,IAAI,IAAI,IAAI;EACxB,KAAK,MAAM,QAAQ,WAAW;GAC5B,MAAM,MAAM,SAAS,OAAO,OAAO,MAAM,IAAI,OAC3C,QAAQ,SAAS;IAAE,IAAI,MAAM,IAAI;IAAG,SAAS;IAAY,UAAU,KAAK;IAAM;IAAM;IAAI;GAAG,CAAC,CAC9F;GACA,KAAK,KAAK,GAAG;GACb,IAAI,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC;EACtC;EACA,MAAM,oBAA0B;GAC9B,KAAK,MAAM,OAAO,MAAM,IAAI,KAAK;EACnC;EACA,IAAI;EACJ,IAAI;GACF,OAAO,cAAc,aAAa,KAAK,OAAO;EAChD,SAAS,OAAO;GACd,YAAY;GACZ,MAAM;EACR;EASA,OAAO;GAAE,SAAA;IANP,GAAG;IACH,YAAY;KACV,KAAK,KAAK;KACV,YAAY;IACd;GAEa;GAAG;GAAa;EAAY;CAC7C;CAEA,MAAM,UAA0B;EAC9B;EACA,IAAI,SAAS;GACX,OAAO,QAAQ;EACjB;EACA,WAAW,OAAO,MAAM,QAAQ;GAC9B,KAAK,cAAc,OAAO,YAAY;GAEtC,IAAI,WAAW,KAAA,KAAa,CAAC,SAAS,QAAQ,IAAI,OAAO,MAAM;GAC/D,aAAa,KAAK;IAChB,MAAM;IACN,GAAG;IACH,OAAO,EAAE,GAAG,KAAK,MAAM;IACvB,YAAY,EAAE,GAAG,KAAK,WAAW;GACnC,CAAC;GACD,IAAI,SAAS;IACX,UAAU;IACV,MAAM,cAAc,KAAK;GAC3B;GACA,OAAO;EACT;EACA,QAAQ,UAAU,QAAQ;EAC1B,UAAU,UAAU,UAAU;EAC9B,QAAQ,UAAU,QAAQ;EAC1B,WAAW,OAAO;GAChB,UAAU,cAAc;GACxB,KAAK,aAAa,OAAO,YAAY;GACrC,MAAM,OAAO;IACX,MAAM;IACN,UAAU,CAAC;IACX,QAAQ,CAAC;GACX;GACA,YAAY,KAAK,IAAI;GACrB,MAAM,SAA4B;IAChC,SAAS,GAAG,QAAQ;KAClB,UAAU,QAAQ,MAAM,UAAU;KAClC,KAAK,SAAS,KAAK,GAAG,MAAM;KAC5B,OAAO;IACT;IACA,MAAM,QAAQ,WAAW;KACvB,UAAU,GAAG,MAAM,UAAU;KAC7B,IAAI,KAAK,OAAO,YAAY,KAAA,GAC1B,MAAM,IAAI,MAAM,GAAG,MAAM,kBAAkB,QAAQ;KACrD,KAAK,OAAO,UAAU,EAAE,GAAG,UAAU;KACrC,OAAO;IACT;GACF;GACA,OAAO;EACT;EACA,MAAM,OAAO,KAAK;GAChB,KAAK,QAAQ,MAAM,MAAM,OAAO;GAChC,IAAI,WAAW,QAAQ,KAAA,GACrB,MAAM,IAAI,MAAM,SAAS,MAAM,KAAK,mDAAmD;GACzF,OAAO,KAAK,EAAE,GAAG,MAAM,CAAC;GACxB,IAAI,QAAQ,KAAA,GAAW,KAAK,IAAI,MAAM,MAAM,GAAG;GAC/C,IAAI,SAAS;IACX,UAAU;IACV,MAAM,SAAS;KAAE,GAAG;KAAO,MAAM,EAAE,GAAG,MAAM,KAAK;IAAE,GAAG,GAAI;GAC5D;GACA,OAAO;EACT;EACA,aAAa,QAAQ;GACnB,UAAU,mBAAmB;GAC7B,IAAI,WAAW,MAAM,MAAM,IAAI,MAAM,GAAG,KAAK,6BAA6B;GAC1E,SAAS;GACT,OAAO;EACT;EACA,QAAQ,OAAO,MAAM;GACnB,UAAU,sBAAsB,OAAO;GACvC,UAAU,IAAI,OAAO,CAAC,GAAI,UAAU,IAAI,KAAK,KAAK,CAAC,GAAI,IAAI,CAAC;GAC5D,OAAO;EACT;EACA,SAAS,MAAM;GACb,UAAU,gBAAgB,KAAK,MAAM;GACrC,KAAK,QAAQ,KAAK,MAAM,OAAO;GAE/B,OAAO,KAAK;IACV,MAAM,KAAK;IACX,YAAY;IACZ,MAAM;IACN,eAAe;IACf,OAAO,KAAK;IACZ,MAAM,EAAE,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,UAAU,KAAK,IAAI;GACnB,OAAO;EACT;EACA,UAAU,QAAQ;GAChB,UAAU,WAAW;GACrB,YAAY;GACZ,OAAO;EACT;EACA,SAAS,QAAQ;GACf,UAAU,UAAU;GACpB,WAAW;GACX,OAAO;EACT;EACA,QAAQ,QAAQ;GACd,UAAU,oBAAoB;GAC9B,UAAU;GACV,OAAO;EACT;EACA,OAAO,UAAU;GACf,UAAU,YAAY;GACtB,UAAU,KAAK,EAAE,GAAG,SAAS,CAAC;GAC9B,OAAO;EACT;EACA,MAAM,OAAO,KAAK;GAChB,UAAU,kBAAkB;GAC5B,KAAK,QAAQ,OAAO,gBAAgB;GACpC,OAAO,KAAK;IAAE,MAAM;IAAO;GAAI,CAAC;GAChC,OAAO;EACT;EACA,kBAAkB,QAAQ;GACxB,UAAU,wBAAwB;GAClC,cAAc;GACd,OAAO;EACT;EACA,WAAW;GACT,MAAM,UAAoB,CAAC;GAC3B,IAAI,cAAc,KAAA,GAAW,QAAQ,KAAK,2BAA2B;GACrE,IAAI,aAAa,KAAA,GAAW,QAAQ,KAAK,yBAAyB;GAClE,IAAI,YAAY,KAAA,GAAW,QAAQ,KAAK,gCAAgC;GACxE,IAAI,OAAO,WAAW,GAAG,QAAQ,KAAK,iCAAiC;GACvE,IAAI,gBAAgB,KAAA,GAClB,QAAQ,KAAK,2DAA2D;GAC1E,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,GAAG,KAAK,oBAAoB,QAAQ,KAAK,IAAI,EAAE,8BACjD;GACF,MAAM,QAAQ,IAAI,IAAI,aAAa,KAAK,UAAU,MAAM,IAAI,CAAC;GAC7D,MAAM,WAAqB,CAAC;GAC5B,MAAM,WAAiC;IAAa;IAAsB;GAAU;GACpF,KAAK,MAAM,QAAQ,CAAC,aAAa,UAAU,GACzC,SAAS,KAAK,GAAG,eAAe,MAAM,SAAS,KAAK,CAAC;GACvD,KAAK,MAAM,SAAS,QAAQ;IAC1B,IAAI,MAAM,eAAe,QAAQ,CAAC,MAAM,IAAI,MAAM,UAAU,GAC1D,SAAS,KACP,SAAS,MAAM,KAAK,aAAa,MAAM,WAAW,4BACpD;IAEF,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,KAAK,CAAC,OAAO,MAAM,KAAK,OAAO,GAC5D,SAAS,KAAK,SAAS,MAAM,KAAK,oCAAoC;SACjE,IAAI,CAAC,MAAM,MAAM,KAAK,QAAQ,KAAK,CAAC,MAAM,MAAM,KAAK,OAAO,GACjE,SAAS,KAAK,SAAS,MAAM,KAAK,4DAA4D;IAEhG,IAAI,EAAE,MAAM,KAAK,UAAU,KAAK,MAAM,KAAK,SAAS,IAClD,SAAS,KAAK,SAAS,MAAM,KAAK,mBAAmB,MAAM,KAAK,OAAO,yBAAyB;IAElG,MAAM,SAAS,MAAM,UAAU,OAAO,OAAO,SAAS,MAAM;IAC5D,IACE,QAAQ,SAAS,cACjB,OAAO,MAAM,KAAK,QAAQ,KAC1B,MAAM,OAAO,QAAQ,KACrB,QAAQ,MAAM,KAAK,UAAU,GAAG,MAAM,QAAQ,OAAO,UAAU,GAAG,GAElE,SAAS,KACP,SAAS,MAAM,KAAK,eAAe,QAAQ,MAAM,KAAK,UAAU,GAAG,EAAE,cAAc,MAAM,MAAM,+BAA+B,QAAQ,OAAO,UAAU,GAAG,EAAE,GAC9J;GACJ;GACA,KAAK,MAAM,cAAc,aAAa;IACpC,KAAK,MAAM,UAAU,WAAW,UAC9B,IAAI,CAAC,MAAM,IAAI,MAAM,GACnB,SAAS,KACP,cAAc,WAAW,KAAK,YAAY,OAAO,4BACnD;IAEJ,KAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,WAAW,MAAM,GAC/D,SAAS,KAAK,GAAG,kBAAkB,cAAc,WAAW,QAAQ,OAAO,SAAS,CAAC;GACzF;GACA,KAAK,MAAM,cAAc,cACvB,KAAK,MAAM,CAAC,MAAM,cAAc,OAAO,QAAQ,WAAW,UAAU,GAClE,SAAS,KAAK,GAAG,kBAAkB,cAAc,WAAW,QAAQ,MAAM,SAAS,CAAC;GACxF,KAAK,MAAM,SAAS,QAClB,IAAI,CAAC,OAAO,MAAM,GAAG,GAAG,SAAS,KAAK,SAAS,MAAM,KAAK,qBAAqB;GAEjF,MAAM,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,QAAQ,SAAS,UAAU,IAAI,KAAK,CAAC;GACtF,KAAK,IAAI,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM,GACzC,IAAI,UAAU,OAAO,GAAI,IAAI,UAAU,OAAO,KAAK,EAAG,GACpD,SAAS,KACP,cAAc,OAAO,IAAK,iBAAiB,OAAO,KAAK,GAAI,uBAAuB,OAAO,KAAK,IAAI,GACpG;GACJ,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG;GAC1E,MAAM,iBAA+B,YAAY,KAAK,WAAW;IAC/D,MAAM,MAAM;IACZ,UAAU,CAAC,GAAG,MAAM,QAAQ;IAC5B,QAAQ,EAAE,GAAG,MAAM,OAAO;GAC5B,EAAE;GACF,OAAO;IACL,QAAQ;IACR;IACA;IACA,YAAY,WAAW,KAAK,WAAW;KAAE,GAAG;KAAO,OAAO,EAAE,GAAG,MAAM,MAAM;IAAE,EAAE;IAC/E,cAAc,aAAa,KAAK,WAAW;KACzC,GAAG;KACH,OAAO,EAAE,GAAG,MAAM,MAAM;KACxB,YAAY,EAAE,GAAG,MAAM,WAAW;IACpC,EAAE;IACF,aAAa;IACb,QAAQ,OAAO,KAAK,WAAW;KAAE,GAAG;KAAO,MAAM,EAAE,GAAG,MAAM,KAAK;IAAE,EAAE;IACrE,UAAU;KAAa;KAAsB;KAAoB;IAAS;IAC1E,WAAW,UAAU,KAAK,WAAW,EAAE,GAAG,MAAM,EAAE;IAClD,UAAU;KACR,QAAQ,OAAO,KAAK,WAAW,EAAE,GAAG,MAAM,EAAE;KAC5C,mBAAmB;IACrB;GACF;EACF;EACA,MAAM,UAAU,CAAC,GAAG;GAElB,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,uBAAO,IAAI,MAAM,GAAG,KAAK,oCAAoC,CAAC;GACxG,IAAI;GACJ,IAAI;IACF,OAAO,YAAY,OAAO,CAAC,CAAC;GAC9B,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,KAAK;GAC7B;GACA,UAAU;GACV,OAAO;GACP,UAAU,KAAK,OAAO,CAAC,CAAC,WAAW,IAAI;GACvC,OAAO;EACT;EACA,IAAI,UAAU,CAAC,GAAG;GAChB,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,uBAAO,IAAI,MAAM,GAAG,KAAK,2DAA2D,CAAC;GAC/H,IAAI;GACJ,IAAI;IACF,MAAM,cAAc,QAAQ,SAAS;IACrC,MAAM,aAAyB;KAC7B,YAAY;KACZ,cAAc;KACd,cAAc;KACd;KACA,mBAAmB,CAAC;IACtB;IACA,MAAM,WAAW,kBAAkB,aAAa,UAAU;IAC1D,IAAI,SAAS,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG;IAC1E,MAAM,OAAO,YAAY,OAAO;IAChC,UAAU;IACV,OAAO,KAAK;IACZ,OAAO,UAAU,KAAK,aAAa,KAAK,SAAS;KAAE,GAAG;KAAY,aAAa,KAAK;IAAY,GAAG,OAAO;GAC5G,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,KAAK;GAC7B;GACA,UAAU,KAAK,QAAQ,WAAW,IAAI;GACtC,OAAO;EACT;CACF;CACA,OAAO;AACT;;;AC/YA,MAAM,SAAS,UACb,UAAU,SAAS,UAAU,QAAQ,WAAW,OAAO,KAAK;AAC9D,MAAM,SAAS,SACb,KAAK,OAAO,KAAA,IAAY,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,KAAK;;AAE3D,MAAMC,aAAW,MAAe,QAAmC;CACjE,MAAM,QAAQ,KAAK;CACnB,OAAO,OAAO,UAAU,YAAY,UAAU,SAAS,UAAU,QAAQ,QAAQ,KAAA;AACnF;;AAGA,MAAa,gBAAgB,SAAsC,CACjE,KAAK,MACL,GAAI,KAAK,aAAa,CAAC,CACzB;;AAGA,MAAa,aAAa,SAAyB;CACjD,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,cAAc,aAAa,IAAI,GACxC,KAAK,MAAM,QAAQ,CAAC,GAAG,WAAW,IAAI,OAAO,GAAG,WAAW,IAAI,KAAK,GAAG;EACrE,MAAM,MAAM,MAAM,IAAI;EACtB,IAAI,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,KAAK,GAAG;CACtC;CAEF,OAAO;AACT;;AAGA,MAAM,UAAU,MAAY,SAAiC;CAC3D,IAAI,QAAQ,KAAK,SAAS;CAC1B,KAAK,MAAM,QAAQ,KAAK,OAAO;EAC7B,MAAM,QAAQA,UAAQ,MAAM,MAAM,IAAI,CAAC;EACvC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,SAAS,KAAK,OAAO,MAAM,KAAK;CAClC;CACA,OAAO;AACT;;AAGA,MAAM,YAAY,YAAwB,OAAe,SAA0B;CACjF,IAAI,MAAM,QAAQ,KAAK,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,UAAU,CAAC;CACvE,IAAI,OAAO,KAAK,WAAW,UAAU,OAAO,KAAK;CACjD,OAAO,OAAO,WAAW,WAAW,WAAW,WAAW,SAAS;AACrE;;;;;;AAOA,MAAa,YAAY,MAAY,SACnC,aAAa,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU;CAC5C,MAAM,MAAM,OAAO,WAAW,KAAK,IAAI;CACvC,MAAM,MAAM,OAAO,WAAW,KAAK,IAAI;CACvC,MAAM,SAAS,SAAS,YAAY,OAAO,IAAI;CAC/C,IAAI,QAAQ,QAAQ,QAAQ,MAAM,OAAO;EAAE;EAAK;EAAK;EAAQ,OAAO;CAAK;CASzE,OAAO;EAAE;EAAK;EAAK;EAAQ,OAPzB,WAAW,OAAO,MACd,MAAM,SAAS,MACf,WAAW,OAAO,OAChB,MAAM,UAAU,MAChB,WAAW,OAAO,MAChB,MAAM,MAAM,SACZ,OAAO,MAAM;CACU;AACnC,CAAC;;;;;;;AAQH,MAAa,aAAa,MAAY,MAAe,cAA6C;CAChG,MAAM,aAAa,UAAU,SAAS,OAAO,UAAW,MAAM,UAAU,OAAO,CAAC,IAAI,CAAC,KAAK,CAAE;CAC5F,IAAI,WAAW,WAAW,GAAG,OAAO;CACpC,IAAI,WAAW,MAAM,UAAU,UAAU,MAAM,EAAE,UAAU,KAAK,GAAG,OAAO;CAC1E,MAAM,MAAM,aAAa,IAAI;CAQ7B,OAPiB,WAAW,MAAM,UAAU;EAC1C,MAAM,aAAa,IAAI;EACvB,OAAO,CAAC,GAAG,WAAW,IAAI,OAAO,GAAG,WAAW,IAAI,KAAK,CAAC,CAAC,MAAM,SAAS;GACvE,MAAM,QAAQA,UAAQ,MAAM,MAAM,IAAI,CAAC;GACvC,OAAO,UAAU,KAAA,KAAa,MAAM,KAAK,MAAM;EACjD,CAAC;CACH,CACc,IAAI,cAAc;AAClC;;;;AC3GA,MAAa,gBAAmC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,aAAa,WACjBC,UAAE,SAAS,aAAaA,UAAE,WAAW,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,CAAC;;AAEvE,MAAM,WAAW,WACf,OAAO,UAAU,aAAa,QAAQ,UAAU,MAAM;;AAExD,MAAM,QAAQ,WACZ,OAAO,WAAW,IAAI,KAAA,IAAY,KAAK,IAAI,GAAG,MAAM;;AAGtD,MAAa,mBACX,KACA,aACoF;CACpF,MAAM,OAA8B,CAAC;CACrC,MAAM,OAA+B,CAAC;CACtC,MAAM,OAAO,QAAgB,OAA0B,SAAuB;EAC5E,IAAI,UAAU,KAAA,GAAW;EACzB,KAAK,UAAU;EACf,KAAK,UAAU;CACjB;CACA,MAAM,SAAS,IAAI,OAAO,QAAQ,UAAU,MAAM,UAAU,IAAI;CAChE,MAAM,kBAAkB,OAAO,QAAQ,UAAU,MAAM,UAAU,WAAW;CAC5E,MAAM,iBAAiB,OAAO,QAAQ,UAAU,MAAM,UAAU,UAAU;CAC1E,IACE,+BACA,KAAK,OAAO,KAAK,UAAU,UAAU,MAAM,KAAK,QAAQ,CAAC,CAAC,GAC1D,qCACF;CACA,IACE,yBACA,KAAK,OAAO,KAAK,UAAU,UAAU,MAAM,KAAK,OAAO,CAAC,CAAC,GACzD,oCACF;CACA,IACE,wBACA,KACE,OAAO,KACJ,UACC,UAAU,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM,KAAK,QAAQ,KAAK,IAAI,MAAM,KAAK,OACrF,CACF,GACA,8DACF;CAEA,MAAM,YAAY,QAAuB,WACvC,KACE,OAAO,KAAK,UAAU,UAAU,SAAS,IAAI,SAAS,SAAS,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,CAAC,CAC5F;CACF,MAAM,YAAY,SAAS,iBAAiB,WAAW;CACvD,MAAM,WAAW,SAAS,gBAAgB,UAAU;CACpD,IAAI,sCAAsC,WAAW,yCAAyC;CAC9F,IAAI,cAAc,KAAA,GAChB,IACE,kCACA,YAAY,UAAU,SAAS,OAAO,SAAS,GAC/C,+CACF;CAEF,IAAI,aAAa,KAAA,GACf,IACE,iCACA,WAAW,UAAU,SAAS,OAAO,QAAQ,GAC7C,+CACF;CAEF,IAAI,sBAAsB,UAAU,IAAI,SAAS,OAAO,GAAG,6BAA6B;CACxF,IACE,8BACA,IAAI,SAAS,OAAO,QAAQ,OAAO,UAAU,QAAQ,UAAU,MAAM,GAAG,GAAG,CAAC,GAC5E,sCAAsC,IAAI,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAClG;CACA,IACE,8BACA,QAAQ,IAAI,SAAS,iBAAiB,GACtC,iCACF;CAEA,MAAM,OAAO,IAAI,SAAS,OAAO,MAAM,UAAU,MAAM,SAAS,gBAAgB;CAChF,IACE,0BACA,SAAS,KAAA,IAAY,IAAI,UAAU,KAAK,GAAG,GAC3C,SAAS,KAAA,IACL,uEACA,6EACN;CACA,KAAK,MAAM,CAAC,QAAQ,WAAW,OAAO,QAAQ,SAAS,QAAQ,GAC7D,IAAI,QAAQ,QAAQ,MAAM,GAAG,cAAc;CAC7C,OAAO;EAAE;EAAM;CAAK;AACtB;;AAGA,MAAM,sBAAsB,YAAwB,aAA8C;CAChG,MAAM,OAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,WAAW,KAAK,GAC1D,KAAK,GAAG,KAAK,YAAY,QAAQ,MAAM;CACzC,KAAK,MAAM,CAAC,QAAQ,WAAW,OAAO,QAAQ,SAAS,eAAe,WAAW,SAAS,CAAC,CAAC,GAC1F,KAAK,OAAO,SAAS,GAAG,IAAI,SAAS,GAAG,OAAO,gBAAgB,QAAQ,MAAM;CAE/E,OAAO;AACT;AAEA,MAAM,SAAS;;AAGf,MAAa,QACX,KACA,UACA,UACc;CACd,MAAM,UAAU,gBAAgB,KAAK,QAAQ,CAAC,CAAC;CAC/C,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,cAAc,SAAS,KAAK,EAAE,GAAG;EACtC,MAAM,UAAU,UAAU,IAAI;EAC9B,MAAM,SAAmC,QAAQ,MAAM,WAAW,OAAO,KAAK,MAAM,CAAC,IACjF,IAAI,aAAa,KAAK,eAAe,CACnC,WAAW,MACX;GAAE,GAAG;GAAS,GAAG,mBAAmB,YAAY,QAAQ;EAAE,CAC5D,CAAC,IACD,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC;EACxB,KAAK,MAAM,CAAC,OAAO,SAAS,QAAQ;GAClC,MAAM,YAAY,SAAS,MAAM,IAAI;GACrC,SAAS,KAAK;IACZ,MAAM,KAAK;IACX;IACA,SAAS,UAAU,MAAM,MAAM,SAAS;IACxC,WAAW,KAAK;IAChB,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;IAC3D;IACA,SAAS,QAAQ,QAAQ,WAAW,KAAK,YAAY,KAAA,CAAS;GAChE,CAAC;EACH;CACF;CACA,OAAO;AACT;;;;;;;;AC3LA,MAAM,SAAS,CAAC;CAAE,KAAK;CAA+D,MAAM;AAAuB,CAAC;AACpH,MAAM,QAAQ,IAAY,WAA+F;CACvH,QAAQ;CACR,IAAI,mBAAmB;CACvB;CACA,OAAO;CACP,SAAS;CACT,SAAS;AACX;AACA,MAAM,KACJ,IACA,MACA,IACA,IACA,QAAgC,CAAC,OACd;CAAE;CAAI;CAAM;CAAI;CAAI,GAAG;AAAM;;;;;;AAOlD,MAAa,eAAwB;CACnC,GAAG,KAAK,QAAQ,kBAAkB;CAClC,QAAQ;EACN,QAAQ,EAAE,SAAS,KAAK;EACxB,UAAU,EAAE,MAAM,gEAAgE;EAClF,UAAU,EAAE,MAAM,0CAA0C;EAC5D,MAAM,EAAE,OAAO,KAAK;EACpB,QAAQ,EAAE,OAAO,KAAK;CACxB;CACA,QAAQ;EACN,iBAAiB,EAAE,MAAM,QAAQ;EACjC,qBAAqB,EAAE,MAAM,QAAQ;EACrC,cAAc,EAAE,MAAM,QAAQ;EAC9B,cAAc,EAAE,MAAM,QAAQ;EAC9B,gBAAgB,EAAE,MAAM,QAAQ;EAChC,uBAAuB,EAAE,MAAM,QAAQ;CACzC;CACA,OAAO,EAAE,SAAS;EAAE,KAAK;EAAoB,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,EAAE;CAC1F,QAAQ,EACN,gBAAgB;EACd,UAAU;GAAE,MAAM;GAAW,MAAM;EAAI;EACvC,MAAM,CAAC,cAAc;EACrB,SAAS,CAAC,cAAc,cAAc;EACtC,OAAO;EACP,MAAM;CACR,EACF;CACA,aAAa;EACX,EAAE,SAAS,UAAU,iBAAiB,UAAU;EAChD,EAAE,aAAa,YAAY,qBAAqB,UAAU;EAC1D,EAAE,cAAc,CAAC,UAAU,UAAU,GAAG,cAAc,UAAU;EAChE,EAAE,QAAQ,YAAY,cAAc,MAAM;EAC1C,EAAE,QAAQ;GAAC;GAAU;GAAY;EAAU,GAAG,gBAAgB,QAAQ;EAEtE,EAAE,sBAAsB,UAAU,uBAAuB,UAAU,EAAE,OAAO,iBAAiB,CAAC;EAC9F,EAAE,wBAAwB,YAAY,uBAAuB,YAAY,EAAE,OAAO,iBAAiB,CAAC;EACpG,EAAE,wBAAwB,YAAY,uBAAuB,YAAY,EAAE,OAAO,iBAAiB,CAAC;CACtG;AACF;;AAGA,MAAa,gBAAyB;CACpC,GAAG,KAAK,SAAS,sCAAsC;CACvD,QAAQ;EACN,SAAS;GAAE,SAAS;GAAM,MAAM;EAAkE;EAClG,UAAU,CAAC;CACb;CACA,QAAQ;EAAE,WAAW,EAAE,MAAM,QAAQ;EAAG,aAAa,EAAE,MAAM,QAAQ;CAAE;CACvE,OAAO;EACL,YAAY;GAAE,KAAK;GAA6B,OAAO;GAAS,MAAM;GAAS,SAAS;EAAK;EAC7F,eAAe;GAAE,KAAK;GAAgC,OAAO;GAAS,MAAM;GAAS,SAAS;EAAK;CACrG;CACA,QAAQ,CAAC;CACT,aAAa;EACX,EAAE,QAAQ,YAAY,WAAW,UAAU;EAC3C,EAAE,cAAc,YAAY,aAAa,YAAY,EAAE,OAAO,oCAAoC,CAAC;EACnG,EAAE,QAAQ,YAAY,aAAa,WAAW,EAAE,OAAO,qCAAqC,CAAC;EAC7F,EAAE,QAAQ,WAAW,aAAa,SAAS;EAC3C,EAAE,cAAc,WAAW,WAAW,WAAW,EAAE,OAAO,qCAAqC,CAAC;EAChG,EAAE,WAAW,WAAW,WAAW,YAAY,EAAE,OAAO,sCAAsC,CAAC;CACjG;AACF;;;;;AAMA,MAAa,iBAA0B;CACrC,GAAG,KAAK,UAAU,oCAAoC;CACtD,QAAQ;EACN,UAAU;GAAE,SAAS;GAAM,MAAM;EAAiE;EAClG,SAAS,EAAE,MAAM,uBAAuB;EACxC,SAAS,CAAC;EACV,YAAY,EAAE,MAAM,0BAA0B;CAChD;CACA,QAAQ;EACN,WAAW,EAAE,MAAM,QAAQ;EAC3B,aAAa,EAAE,MAAM,QAAQ;EAC7B,iBAAiB,EAAE,MAAM,QAAQ;EACjC,oBAAoB,EAAE,MAAM,QAAQ;CACtC;CACA,OAAO;EACL,YAAY;GAAE,KAAK;GAA6B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;EACzF,eAAe;GAAE,KAAK;GAAgC,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;CACjG;CACA,QAAQ;EACN,aAAa;GACX,UAAU;IAAE,MAAM;IAAc,MAAM;GAAI;GAC1C,MAAM,CAAC,eAAe;GACtB,SAAS,CAAC,SAAS;GACnB,OAAO;EACT;EACA,gBAAgB;GACd,UAAU;IAAE,MAAM;IAAiB,MAAM;GAAI;GAC7C,MAAM,CAAC,kBAAkB;GACzB,SAAS,CAAC,WAAW;GACrB,OAAO;EACT;CACF;CACA,aAAa;EACX,EAAE,QAAQ,YAAY,WAAW,UAAU;EAC3C,EAAE,iBAAiB,YAAY,aAAa,WAAW,EAAE,OAAO,iBAAiB,CAAC;EAClF,EAAE,gBAAgB,YAAY,aAAa,WAAW,EAAE,OAAO,iBAAiB,CAAC;EACjF,EAAE,iBAAiB,WAAW,aAAa,SAAS;EACpD,EAAE,gBAAgB,WAAW,WAAW,UAAU;EAClD,EAAE,QAAQ,WAAW,iBAAiB,WAAW,EAAE,OAAO,cAAc,CAAC;EACzE,EAAE,QAAQ,WAAW,aAAa,SAAS;EAC3C,EAAE,mBAAmB,WAAW,WAAW,YAAY,EAAE,OAAO,+BAA+B,CAAC;EAChG,EAAE,oBAAoB,WAAW,WAAW,cAAc,EAAE,OAAO,oBAAoB,CAAC;EACxF,EAAE,oBAAoB,cAAc,WAAW,YAAY;EAC3D,EAAE,mBAAmB,cAAc,aAAa,SAAS;EACzD,EAAE,WAAW,cAAc,oBAAoB,YAAY,EAAE,OAAO,iBAAiB,CAAC;CACxF;AACF;;;;;AAMA,MAAa,iBAA0B;CACrC,GAAG,KAAK,UAAU,uBAAuB;CACzC,QAAQ;EACN,MAAM;GAAE,SAAS;GAAM,MAAM;EAAyB;EACtD,SAAS,EAAE,MAAM,8CAA8C;EAC/D,SAAS,EAAE,MAAM,8BAA8B;EAC/C,MAAM,EAAE,MAAM,uDAAuD;EACrE,SAAS,EAAE,OAAO,KAAK;CACzB;CACA,QAAQ;EACN,OAAO,EAAE,MAAM,QAAQ;EACvB,OAAO,EAAE,MAAM,QAAQ;EACvB,MAAM,EAAE,MAAM,QAAQ;EACtB,QAAQ,EAAE,MAAM,QAAQ;EACxB,SAAS,EAAE,MAAM,QAAQ;EACzB,eAAe,EAAE,MAAM,QAAQ;EAC/B,MAAM,EAAE,MAAM,QAAQ;CACxB;CACA,OAAO;EACL,UAAU;GAAE,KAAK;GAA+B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;EACzF,SAAS;GAAE,KAAK;GAA8B,OAAO;GAAS,MAAM;GAAK,SAAS;EAAK;CACzF;CACA,QAAQ;EACN,MAAM;GACJ,UAAU;IAAE,MAAM;IAAY,MAAM;GAAI;GACxC,MAAM,CAAC,SAAS,MAAM;GACtB,SAAS,CAAC,MAAM;GAChB,OAAO;GACP,MAAM;EACR;EACA,cAAc;GACZ,UAAU;IAAE,MAAM;IAAW,MAAM;GAAI;GACvC,MAAM,CAAC,eAAe;GACtB,SAAS,CAAC,UAAU,MAAM;GAC1B,OAAO;EACT;CACF;CACA,aAAa;EACX,EAAE,SAAS,QAAQ,SAAS,SAAS;EACrC,EAAE,SAAS,QAAQ,SAAS,SAAS;EACrC,EAAE,uBAAuB,WAAW,SAAS,WAAW,EAAE,MAAM,mDAAmD,CAAC;EACpH,EAAE,oBAAoB,QAAQ,SAAS,MAAM;EAC7C,EAAE,QAAQ,WAAW,QAAQ,WAAW,EAAE,OAAO,8CAA8C,CAAC;EAChG,EAAE,eAAe,WAAW,QAAQ,WAAW,EAAE,OAAO,sCAAsC,CAAC;EAC/F,EAAE,gBAAgB,WAAW,QAAQ,WAAW,EAAE,MAAM,8CAA8C,CAAC;EACvG,EAAE,aAAa,QAAQ,QAAQ,QAAQ,EAAE,MAAM,8CAA8C,CAAC;EAC9F,EAAE,eAAe,WAAW,UAAU,WAAW,EAAE,OAAO,gBAAgB,CAAC;EAC3E,EAAE,gBAAgB,WAAW,UAAU,QAAQ,EAAE,OAAO,2BAA2B,CAAC;EACpF,EAAE,WAAW,WAAW,WAAW,QAAQ,EAAE,OAAO,eAAe,CAAC;EACpE,EAAE,oBAAoB,QAAQ,eAAe,WAAW,EAAE,OAAO,gBAAgB,CAAC;EAClF,EAAE,qBAAqB,QAAQ,eAAe,QAAQ,EAAE,OAAO,2BAA2B,CAAC;EAC3F,EAAE,QAAQ;GAAC;GAAQ;GAAW;GAAW;EAAM,GAAG,QAAQ,SAAS;EACnE,EAAE,qBAAqB,WAAW;GAAC;GAAU;GAAe;EAAS,GAAG,SAAS;CACnF;AACF;;;;;AAMA,MAAa,eAAwB;CACnC,GAAG,KAAK,QAAQ,kBAAkB;CAClC,QAAQ;EAAE,SAAS,EAAE,SAAS,KAAK;EAAG,UAAU,CAAC;EAAG,QAAQ,EAAE,OAAO,KAAK;CAAE;CAC5E,QAAQ;EACN,gBAAgB;GAAE,MAAM;GAAO,MAAM;EAAyC;EAC9E,eAAe,EAAE,MAAM,MAAM;EAC7B,gBAAgB,EAAE,MAAM,QAAQ;EAChC,OAAO,EAAE,MAAM,QAAQ;EACvB,iBAAiB,EAAE,MAAM,QAAQ;CACnC;CACA,OAAO,CAAC;CACR,QAAQ,CAAC;CACT,aAAa;EACX,EAAE,WAAW,WAAW,gBAAgB,UAAU;EAClD,EAAE,gBAAgB,WAAW,gBAAgB,UAAU;EACvD,EAAE,iBAAiB,WAAW,SAAS,SAAS;EAChD,EAAE,UAAU,YAAY,eAAe,UAAU;EACjD,EAAE,yBAAyB,YAAY,gBAAgB,UAAU;EACjE,EAAE,SAAS,YAAY,SAAS,UAAU;EAC1C,EAAE,QAAQ,YAAY,iBAAiB,UAAU,EAAE,MAAM,4CAA4C,CAAC;CACxG;AACF;;AAGA,MAAa,mBAA4B;CACvC,GAAG,KAAK,YAAY,gCAAgC;CACpD,QAAQ;EAAE,UAAU,EAAE,SAAS,KAAK;EAAG,SAAS,CAAC;EAAG,SAAS,EAAE,OAAO,KAAK;CAAE;CAC7E,QAAQ;EAAE,MAAM,EAAE,MAAM,QAAQ;EAAG,MAAM,EAAE,MAAM,QAAQ;CAAE;CAC3D,OAAO;EACL,MAAM;GAAE,KAAK;GAAiB,OAAO;GAAW,MAAM;GAAK,SAAS;EAAK;EACzE,WAAW;GAAE,KAAK;GAAsB,OAAO;GAAW,MAAM;GAAK,SAAS;EAAK;CACrF;CACA,QAAQ,EACN,MAAM;EAAE,UAAU;GAAE,MAAM;GAAQ,MAAM;EAAI;EAAG,MAAM,CAAC,kBAAkB,MAAM;EAAG,SAAS,CAAC,MAAM;EAAG,OAAO;CAAO,EACpH;CACA,aAAa;EACX,EAAE,WAAW,YAAY,QAAQ,YAAY,EAAE,OAAO,wBAAwB,CAAC;EAC/E,EAAE,SAAS,YAAY,QAAQ,WAAW,EAAE,OAAO,uBAAuB,CAAC;EAC3E,EAAE,iBAAiB,WAAW,QAAQ,WAAW,EAAE,OAAO,uBAAuB,CAAC;EAClF,EAAE,WAAW,WAAW,QAAQ,YAAY,EAAE,OAAO,wBAAwB,CAAC;EAC9E,EAAE,QAAQ,CAAC,YAAY,SAAS,GAAG,QAAQ,SAAS;CACtD;AACF;;AAGA,MAAa,WAAW,UAA0B,GAAG,MAAM,QAAQ,kBAAkB,GAAG,EAAE;;;;;;AAO1F,MAAa,gBAAgB,gBAA6C;CACxE,MAAM,SAAS,YAAY,SAAS;CACpC,MAAM,SAAgF,EACpF,SAAS,EAAE,SAAS,KAAK,EAC3B;CACA,KAAK,MAAM,SAAS,QAAQ,OAAO,MAAM,QAAQ,CAAC;CAClD,OAAO,UAAU,EAAE,OAAO,KAAK;CAC/B,MAAM,QAA0B,OAAO,YAAY,CACjD,GAAG,OAAO,KAAK,UAAU,CACvB,QAAQ,MAAM,IAAI,GAClB;EAAE,KAAK,mBAAmB,MAAM,KAAK;EAAO,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,CACzF,CAAC,GACD,CAAC,sBAAsB;EAAE,KAAK;EAA8B,OAAO;EAAW,MAAM;EAAK,SAAS;CAAK,CAAC,CAC1G,CAAC;CACD,MAAM,SAAoD,CAAC;CAC3D,KAAK,MAAM,SAAS,QAClB,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE,WAAW;EACvC,UAAU;GAAE,MAAM,QAAQ,MAAM,IAAI;GAAG,MAAM;EAAI;EACjD,MAAM,CAAC,SAAS,MAAM,MAAM;EAC5B,SAAS,CAAC,YAAY;EACtB,OAAO;CACT;CACF,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,OAAO,GAC/C,OAAO,uBAAuB;EAC5B,UAAU;GAAE,MAAM;GAAsD,MAAM;EAAI;EAClF,MAAM,CAAC,aAAa;EACpB,SAAS,CAAC,cAAc,WAAW;EACnC,OAAO;CACT;CACF,MAAM,cAA+B,CAAC;CACtC,MAAM,QAAQ;EAAC;EAAW,GAAG,OAAO,KAAK,UAAU,MAAM,IAAI;EAAG;CAAM;CACtE,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG;EAC/C,MAAM,OAAO,MAAM;EACnB,MAAM,KAAK,MAAM,KAAK;EACtB,YAAY,KACV,OAAO,IACH,EAAE,SAAS,MAAM,gBAAgB,EAAE,IACnC,EAAE,SAAS,QAAQ,MAAM,CAAC,cAAc,WAAW,GAAG,IAAI;GACxD,OAAO,GAAG,QAAQ,IAAI,EAAE;GACxB,MAAM;EACR,CAAC,CACP;CACF;CACA,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,OAAO,GAC/C,YAAY,KAAK,EAAE,qBAAqB,SAAS,2BAA2B,SAAS,EAAE,OAAO,oBAAoB,CAAC,CAAC;CAGtH,KAAK,MAAM,SAAS,CAAC,GAAG,OAAO,KAAK,UAAU,MAAM,IAAI,GAAG,MAAM,GAC/D,YAAY,KAAK,EAAE,gBAAgB,SAAS,OAAO,gBAAgB,KAAK,CAAC;CAC3E,OAAO;EACL,GAAG,KAAK,SAAS,gBAAgB,YAAY,MAAM;EACnD;EACA,QAAQ;GACN,gBAAgB,EAAE,MAAM,MAAM;GAC9B,cAAc,EAAE,MAAM,QAAQ;GAC9B,aAAa,EAAE,MAAM,QAAQ;GAC7B,2BAA2B,EAAE,MAAM,QAAQ;GAC3C,gBAAgB,EAAE,MAAM,QAAQ;EAClC;EACA;EACA;EACA;CACF;AACF;;AAGA,MAAa,WAAgF;CAC3F,MAAM;CACN,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,UAAU;AACZ;;;;;;;AAQA,MAAa,UAAU,KAAc,WAAgD;CACnF,MAAM,WAAqB,CAAC;CAC5B,MAAM,qBAAK,IAAI,IAAoB;CACnC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK,MAAM;EAC9C,IAAI,IAAI,OAAO,WAAW,KAAA,GAAW,SAAS,KAAK,GAAG,MAAM,SAAS,cAAc,MAAM,aAAa,IAAI,IAAI;EAC9G,IAAI,MAAM,SAAS,OACjB,SAAS,KAAK,GAAG,MAAM,SAAS,qBAAqB,MAAM,KAAK,MAAM,MAAM,GAAG,yBAAyB,OAAO;EAIjH,IAAI,CAHY,IAAI,YAAY,MAC7B,SAAS,WAAW,KAAK,KAAK,IAAI,CAAC,CAAC,SAAS,MAAM,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,KAAK,OAAO,MAAM,EAEzG,GAAG,SAAS,KAAK,GAAG,MAAM,SAAS,IAAI,IAAI,GAAG,0BAA0B,MAAM,KAAK,MAAM,MAAM,GAAG,MAAM,MAAM,IAAI;EAC7H,GAAG,IAAI,MAAM,UAAU,MAAM,EAAE;CACjC;CACA,OAAO;AACT"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/service",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "description": "A service declared as data before it starts, and its runtime: checks on jittered loops, a latch for each policy, probes from the cache, the startup in its sequence, and a staged, capped drain. The timer-ordering law checks the declaration.",
6
6
  "keywords": [
7
7
  "service",
@@ -52,8 +52,8 @@
52
52
  "node": ">=22.19"
53
53
  },
54
54
  "dependencies": {
55
- "@mark1russell7/algebra": "^0.2.0",
56
- "@mark1russell7/quantity": "^0.1.0"
55
+ "@mark1russell7/algebra": "^0.4.0",
56
+ "@mark1russell7/quantity": "^0.2.0"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "ioredis": "^6.0.0",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@mark1russell7/cue": "github:mark1russell7/cue",
69
- "@mark1russell7/harness": "^0.2.0",
69
+ "@mark1russell7/harness": "^0.6.0",
70
70
  "@types/node": "^26.6.4",
71
71
  "ioredis": "6.0.0",
72
72
  "testcontainers": "12.2.0",