@mark1russell7/service 0.5.13 → 0.5.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../src/fakes.ts","../src/contract.ts","../src/conformance.ts","../src/testing.ts"],"sourcesContent":["// The fakes of the runtime for tests: a fake clock gives exact, repeatable timing, a seeded random\n// gives the same jitter on each run, and a fake process takes signals and keeps the exit codes.\n\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { ExitSignal, ExitTrigger, ProcessPort } from \"./process.ts\";\n\n/** A fake clock moves only when a test tells it to. */\nexport interface FakeClock extends Clock {\n /** This method moves the clock forward by `ms`, and it starts each timer that comes due, in sequence. */\n advance(ms: number): Promise<void>;\n /**\n * This method moves the clock forward by `ms` and starts no timer, as a blocked event loop does.\n * The next `advance` then starts each timer that came due during the block, late.\n */\n block(ms: number): void;\n /** This count is the number of timers that are not stopped and not called. */\n readonly pending: number;\n}\n\n/** This function lets the promises that are ready continue, before the clock moves again. */\nconst settle = async (): Promise<void> => {\n for (let turn = 0; turn < 64; turn += 1) await Promise.resolve();\n};\n\n/** This function makes a fake clock. Its time starts at `start` milliseconds. */\nexport const fakeClock = (start = 0): FakeClock => {\n let now = start;\n let sequence = 0;\n const timers: Array<{ at: number; order: number; run: () => void; live: boolean }> = [];\n const due = (end: number) => {\n let best: (typeof timers)[number] | undefined;\n for (const timer of timers)\n if (timer.live && timer.at <= end && (best === undefined || timer.at < best.at || (timer.at === best.at && timer.order < best.order)))\n best = timer;\n return best;\n };\n return {\n now: () => now,\n after(ms, run): Cancel {\n const timer = { at: now + Math.max(0, ms), order: sequence++, run, live: true };\n timers.push(timer);\n return () => {\n timer.live = false;\n };\n },\n async advance(ms) {\n // a monotonic clock does not go back\n const end = now + Math.max(0, ms);\n await settle();\n for (;;) {\n const timer = due(end);\n if (timer === undefined) break;\n timer.live = false;\n // a timer that came due during a block runs late, at the time of the clock\n now = Math.max(now, timer.at);\n timer.run();\n await settle();\n }\n now = Math.max(now, end);\n for (let at = timers.length - 1; at >= 0; at -= 1) if (!timers[at]!.live) timers.splice(at, 1);\n await settle();\n },\n block(ms) {\n now += Math.max(0, ms);\n },\n get pending() {\n return timers.filter((timer) => timer.live).length;\n },\n };\n};\n\n/** A fake process receives triggers from a test, and keeps each exit code in place of an exit. */\nexport interface FakeProcess extends ProcessPort {\n /** This method sends a signal to each handler of it. */\n signal(name: ExitSignal): void;\n /** This method sends a trigger to each handler of it: a signal, or a process trigger, for example `stdin-close`. */\n trigger(name: ExitTrigger): void;\n /** This method sends a message of the IPC channel to each handler of the trigger `ipc-message`. */\n message(value: unknown): void;\n /** These are the exit codes, in sequence. A correct runtime exits one time. */\n readonly exits: readonly number[];\n /** This count is the number of handlers that are registered. */\n readonly handlers: number;\n /** These are the triggers that have a handler, in the sequence of their first handler. */\n readonly triggers: readonly ExitTrigger[];\n /** These are the times of the clock of the options at each flush and each exit, when the options give a clock. */\n readonly log: readonly string[];\n /** This count is the number of flushes of the output. */\n readonly flushes: number;\n /** This count is the number of holds of the process that are not released. */\n readonly holds: number;\n /** These counts are the holds that were not released at each exit, in sequence. */\n readonly heldAtExit: readonly number[];\n}\n\n/** These options shape a fake process. */\nexport interface FakeProcessOptions {\n /** This function is the flush of the output, for example a wait on a fake clock. Without it, a flush ends at once. */\n readonly flush?: (() => Promise<void> | void) | undefined;\n /** This clock stamps each line of the log. */\n readonly clock?: Clock | undefined;\n}\n\n/** This function makes a fake process. */\nexport const fakeProcess = (options: FakeProcessOptions = {}): FakeProcess => {\n const handlers = new Map<ExitTrigger, Set<(message?: unknown) => void>>();\n const exits: number[] = [];\n const log: string[] = [];\n let flushes = 0;\n let holds = 0;\n const heldAtExit: number[] = [];\n const stamp = (what: string): void => {\n log.push(options.clock === undefined ? what : `${options.clock.now()} ${what}`);\n };\n const send = (name: ExitTrigger, message?: unknown): void => {\n for (const handler of [...(handlers.get(name) ?? [])]) handler(message);\n };\n return {\n on(trigger, handler) {\n const set = handlers.get(trigger) ?? new Set();\n set.add(handler);\n handlers.set(trigger, set);\n return () => {\n set.delete(handler);\n };\n },\n async flush() {\n stamp(\"flush\");\n await options.flush?.();\n flushes += 1;\n stamp(\"flushed\");\n },\n hold() {\n holds += 1;\n let held = true;\n return () => {\n if (!held) return;\n held = false;\n holds -= 1;\n };\n },\n exit(code) {\n stamp(`exit ${code}`);\n heldAtExit.push(holds);\n exits.push(code);\n },\n signal: (name) => send(name),\n trigger: (name) => send(name),\n message: (value) => send(\"ipc-message\", value),\n get exits() {\n return [...exits];\n },\n get handlers() {\n let count = 0;\n for (const set of handlers.values()) count += set.size;\n return count;\n },\n get triggers() {\n return [...handlers.keys()];\n },\n get log() {\n return [...log];\n },\n get flushes() {\n return flushes;\n },\n get holds() {\n return holds;\n },\n get heldAtExit() {\n return [...heldAtExit];\n },\n };\n};\n\n/** This function gives a seeded random number generator (mulberry32), with numbers in [0, 1). */\nexport const seeded = (seed: number): (() => number) => {\n let state = seed >>> 0;\n return () => {\n state = (state + 0x6d2b79f5) >>> 0;\n let t = state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { ServiceBuilder } from \"./builder.ts\";\nimport type { Check, ServiceDescription } from \"./description.ts\";\nimport type { FakeClock } from \"./fakes.ts\";\nimport { fakeClock, seeded } from \"./fakes.ts\";\nimport type { Health, HealthVerdict, StartOptions } from \"./health.ts\";\nimport { failSpan, recoverSpan } from \"./timing.ts\";\n\n/** A contract case is one generated case of the contract test of a declaration. It throws on a failure. */\nexport interface ContractCase {\n readonly name: string;\n run(): Promise<void>;\n}\n\n/** These options tune the contract test. */\nexport interface ContractOptions {\n /** This number is the latency that a degraded dependency adds to each check, in milliseconds. The default of the contract is 100. */\n readonly degradeBy?: number | undefined;\n /** This number is the seed of the jitter. */\n readonly seed?: number | undefined;\n}\n\nconst ms = (amount: { readonly value: number; readonly unit: string }): number => valueIn(amount, \"ms\");\n\n/** The part of a verdict that the contract compares: what a caller and the platform see. */\nconst visible = (verdict: HealthVerdict) => ({\n ready: verdict.ready,\n alive: verdict.alive,\n controllers: verdict.controllers,\n dependencies: verdict.dependencies,\n});\n\nconst same = (expected: unknown, actual: unknown): boolean => JSON.stringify(expected) === JSON.stringify(actual);\n\nconst failure = (what: string, expected: unknown, actual: unknown): Error =>\n new Error(`${what}: expected ${JSON.stringify(expected)}, but the verdict was ${JSON.stringify(actual)}`);\n\n/**\n * This function generates the contract test of a declaration. `declare` gives a new builder with\n * the declaration and the function of each check, each time. The checks must pass at rest. The\n * cases are:\n * - The service at rest: each controller and each dependency is ready, and the service is alive.\n * - Each dependency unavailable: its polls fail at once (the force-fail list). The dependency and\n * each controller that needs it become not ready, and the other controllers stay ready. Liveness\n * stays, unless a liveness check exercises the dependency.\n * - Each dependency degraded: each call of its checks takes the latency longer. Nothing can fail.\n * - Each dependency recovers: after it is unavailable, the service is again at rest.\n * - A negative control: a client that the declaration does not name fails, and nothing changes.\n *\n * A dependency that no check exercises is a finding: the service cannot see it fail.\n */\nexport const contractCases = (declare: () => ServiceBuilder, options: ContractOptions = {}): ContractCase[] => {\n const degradeBy = options.degradeBy ?? 100;\n const seed = options.seed ?? 1;\n const description: ServiceDescription = declare().describe();\n const service = description.name;\n const gating = description.checks.filter((check) => check.gates !== null);\n const timingOf = (check: Check) => description.policies[check.gates!];\n const failMax = (check: Check): number => ms(failSpan(timingOf(check), check.poll, \"error\").max);\n const recoverMax = (check: Check): number => ms(recoverSpan(timingOf(check), check.poll).max);\n const most = (values: readonly number[]): number => values.reduce((a, b) => Math.max(a, b), 0);\n const settleTime = most(gating.filter((check) => check.gates === \"readiness\").map(recoverMax)) + 1;\n const quietTime = most(gating.map(failMax)) + 1;\n\n /** This function starts the declaration on a fake clock, and moves it to rest. */\n const boot = async (\n extra: (clock: FakeClock) => Partial<StartOptions> = () => ({}),\n ): Promise<{ health: Health; clock: FakeClock; rest: ReturnType<typeof visible> }> => {\n const clock = fakeClock();\n // the warm-up sweep can wait on the clock (a degraded check), thus the clock moves while it starts\n const starting = declare().start({ clock, random: seeded(seed), ...extra(clock) });\n let started = false;\n void starting.then(\n () => (started = true),\n () => (started = true),\n );\n for (let step = 0; step < 1000 && !started; step += 1) await clock.advance(10);\n const health = await starting;\n await clock.advance(settleTime);\n return { health, clock, rest: visible(health.verdict()) };\n };\n const stop = (health: Health): void => health.stop();\n\n const cases: ContractCase[] = [\n {\n name: `${service}: at rest, each controller and each dependency is ready, and the service is alive`,\n async run() {\n const { health, rest } = await boot();\n try {\n const expected = {\n ready: true,\n alive: true,\n controllers: Object.fromEntries(description.controllers.map((each) => [each.name, true])),\n dependencies: Object.fromEntries(description.dependencies.map((each) => [each.name, true])),\n };\n if (!same(expected, rest)) throw failure(\"at rest\", expected, rest);\n } finally {\n stop(health);\n }\n },\n },\n ];\n\n for (const dependency of description.dependencies) {\n const on = gating.filter((check) => check.dependency === dependency.name);\n const readiness = on.filter((check) => check.gates === \"readiness\");\n const liveness = on.filter((check) => check.gates === \"liveness\");\n const unavailable = (rest: ReturnType<typeof visible>) => ({\n ready: readiness.length === 0 ? rest.ready : false,\n alive: liveness.length === 0 ? rest.alive : false,\n controllers: Object.fromEntries(\n description.controllers.map((each) => [\n each.name,\n each.requires.includes(dependency.name) && readiness.length > 0 ? false : rest.controllers[each.name],\n ]),\n ),\n dependencies: { ...rest.dependencies, [dependency.name]: readiness.length === 0 },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} unavailable`,\n async run() {\n if (on.length === 0)\n throw new Error(`no check that gates a policy exercises ${dependency.name}: the service cannot see it fail`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(dependency.name, true);\n await clock.advance(most(on.map(failMax)) + 1);\n const now = visible(health.verdict());\n const expected = unavailable(rest);\n if (!same(expected, now)) throw failure(`${dependency.name} unavailable`, expected, now);\n } finally {\n stop(health);\n }\n },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} degraded by ${degradeBy} ms, and nothing fails`,\n async run() {\n const tight = on.filter((check) => ms(check.poll.timeout) <= degradeBy);\n if (tight.length > 0)\n throw new Error(\n `${tight.map((check) => `${check.name} times out in ${ms(check.poll.timeout)} ms`).join(\", \")}: a latency of ${degradeBy} ms fails it`,\n );\n const { health, clock, rest } = await boot((at) => ({\n wrapCheck: (check, run) =>\n check.dependency !== dependency.name\n ? run\n : (signal) => new Promise((resolve) => at.after(degradeBy, () => resolve(run(signal)))),\n }));\n try {\n await clock.advance(quietTime + most(on.map((check) => ms(check.poll.interval))));\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(`${dependency.name} degraded`, rest, now);\n } finally {\n stop(health);\n }\n },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} recovers`,\n async run() {\n if (on.length === 0)\n throw new Error(`no check that gates a policy exercises ${dependency.name}: the service cannot see it recover`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(dependency.name, true);\n await clock.advance(most(on.map(failMax)) + 1);\n health.forceFail(dependency.name, false);\n await clock.advance(most(on.map(recoverMax)) + 1);\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(`${dependency.name} recovered`, rest, now);\n } finally {\n stop(health);\n }\n },\n });\n }\n\n const never = `${service}.client-never-registered`;\n cases.push({\n name: `${service}: a client never registered fails, and nothing changes (the negative control)`,\n async run() {\n if (description.dependencies.some((each) => each.name === never))\n throw new Error(`the declaration has a dependency named ${never}`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(never, true);\n await clock.advance(quietTime);\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(\"the negative control\", rest, now);\n } finally {\n stop(health);\n }\n // and a check on a client never registered is refused before the start\n let refused = false;\n try {\n declare()\n .check({\n name: `${never}.check`,\n dependency: never,\n step: \"round-trip\",\n ownConnection: true,\n gates: null,\n poll: gating[0]?.poll ?? { interval: { value: 1, unit: \"s\" }, jitter: 0, timeout: { value: 0.5, unit: \"s\" } },\n })\n .describe();\n } catch {\n refused = true;\n }\n if (!refused) throw new Error(`the declaration accepted a check on ${never}, which is not a dependency`);\n },\n });\n\n return cases;\n};\n","import type { Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { ContractCase } from \"./contract.ts\";\nimport { fakeClock } from \"./fakes.ts\";\nimport type { Health } from \"./health.ts\";\nimport type { Lifecycle, TransportImpl } from \"./lifecycle.ts\";\n\n/** A held work is one unit of work that stays in flight until the test releases it. */\nexport interface HeldWork {\n /** This method lets the work end. Its promise ends when the caller of the work has its answer, or its error. */\n release(): Promise<void>;\n /** This flag tells if the work got the cooperative abort of the drain. */\n readonly aborted: boolean;\n}\n\n/**\n * A transport subject is a transport and the client side of its work. The kit offers work through\n * it, holds work in flight through it, and reads the count of the transport.\n */\nexport interface TransportSubject {\n readonly impl: TransportImpl;\n /** This lifecycle goes to `start()`. Without it, the kit gives a detached lifecycle. */\n readonly life?: Lifecycle | undefined;\n /** This method offers one unit of new work. It gives true when the transport took it, and false when it refused it. */\n offer(): Promise<boolean>;\n /** This method starts one unit of work that stays in flight until its release. The promise ends when the transport has it. */\n hold(): Promise<HeldWork>;\n /** This method frees what the subject holds outside the transport: its clients and their sockets. */\n dispose?(): Promise<void> | void;\n}\n\n/**\n * This function gives a detached lifecycle: a lifecycle with no service, for the start of a\n * transport in a test. Its state is `running`, it keeps each connection, and nothing is in flight. Each request\n * and each use of its health throws, because no service declares a route.\n */\nexport const detachedLifecycle = (): Lifecycle => {\n const none = (): never => {\n throw new Error(\"a detached lifecycle has no service: give the transport the lifecycle of a service\");\n };\n const health = { verdict: none, probe: none, subscribe: none, forceFail: none, drain: none, stop: none } as Health;\n return {\n health,\n state: \"running\",\n exitCode: null,\n errors: [],\n started: Promise.resolve(),\n done: new Promise<number>(() => {}),\n signal: new AbortController().signal,\n shutdown: none,\n request: none,\n inFlight: () => 0,\n keepAlive: () => true,\n };\n};\n\n/** A subject factory makes a new subject for each case, on the clock of the case. */\nexport type SubjectFactory = (clock: Clock) => TransportSubject | Promise<TransportSubject>;\n\n/** A conformance record is one measurement of a case, for the raw records of a run in CI. */\nexport interface ConformanceRecord {\n readonly case: string;\n readonly what: string;\n /** This time is the limit of the contract, in milliseconds. */\n readonly expected: number;\n /** This time is what the case measured, in milliseconds of the real clock. */\n readonly measured: number;\n}\n\n/** These options tune the conformance kit. */\nexport interface ConformanceOptions {\n /** This name goes before the name of each case. */\n readonly name?: string | undefined;\n /** This clock moves the cases: `fake` gives exact and fast cases, and `real` measures the timing. The default is `fake`. */\n readonly clock?: \"fake\" | \"real\" | undefined;\n /** This time is the cap of each drain of the cases, in milliseconds. The default is 1000. */\n readonly cap?: number | undefined;\n /** This time is the most that a close can take, in milliseconds of the real clock. The default is 1000. */\n readonly closeBound?: number | undefined;\n /** This time is the most that an event can take to reach the transport, in real milliseconds. The default is 100. */\n readonly slack?: number | undefined;\n /** False tells that the work of the transport cannot see the cooperative abort. The default is true. */\n readonly cooperative?: boolean | undefined;\n /** This count is the number of works that the count case holds. The default is 3. */\n readonly holds?: number | undefined;\n /** This function receives each measurement, for the raw records of a run. */\n readonly record?: ((record: ConformanceRecord) => void) | undefined;\n}\n\ninterface Time {\n readonly clock: Clock;\n pass(ms: number): Promise<void>;\n}\n\ninterface Tracked {\n readonly settled: boolean;\n readonly error: unknown;\n readonly failed: boolean;\n}\n\nconst realNow = (): number => performance.now();\nconst turn = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** This function waits on the real clock, until the test is true or the bound ends. It gives the test at the end. */\nconst within = async (bound: number, test: () => boolean): Promise<boolean> => {\n const end = realNow() + bound;\n for (;;) {\n if (test()) return true;\n if (realNow() > end) return test();\n await turn();\n }\n};\n\n/** This function lets the events that are ready reach the transport, with no move of the clock of the case. */\nconst quiet = async (): Promise<void> => {\n for (let at = 0; at < 8; at += 1) await turn();\n};\n\n/** This function uses a method of the transport, and gives a throw of the method as a rejection. */\nconst attempt = (call: () => Promise<void> | void): Promise<void> => {\n try {\n return Promise.resolve(call());\n } catch (error) {\n return Promise.reject(error);\n }\n};\n\nconst track = (promise: Promise<unknown>): Tracked => {\n const state = { settled: false, error: undefined as unknown, failed: false };\n promise.then(\n () => {\n state.settled = true;\n },\n (error: unknown) => {\n state.settled = true;\n state.failed = true;\n state.error = error;\n },\n );\n return state;\n};\n\n/**\n * This function generates the conformance cases of a transport (S6 A9). `make` gives a new subject\n * for each case. Each case throws on a failure, and its message names the part of the contract.\n * - The start: nothing is in flight, and the transport takes work.\n * - The count: it follows each work from its events, with no move of the clock.\n * - `stopIntake` refuses new work, keeps the work in flight, and a second stop has no effect.\n * - `drain` ends at once with no work, ends with the last work, and holds to its deadline and not\n * past it. It gives the cooperative abort to the work, and it does not throw.\n * - `close` ends the work in flight in its bound, and a second close has no effect.\n * - The stages in their sequence, as the lifecycle does them.\n */\nexport const transportConformance = (make: SubjectFactory, options: ConformanceOptions = {}): ContractCase[] => {\n const name = options.name ?? \"transport\";\n const fake = (options.clock ?? \"fake\") === \"fake\";\n const cap = options.cap ?? 1000;\n const closeBound = options.closeBound ?? 1000;\n const slack = options.slack ?? 100;\n const cooperative = options.cooperative ?? true;\n const holds = options.holds ?? 3;\n\n const timeOf = (): Time => {\n if (!fake) return { clock: realClock, pass: sleep };\n const clock = fakeClock();\n return { clock, pass: (ms) => clock.advance(ms) };\n };\n\n const check = (ok: boolean, what: string): void => {\n if (!ok) throw new Error(`${name}: ${what}`);\n };\n\n const each = (title: string, body: (subject: TransportSubject, time: Time, title: string) => Promise<void>): ContractCase => ({\n name: `${name}: ${title}`,\n async run() {\n const time = timeOf();\n const subject = await make(time.clock);\n try {\n await subject.impl.start(new AbortController().signal, subject.life ?? detachedLifecycle());\n await body(subject, time, title);\n } finally {\n try {\n await subject.impl.close();\n } finally {\n await subject.dispose?.();\n }\n }\n },\n });\n\n const count = (subject: TransportSubject, expected: number, what: string): Promise<void> =>\n within(slack, () => subject.impl.inFlight() === expected).then((ok) =>\n check(ok, `${what}: inFlight() gives ${subject.impl.inFlight()}, not ${expected}`),\n );\n\n const closes = async (subject: TransportSubject, title: string): Promise<void> => {\n const began = realNow();\n const closed = track(attempt(() => subject.impl.close()));\n const inBound = await within(closeBound, () => closed.settled);\n const took = realNow() - began;\n options.record?.({ case: title, what: \"close\", expected: closeBound, measured: took });\n check(inBound, `close did not end in its bound of ${closeBound} ms`);\n check(!closed.failed, `close threw: ${String(closed.error)}`);\n };\n\n const cases: ContractCase[] = [\n each(\"nothing is in flight after the start, and it takes new work\", async (subject) => {\n check(subject.impl.inFlight() === 0, `inFlight() gives ${subject.impl.inFlight()} after the start, not 0`);\n check(await subject.offer(), \"the transport refused new work after its start\");\n await count(subject, 0, \"the work ended\");\n }),\n\n each(\"the count follows each work in flight from its events, with no move of the clock\", async (subject) => {\n const held: HeldWork[] = [];\n for (let at = 1; at <= holds; at += 1) {\n held.push(await subject.hold());\n await count(subject, at, `${at} works are in flight`);\n }\n for (let at = holds - 1; at >= 0; at -= 1) {\n await held[at]!.release();\n await count(subject, at, `${at} works are in flight after a release`);\n }\n }),\n\n each(\"stopIntake refuses new work, keeps the work in flight, and a second stop has no effect\", async (subject) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after stopIntake\");\n await count(subject, 1, \"stopIntake cut the work in flight\");\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after a second stopIntake\");\n await work.release();\n await count(subject, 0, \"the work in flight ended after stopIntake\");\n }),\n\n each(\"drain ends at once when nothing is in flight\", async (subject, time) => {\n await subject.impl.stopIntake();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, new AbortController().signal)));\n check(await within(slack, () => drained.settled), \"drain did not end with nothing in flight\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n }),\n\n each(\"drain ends when the last work ends, before its deadline\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const deadline = time.clock.now() + cap;\n const drained = track(attempt(() => subject.impl.drain(deadline, new AbortController().signal)));\n await time.pass(cap / 4);\n await quiet();\n check(!drained.settled, \"drain ended while work was in flight, before its deadline\");\n await work.release();\n check(await within(slack, () => drained.settled), \"drain did not end when the last work ended\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n check(time.clock.now() < deadline, \"drain waited until its deadline after the last work ended\");\n }),\n\n each(\"drain holds to its deadline and not past it, and does not throw\", async (subject, time, title) => {\n await subject.hold();\n await subject.impl.stopIntake();\n const began = realNow();\n const deadline = time.clock.now() + cap;\n const drained = track(attempt(() => subject.impl.drain(deadline, new AbortController().signal)));\n if (fake) {\n await time.pass(cap - 1);\n await quiet();\n check(!drained.settled, \"drain ended before its deadline, with work in flight\");\n await time.pass(1);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline\");\n } else {\n const ended = await within(cap + slack, () => drained.settled);\n const took = realNow() - began;\n options.record?.({ case: title, what: \"drain to its deadline\", expected: cap, measured: took });\n check(ended, `drain did not end in ${cap + slack} ms, with a deadline at ${cap} ms`);\n check(took >= cap - slack, `drain ended after ${Math.round(took)} ms, before its deadline at ${cap} ms`);\n }\n check(!drained.failed, `drain threw at its deadline: ${String(drained.error)}`);\n }),\n\n ...(cooperative\n ? [\n each(\"drain gives the cooperative abort to the work in flight\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const abort = new AbortController();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, abort.signal)));\n await time.pass(cap / 2);\n await quiet();\n check(!work.aborted, \"the work got an abort before the signal of the drain aborted\");\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n check(await within(slack, () => work.aborted), \"the work did not get the cooperative abort\");\n await time.pass(cap / 2);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline after the cooperative abort\");\n check(!drained.failed, `drain threw after the cooperative abort: ${String(drained.error)}`);\n }),\n ]\n : []),\n\n each(\"drain with an aborted signal and a deadline in the past ends at once\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const abort = new AbortController();\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() - 1, abort.signal)));\n check(await within(slack, () => drained.settled), \"drain did not end at once with its deadline in the past\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n if (cooperative) check(await within(slack, () => work.aborted), \"the work did not get an abort that came before the drain\");\n }),\n\n each(\"close ends the work in flight, in its bound\", async (subject, _time, title) => {\n for (let at = 0; at < holds; at += 1) await subject.hold();\n await count(subject, holds, \"the works are in flight before the close\");\n await closes(subject, title);\n await count(subject, 0, \"close left work in flight\");\n }),\n\n each(\"close is idempotent: a second close has no effect\", async (subject, _time, title) => {\n await subject.hold();\n const first = track(attempt(() => subject.impl.close()));\n const second = track(attempt(() => subject.impl.close()));\n check(await within(closeBound, () => first.settled && second.settled), \"two closes did not end in the bound\");\n check(!first.failed && !second.failed, `a close threw: ${String(first.error ?? second.error)}`);\n await closes(subject, title);\n await count(subject, 0, \"the closes left work in flight\");\n }),\n\n each(\"close right after the start ends in its bound\", async (subject, _time, title) => {\n await closes(subject, title);\n check(subject.impl.inFlight() === 0, \"inFlight() is not 0 after a close with no work\");\n }),\n\n each(\"the stages in their sequence: stop intake, drain with its cooperative abort, close\", async (subject, time, title) => {\n const short = await subject.hold();\n const long = await subject.hold();\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after stopIntake\");\n const abort = new AbortController();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, abort.signal)));\n await short.release();\n await count(subject, 1, \"one work ended in the drain\");\n await time.pass(cap / 2);\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n if (cooperative) check(await within(slack, () => long.aborted), \"the long work did not get the cooperative abort\");\n await time.pass(cap / 2);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline\");\n await closes(subject, title);\n await count(subject, 0, \"close left work in flight\");\n }),\n ];\n return cases;\n};\n","// @mark1russell7/service/testing: the seams of the runtime for tests. A fake clock gives exact,\n// repeatable timing. A seeded random gives the same jitter on each run. A fake process takes\n// signals and keeps the exit codes. A quiet server answers until it goes quiet, and then it holds\n// its connections open without an answer. `contractCases` generates the contract test of a\n// declaration, and `transportConformance` generates the conformance test of a transport.\n\nimport type { AddressInfo, Server, Socket } from \"node:net\";\nimport { createServer } from \"node:net\";\nimport type { Outcome } from \"./latch.ts\";\nimport type { CheckFn } from \"./poller.ts\";\n\nexport type { FakeClock, FakeProcess, FakeProcessOptions } from \"./fakes.ts\";\nexport { fakeClock, fakeProcess, seeded } from \"./fakes.ts\";\nexport type { ContractCase, ContractOptions } from \"./contract.ts\";\nexport { contractCases } from \"./contract.ts\";\nexport type {\n ConformanceOptions,\n ConformanceRecord,\n HeldWork,\n SubjectFactory,\n TransportSubject,\n} from \"./conformance.ts\";\nexport { detachedLifecycle, transportConformance } from \"./conformance.ts\";\n\n/**\n * A behavior is what a scripted check does when the runtime starts it:\n * - `\"ok\"`, `\"error\"` and `\"indeterminate\"` give that outcome at once.\n * - `\"throw\"` throws at once.\n * - `\"timeout\"` gives no answer until the runtime aborts the use at the timeout of the poll.\n * - `\"hang\"` gives no answer, and it ignores the abort, until `heal()`.\n */\nexport type Behavior = Outcome | \"throw\" | \"timeout\" | \"hang\";\n\n/** A scripted check is a check function whose behavior a test sets. */\nexport interface ScriptedCheck {\n readonly run: CheckFn;\n /** This behavior applies to each later call. */\n behavior: Behavior;\n /** This count is the number of calls of the check. */\n readonly calls: number;\n /** This count is the number of calls that are pending. */\n readonly hung: number;\n /** This method ends each hung call with a pass, as when a driver gets its answer late. */\n heal(): void;\n}\n\n/** This function makes a scripted check with a behavior at the start. */\nexport const scriptedCheck = (behavior: Behavior = \"ok\"): ScriptedCheck => {\n let calls = 0;\n const hung = new Set<() => void>();\n const script: ScriptedCheck = {\n behavior,\n get calls() {\n return calls;\n },\n get hung() {\n return hung.size;\n },\n heal() {\n for (const end of [...hung]) end();\n },\n run: (signal) => {\n calls += 1;\n const now = script.behavior;\n if (now === \"throw\") throw new Error(\"the scripted check threw\");\n if (now === \"timeout\")\n return new Promise<Outcome>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(new Error(\"aborted at the timeout\"))),\n );\n if (now === \"hang\")\n return new Promise<Outcome>((resolve) => {\n const end = (): void => {\n hung.delete(end);\n resolve(\"ok\");\n };\n hung.add(end);\n });\n return now;\n },\n };\n return script;\n};\n\n/** A quiet server is a TCP echo server that can stop its answers and keep its connections open. */\nexport interface QuietServer {\n readonly port: number;\n /** This method stops the answers. The connections stay open, and the server reads and discards the data. */\n quiet(): void;\n /** This method starts the answers again. */\n speak(): void;\n /** This count is the number of open connections. */\n readonly connections: number;\n close(): Promise<void>;\n}\n\n/**\n * This function starts a quiet server on the loopback address. A check against it passes while it\n * answers. When it goes quiet, the check sees a connection that is open but silent. A frozen peer\n * and a black hole that keeps the connection look the same.\n */\nexport const quietServer = async (): Promise<QuietServer> => {\n let silent = false;\n const sockets = new Set<Socket>();\n const server: Server = createServer((socket) => {\n sockets.add(socket);\n socket.on(\"close\", () => sockets.delete(socket));\n socket.on(\"error\", () => sockets.delete(socket));\n socket.on(\"data\", (chunk) => {\n if (!silent) socket.write(chunk);\n });\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => resolve());\n });\n return {\n port: (server.address() as AddressInfo).port,\n quiet: () => {\n silent = true;\n },\n speak: () => {\n silent = false;\n },\n get connections() {\n return sockets.size;\n },\n close: () =>\n new Promise<void>((resolve) => {\n for (const socket of sockets) socket.destroy();\n server.close(() => resolve());\n }),\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/fakes.ts","../src/contract.ts","../src/conformance.ts","../src/testing.ts"],"sourcesContent":["// The fakes of the runtime for tests: a fake clock gives exact, repeatable timing, a seeded random\n// gives the same jitter on each run, and a fake process takes signals and keeps the exit codes.\n\nimport type { Cancel, Clock } from \"./clock.ts\";\nimport type { ExitSignal, ExitTrigger, ProcessPort } from \"./process.ts\";\n\n/** A fake clock moves only when a test tells it to. */\nexport interface FakeClock extends Clock {\n /** This method moves the clock forward by `ms`, and it starts each timer that comes due, in sequence. */\n advance(ms: number): Promise<void>;\n /**\n * This method moves the clock forward by `ms` and starts no timer, as a blocked event loop does.\n * The next `advance` then starts each timer that came due during the block, late.\n */\n block(ms: number): void;\n /** This count is the number of timers that are not stopped and not called. */\n readonly pending: number;\n}\n\n/** This function lets the promises that are ready continue, before the clock moves again. */\nconst settle = async (): Promise<void> => {\n for (let turn = 0; turn < 64; turn += 1) await Promise.resolve();\n};\n\n/** This function makes a fake clock. Its time starts at `start` milliseconds. */\nexport const fakeClock = (start = 0): FakeClock => {\n let now = start;\n let sequence = 0;\n const timers: Array<{ at: number; order: number; run: () => void; live: boolean }> = [];\n const due = (end: number) => {\n let best: (typeof timers)[number] | undefined;\n for (const timer of timers)\n if (timer.live && timer.at <= end && (best === undefined || timer.at < best.at || (timer.at === best.at && timer.order < best.order)))\n best = timer;\n return best;\n };\n return {\n now: () => now,\n after(ms, run): Cancel {\n const timer = { at: now + Math.max(0, ms), order: sequence++, run, live: true };\n timers.push(timer);\n return () => {\n timer.live = false;\n };\n },\n async advance(ms) {\n // a monotonic clock does not go back\n const end = now + Math.max(0, ms);\n await settle();\n for (;;) {\n const timer = due(end);\n if (timer === undefined) break;\n timer.live = false;\n // a timer that came due during a block runs late, at the time of the clock\n now = Math.max(now, timer.at);\n timer.run();\n await settle();\n }\n now = Math.max(now, end);\n for (let at = timers.length - 1; at >= 0; at -= 1) if (!timers[at]!.live) timers.splice(at, 1);\n await settle();\n },\n block(ms) {\n now += Math.max(0, ms);\n },\n get pending() {\n return timers.filter((timer) => timer.live).length;\n },\n };\n};\n\n/** A fake process receives triggers from a test, and keeps each exit code in place of an exit. */\nexport interface FakeProcess extends ProcessPort {\n /** This method sends a signal to each handler of it. */\n signal(name: ExitSignal): void;\n /** This method sends a trigger to each handler of it: a signal, or a process trigger, for example `stdin-close`. */\n trigger(name: ExitTrigger): void;\n /** This method sends a message of the IPC channel to each handler of the trigger `ipc-message`. */\n message(value: unknown): void;\n /** These are the exit codes, in sequence. A correct runtime exits one time. */\n readonly exits: readonly number[];\n /** This count is the number of handlers that are registered. */\n readonly handlers: number;\n /** These are the triggers that have a handler, in the sequence of their first handler. */\n readonly triggers: readonly ExitTrigger[];\n /** These are the times of the clock of the options at each flush and each exit, when the options give a clock. */\n readonly log: readonly string[];\n /** This count is the number of flushes of the output. */\n readonly flushes: number;\n /** This count is the number of holds of the process that are not released. */\n readonly holds: number;\n /** These counts are the holds that were not released at each exit, in sequence. */\n readonly heldAtExit: readonly number[];\n}\n\n/** These options shape a fake process. */\nexport interface FakeProcessOptions {\n /** This function is the flush of the output, for example a wait on a fake clock. Without it, a flush ends at once. */\n readonly flush?: (() => Promise<void> | void) | undefined;\n /** This clock stamps each line of the log. */\n readonly clock?: Clock | undefined;\n}\n\n/** This function makes a fake process. */\nexport const fakeProcess = (options: FakeProcessOptions = {}): FakeProcess => {\n const handlers = new Map<ExitTrigger, Set<(message?: unknown) => void>>();\n const exits: number[] = [];\n const log: string[] = [];\n let flushes = 0;\n let holds = 0;\n const heldAtExit: number[] = [];\n const stamp = (what: string): void => {\n log.push(options.clock === undefined ? what : `${options.clock.now()} ${what}`);\n };\n const send = (name: ExitTrigger, message?: unknown): void => {\n for (const handler of [...(handlers.get(name) ?? [])]) handler(message);\n };\n return {\n on(trigger, handler) {\n const set = handlers.get(trigger) ?? new Set();\n set.add(handler);\n handlers.set(trigger, set);\n return () => {\n set.delete(handler);\n };\n },\n async flush() {\n stamp(\"flush\");\n await options.flush?.();\n flushes += 1;\n stamp(\"flushed\");\n },\n hold() {\n holds += 1;\n let held = true;\n return () => {\n if (!held) return;\n held = false;\n holds -= 1;\n };\n },\n exit(code) {\n stamp(`exit ${code}`);\n heldAtExit.push(holds);\n exits.push(code);\n },\n signal: (name) => send(name),\n trigger: (name) => send(name),\n message: (value) => send(\"ipc-message\", value),\n get exits() {\n return [...exits];\n },\n get handlers() {\n let count = 0;\n for (const set of handlers.values()) count += set.size;\n return count;\n },\n get triggers() {\n return [...handlers.keys()];\n },\n get log() {\n return [...log];\n },\n get flushes() {\n return flushes;\n },\n get holds() {\n return holds;\n },\n get heldAtExit() {\n return [...heldAtExit];\n },\n };\n};\n\n/** This function gives a seeded random number generator (mulberry32), with numbers in [0, 1). */\nexport const seeded = (seed: number): (() => number) => {\n let state = seed >>> 0;\n return () => {\n state = (state + 0x6d2b79f5) >>> 0;\n let t = state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { ServiceBuilder } from \"./builder.ts\";\nimport type { Check, ServiceDescription } from \"./description.ts\";\nimport type { FakeClock } from \"./fakes.ts\";\nimport { fakeClock, seeded } from \"./fakes.ts\";\nimport type { Health, HealthVerdict, StartOptions } from \"./health.ts\";\nimport { failSpan, recoverSpan } from \"./timing.ts\";\n\n/** A contract case is one generated case of the contract test of a declaration. It throws on a failure. */\nexport interface ContractCase {\n readonly name: string;\n run(): Promise<void>;\n}\n\n/** These options tune the contract test. */\nexport interface ContractOptions {\n /** This number is the latency that a degraded dependency adds to each check, in milliseconds. The default of the contract is 100. */\n readonly degradeBy?: number | undefined;\n /** This number is the seed of the jitter. */\n readonly seed?: number | undefined;\n}\n\nconst ms = (amount: { readonly value: number; readonly unit: string }): number => valueIn(amount, \"ms\");\n\n/** The part of a verdict that the contract compares: what a caller and the platform see. */\nconst visible = (verdict: HealthVerdict) => ({\n ready: verdict.ready,\n alive: verdict.alive,\n controllers: verdict.controllers,\n dependencies: verdict.dependencies,\n});\n\nconst same = (expected: unknown, actual: unknown): boolean => JSON.stringify(expected) === JSON.stringify(actual);\n\nconst failure = (what: string, expected: unknown, actual: unknown): Error =>\n new Error(`${what}: expected ${JSON.stringify(expected)}, but the verdict was ${JSON.stringify(actual)}`);\n\n/**\n * This function generates the contract test of a declaration. `declare` gives a new builder with\n * the declaration and the function of each check, each time. The checks must pass at rest. The\n * cases are:\n * - The service at rest: each controller and each dependency is ready, and the service is alive.\n * - Each dependency unavailable: its polls fail at once (the force-fail list). The dependency and\n * each controller that needs it become not ready, and the other controllers stay ready. Liveness\n * stays, unless a liveness check exercises the dependency.\n * - Each dependency degraded: each call of its checks takes the latency longer. Nothing can fail.\n * - Each dependency recovers: after it is unavailable, the service is again at rest.\n * - A negative control: a client that the declaration does not name fails, and nothing changes.\n *\n * A dependency that no check exercises is a finding: the service cannot see it fail.\n */\nexport const contractCases = (declare: () => ServiceBuilder, options: ContractOptions = {}): ContractCase[] => {\n const degradeBy = options.degradeBy ?? 100;\n const seed = options.seed ?? 1;\n const description: ServiceDescription = declare().describe();\n const service = description.name;\n const gating = description.checks.filter((check) => check.gates !== null);\n const timingOf = (check: Check) => description.policies[check.gates!];\n const failMax = (check: Check): number => ms(failSpan(timingOf(check), check.poll, \"error\").max);\n const recoverMax = (check: Check): number => ms(recoverSpan(timingOf(check), check.poll).max);\n const most = (values: readonly number[]): number => values.reduce((a, b) => Math.max(a, b), 0);\n const settleTime = most(gating.filter((check) => check.gates === \"readiness\").map(recoverMax)) + 1;\n const quietTime = most(gating.map(failMax)) + 1;\n\n /** This function starts the declaration on a fake clock, and moves it to rest. */\n const boot = async (\n extra: (clock: FakeClock) => Partial<StartOptions> = () => ({}),\n ): Promise<{ health: Health; clock: FakeClock; rest: ReturnType<typeof visible> }> => {\n const clock = fakeClock();\n // the warm-up sweep can wait on the clock (a degraded check), thus the clock moves while it starts\n const starting = declare().start({ clock, random: seeded(seed), ...extra(clock) });\n let started = false;\n void starting.then(\n () => (started = true),\n () => (started = true),\n );\n for (let step = 0; step < 1000 && !started; step += 1) await clock.advance(10);\n const health = await starting;\n await clock.advance(settleTime);\n return { health, clock, rest: visible(health.verdict()) };\n };\n const stop = (health: Health): void => health.stop();\n\n const cases: ContractCase[] = [\n {\n name: `${service}: at rest, each controller and each dependency is ready, and the service is alive`,\n async run() {\n const { health, rest } = await boot();\n try {\n const expected = {\n ready: true,\n alive: true,\n controllers: Object.fromEntries(description.controllers.map((each) => [each.name, true])),\n dependencies: Object.fromEntries(description.dependencies.map((each) => [each.name, true])),\n };\n if (!same(expected, rest)) throw failure(\"at rest\", expected, rest);\n } finally {\n stop(health);\n }\n },\n },\n ];\n\n for (const dependency of description.dependencies) {\n const on = gating.filter((check) => check.dependency === dependency.name);\n const readiness = on.filter((check) => check.gates === \"readiness\");\n const liveness = on.filter((check) => check.gates === \"liveness\");\n const unavailable = (rest: ReturnType<typeof visible>) => ({\n ready: readiness.length === 0 ? rest.ready : false,\n alive: liveness.length === 0 ? rest.alive : false,\n controllers: Object.fromEntries(\n description.controllers.map((each) => [\n each.name,\n each.requires.includes(dependency.name) && readiness.length > 0 ? false : rest.controllers[each.name],\n ]),\n ),\n dependencies: { ...rest.dependencies, [dependency.name]: readiness.length === 0 },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} unavailable`,\n async run() {\n if (on.length === 0)\n throw new Error(`no check that gates a policy exercises ${dependency.name}: the service cannot see it fail`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(dependency.name, true);\n await clock.advance(most(on.map(failMax)) + 1);\n const now = visible(health.verdict());\n const expected = unavailable(rest);\n if (!same(expected, now)) throw failure(`${dependency.name} unavailable`, expected, now);\n } finally {\n stop(health);\n }\n },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} degraded by ${degradeBy} ms, and nothing fails`,\n async run() {\n const tight = on.filter((check) => ms(check.poll.timeout) <= degradeBy);\n if (tight.length > 0)\n throw new Error(\n `${tight.map((check) => `${check.name} times out in ${ms(check.poll.timeout)} ms`).join(\", \")}: a latency of ${degradeBy} ms fails it`,\n );\n const { health, clock, rest } = await boot((at) => ({\n wrapCheck: (check, run) =>\n check.dependency !== dependency.name\n ? run\n : (signal) => new Promise((resolve) => at.after(degradeBy, () => resolve(run(signal)))),\n }));\n try {\n await clock.advance(quietTime + most(on.map((check) => ms(check.poll.interval))));\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(`${dependency.name} degraded`, rest, now);\n } finally {\n stop(health);\n }\n },\n });\n\n cases.push({\n name: `${service}: ${dependency.name} recovers`,\n async run() {\n if (on.length === 0)\n throw new Error(`no check that gates a policy exercises ${dependency.name}: the service cannot see it recover`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(dependency.name, true);\n await clock.advance(most(on.map(failMax)) + 1);\n health.forceFail(dependency.name, false);\n await clock.advance(most(on.map(recoverMax)) + 1);\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(`${dependency.name} recovered`, rest, now);\n } finally {\n stop(health);\n }\n },\n });\n }\n\n const never = `${service}.client-never-registered`;\n cases.push({\n name: `${service}: a client never registered fails, and nothing changes (the negative control)`,\n async run() {\n if (description.dependencies.some((each) => each.name === never))\n throw new Error(`the declaration has a dependency named ${never}`);\n const { health, clock, rest } = await boot();\n try {\n health.forceFail(never, true);\n await clock.advance(quietTime);\n const now = visible(health.verdict());\n if (!same(rest, now)) throw failure(\"the negative control\", rest, now);\n } finally {\n stop(health);\n }\n // and a check on a client never registered is refused before the start\n let refused = false;\n try {\n declare()\n .check({\n name: `${never}.check`,\n dependency: never,\n step: \"round-trip\",\n ownConnection: true,\n gates: null,\n poll: gating[0]?.poll ?? { interval: { value: 1, unit: \"s\" }, jitter: 0, timeout: { value: 0.5, unit: \"s\" } },\n })\n .describe();\n } catch {\n refused = true;\n }\n if (!refused) throw new Error(`the declaration accepted a check on ${never}, which is not a dependency`);\n },\n });\n\n return cases;\n};\n","import type { Clock } from \"./clock.ts\";\nimport { realClock } from \"./clock.ts\";\nimport type { ContractCase } from \"./contract.ts\";\nimport { fakeClock } from \"./fakes.ts\";\nimport type { Health } from \"./health.ts\";\nimport type { Lifecycle, TransportImpl } from \"./lifecycle.ts\";\n\n/** A held work is one unit of work that stays in flight until the test releases it. */\nexport interface HeldWork {\n /** This method lets the work end. Its promise ends when the caller of the work has its answer, or its error. */\n release(): Promise<void>;\n /** This flag tells if the work got the cooperative abort of the drain. */\n readonly aborted: boolean;\n}\n\n/**\n * A transport subject is a transport and the client side of its work. The kit offers work through\n * it, holds work in flight through it, and reads the count of the transport.\n */\nexport interface TransportSubject {\n readonly impl: TransportImpl;\n /** This lifecycle goes to `start()`. Without it, the kit gives a detached lifecycle. */\n readonly life?: Lifecycle | undefined;\n /** This method offers one unit of new work. It gives true when the transport took it, and false when it refused it. */\n offer(): Promise<boolean>;\n /** This method starts one unit of work that stays in flight until its release. The promise ends when the transport has it. */\n hold(): Promise<HeldWork>;\n /** This method frees what the subject holds outside the transport: its clients and their sockets. */\n dispose?(): Promise<void> | void;\n}\n\n/**\n * This function gives a detached lifecycle: a lifecycle with no service, for the start of a\n * transport in a test. Its state is `running`, it keeps each connection, and nothing is in flight. Each request\n * and each use of its health throws, because no service declares a route.\n */\nexport const detachedLifecycle = (): Lifecycle => {\n const none = (): never => {\n throw new Error(\"a detached lifecycle has no service: give the transport the lifecycle of a service\");\n };\n const health = { verdict: none, probe: none, metrics: none, subscribe: none, forceFail: none, drain: none, stop: none } as Health;\n return {\n health,\n state: \"running\",\n exitCode: null,\n errors: [],\n started: Promise.resolve(),\n done: new Promise<number>(() => {}),\n signal: new AbortController().signal,\n shutdown: none,\n request: none,\n inFlight: () => 0,\n keepAlive: () => true,\n };\n};\n\n/** A subject factory makes a new subject for each case, on the clock of the case. */\nexport type SubjectFactory = (clock: Clock) => TransportSubject | Promise<TransportSubject>;\n\n/** A conformance record is one measurement of a case, for the raw records of a run in CI. */\nexport interface ConformanceRecord {\n readonly case: string;\n readonly what: string;\n /** This time is the limit of the contract, in milliseconds. */\n readonly expected: number;\n /** This time is what the case measured, in milliseconds of the real clock. */\n readonly measured: number;\n}\n\n/** These options tune the conformance kit. */\nexport interface ConformanceOptions {\n /** This name goes before the name of each case. */\n readonly name?: string | undefined;\n /** This clock moves the cases: `fake` gives exact and fast cases, and `real` measures the timing. The default is `fake`. */\n readonly clock?: \"fake\" | \"real\" | undefined;\n /** This time is the cap of each drain of the cases, in milliseconds. The default is 1000. */\n readonly cap?: number | undefined;\n /** This time is the most that a close can take, in milliseconds of the real clock. The default is 1000. */\n readonly closeBound?: number | undefined;\n /** This time is the most that an event can take to reach the transport, in real milliseconds. The default is 100. */\n readonly slack?: number | undefined;\n /** False tells that the work of the transport cannot see the cooperative abort. The default is true. */\n readonly cooperative?: boolean | undefined;\n /** This count is the number of works that the count case holds. The default is 3. */\n readonly holds?: number | undefined;\n /** This function receives each measurement, for the raw records of a run. */\n readonly record?: ((record: ConformanceRecord) => void) | undefined;\n}\n\ninterface Time {\n readonly clock: Clock;\n pass(ms: number): Promise<void>;\n}\n\ninterface Tracked {\n readonly settled: boolean;\n readonly error: unknown;\n readonly failed: boolean;\n}\n\nconst realNow = (): number => performance.now();\nconst turn = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** This function waits on the real clock, until the test is true or the bound ends. It gives the test at the end. */\nconst within = async (bound: number, test: () => boolean): Promise<boolean> => {\n const end = realNow() + bound;\n for (;;) {\n if (test()) return true;\n if (realNow() > end) return test();\n await turn();\n }\n};\n\n/** This function lets the events that are ready reach the transport, with no move of the clock of the case. */\nconst quiet = async (): Promise<void> => {\n for (let at = 0; at < 8; at += 1) await turn();\n};\n\n/** This function uses a method of the transport, and gives a throw of the method as a rejection. */\nconst attempt = (call: () => Promise<void> | void): Promise<void> => {\n try {\n return Promise.resolve(call());\n } catch (error) {\n return Promise.reject(error);\n }\n};\n\nconst track = (promise: Promise<unknown>): Tracked => {\n const state = { settled: false, error: undefined as unknown, failed: false };\n promise.then(\n () => {\n state.settled = true;\n },\n (error: unknown) => {\n state.settled = true;\n state.failed = true;\n state.error = error;\n },\n );\n return state;\n};\n\n/**\n * This function generates the conformance cases of a transport (S6 A9). `make` gives a new subject\n * for each case. Each case throws on a failure, and its message names the part of the contract.\n * - The start: nothing is in flight, and the transport takes work.\n * - The count: it follows each work from its events, with no move of the clock.\n * - `stopIntake` refuses new work, keeps the work in flight, and a second stop has no effect.\n * - `drain` ends at once with no work, ends with the last work, and holds to its deadline and not\n * past it. It gives the cooperative abort to the work, and it does not throw.\n * - `close` ends the work in flight in its bound, and a second close has no effect.\n * - The stages in their sequence, as the lifecycle does them.\n */\nexport const transportConformance = (make: SubjectFactory, options: ConformanceOptions = {}): ContractCase[] => {\n const name = options.name ?? \"transport\";\n const fake = (options.clock ?? \"fake\") === \"fake\";\n const cap = options.cap ?? 1000;\n const closeBound = options.closeBound ?? 1000;\n const slack = options.slack ?? 100;\n const cooperative = options.cooperative ?? true;\n const holds = options.holds ?? 3;\n\n const timeOf = (): Time => {\n if (!fake) return { clock: realClock, pass: sleep };\n const clock = fakeClock();\n return { clock, pass: (ms) => clock.advance(ms) };\n };\n\n const check = (ok: boolean, what: string): void => {\n if (!ok) throw new Error(`${name}: ${what}`);\n };\n\n const each = (title: string, body: (subject: TransportSubject, time: Time, title: string) => Promise<void>): ContractCase => ({\n name: `${name}: ${title}`,\n async run() {\n const time = timeOf();\n const subject = await make(time.clock);\n try {\n await subject.impl.start(new AbortController().signal, subject.life ?? detachedLifecycle());\n await body(subject, time, title);\n } finally {\n try {\n await subject.impl.close();\n } finally {\n await subject.dispose?.();\n }\n }\n },\n });\n\n const count = (subject: TransportSubject, expected: number, what: string): Promise<void> =>\n within(slack, () => subject.impl.inFlight() === expected).then((ok) =>\n check(ok, `${what}: inFlight() gives ${subject.impl.inFlight()}, not ${expected}`),\n );\n\n const closes = async (subject: TransportSubject, title: string): Promise<void> => {\n const began = realNow();\n const closed = track(attempt(() => subject.impl.close()));\n const inBound = await within(closeBound, () => closed.settled);\n const took = realNow() - began;\n options.record?.({ case: title, what: \"close\", expected: closeBound, measured: took });\n check(inBound, `close did not end in its bound of ${closeBound} ms`);\n check(!closed.failed, `close threw: ${String(closed.error)}`);\n };\n\n const cases: ContractCase[] = [\n each(\"nothing is in flight after the start, and it takes new work\", async (subject) => {\n check(subject.impl.inFlight() === 0, `inFlight() gives ${subject.impl.inFlight()} after the start, not 0`);\n check(await subject.offer(), \"the transport refused new work after its start\");\n await count(subject, 0, \"the work ended\");\n }),\n\n each(\"the count follows each work in flight from its events, with no move of the clock\", async (subject) => {\n const held: HeldWork[] = [];\n for (let at = 1; at <= holds; at += 1) {\n held.push(await subject.hold());\n await count(subject, at, `${at} works are in flight`);\n }\n for (let at = holds - 1; at >= 0; at -= 1) {\n await held[at]!.release();\n await count(subject, at, `${at} works are in flight after a release`);\n }\n }),\n\n each(\"stopIntake refuses new work, keeps the work in flight, and a second stop has no effect\", async (subject) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after stopIntake\");\n await count(subject, 1, \"stopIntake cut the work in flight\");\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after a second stopIntake\");\n await work.release();\n await count(subject, 0, \"the work in flight ended after stopIntake\");\n }),\n\n each(\"drain ends at once when nothing is in flight\", async (subject, time) => {\n await subject.impl.stopIntake();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, new AbortController().signal)));\n check(await within(slack, () => drained.settled), \"drain did not end with nothing in flight\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n }),\n\n each(\"drain ends when the last work ends, before its deadline\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const deadline = time.clock.now() + cap;\n const drained = track(attempt(() => subject.impl.drain(deadline, new AbortController().signal)));\n await time.pass(cap / 4);\n await quiet();\n check(!drained.settled, \"drain ended while work was in flight, before its deadline\");\n await work.release();\n check(await within(slack, () => drained.settled), \"drain did not end when the last work ended\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n check(time.clock.now() < deadline, \"drain waited until its deadline after the last work ended\");\n }),\n\n each(\"drain holds to its deadline and not past it, and does not throw\", async (subject, time, title) => {\n await subject.hold();\n await subject.impl.stopIntake();\n const began = realNow();\n const deadline = time.clock.now() + cap;\n const drained = track(attempt(() => subject.impl.drain(deadline, new AbortController().signal)));\n if (fake) {\n await time.pass(cap - 1);\n await quiet();\n check(!drained.settled, \"drain ended before its deadline, with work in flight\");\n await time.pass(1);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline\");\n } else {\n const ended = await within(cap + slack, () => drained.settled);\n const took = realNow() - began;\n options.record?.({ case: title, what: \"drain to its deadline\", expected: cap, measured: took });\n check(ended, `drain did not end in ${cap + slack} ms, with a deadline at ${cap} ms`);\n check(took >= cap - slack, `drain ended after ${Math.round(took)} ms, before its deadline at ${cap} ms`);\n }\n check(!drained.failed, `drain threw at its deadline: ${String(drained.error)}`);\n }),\n\n ...(cooperative\n ? [\n each(\"drain gives the cooperative abort to the work in flight\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const abort = new AbortController();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, abort.signal)));\n await time.pass(cap / 2);\n await quiet();\n check(!work.aborted, \"the work got an abort before the signal of the drain aborted\");\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n check(await within(slack, () => work.aborted), \"the work did not get the cooperative abort\");\n await time.pass(cap / 2);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline after the cooperative abort\");\n check(!drained.failed, `drain threw after the cooperative abort: ${String(drained.error)}`);\n }),\n ]\n : []),\n\n each(\"drain with an aborted signal and a deadline in the past ends at once\", async (subject, time) => {\n const work = await subject.hold();\n await subject.impl.stopIntake();\n const abort = new AbortController();\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() - 1, abort.signal)));\n check(await within(slack, () => drained.settled), \"drain did not end at once with its deadline in the past\");\n check(!drained.failed, `drain threw: ${String(drained.error)}`);\n if (cooperative) check(await within(slack, () => work.aborted), \"the work did not get an abort that came before the drain\");\n }),\n\n each(\"close ends the work in flight, in its bound\", async (subject, _time, title) => {\n for (let at = 0; at < holds; at += 1) await subject.hold();\n await count(subject, holds, \"the works are in flight before the close\");\n await closes(subject, title);\n await count(subject, 0, \"close left work in flight\");\n }),\n\n each(\"close is idempotent: a second close has no effect\", async (subject, _time, title) => {\n await subject.hold();\n const first = track(attempt(() => subject.impl.close()));\n const second = track(attempt(() => subject.impl.close()));\n check(await within(closeBound, () => first.settled && second.settled), \"two closes did not end in the bound\");\n check(!first.failed && !second.failed, `a close threw: ${String(first.error ?? second.error)}`);\n await closes(subject, title);\n await count(subject, 0, \"the closes left work in flight\");\n }),\n\n each(\"close right after the start ends in its bound\", async (subject, _time, title) => {\n await closes(subject, title);\n check(subject.impl.inFlight() === 0, \"inFlight() is not 0 after a close with no work\");\n }),\n\n each(\"the stages in their sequence: stop intake, drain with its cooperative abort, close\", async (subject, time, title) => {\n const short = await subject.hold();\n const long = await subject.hold();\n await subject.impl.stopIntake();\n check(!(await subject.offer()), \"the transport took new work after stopIntake\");\n const abort = new AbortController();\n const drained = track(attempt(() => subject.impl.drain(time.clock.now() + cap, abort.signal)));\n await short.release();\n await count(subject, 1, \"one work ended in the drain\");\n await time.pass(cap / 2);\n abort.abort(new Error(\"the cooperative abort of the conformance kit\"));\n if (cooperative) check(await within(slack, () => long.aborted), \"the long work did not get the cooperative abort\");\n await time.pass(cap / 2);\n check(await within(slack, () => drained.settled), \"drain did not end at its deadline\");\n await closes(subject, title);\n await count(subject, 0, \"close left work in flight\");\n }),\n ];\n return cases;\n};\n","// @mark1russell7/service/testing: the seams of the runtime for tests. A fake clock gives exact,\n// repeatable timing. A seeded random gives the same jitter on each run. A fake process takes\n// signals and keeps the exit codes. A quiet server answers until it goes quiet, and then it holds\n// its connections open without an answer. `contractCases` generates the contract test of a\n// declaration, and `transportConformance` generates the conformance test of a transport.\n\nimport type { AddressInfo, Server, Socket } from \"node:net\";\nimport { createServer } from \"node:net\";\nimport type { Outcome } from \"./latch.ts\";\nimport type { CheckFn } from \"./poller.ts\";\n\nexport type { FakeClock, FakeProcess, FakeProcessOptions } from \"./fakes.ts\";\nexport { fakeClock, fakeProcess, seeded } from \"./fakes.ts\";\nexport type { ContractCase, ContractOptions } from \"./contract.ts\";\nexport { contractCases } from \"./contract.ts\";\nexport type {\n ConformanceOptions,\n ConformanceRecord,\n HeldWork,\n SubjectFactory,\n TransportSubject,\n} from \"./conformance.ts\";\nexport { detachedLifecycle, transportConformance } from \"./conformance.ts\";\n\n/**\n * A behavior is what a scripted check does when the runtime starts it:\n * - `\"ok\"`, `\"error\"` and `\"indeterminate\"` give that outcome at once.\n * - `\"throw\"` throws at once.\n * - `\"timeout\"` gives no answer until the runtime aborts the use at the timeout of the poll.\n * - `\"hang\"` gives no answer, and it ignores the abort, until `heal()`.\n */\nexport type Behavior = Outcome | \"throw\" | \"timeout\" | \"hang\";\n\n/** A scripted check is a check function whose behavior a test sets. */\nexport interface ScriptedCheck {\n readonly run: CheckFn;\n /** This behavior applies to each later call. */\n behavior: Behavior;\n /** This count is the number of calls of the check. */\n readonly calls: number;\n /** This count is the number of calls that are pending. */\n readonly hung: number;\n /** This method ends each hung call with a pass, as when a driver gets its answer late. */\n heal(): void;\n}\n\n/** This function makes a scripted check with a behavior at the start. */\nexport const scriptedCheck = (behavior: Behavior = \"ok\"): ScriptedCheck => {\n let calls = 0;\n const hung = new Set<() => void>();\n const script: ScriptedCheck = {\n behavior,\n get calls() {\n return calls;\n },\n get hung() {\n return hung.size;\n },\n heal() {\n for (const end of [...hung]) end();\n },\n run: (signal) => {\n calls += 1;\n const now = script.behavior;\n if (now === \"throw\") throw new Error(\"the scripted check threw\");\n if (now === \"timeout\")\n return new Promise<Outcome>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(new Error(\"aborted at the timeout\"))),\n );\n if (now === \"hang\")\n return new Promise<Outcome>((resolve) => {\n const end = (): void => {\n hung.delete(end);\n resolve(\"ok\");\n };\n hung.add(end);\n });\n return now;\n },\n };\n return script;\n};\n\n/** A quiet server is a TCP echo server that can stop its answers and keep its connections open. */\nexport interface QuietServer {\n readonly port: number;\n /** This method stops the answers. The connections stay open, and the server reads and discards the data. */\n quiet(): void;\n /** This method starts the answers again. */\n speak(): void;\n /** This count is the number of open connections. */\n readonly connections: number;\n close(): Promise<void>;\n}\n\n/**\n * This function starts a quiet server on the loopback address. A check against it passes while it\n * answers. When it goes quiet, the check sees a connection that is open but silent. A frozen peer\n * and a black hole that keeps the connection look the same.\n */\nexport const quietServer = async (): Promise<QuietServer> => {\n let silent = false;\n const sockets = new Set<Socket>();\n const server: Server = createServer((socket) => {\n sockets.add(socket);\n socket.on(\"close\", () => sockets.delete(socket));\n socket.on(\"error\", () => sockets.delete(socket));\n socket.on(\"data\", (chunk) => {\n if (!silent) socket.write(chunk);\n });\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => resolve());\n });\n return {\n port: (server.address() as AddressInfo).port,\n quiet: () => {\n silent = true;\n },\n speak: () => {\n silent = false;\n },\n get connections() {\n return sockets.size;\n },\n close: () =>\n new Promise<void>((resolve) => {\n for (const socket of sockets) socket.destroy();\n server.close(() => resolve());\n }),\n };\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { Y as Poll, wt as Operation } from "./lifecycle-fgYfxrHK.js";
2
- import { C as MANIFESTATIONS, D as firstDetector, E as Situation, O as unwatched, S as Detector, T as Meeting, _ as Sent, a as ClientKnobs, b as retryProblems, c as ClientSpec, d as Factory, f as ReadyWait, g as SendOptions, h as STARTING_POLL, i as ClientJoin, l as ClientStep, m as RetrySchedule, n as CheckOptions, o as ClientOptions, p as RetryKnobs, r as Client, s as ClientSocket, t as Attempt, u as DialFn, v as readyWait, w as Manifestation, x as retrySchedule, y as retryBackoff } from "./core-BPLRDcUJ.js";
3
- import { n as statusLineJudge, t as Judge } from "./dial-DA8vJfh2.js";
1
+ import { jt as Operation, nt as Poll } from "./lifecycle-CMedxaQd.js";
2
+ import { C as MANIFESTATIONS, D as firstDetector, E as Situation, O as unwatched, S as Detector, T as Meeting, _ as Sent, a as ClientKnobs, b as retryProblems, c as ClientSpec, d as Factory, f as ReadyWait, g as SendOptions, h as STARTING_POLL, i as ClientJoin, l as ClientStep, m as RetrySchedule, n as CheckOptions, o as ClientOptions, p as RetryKnobs, r as Client, s as ClientSocket, t as Attempt, u as DialFn, v as readyWait, w as Manifestation, x as retrySchedule, y as retryBackoff } from "./core-D8iXeoE4.js";
3
+ import { n as statusLineJudge, t as Judge } from "./dial-DJolDmOK.js";
4
4
  import { Extent, Quantity } from "@mark1russell7/quantity";
5
5
  import { Machine } from "@mark1russell7/algebra";
6
6
  //#region src/websocket/connection.d.ts
@@ -1,7 +1,7 @@
1
1
  import { realClock } from "./clock.js";
2
2
  import { heartbeat } from "./heartbeat.js";
3
3
  import { i as heartbeatDetection } from "./timing-DGRi3XCI.js";
4
- import { A as retrySchedule, B as zeroProblems, C as notSent, D as readyWait, F as timeProblems, G as immediate, H as NEVER, I as trackWithTimer, J as keepaliveSilent, K as keepaliveAnswered, L as wholeProblems, M as sendWithReissue, N as socketEnds, O as retryBackoff, P as stepChecks, R as withinBound, S as ms, U as WHAT, V as MANIFESTATIONS, W as firstDetector, X as unwatched, Y as kernelGiveUp, _ as inFlight, a as helloDial, b as message, c as ageProblems, d as clientProblems, f as clientRecorder, h as finiteMs, i as authority, j as roundTripCheck, k as retryProblems, l as attachClient, m as dialCheck, n as closeTimer, o as statusLineJudge, q as keepaliveReset, r as transition, s as STARTING_POLL, t as clientKnob, u as clientBounds, w as operationTable, x as missesProblems, y as localStateCheck, z as wrapperTimerProblems } from "./machine-BhTRu3u9.js";
4
+ import { A as retrySchedule, B as zeroProblems, C as notSent, D as readyWait, F as timeProblems, G as immediate, H as NEVER, I as trackWithTimer, J as keepaliveSilent, K as keepaliveAnswered, L as wholeProblems, M as sendWithReissue, N as socketEnds, O as retryBackoff, P as stepChecks, R as withinBound, S as ms, U as WHAT, V as MANIFESTATIONS, W as firstDetector, X as unwatched, Y as kernelGiveUp, _ as inFlight, a as helloDial, b as message, c as ageProblems, d as clientProblems, f as clientRecorder, h as finiteMs, i as authority, j as roundTripCheck, k as retryProblems, l as attachClient, m as dialCheck, n as closeTimer, o as statusLineJudge, q as keepaliveReset, r as transition, s as STARTING_POLL, t as clientKnob, u as clientBounds, w as operationTable, x as missesProblems, y as localStateCheck, z as wrapperTimerProblems } from "./machine-DZMb_QEU.js";
5
5
  //#region src/websocket/connection.ts
6
6
  /** This function gives a message of ws as one buffer. At the default `binaryType` of ws, a message is a buffer already. */
7
7
  const asBuffer = (data) => {
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.5.13",
4
+ "version": "0.5.14",
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. Its clock, heartbeat and backoff also work alone, in a browser too.",
6
6
  "keywords": [
7
7
  "service",
@@ -1 +0,0 @@
1
- {"version":3,"file":"machine-BhTRu3u9.js","names":["dialTcp"],"sources":["../src/client/meets.ts","../src/client/core.ts","../src/client/dial.ts","../src/client/machine.ts"],"sourcesContent":["// The manifestations that a client can meet, and their detectors (E9). The shapes, the two queries,\n// the detectors of the kernel and the words of each manifestation are the same for each client.\n// Each client entry gives its own list of meetings, because the timers of each driver differ.\n\nimport type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\n\n/** A situation is what the connection did when the failure started: a command in flight, or idle. */\nexport type Situation = \"in flight\" | \"idle\";\n\n/** A detector is a mechanism that finds a failure, and the most time that it takes. */\nexport interface Detector {\n /** This name identifies the mechanism: a knob of the declaration, or the kernel. */\n readonly by: string;\n /** This time is the most that the detector takes, after the failure starts. \"infinite\" tells that it does not find the failure. */\n readonly within: Extent;\n readonly situation: Situation;\n /** This text tells what the detector does, for example that it fails a command without a change of state. */\n readonly effect: string;\n}\n\n/**\n * A meeting is a manifestation that the client can meet, and its detectors (E9). The names of the\n * manifestations are the names of the vocabulary of the harness and the corpus. `app-stall` is a\n * server whose process is alive and does not answer, a proposal of the vocabulary.\n */\nexport interface Meeting {\n readonly manifestation: string;\n /** This text tells the failure in words. */\n readonly what: string;\n readonly detectors: readonly Detector[];\n}\n\n/** These are the manifestations that each client gives a meeting for, in this sequence. */\nexport const MANIFESTATIONS = [\"rst\", \"fin\", \"blackhole\", \"freeze-host\", \"freeze-process\", \"app-stall\"] as const;\n\n/** A manifestation is one name of `MANIFESTATIONS`. */\nexport type Manifestation = (typeof MANIFESTATIONS)[number];\n\n/**\n * These words tell each manifestation that does not depend on the driver. The words of `app-stall`\n * name an example, and the example is a fact of each server, thus each client gives them.\n */\nexport const WHAT: Readonly<Record<Exclude<Manifestation, \"app-stall\">, string>> = {\n rst: \"the server answers each segment with a reset, and refuses each new connection\",\n fin: \"the process of the server ends, and its kernel closes the connection\",\n blackhole: \"the network drops each segment in the two directions\",\n \"freeze-host\": \"the host of the server stops, without a close: the death without a close\",\n \"freeze-process\": \"the process of the server stops, and its kernel stays up\",\n};\n\n/** This amount is a detector that does not find the failure. */\nexport const NEVER: Extent = { value: \"infinite\", unit: \"s\" };\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst seconds = (amount: Extent): number =>\n amount.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(amount as Quantity, \"s\");\n\n/** Node gives keepalive 10 probes, 1 s apart, after its idle time: a silent peer is found 10 s after the idle time. */\nconst NODE_PROBES = 10;\nconst NODE_INTERVAL = 1;\n\n/** This function gives the time at which Node's TCP keepalive gives up a silent peer: the idle time, then 10 probes 1 s apart. */\nexport const keepaliveGiveUp = (idle: Extent): Extent => (idle.value === \"infinite\" ? NEVER : s(seconds(idle) + NODE_PROBES * NODE_INTERVAL));\n\n/** This detector is a segment that arrives at once: a reset or a FIN. */\nexport const immediate = (situation: Situation, effect: string): Detector => ({ by: \"the next segment\", within: s(0), situation, effect });\n\n/** This detector is TCP keepalive on a silent peer. It gives up after its probes, and the socket closes. */\nexport const keepaliveSilent = (idle: Extent): Detector => ({\n by: \"keepalive_idle\",\n within: keepaliveGiveUp(idle),\n situation: \"idle\",\n effect: \"TCP keepalive gives up, and the socket closes\",\n});\n\n/** This detector is TCP keepalive on a peer whose kernel stays up. That kernel answers each probe, thus keepalive does not find the failure. */\nexport const keepaliveAnswered = (): Detector => ({\n by: \"keepalive_idle\",\n within: NEVER,\n situation: \"idle\",\n effect: \"the kernel of the server answers each probe\",\n});\n\n/** This detector is TCP keepalive on a peer that resets: the first probe gets the reset. */\nexport const keepaliveReset = (idle: Extent): Detector => ({\n by: \"keepalive_idle\",\n within: idle.value === \"infinite\" ? NEVER : idle,\n situation: \"idle\",\n effect: \"the first probe gets a reset\",\n});\n\n/** This detector is the kernel, which gives up the retransmissions of a segment in flight. A driver can add what follows. */\nexport const kernelGiveUp = (within: Extent, after?: string): Detector => ({\n by: \"retrans_giveup_time\",\n within,\n situation: \"in flight\",\n effect: `the kernel gives up the retransmissions and closes the socket${after === undefined ? \"\" : `, and ${after}`}`,\n});\n\n/** This function gives the first detector of a manifestation in a situation, or null when no detector has an end (C3). */\nexport const firstDetector = (meeting: Meeting, situation: Situation): Detector | null => {\n const ranked = meeting.detectors\n .filter((detector) => detector.situation === situation && detector.within.value !== \"infinite\")\n .sort((a, b) => seconds(a.within) - seconds(b.within));\n return ranked[0] ?? null;\n};\n\n/** This function gives each manifestation and situation that no detector of the client finds: the standing rule, as a query (C3, E9). */\nexport const unwatched = (meets: readonly Meeting[]): Array<{ readonly manifestation: string; readonly situation: Situation }> =>\n meets.flatMap((meeting) =>\n ([\"in flight\", \"idle\"] as const).flatMap((situation) =>\n firstDetector(meeting, situation) === null ? [{ manifestation: meeting.manifestation, situation }] : [],\n ),\n );\n","// The parts of a client wrapper that do not change from one client to the next. Each client entry\n// (`./redis`, `./pg`, `./http-client`, `./websocket`, and each later client of S1) fills them in with its driver. The core gives\n// the shapes of a client, the outcome of a call in three values, the rule that sends a call again,\n// the record of the calls in flight, the bounded close, the checks of the steps of a connection,\n// and the problems of a spec and of a client in a description. It loads only quantity.\n// The section \"Unified or separate\" of docs/S6.md gives each part, and the reason for each part that stays in a driver.\n\nimport type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Backoff } from \"../backoff.ts\";\nimport { backoffDelay } from \"../backoff.ts\";\nimport type { ServiceBuilder } from \"../builder.ts\";\nimport type { Cancel, Clock } from \"../clock.ts\";\nimport type { Check, Dependency, Gate, ServiceDescription, Step } from \"../description.ts\";\nimport type { Recorder } from \"../events.ts\";\nimport type { Outcome } from \"../latch.ts\";\nimport type { DependencyHandle } from \"../lifecycle.ts\";\nimport type { Operation } from \"../operation.ts\";\nimport { mayReissue } from \"../operation.ts\";\nimport type { CheckFn } from \"../poller.ts\";\nimport type { Poll } from \"../timing.ts\";\nimport type { Meeting } from \"./meets.ts\";\n\n/**\n * A sent result is the outcome of a call, in three values (C8):\n * - `ok`: the server answered.\n * - `error`: the call did not apply. The server answered with an error (`reply`), or the call did\n * not leave the client.\n * - `indeterminate`: the call can have reached the server, and nothing tells if it applied.\n */\nexport type Sent<T> =\n | { readonly outcome: \"ok\"; readonly value: T; readonly attempts: number }\n | { readonly outcome: \"error\"; readonly error: string; readonly reply: boolean; readonly attempts: number }\n | { readonly outcome: \"indeterminate\"; readonly error: string; readonly attempts: number };\n\n/** This type is the outcome of one try of a call, before the core counts the tries. */\nexport type Attempt<T = unknown> =\n | { readonly outcome: \"ok\"; readonly value: T }\n | { readonly outcome: \"error\"; readonly error: string; readonly reply: boolean }\n | { readonly outcome: \"indeterminate\"; readonly error: string };\n\n/** Send options tell how the client sends one call. */\nexport interface SendOptions {\n /** This signal abandons the call. A call that left the client is then indeterminate. */\n readonly signal?: AbortSignal | undefined;\n /** False stops the client: it does not send the call again. A check that must fail at once uses it. */\n readonly reissue?: boolean | undefined;\n}\n\n/** A dial function opens a new connection to the server, and reads one reply of the protocol. The `dial` check uses it. */\nexport type DialFn = (host: string, port: number, signal: AbortSignal) => Promise<void>;\n\n/** The steps of a connection that each client gives a check for (B14). A client can add more steps. */\nexport type ClientStep = Extract<Step, \"dial\" | \"round-trip\" | \"local-state\">;\n\n/** Check options tell which checks the client gives the health runtime, and which policy each one controls. */\nexport interface CheckOptions<S extends Step = ClientStep> {\n readonly poll: Poll;\n /** For each step, the policy that its check controls, or null for a check that only reports. A step that is absent has no check. */\n readonly steps: Partial<Record<S, Gate | null>>;\n}\n\n/** A step check is the function of one step, and if it uses the connection of the application. */\nexport interface StepCheck {\n readonly ownConnection: boolean;\n readonly run: CheckFn;\n}\n\n/** This function gives an amount in milliseconds. \"infinite\" is `Infinity`. */\nexport const ms = (amount: Extent): number =>\n amount.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(amount as Quantity, \"ms\");\n\n/** This function gives an amount in milliseconds, or undefined for \"infinite\". Each driver then writes its own form of \"no timer\". */\nexport const finiteMs = (amount: Extent): number | undefined => {\n const value = ms(amount);\n return Number.isFinite(value) ? value : undefined;\n};\n\n/** This function gives the text of an error. */\nexport const message = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/**\n * These are the knobs that each client has, by the names of the ordering law. Each driver adds its\n * own knobs. The comments of each driver tell which option of the driver a knob sets.\n */\nexport interface ClientKnobs {\n /** This time bounds a new connection. */\n readonly connect_bound: Quantity;\n /** This time bounds each call on the connection of the application. */\n readonly request_timeout: Quantity;\n /** This time is the idle time of TCP keepalive. Node then sends 10 probes 1 s apart. */\n readonly keepalive_idle: Extent;\n /** This time is the age at which the client replaces a connection. */\n readonly max_connection_age: Extent;\n /** This time bounds the close of the client at the shutdown. */\n readonly close_bound: Quantity;\n /**\n * This time is when the kernel gives up a segment that it retransmits. Node cannot set it, thus\n * the declaration states it for the ordering law (O17), from the platform.\n */\n readonly retrans_giveup_time: Extent;\n}\n\n/** A client spec is the declaration of one client: where it connects, and its knobs. Each driver adds its own fields. */\nexport interface ClientSpec<K extends ClientKnobs = ClientKnobs> {\n readonly host: string;\n readonly port: number;\n readonly password?: string | undefined;\n /** This identifier is the component in the corpus, for example `systems.component.redis`, or null. */\n readonly system: string | null;\n readonly knobs: K;\n /** These operations add to the table of the client, or change it. */\n readonly operations?: Readonly<Record<string, Operation>> | undefined;\n}\n\n/** A factory makes the connection or the pool of a client from the options of its driver. */\nexport type Factory<O, C> = (options: O) => C | Promise<C>;\n\n/** These options make a client. Without them, a client uses its driver, the real clock, and the dial of its protocol. */\nexport interface ClientOptions<F> {\n readonly create?: F | undefined;\n readonly clock?: Clock | undefined;\n /** This function receives each transition of the client, for the prober (D3). */\n readonly record?: Recorder | undefined;\n readonly dial?: DialFn | undefined;\n}\n\n/** A client socket is the part of the socket of a connection that the join of the prober and the bounded close read. */\nexport interface ClientSocket {\n readonly localAddress?: string | undefined;\n readonly localPort?: number | undefined;\n readonly remoteAddress?: string | undefined;\n readonly remotePort?: number | undefined;\n /** This method closes the socket at once. A close of the driver only half-closes it, and a silent peer does not answer that. */\n destroy?(): void;\n}\n\n/**\n * A client join is one connection, at the time when it can serve (D1, D2). The prober joins its\n * kernel records to the server's view of the connection through it. Each driver adds the server's id of the connection.\n */\nexport interface ClientJoin {\n readonly generation: number;\n /** This time is when the connection became ready, in milliseconds on the monotonic clock. */\n readonly at: number;\n readonly localAddress: string | null;\n readonly localPort: number | null;\n readonly remoteAddress: string | null;\n readonly remotePort: number | null;\n}\n\n/** This function gives the addresses and the ports of a socket for a join. Each one is null when the socket does not give it. */\nexport const socketEnds = (\n socket: ClientSocket | undefined,\n): Pick<ClientJoin, \"localAddress\" | \"localPort\" | \"remoteAddress\" | \"remotePort\"> => ({\n localAddress: socket?.localAddress ?? null,\n localPort: socket?.localPort ?? null,\n remoteAddress: socket?.remoteAddress ?? null,\n remotePort: socket?.remotePort ?? null,\n});\n\n/** A client is a dependency of a service: its declaration, its handle and its checks. Each driver adds the method that sends a call. */\nexport interface Client<S extends Step = ClientStep, J extends ClientJoin = ClientJoin> {\n readonly name: string;\n /** This declaration goes to `svc.dependency(name, declaration, handle)`. */\n readonly declaration: Omit<Dependency, \"name\">;\n readonly handle: DependencyHandle;\n /** These are the manifestations that the client can meet, and the detectors of each (E9). */\n readonly meets: readonly Meeting[];\n /** This text is the state of the client, as the machine document of the client names it. */\n readonly status: string;\n /** These are the connections of the client, the latest last. */\n readonly joins: readonly J[];\n /** This method gives the checks of the client and their functions, for `svc.check(check, run)`. */\n checks(options: CheckOptions<S>): ReadonlyArray<readonly [Check, CheckFn]>;\n /** This method declares the client in a service: its dependency, its handle and its checks. */\n attach(svc: ServiceBuilder, options: CheckOptions<S>): ServiceBuilder;\n /** This method gives the problems of the client in a description: a close that does not fit in its phase, for example. */\n problems(description: ServiceDescription): string[];\n}\n\n/** This function declares a client in a service: its dependency, its handle, and each of its checks. */\nexport const attachClient = (\n svc: ServiceBuilder,\n name: string,\n declaration: Omit<Dependency, \"name\">,\n handle: DependencyHandle,\n checks: ReadonlyArray<readonly [Check, CheckFn]>,\n): ServiceBuilder => {\n svc.dependency(name, declaration, handle);\n for (const [check, run] of checks) svc.check(check, run);\n return svc;\n};\n\n/** This function gives the table of operations of a client: the defaults of its driver, then the operations of its spec. */\nexport const operationTable = (\n defaults: Readonly<Record<string, Operation>>,\n more: Readonly<Record<string, Operation>> | undefined,\n): Readonly<Record<string, Operation>> => ({ ...defaults, ...more });\n\n/** A client recorder gives one transition of a client to the recorder of the options, for the prober (D3). */\nexport type ClientRecorder = (from: string, to: string, on: string, detail?: string) => void;\n\n/** This function makes the recorder of a client. Each event has the machine \"client\", and the name of the client as its instance. */\nexport const clientRecorder =\n (record: Recorder | undefined, clock: Clock, name: string): ClientRecorder =>\n (from, to, on, detail) =>\n record?.({ at: clock.now(), machine: \"client\", instance: name, from, to, on, ...(detail === undefined ? {} : { detail }) });\n\n/** This function gives the outcome of a call that did not leave the client. It is certain: the call did not apply. */\nexport const notSent = (error: string): Attempt<never> => ({ outcome: \"error\", error, reply: false });\n\n/** A settle function gives the outcome of a call in flight. A driver can give a cause, for example the error that ends a client of a pool. */\nexport type Settle<T = unknown> = (value: Attempt<T>, cause?: Error) => void;\n\n/**\n * This record holds the calls in flight of a client. Each call settles one time. A call that the\n * caller abandons after it left the client is indeterminate, because a sent call cannot be\n * cancelled (principle 9). The close and a closed connection settle each call that stays (C10).\n */\nexport interface InFlight {\n /** This number is the count of the calls in flight. */\n readonly size: number;\n /**\n * This method sends one call, and records it until it settles. `start` sends the call, and gives\n * its outcome to `settle`. `after` operates one time, after the call settles and before its promise resolves.\n */\n track<T>(\n signal: AbortSignal | undefined,\n abandoned: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n ): Promise<Attempt<T>>;\n /** This method settles each call in flight as indeterminate, with this error. */\n settleAll(error: string): void;\n /** This method resolves when no call is in flight. A close that waits for its calls uses it in its bound. */\n idle(): Promise<void>;\n}\n\n/** This function makes the record of the calls in flight of a client. */\nexport const inFlight = (): InFlight => {\n const closes = new Set<(error: string) => void>();\n const waiters = new Set<() => void>();\n const wake = (): void => {\n if (closes.size > 0) return;\n for (const waiter of [...waiters]) waiter();\n waiters.clear();\n };\n return {\n get size() {\n return closes.size;\n },\n idle: () =>\n new Promise<void>((resolve) => {\n if (closes.size === 0) resolve();\n else waiters.add(resolve);\n }),\n track<T>(\n signal: AbortSignal | undefined,\n abandoned: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n ): Promise<Attempt<T>> {\n return new Promise<Attempt<T>>((resolve) => {\n let settled = false;\n const settle: Settle<T> = (value, cause) => {\n if (settled) return;\n settled = true;\n closes.delete(close);\n signal?.removeEventListener(\"abort\", aborted);\n after?.(value, cause);\n resolve(value);\n wake();\n };\n const close = (error: string): void => settle({ outcome: \"indeterminate\", error }, new Error(error));\n const aborted = (): void => close(abandoned);\n closes.add(close);\n signal?.addEventListener(\"abort\", aborted, { once: true });\n start(settle);\n });\n },\n settleAll(error) {\n for (const close of [...closes]) close(error);\n },\n };\n};\n\n/**\n * This function sends one call through the record of the calls in flight, with a timer of the\n * wrapper. A driver with no timer for its call uses it: amqplib, and the agent of Node. At `bound`\n * milliseconds the call is indeterminate with the error `timedOut`, because it left the client.\n * The timer stops when the call settles, before `after` operates.\n */\nexport const trackWithTimer = <T>(\n flight: InFlight,\n clock: Clock,\n bound: number,\n signal: AbortSignal | undefined,\n abandoned: string,\n timedOut: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n): Promise<Attempt<T>> => {\n let stop: Cancel = () => {};\n return flight.track<T>(\n signal,\n abandoned,\n (settle) => {\n stop = clock.after(bound, () => settle({ outcome: \"indeterminate\", error: timedOut }));\n start(settle);\n },\n (value, cause) => {\n stop();\n after?.(value, cause);\n },\n );\n};\n\n/**\n * This function waits for the work of a close, for `bound` milliseconds at most. It gives true when\n * the work ended in its bound, with a success or a failure. It gives false at the bound. The driver\n * then destroys each socket that stays, because a silent server does not close its half.\n */\nexport const withinBound = (clock: Clock, bound: number, work: Promise<unknown>): Promise<boolean> => {\n const done = work.then(\n () => true,\n () => true,\n );\n let cancel: Cancel = () => {};\n const timer = new Promise<boolean>((resolve) => {\n cancel = clock.after(bound, () => resolve(false));\n });\n return Promise.race([done, timer]).then((ended) => {\n cancel();\n return ended;\n });\n};\n\n/**\n * A ready wait holds the calls that wait for a ready connection before their second try. A client\n * that connects again by itself uses it: Redis and RabbitMQ.\n */\nexport interface ReadyWait {\n /** This method waits until the client is ready, for `within` milliseconds at most. It gives true when the client is ready. */\n wait(within: number, signal?: AbortSignal): Promise<boolean>;\n /** This method ends each wait with true. The client uses it when its connection is ready. */\n wake(): void;\n}\n\n/** This function makes the ready wait of a client. The function `ready` tells if the client is ready. */\nexport const readyWait = (clock: Clock, ready: () => boolean): ReadyWait => {\n const waiters = new Set<() => void>();\n return {\n wait: (within, signal) =>\n new Promise((resolve) => {\n if (ready()) {\n resolve(true);\n return;\n }\n let cancel: Cancel = () => {};\n const done = (value: boolean): void => {\n waiters.delete(wake);\n cancel();\n signal?.removeEventListener(\"abort\", aborted);\n resolve(value);\n };\n const wake = (): void => done(true);\n const aborted = (): void => done(false);\n waiters.add(wake);\n cancel = clock.after(within, () => done(ready()));\n signal?.addEventListener(\"abort\", aborted, { once: true });\n }),\n wake() {\n for (const wake of [...waiters]) wake();\n },\n };\n};\n\n/** These are the knobs of a client that connects again by itself, after a delay that doubles. */\nexport interface RetryKnobs {\n /** This time is the first delay before a new try to connect. The delay doubles after each try. */\n readonly retry_base: Quantity;\n /** This time is the most that one delay can be, before its jitter. */\n readonly retry_cap: Quantity;\n}\n\n/**\n * This function gives the backoff of the tries to connect: from `retry_base`, doubling to\n * `retry_cap`, each with a jitter up to the base. The client rounds each delay to a whole millisecond.\n */\nexport const retryBackoff = (knobs: RetryKnobs): Backoff => ({\n base: ms(knobs.retry_base),\n factor: 2,\n cap: ms(knobs.retry_cap),\n jitter: { kind: \"add\", amount: ms(knobs.retry_base) },\n});\n\n/**\n * A retry schedule holds the next try to connect, for a client that connects again by itself. It\n * counts the tries since the last join. Each delay comes from the backoff, rounded to a whole millisecond.\n */\nexport interface RetrySchedule {\n /** This count is the number of tries since the last reset. */\n readonly tries: number;\n /** This method counts one more try, and starts `due` after the delay of that try. It stops a try that waits. */\n schedule(due: () => void): void;\n /** This method stops the try that waits. The close uses it. */\n cancel(): void;\n /** This method sets the count of tries to zero. The client uses it at each join. */\n reset(): void;\n}\n\n/** This function makes the retry schedule of a client, from its backoff and its random numbers. RabbitMQ and WebSocket use it. */\nexport const retrySchedule = (clock: Clock, backoff: Backoff, random: () => number): RetrySchedule => {\n let tries = 0;\n let timer: Cancel | null = null;\n return {\n get tries() {\n return tries;\n },\n schedule(due) {\n tries += 1;\n const delay = Math.round(backoffDelay(backoff, tries, random));\n timer?.();\n timer = clock.after(delay, () => {\n timer = null;\n due();\n });\n },\n cancel() {\n timer?.();\n timer = null;\n },\n reset() {\n tries = 0;\n },\n };\n};\n\n/** This function gives the problems of the retry knobs: the cap must not be below the base. */\nexport const retryProblems = (name: string, knobs: RetryKnobs): string[] =>\n ms(knobs.retry_cap) < ms(knobs.retry_base) ? [`${name}'s retry_cap is below its retry_base`] : [];\n\n/**\n * This function gives the problem of a count of misses that is not a whole number above zero. A\n * client that closes its connection after a count of heartbeat misses uses it. `mechanism` names the heartbeat, for example \"watchdog\".\n */\nexport const missesProblems = (name: string, misses: number, mechanism: string): string[] =>\n Number.isInteger(misses) && misses >= 1 ? [] : [`${name}'s ${mechanism} closes the connection after ${misses} misses, not a whole number above zero`];\n\n/** This function gives the problems of the knobs of a spec that each client has: each knob must be a time of zero or more. */\nexport const timeProblems = (name: string, knobs: object): string[] => {\n const problems: string[] = [];\n for (const [knob, amount] of Object.entries(knobs) as Array<[string, Extent]>) {\n if (amount.value !== \"infinite\" && !(ms(amount) >= 0)) problems.push(`${name}'s ${knob} is not a time of zero or more`);\n }\n return problems;\n};\n\n/** This function writes a bound for a driver that reads 0 as no timer: \"infinite\" is 0. */\nexport const orZero = (amount: Extent): number => finiteMs(amount) ?? 0;\n\n/** This function tells if an amount is a whole number of milliseconds. \"infinite\" is whole. */\nexport const wholeMs = (amount: Extent): boolean => amount.value === \"infinite\" || Number.isInteger(ms(amount));\n\n/** This function tells if an amount is a whole number of seconds. \"infinite\" is whole. */\nexport const wholeSeconds = (amount: Extent): boolean =>\n amount.value === \"infinite\" || Number.isInteger(valueIn(amount as Quantity, \"s\"));\n\n/** These are the knobs of a spec that `zeroProblems` and `wholeProblems` examine, by their kind. */\nexport interface KnobKinds {\n /** These knobs must be a finite time above zero, because the driver reads 0 as no bound. */\n readonly bounded?: readonly string[] | undefined;\n /** These knobs can be \"infinite\", which the driver writes as 0. Thus 0 itself is not a bound. */\n readonly open?: readonly string[] | undefined;\n /** These knobs go to the driver in whole milliseconds. */\n readonly millis?: readonly string[] | undefined;\n /** These knobs go to the driver or to Node in whole seconds. */\n readonly seconds?: readonly string[] | undefined;\n}\n\nconst knobOf = (knobs: object, knob: string): Extent => (knobs as Record<string, Extent>)[knob]!;\n\n/**\n * This function gives the problems of the knobs of a driver that reads 0 as no timer. pg and the\n * MongoDB driver read 0 so. A bounded knob must be a finite time above zero. An open knob can be\n * \"infinite\", and 0 is then a mistake.\n */\nexport const zeroProblems = (name: string, driver: string, knobs: object, kinds: KnobKinds): string[] => {\n const problems: string[] = [];\n for (const knob of kinds.bounded ?? []) {\n const value = ms(knobOf(knobs, knob));\n if (!(value > 0) || !Number.isFinite(value)) problems.push(`${name}'s ${knob} is not a finite time above zero: ${driver} reads 0 as no bound`);\n }\n for (const knob of kinds.open ?? []) {\n const amount = knobOf(knobs, knob);\n if (amount.value !== \"infinite\" && !(ms(amount) > 0)) problems.push(`${name}'s ${knob} is zero: give \"infinite\" for no bound`);\n }\n return problems;\n};\n\n/** This function gives the problems of the knobs that a driver takes in whole milliseconds or in whole seconds. */\nexport const wholeProblems = (name: string, knobs: object, kinds: KnobKinds): string[] => [\n ...(kinds.millis ?? []).filter((knob) => !wholeMs(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of milliseconds`),\n ...(kinds.seconds ?? []).filter((knob) => !wholeSeconds(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of seconds`),\n];\n\n/** This function gives the problems of the sizes of a pool: at most `max` connections, and `min` from 0 to `max`. */\nexport const poolProblems = (name: string, pool: { readonly max: number; readonly min: number }): string[] => {\n const problems: string[] = [];\n if (!Number.isInteger(pool.max) || pool.max < 1) problems.push(`${name}'s pool has a max of ${pool.max}, not a whole number above zero`);\n if (!Number.isInteger(pool.min) || pool.min < 0 || pool.min > pool.max)\n problems.push(`${name}'s pool has a min of ${pool.min}, not a whole number from 0 to its max`);\n return problems;\n};\n\n/**\n * This function gives the problem O07 of a client whose server has a deadline, or null. The client\n * must wait longer than the deadline of the server. Then the server ends a slow call with an error\n * first. Otherwise the client gives up first, and the server goes on with the call. `call` names\n * the call of the server, for example a statement.\n */\nexport const deadlineProblem = (name: string, request: Quantity, deadline: Extent, call: string): string | null =>\n deadline.value !== \"infinite\" && ms(request) <= ms(deadline)\n ? `${name}'s request_timeout is not above its statement_timeout: the server goes on with a ${call} that the client gave up (O07)`\n : null;\n\n/**\n * This function gives the problem O01 of a client pool, or null. The pool must close an idle\n * connection before its server does. Otherwise a request can go on a connection that the server\n * closed. A server that keeps an idle connection with no bound gives no problem. `knob` names the\n * idle time of the client: `pool_idle_expiry` of a pool, or `client_idle_timeout` of a gRPC channel.\n */\nexport const idleCloseProblem = (name: string, expiry: Extent, close: Extent, knob = \"pool_idle_expiry\"): string | null =>\n close.value !== \"infinite\" && !(ms(expiry) < ms(close))\n ? `${name}'s ${knob} is not below the idle close of its server: a request can go on a connection that the server closed (O01)`\n : null;\n\n/**\n * This function gives the problems of the timers that the wrapper has, because its driver has none.\n * Each one must be a finite time above zero. `bounds` gives what each knob bounds, for example \"a call\".\n */\nexport const wrapperTimerProblems = (name: string, knobs: object, bounds: Readonly<Record<string, string>>): string[] =>\n Object.entries(bounds)\n .filter(([knob]) => {\n const value = ms(knobOf(knobs, knob));\n return !(value > 0) || !Number.isFinite(value);\n })\n .map(([knob, what]) => `${name}'s ${knob} is not a finite time above zero: the timer of the wrapper is the only bound of ${what}`);\n\n/** This function gives the problem of a finite connection age, for a client that does not replace a connection at an age. `reason` tells why. */\nexport const ageProblems = (name: string, knobs: Pick<ClientKnobs, \"max_connection_age\">, reason: string): string[] =>\n knobs.max_connection_age.value === \"infinite\" ? [] : [`${name}'s max_connection_age is not \"infinite\": ${reason}`];\n\n/** This function gives the problem of a keepalive that is not a finite time above zero, for a driver that always turns keepalive on. */\nexport const keepaliveOnProblems = (name: string, knobs: Pick<ClientKnobs, \"keepalive_idle\">, driver: string): string[] => {\n const value = ms(knobs.keepalive_idle);\n return value > 0 && Number.isFinite(value) ? [] : [`${name}'s keepalive_idle is not a finite time above zero: ${driver} always turns keepalive on`];\n};\n\n/** These are the parts of one call that the core uses to send it, and to send it again. */\nexport interface Reissue<T> {\n readonly clock: Clock;\n /** This time bounds the call and its second try, in milliseconds from its start. */\n readonly bound: number;\n /** This operation is the declaration of the call, or undefined when the table does not name it. */\n readonly operation: Operation | undefined;\n readonly options: SendOptions;\n /** This function makes one try. Its signal ends the try. */\n readonly attempt: (signal: AbortSignal | undefined) => Promise<Attempt<T>>;\n /** This function operates before the second try, with the time that is left. False stops the second try. */\n readonly beforeAgain?: ((left: number, signal: AbortSignal | undefined) => Promise<boolean>) | undefined;\n /** This function receives the start of a second try, for the record of the client. */\n readonly onReissue: () => void;\n}\n\n/**\n * This function sends a call, and gives its outcome in three values. It sends again only an\n * idempotent operation, one time at most, after an outcome that is not certain. A reply of the\n * server is certain. The second try ends at the bound of the call, not one bound after its own start.\n */\nexport const sendWithReissue = async <T>(call: Reissue<T>): Promise<Sent<T>> => {\n const { clock, bound } = call;\n const { signal } = call.options;\n const operation = call.options.reissue === false ? undefined : call.operation;\n const started = clock.now();\n const first = await call.attempt(signal);\n if (first.outcome === \"ok\") return { outcome: \"ok\", value: first.value, attempts: 1 };\n const again =\n !(first.outcome === \"error\" && first.reply) &&\n operation !== undefined &&\n signal?.aborted !== true &&\n mayReissue(operation, quantity((clock.now() - started) / 1000, \"s\"));\n const left = bound - (clock.now() - started);\n if (!again || left <= 0) return { ...first, attempts: 1 };\n if (call.beforeAgain !== undefined && !(await call.beforeAgain(left, signal))) return { ...first, attempts: 1 };\n call.onReissue();\n const end = new AbortController();\n const stop = clock.after(Math.max(0, bound - (clock.now() - started)), () => end.abort());\n const forward = (): void => end.abort();\n signal?.addEventListener(\"abort\", forward, { once: true });\n const second = await call.attempt(end.signal);\n stop();\n signal?.removeEventListener(\"abort\", forward);\n return second.outcome === \"ok\" ? { outcome: \"ok\", value: second.value, attempts: 2 } : { ...second, attempts: 2 };\n};\n\n/**\n * This function gives the checks of a client, for `svc.check(check, run)`. It gives a check only for\n * each step that the options name, and only when the client has a function for that step.\n */\nexport const stepChecks = <S extends Step>(\n name: string,\n options: CheckOptions<S>,\n runs: Readonly<Partial<Record<S, StepCheck>>>,\n): ReadonlyArray<readonly [Check, CheckFn]> => {\n const made: Array<readonly [Check, CheckFn]> = [];\n for (const [step, run] of Object.entries(runs) as Array<[S, StepCheck | undefined]>) {\n if (run === undefined || !(step in options.steps)) continue;\n made.push([\n { name: `${name}.${step}`, dependency: name, step, ownConnection: run.ownConnection, gates: options.steps[step] ?? null, poll: options.poll },\n run.run,\n ]);\n }\n return made;\n};\n\n/** This function makes a check outcome from a sent result: only `ok` passes. */\nexport const passes = (sent: Sent<unknown>): Outcome => (sent.outcome === \"ok\" ? \"ok\" : \"error\");\n\n/** This check gives the state of the client, without a round trip. */\nexport const localStateCheck = (ready: () => boolean): StepCheck => ({ ownConnection: true, run: () => (ready() ? \"ok\" : \"error\") });\n\n/**\n * This check sends one call on the connection of the application. A check measures the step at this time.\n * Thus it does not send the call again, and only `ok` passes.\n */\nexport const roundTripCheck = (send: (options: SendOptions) => Promise<Sent<unknown>>): StepCheck => ({\n ownConnection: true,\n run: async (signal) => passes(await send({ signal, reissue: false })),\n});\n\n/**\n * This check opens a new connection each time. It finds a server that refuses connections, but not\n * a stalled connection of the application. A failed dial throws, and the poller counts the throw as an error.\n */\nexport const dialCheck = (dial: DialFn, host: string, port: number): StepCheck => ({\n ownConnection: false,\n run: async (signal) => {\n await dial(host, port, signal);\n return \"ok\" as const;\n },\n});\n\n/** These are the bounds of a client that the core examines in a description. */\nexport interface ClientBounds {\n /** This time bounds the close of the client, in milliseconds. */\n readonly close: number;\n /** This time bounds a call on the connection of the application, in milliseconds. */\n readonly request: number;\n}\n\n/** This function gives the bounds of a client that `clientProblems` examines, from its knobs. */\nexport const clientBounds = (knobs: ClientKnobs): ClientBounds => ({ close: ms(knobs.close_bound), request: ms(knobs.request_timeout) });\n\n/**\n * This function gives the problems of a client in a description:\n * - The client needs the phase \"tear down dependencies\", and its close must fit in the cap of that phase.\n * - A round-trip check must end in one jittered poll (B7, O13).\n */\nexport const clientProblems = (name: string, description: ServiceDescription, bounds: ClientBounds): string[] => {\n const found: string[] = [];\n const teardown = description.shutdown.phases.find((phase) => phase.name === \"tear down dependencies\");\n if (teardown === undefined) found.push(`${name} needs the phase \"tear down dependencies\" to close`);\n else if (bounds.close >= valueIn(teardown.cap, \"ms\"))\n found.push(`${name}'s close_bound of ${bounds.close} ms does not fit in the cap of \"tear down dependencies\"`);\n for (const check of description.checks) {\n if (check.dependency !== name || check.step !== \"round-trip\") continue;\n const slowest = valueIn(check.poll.timeout, \"ms\") + valueIn(check.poll.interval, \"ms\") * (1 + check.poll.jitter);\n if (bounds.request > slowest) found.push(`${name}'s request_timeout outlives one jittered poll of ${check.name} (B7, O13)`);\n }\n return found;\n};\n\n/**\n * This poll is a poll of the checks to start from: each 5 s with a jitter of 20 %, and a timeout of\n * 4 s (B6, O12). Its slowest poll is 4 s + 5 s × 1.2 = 10 s, the slowest poll of the cells of O13.\n * It does not depend on a driver. The starting knobs of each driver fit it.\n */\nexport const STARTING_POLL: Poll = { interval: { value: 5, unit: \"s\" }, jitter: 0.2, timeout: { value: 4, unit: \"s\" } };\n","// The dial of a client: a new TCP connection that sends the first bytes of its protocol and reads\n// the answer. Each driver gives its bytes and its rule for the answer. The core keeps node:net out\n// of `core.ts`, thus only an entry that gives a dial loads it.\n\nimport { connect as dialTcp } from \"node:net\";\nimport type { DialFn } from \"./core.ts\";\n\n/**\n * A judge reads the bytes that the server sent so far. It gives null for a good answer, an error for\n * a bad answer, or \"more\" when it must wait for more bytes.\n */\nexport type Judge = (received: Buffer) => Error | null | \"more\";\n\n/**\n * This function makes a dial function. The dial opens a TCP connection, sends `hello`, and reads the\n * answer until `judge` decides. An abort, an error of the socket, and a close before the decision fail the dial.\n * The dial destroys its socket at its end.\n */\nexport const helloDial =\n (hello: string | Uint8Array, judge: Judge): DialFn =>\n (host, port, signal) =>\n new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error(\"the dial was aborted\"));\n return;\n }\n const socket = dialTcp({ host, port });\n let received = Buffer.alloc(0);\n const end = (error?: Error): void => {\n signal.removeEventListener(\"abort\", onAbort);\n socket.destroy();\n if (error === undefined) resolve();\n else reject(error);\n };\n const onAbort = (): void => end(new Error(\"the dial was aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n socket.once(\"connect\", () => socket.write(hello));\n socket.on(\"data\", (chunk: Buffer) => {\n received = Buffer.concat([received, chunk]);\n const verdict = judge(received);\n if (verdict === \"more\") return;\n end(verdict ?? undefined);\n });\n socket.once(\"error\", (error) => end(error));\n socket.once(\"close\", () => end(new Error(\"the server closed the dial\")));\n });\n\n/** This function gives the authority of a host and a port for a Host header. An IPv6 address goes in brackets. */\nexport const authority = (host: string, port: number): string => `${host.includes(\":\") ? `[${host}]` : host}:${port}`;\n\n/**\n * This function makes a judge of the status line of an HTTP/1.1 answer. The judge waits for the end\n * of the line, in the first 8 KiB. Then `accept` reads the status code, and gives null for a good status or an error.\n * The HTTP client accepts each status. The WebSocket client accepts only 101.\n */\nexport const statusLineJudge =\n (accept: (status: number, line: string) => Error | null): Judge =>\n (received) => {\n const end = received.indexOf(\"\\r\\n\");\n if (end < 0) return received.length > 8192 ? new Error(\"the answer has no status line in its first 8 KiB\") : \"more\";\n const line = received.toString(\"latin1\", 0, end);\n const status = /^HTTP\\/1\\.[01] (\\d{3})/.exec(line);\n return status === null ? new Error(`the answer ${JSON.stringify(line)} is not an HTTP/1.1 status line`) : accept(Number(status[1]), line);\n };\n","// The parts of the machine document of a client that each client has. Each client entry writes its\n// own states and transitions, because the states are the states of its driver. The helpers, the\n// knob shape, the timer of the bounded close and the events that the core records are the same.\n\nimport type { TransitionDef } from \"@mark1russell7/algebra\";\n\n/** This function gives one transition of a machine document. */\nexport const transition = (\n id: string,\n from: string | readonly string[],\n on: string | readonly string[],\n to: string,\n extra: Partial<TransitionDef> = {},\n): TransitionDef => ({\n id,\n from,\n on,\n to,\n ...extra,\n});\n\n/** A client knob is a knob of a client in a machine document: a time in seconds, with no default. */\nexport interface ClientKnob {\n readonly key: string;\n readonly scope: \"client\";\n readonly unit: \"s\";\n readonly default: null;\n}\n\n/** This function gives a knob of a client in a machine document, by the option of the driver that it sets. */\nexport const clientKnob = (key: string): ClientKnob => ({ key, scope: \"client\", unit: \"s\", default: null });\n\n/** A close timer is the timer of the bounded close in a machine document. */\nexport interface CloseTimer {\n readonly duration: { readonly knob: \"close_bound\"; readonly unit: \"s\" };\n readonly arms: readonly [\"close.request\"];\n readonly cancels: readonly [\"status.end\"];\n readonly fires: \"close.bound\";\n}\n\n/** This timer is the bound of the close. The close request arms it, and the end cancels it. At its end, the driver destroys each socket. */\nexport const closeTimer = (): CloseTimer => ({\n duration: { knob: \"close_bound\", unit: \"s\" },\n arms: [\"close.request\"],\n cancels: [\"status.end\"],\n fires: \"close.bound\",\n});\n\n/**\n * These are the events that each machine document of a client declares. The core and the shared\n * parts of each client record them: a second try, a failed connect, and the steps of the bounded close.\n */\nexport const CLIENT_EVENTS = [\"status.connecting\", \"connect.failed\", \"command.reissue\", \"close.request\", \"close.bound\", \"close.destroy\", \"status.end\"] as const;\n"],"mappings":"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"}