@mark1russell7/service 0.5.12 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -1
- package/build/{core-C2wnF4mg.d.ts → core-BPLRDcUJ.d.ts} +45 -2
- package/build/dial-DA8vJfh2.d.ts +16 -0
- package/build/grpc.d.ts +307 -0
- package/build/grpc.js +859 -0
- package/build/grpc.js.map +1 -0
- package/build/http-client.d.ts +248 -0
- package/build/http-client.js +612 -0
- package/build/http-client.js.map +1 -0
- package/build/index.d.ts +3 -147
- package/build/{machine-Czfb6fV4.js → machine-BhTRu3u9.js} +182 -2
- package/build/machine-BhTRu3u9.js.map +1 -0
- package/build/mongodb.d.ts +307 -0
- package/build/mongodb.js +688 -0
- package/build/mongodb.js.map +1 -0
- package/build/pg.d.ts +1 -1
- package/build/pg.js +30 -33
- package/build/pg.js.map +1 -1
- package/build/queue-snRHK3Jm.d.ts +150 -0
- package/build/rabbitmq.d.ts +294 -0
- package/build/rabbitmq.js +748 -0
- package/build/rabbitmq.js.map +1 -0
- package/build/redis.d.ts +3 -9
- package/build/redis.js +6 -38
- package/build/redis.js.map +1 -1
- package/build/websocket.d.ts +275 -0
- package/build/websocket.js +780 -0
- package/build/websocket.js.map +1 -0
- package/package.json +38 -1
- package/build/machine-Czfb6fV4.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.js","names":["knob","t"],"sources":["../src/http-client/connection.ts","../src/http-client/meets.ts","../src/http-client/client.ts","../src/http-client/machine.ts","../src/http-client/starting.ts"],"sourcesContent":["import type { ClientSocket, Factory } from \"../client/core.ts\";\n\n/** An HTTP socket is the socket of a client in the core: the join of the prober and the bounded close read it. */\nexport type HttpSocket = ClientSocket;\n\n/** An HTTP response is a whole response: its status, its headers, and its whole body. */\nexport interface HttpResponse {\n readonly status: number;\n /** These are the headers of the response, by their names in lower case, as Node gives them. */\n readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;\n readonly body: Buffer;\n}\n\n/** An agent request is one request that the wrapper gives the agent. */\nexport interface AgentRequest {\n readonly host: string;\n readonly port: number;\n readonly method: string;\n readonly path: string;\n readonly headers: Readonly<Record<string, string>>;\n readonly body?: Uint8Array | undefined;\n}\n\n/**\n * These are the events of one request that the wrapper reads. The agent gives each one at most one\n * time. `connect` comes only for a new connection. A request on a free socket left the client when it got that socket.\n */\nexport interface AgentEvents {\n /** The agent gave the request a socket. `reused` is true for a socket from the free list (`req.reusedSocket`). */\n socket(socket: HttpSocket, reused: boolean): void;\n /** The new socket of the request connected. The request then leaves the client. */\n connect(): void;\n}\n\n/** An agent exchange is one request in the agent: the promise of its whole response, and the abort that destroys its socket. */\nexport interface AgentExchange {\n /** This promise resolves with the whole response, or rejects with the error of the request, the socket or the body. */\n readonly response: Promise<HttpResponse>;\n /** This method destroys the request and its socket. A request that waits for a socket leaves the queue of the agent. */\n abort(error: Error): void;\n}\n\n/**\n * An agent is the part of an `http.Agent` of Node that the wrapper uses, with `http.request` on it.\n * A fake for tests has each part too. Thus the wrapper does not load node:http until it connects.\n */\nexport interface HttpAgent {\n /** This method sends one request on the agent. */\n request(request: AgentRequest, events: AgentEvents): AgentExchange;\n /** This method gives each socket of the agent that is open: the busy ones and the free ones. */\n sockets(): readonly HttpSocket[];\n /** This method destroys each socket of the agent. A request on a socket then fails. */\n destroy(): void;\n}\n\n/**\n * These are the options that the wrapper gives `new http.Agent`. Each one comes from the declaration.\n * The wrapper sets each option that has an effect on a timer, thus no default of Node decides one.\n */\nexport interface HttpAgentOptions {\n readonly keepAlive: true;\n /**\n * This time is the free socket timer (`timeout`). The agent closes a free socket at the smaller of\n * this time and the hint of the server less `agentKeepAliveTimeoutBuffer`. 0 is no timer, and then the hint does not apply.\n */\n readonly timeout: number;\n /** This time is subtracted from the hint of the server (`Keep-Alive: timeout=…`). A hint at or below it gives no reuse. */\n readonly agentKeepAliveTimeoutBuffer: number;\n /** This time is the idle time of TCP keepalive. The agent always turns keepalive on, and Node truncates the time to whole seconds. */\n readonly keepAliveMsecs: number;\n /** This count is the most sockets for one origin. A request waits in the agent for a socket above it. */\n readonly maxSockets: number;\n /** This count is the most free sockets for one origin. */\n readonly maxFreeSockets: number;\n /** The agent gives the socket that was free last, as the global agent does. */\n readonly scheduling: \"lifo\";\n}\n\n/** An agent factory makes the agent of the client from its options. */\nexport type AgentFactory = Factory<HttpAgentOptions, HttpAgent>;\n\n/** These are the parts of `node:http` that the factory uses. The specifier is a value, thus the entry loads node:http only at the connect. */\ninterface NodeHttp {\n readonly Agent: new (options: HttpAgentOptions) => NodeAgent;\n request(options: object): NodeRequest;\n}\n\ninterface NodeAgent {\n readonly sockets: Readonly<Record<string, readonly HttpSocket[] | undefined>>;\n readonly freeSockets: Readonly<Record<string, readonly HttpSocket[] | undefined>>;\n destroy(): void;\n}\n\ninterface NodeSocket extends HttpSocket {\n readonly connecting: boolean;\n once(event: \"connect\", listener: () => void): unknown;\n}\n\ninterface NodeIncoming {\n readonly statusCode?: number | undefined;\n readonly headers: Readonly<Record<string, string | string[] | undefined>>;\n readonly complete: boolean;\n on(event: \"data\", listener: (chunk: Buffer) => void): unknown;\n on(event: \"end\" | \"close\", listener: () => void): unknown;\n on(event: \"error\", listener: (error: Error) => void): unknown;\n}\n\ninterface NodeRequest {\n readonly reusedSocket: boolean;\n on(event: \"socket\", listener: (socket: NodeSocket) => void): unknown;\n on(event: \"response\", listener: (response: NodeIncoming) => void): unknown;\n on(event: \"error\", listener: (error: Error) => void): unknown;\n end(body?: Uint8Array): void;\n destroy(error?: Error): void;\n}\n\n/**\n * This factory makes an `http.Agent` of Node. It loads node:http only when a client connects.\n * Each request goes through `http.request` with the agent. The response settles at the end of its body.\n * A body that ends before it is complete rejects the response.\n */\nexport const nodeAgent: AgentFactory = async (options) => {\n const specifier = \"node:http\";\n const http = (await import(specifier)) as NodeHttp;\n const agent = new http.Agent(options);\n const all = (lists: Readonly<Record<string, readonly HttpSocket[] | undefined>>): HttpSocket[] => Object.values(lists).flatMap((list) => list ?? []);\n return {\n request(request, events) {\n const req = http.request({ host: request.host, port: request.port, method: request.method, path: request.path, headers: request.headers, agent });\n const response = new Promise<HttpResponse>((resolve, reject) => {\n req.on(\"socket\", (socket) => {\n events.socket(socket, req.reusedSocket);\n if (socket.connecting) socket.once(\"connect\", () => events.connect());\n else if (!req.reusedSocket) events.connect();\n });\n req.on(\"response\", (incoming) => {\n const chunks: Buffer[] = [];\n incoming.on(\"data\", (chunk) => chunks.push(chunk));\n incoming.on(\"error\", reject);\n incoming.on(\"end\", () => resolve({ status: incoming.statusCode ?? 0, headers: incoming.headers, body: Buffer.concat(chunks) }));\n incoming.on(\"close\", () => {\n if (!incoming.complete) reject(new Error(\"the connection closed before the end of the body\"));\n });\n });\n req.on(\"error\", reject);\n });\n req.end(request.body);\n return { response, abort: (error) => req.destroy(error) };\n },\n sockets: () => [...all(agent.sockets), ...all(agent.freeSockets)],\n destroy: () => agent.destroy(),\n };\n};\n","import type { Detector, Meeting, Situation } from \"../client/meets.ts\";\nimport { WHAT, immediate, keepaliveAnswered, keepaliveReset, keepaliveSilent, kernelGiveUp } from \"../client/meets.ts\";\nimport type { HttpClientKnobs } from \"./client.ts\";\n\n/**\n * This function gives the manifestations that an HTTP client can meet, and the detectors of each,\n * from its knobs. The HTTP cells measured them on the agent of Node 24.21.0:\n * - The agent has no timer of a call, thus the timer of the wrapper finds each silent call in flight.\n * - The free socket timer closes an idle socket at `pool_idle_expiry` at most. Thus no request goes\n * later on a socket that went silent while it was free.\n * - A FIN on a busy socket fails the call with \"socket hang up\". A FIN on a free socket destroys it.\n */\nexport const httpClientMeetings = (name: string, knobs: HttpClientKnobs): Meeting[] => {\n const call: Detector = {\n by: \"request_timeout\",\n within: knobs.request_timeout,\n situation: \"in flight\",\n effect: `the timer of the wrapper ends the call as indeterminate, and ${name} destroys its socket`,\n };\n const idleExpiry = (situation: Situation): Detector => ({\n by: \"pool_idle_expiry\",\n within: knobs.pool_idle_expiry,\n situation,\n effect: \"the free socket timer of the agent closes the socket, and the next request opens a new socket\",\n });\n const giveUp = kernelGiveUp(knobs.retrans_giveup_time, \"the call fails as indeterminate\");\n const silent = (manifestation: \"blackhole\" | \"freeze-host\"): Meeting => ({\n manifestation,\n what: WHAT[manifestation],\n detectors: [call, giveUp, idleExpiry(\"idle\"), keepaliveSilent(knobs.keepalive_idle)],\n });\n return [\n {\n manifestation: \"rst\",\n what: WHAT.rst,\n detectors: [\n immediate(\"in flight\", \"the call fails with ECONNRESET as indeterminate, and the next request opens a new socket\"),\n idleExpiry(\"idle\"),\n keepaliveReset(knobs.keepalive_idle),\n ],\n },\n {\n manifestation: \"fin\",\n what: WHAT.fin,\n detectors: [\n immediate(\"in flight\", 'the call fails with \"socket hang up\" as indeterminate'),\n immediate(\"idle\", \"the agent destroys the free socket, and the next request opens a new socket\"),\n ],\n },\n silent(\"blackhole\"),\n silent(\"freeze-host\"),\n {\n manifestation: \"freeze-process\",\n what: WHAT[\"freeze-process\"],\n detectors: [call, idleExpiry(\"idle\"), keepaliveAnswered()],\n },\n {\n manifestation: \"app-stall\",\n what: \"the server holds its reply, for example in a slow handler, and its kernel stays up\",\n detectors: [call, idleExpiry(\"idle\"), keepaliveAnswered()],\n },\n ];\n};\n","import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport type { Cancel } from \"../clock.ts\";\nimport { realClock } from \"../clock.ts\";\nimport type { Attempt, CheckOptions, Client, ClientJoin, ClientKnobs, ClientOptions, ClientSpec, ClientStep, DialFn, SendOptions, Sent } from \"../client/core.ts\";\nimport {\n ageProblems,\n attachClient,\n clientBounds,\n clientProblems,\n clientRecorder,\n dialCheck,\n idleCloseProblem,\n inFlight,\n keepaliveOnProblems,\n localStateCheck,\n message,\n ms,\n notSent,\n operationTable,\n poolProblems,\n roundTripCheck,\n sendWithReissue,\n socketEnds,\n stepChecks,\n timeProblems,\n trackWithTimer,\n wholeProblems,\n withinBound,\n wrapperTimerProblems,\n zeroProblems,\n} from \"../client/core.ts\";\nimport type { Judge } from \"../client/dial.ts\";\nimport { authority, helloDial, statusLineJudge } from \"../client/dial.ts\";\nimport type { Dependency } from \"../description.ts\";\nimport type { DependencyHandle } from \"../lifecycle.ts\";\nimport type { Operation } from \"../operation.ts\";\nimport type { AgentExchange, AgentFactory, HttpAgent, HttpAgentOptions, HttpResponse, HttpSocket } from \"./connection.ts\";\nimport { nodeAgent } from \"./connection.ts\";\nimport { httpClientMeetings } from \"./meets.ts\";\n\n/**\n * These are the knobs of the client, each a time, by the names of the ordering law where the law\n * has them. The runtime has no defaults, thus each knob is necessary. The constant\n * `HTTP_CLIENT_STARTING_KNOBS` gives a declaration from the HTTP cells. The knobs that each client has come from the core.\n */\nexport interface HttpClientKnobs extends ClientKnobs {\n /** This time bounds the TCP connect of a new socket. The wrapper has this timer, because the agent has none. */\n readonly connect_bound: Quantity;\n /**\n * This time bounds each call: from its start until the end of the body of its response. It also\n * bounds the wait for a socket. The wrapper has this timer, because the agent has none.\n */\n readonly request_timeout: Quantity;\n /**\n * This time is the free socket timer of the agent (`timeout`). The agent closes a free socket at the\n * smaller of this time and the hint of the server less `hint_buffer`. It must be below `server_idle_close` (O01).\n */\n readonly pool_idle_expiry: Extent;\n /** This time is what the agent subtracts from the hint of the server (`agentKeepAliveTimeoutBuffer`). A hint at or below it gives no reuse. */\n readonly hint_buffer: Quantity;\n /**\n * This time is when the server closes an idle connection, after its response. A Node server closes\n * at `keepAliveTimeout` plus `keepAliveTimeoutBuffer`. \"infinite\" is a server that keeps an idle\n * connection with no bound. The client does not set it: `problems` compares it with `pool_idle_expiry` (O01).\n */\n readonly server_idle_close: Extent;\n /** This time is the idle time of TCP keepalive (`keepAliveMsecs`), in whole seconds. The agent always turns keepalive on. */\n readonly keepalive_idle: Extent;\n /** The agent does not replace a connection at an age, thus this knob must be \"infinite\". */\n readonly max_connection_age: Extent;\n /** This time bounds the close of the client: the wait for the calls in flight, then a destroy of each socket. */\n readonly close_bound: Quantity;\n}\n\n/** A check request is the request of the round-trip check, and of the first connection at the connect. */\nexport interface CheckRequest {\n readonly method: string;\n readonly path: string;\n}\n\n/**\n * This request asks about the server, not about a resource (RFC 9110 §9.3.7). Thus it changes no\n * state. A server that does not route it still answers with a status, and the status tells that it is alive.\n */\nexport const OPTIONS_STAR: CheckRequest = { method: \"OPTIONS\", path: \"*\" };\n\n/** An HTTP client spec is the declaration of one HTTP client: the origin that it connects to, its pool, and its knobs. */\nexport interface HttpClientSpec extends ClientSpec<HttpClientKnobs> {\n /** This count is the most sockets of the pool (`maxSockets`). It is also the most free sockets. */\n readonly pool: { readonly max: number };\n /** This request is the request of the round-trip check and of the first connection. Without it, the client uses `OPTIONS *`. */\n readonly check?: CheckRequest | undefined;\n /**\n * These operations add to the table of the client, or change it. The table names each method by\n * its idempotency in RFC 9110. A caller can name an operation of its own, for example a POST with an `Idempotency-Key`.\n */\n readonly operations?: Readonly<Record<string, Operation>> | undefined;\n}\n\n/** These options make a client. Without them, it uses the agent of Node, the real clock, and a dial with `OPTIONS *`. */\nexport type HttpClientOptions = ClientOptions<AgentFactory>;\n\n/** An HTTP call is one request. A string body goes as UTF-8, with its `content-length`. */\nexport interface HttpCall {\n readonly method: string;\n readonly path: string;\n readonly headers?: Readonly<Record<string, string>> | undefined;\n readonly body?: string | Uint8Array | undefined;\n /** This name is the operation of the call in the table. Without it, the operation is the method. */\n readonly operation?: string | undefined;\n}\n\n/** An HTTP client is a dependency of a service: its declaration, its handle, its checks and its requests. */\nexport interface HttpClient extends Client<ClientStep, ClientJoin> {\n /** This text is the state of the client, as `HTTP_CLIENT_MACHINE` names it. */\n readonly status: string;\n /**\n * This method sends a request, and gives its outcome in three values. A whole response of each\n * status is `ok`. The client sends again only an idempotent operation, one time at most, after an\n * outcome that is not certain. The second try stays in the bound of the request.\n */\n request(call: HttpCall, options?: SendOptions): Promise<Sent<HttpResponse>>;\n}\n\n/**\n * These operations are the methods of HTTP, by their idempotency in RFC 9110 §9.2.2. A second\n * request of GET, HEAD, OPTIONS, TRACE, PUT or DELETE has the same effect as one. POST and PATCH do not.\n */\nexport const HTTP_OPERATIONS: Readonly<Record<string, Operation>> = {\n GET: { idempotent: true },\n HEAD: { idempotent: true },\n OPTIONS: { idempotent: true },\n TRACE: { idempotent: true },\n PUT: { idempotent: true },\n DELETE: { idempotent: true },\n POST: { idempotent: false },\n PATCH: { idempotent: false },\n};\n\n/** This function gives the problems of a spec: the knobs that the agent cannot use. */\nexport const specProblems = (name: string, spec: HttpClientSpec): string[] => {\n const { knobs } = spec;\n const problems = [\n ...timeProblems(name, knobs),\n // the agent has no timer of a connect or of a call\n ...wrapperTimerProblems(name, knobs, { connect_bound: \"a connect\", request_timeout: \"a call\" }),\n // the agent reads a timeout of 0 as no free socket timer, and then the hint of the server does not apply\n ...zeroProblems(name, \"the agent\", knobs, { bounded: [\"pool_idle_expiry\"] }),\n ...keepaliveOnProblems(name, knobs, \"the agent\"),\n // Node truncates the keepalive delay to whole seconds\n ...wholeProblems(name, knobs, { millis: [\"connect_bound\", \"request_timeout\", \"pool_idle_expiry\", \"hint_buffer\"], seconds: [\"keepalive_idle\"] }),\n ...poolProblems(name, { max: spec.pool.max, min: 0 }),\n ...ageProblems(name, knobs, \"the agent does not replace a connection at an age\"),\n ];\n if (!Number.isFinite(ms(knobs.hint_buffer))) problems.push(`${name}'s hint_buffer is not a finite time: the agent subtracts it from the hint of the server`);\n return problems;\n};\n\n/** This function gives the options of `new http.Agent` from a spec. Each timer of the agent comes from the declaration. */\nexport const agentOptions = (spec: HttpClientSpec): HttpAgentOptions => {\n const { knobs } = spec;\n return {\n keepAlive: true,\n timeout: ms(knobs.pool_idle_expiry),\n agentKeepAliveTimeoutBuffer: ms(knobs.hint_buffer),\n keepAliveMsecs: ms(knobs.keepalive_idle),\n maxSockets: spec.pool.max,\n maxFreeSockets: spec.pool.max,\n scheduling: \"lifo\",\n };\n};\n\n/** This function gives the bytes of the dial: `OPTIONS *` with the Host header, and a close after the answer. */\nexport const optionsRequest = (host: string, port: number): string => `OPTIONS * HTTP/1.1\\r\\nHost: ${authority(host, port)}\\r\\nConnection: close\\r\\n\\r\\n`;\n\n/** This judge reads the status line of the answer. Any status passes, because it tells that an HTTP server answered. */\nexport const statusJudge: Judge = statusLineJudge(() => null);\n\n/** This dial function opens a TCP connection, sends `OPTIONS *`, and waits for the status line. It changes no state on the server. */\nexport const httpDial: DialFn = (host, port, signal) => helloDial(optionsRequest(host, port), statusJudge)(host, port, signal);\n\n/**\n * This function makes an HTTP client, a dependency of a service over an `http.Agent` of Node.\n * - It sets each timer of the agent from the declaration. The agent sends no request again by itself,\n * and the client sends again only an idempotent operation of its table.\n * - Its call is a request until the end of the body of its response. The wrapper has a timer for the\n * connect and one for the call, because the agent has none.\n * - It tells the outcome of a call in three values. A request that did not get a connected socket\n * did not leave the client. A failure after that is not certain.\n * - It destroys the socket of each call that is not `ok`, thus no later request gets that socket.\n * - It records each new socket of the pool (D1). HTTP gives no id of a connection.\n *\n * The core of the clients gives the outcomes, the second try, the calls in flight and the timer of a\n * call. It also gives the bounded close and the checks. This entry gives what the agent does in its own way.\n */\nexport const httpClient = (name: string, spec: HttpClientSpec, options: HttpClientOptions = {}): HttpClient => {\n const problems = specProblems(name, spec);\n if (problems.length > 0) throw new Error(problems.join(\"; \"));\n const { knobs } = spec;\n const clock = options.clock ?? realClock;\n const create = options.create ?? nodeAgent;\n const dial = options.dial ?? httpDial;\n const operations = operationTable(HTTP_OPERATIONS, spec.operations);\n const requestTimeout = ms(knobs.request_timeout);\n const connectBound = ms(knobs.connect_bound);\n const check = spec.check ?? OPTIONS_STAR;\n\n let agent: HttpAgent | null = null;\n let status = \"none\";\n const joins: ClientJoin[] = [];\n // the calls that left the client: the close waits for them, in its bound\n const flight = inFlight();\n // the calls that did not leave the client: the close ends each one as not sent\n const waiting = new Set<(error: string) => void>();\n\n const emit = clientRecorder(options.record, clock, name);\n // the state of the client is the outcome of the last connect of a socket of its pool\n const record = (on: string, to: string = status, detail?: string): void => {\n const from = status;\n status = to;\n emit(from, to, on, detail);\n };\n const closing = (): boolean => status === \"closing\" || status === \"end\";\n\n /**\n * This function makes one try of a call. The try waits for a socket of the agent: a free one, or a\n * new one in `connect_bound`. A failure before the socket is ready is certain: the request did not\n * leave the client. After that, the try waits for the whole response in the rest of `request_timeout`.\n */\n const attempt = async (call: HttpCall, signal: AbortSignal | undefined): Promise<Attempt<HttpResponse>> => {\n const a = agent;\n if (a === null) return notSent(`${name} has not connected`);\n if (closing()) return notSent(`${name} is ${status}: the request did not leave the client`);\n if (signal?.aborted) return notSent(\"the caller aborted the request before it left the client\");\n const started = clock.now();\n const body = typeof call.body === \"string\" ? Buffer.from(call.body, \"utf8\") : call.body;\n const headers = { ...call.headers, ...(body === undefined ? {} : { \"content-length\": String(body.byteLength) }) };\n\n let socket: HttpSocket | undefined;\n let reused = false;\n let left = false;\n // true when the wrapper ended the try itself: the error of the agent that follows is not a failed connect\n let ending = false;\n let connectTimer: Cancel = () => {};\n let decide: (value: true | Attempt<never>) => void = () => {};\n const leaving = new Promise<true | Attempt<never>>((resolve) => {\n let decided = false;\n decide = (value) => {\n if (decided) return;\n decided = true;\n resolve(value);\n };\n });\n // a result of the agent that arrives before the try records it\n let deliver: ((value: Attempt<HttpResponse>, cause?: Error) => void) | null = null;\n let early: { readonly value: Attempt<HttpResponse>; readonly cause: Error | undefined } | null = null;\n let settled = false;\n const settleWith = (value: Attempt<HttpResponse>, cause?: Error): void => {\n if (deliver === null) early ??= { value, cause };\n else deliver(value, cause);\n };\n\n let exchange: AgentExchange | null = null;\n const stop = (text: string): void => {\n if (left) return;\n ending = true;\n connectTimer();\n exchange?.abort(new Error(text));\n decide(notSent(text));\n };\n const ready = (): void => {\n left = true;\n connectTimer();\n decide(true);\n };\n exchange = a.request(\n { host: spec.host, port: spec.port, method: call.method, path: call.path, headers, body },\n {\n socket(given, isReused) {\n socket = given;\n reused = isReused;\n if (isReused) {\n ready();\n return;\n }\n connectTimer = clock.after(connectBound, () => {\n if (left) return;\n record(\"connect.failed\", closing() ? status : \"failing\", \"no connection in connect_bound\");\n stop(\"no connection in connect_bound: the request did not leave the client\");\n });\n },\n connect() {\n const generation = joins.length + 1;\n joins.push({ generation, at: clock.now(), ...socketEnds(socket) });\n record(\"join\", closing() ? status : \"ready\", `socket ${generation}`);\n ready();\n },\n },\n );\n // the agent can fail a request in its queue or at its connect: it did not leave the client\n exchange.response.then(\n (response) => settleWith({ outcome: \"ok\", value: response }),\n (error: unknown) => {\n const cause = error instanceof Error ? error : new Error(message(error));\n if (!left) {\n connectTimer();\n if (!ending) record(\"connect.failed\", closing() ? status : \"failing\", message(error));\n decide(notSent(message(error)));\n return;\n }\n // the abort of the wrapper after the call settled is not a failure of the socket\n if (settled) return;\n // a reused socket that fails before the response: the server closed it, as in the keep-alive race\n if (reused) record(\"reuse.failed\", status, message(error));\n settleWith({ outcome: \"indeterminate\", error: message(error) }, cause);\n },\n );\n\n const abandoned = (): void => stop(\"the caller abandoned the request before it left the client\");\n signal?.addEventListener(\"abort\", abandoned, { once: true });\n const queued = clock.after(requestTimeout, () => stop(\"no socket in request_timeout: the request did not leave the client\"));\n waiting.add(stop);\n const got = await leaving;\n signal?.removeEventListener(\"abort\", abandoned);\n queued();\n waiting.delete(stop);\n if (got !== true) return got;\n\n const sent = exchange;\n return trackWithTimer<HttpResponse>(\n flight,\n clock,\n Math.max(0, requestTimeout - (clock.now() - started)),\n signal,\n \"the caller abandoned the request after it left the client\",\n \"no whole response in request_timeout: the server can have the request\",\n (settle) => {\n deliver = settle;\n if (early !== null) settle(early.value, early.cause);\n },\n (value, cause) => {\n settled = true;\n // the socket of a call that is not ok can hold a part of a response: it goes, and no later request gets it\n if (value.outcome !== \"ok\") sent.abort(cause ?? new Error(value.error));\n },\n );\n };\n\n const request = (call: HttpCall, sendOptions: SendOptions = {}): Promise<Sent<HttpResponse>> => {\n const method = call.method.toUpperCase();\n const named = call.operation ?? method;\n return sendWithReissue({\n clock,\n bound: requestTimeout,\n operation: operations[named],\n options: sendOptions,\n attempt: (signal) => attempt({ ...call, method }, signal),\n onReissue: () => record(\"command.reissue\", status, named),\n });\n };\n\n const handle: DependencyHandle = {\n async connect(signal) {\n record(\"status.connecting\", \"connecting\");\n agent = await create(agentOptions(spec));\n if (signal.aborted) throw new Error(`${name}: the service stopped before the connect`);\n // the check request opens the first socket of the pool. A server that is down does not stop the\n // startup: the checks keep readiness tripped until a connect succeeds (readiness is earned)\n await request(check, { signal, reissue: false });\n },\n async close() {\n const a = agent;\n if (a === null || closing()) return;\n record(\"close.request\", \"closing\");\n for (const stop of [...waiting]) stop(\"the client closed before the request left\");\n // the agent has no close that waits: the client waits for its calls in flight, in its bound\n if (!(await withinBound(clock, ms(knobs.close_bound), flight.idle()))) {\n record(\"close.bound\");\n const open = a.sockets().length;\n flight.settleAll(\"the client closed with the request in flight\");\n a.destroy();\n record(\"close.destroy\", status, `open sockets: ${open}`);\n } else {\n // each free socket closes with a FIN\n a.destroy();\n }\n record(\"status.end\", \"end\");\n },\n };\n\n const declaration: Omit<Dependency, \"name\"> = {\n system: spec.system,\n knobs: {\n connect_bound: knobs.connect_bound,\n request_timeout: knobs.request_timeout,\n pool_idle_expiry: knobs.pool_idle_expiry,\n hint_buffer: knobs.hint_buffer,\n keepalive_idle: knobs.keepalive_idle,\n // the agent sends nothing on a free socket, thus its keepalive is the only refresh of an idle flow (O03)\n idle_refresh_interval: knobs.keepalive_idle,\n max_connection_age: knobs.max_connection_age,\n close_bound: knobs.close_bound,\n retrans_giveup_time: knobs.retrans_giveup_time,\n },\n operations,\n };\n\n const checks = (checkOptions: CheckOptions) =>\n stepChecks<ClientStep>(name, checkOptions, {\n // the outcome of the last connect of a socket, without a round trip\n \"local-state\": localStateCheck(() => status === \"ready\"),\n // the check request through the pool of the application. A status of 500 or more fails it\n \"round-trip\": roundTripCheck(async (sendOptions) => {\n const sent = await request(check, sendOptions);\n return sent.outcome === \"ok\" && sent.value.status >= 500\n ? { outcome: \"error\", error: `the server answered ${sent.value.status}`, reply: true, attempts: sent.attempts }\n : sent;\n }),\n // a new connection each time, with OPTIONS *\n dial: dialCheck(dial, spec.host, spec.port),\n });\n\n return {\n name,\n declaration,\n handle,\n meets: httpClientMeetings(name, knobs),\n get status() {\n return status;\n },\n joins,\n request,\n checks,\n attach: (svc, checkOptions) => attachClient(svc, name, declaration, handle, checks(checkOptions)),\n problems(description) {\n // O01: the pool must close an idle socket before the server does\n const idle = idleCloseProblem(name, knobs.pool_idle_expiry, knobs.server_idle_close);\n return [...clientProblems(name, description, clientBounds(knobs)), ...(idle === null ? [] : [idle])];\n },\n };\n};\n","import type { Machine } from \"@mark1russell7/algebra\";\nimport { clientKnob as knob, closeTimer, transition as t } from \"../client/machine.ts\";\n\n/** The states where the agent exists and serves. */\nconst SERVING = [\"connecting\", \"ready\", \"failing\"] as const;\nconst OPEN = [...SERVING, \"closing\"] as const;\n\n/**\n * The HTTP client of the service as a `mark1russell7.machine/1` document. It is the agent as the\n * wrapper sees it, not one socket: the document `node-http-agent` of `@mark1russell7/machines`\n * gives each socket of the agent. The state is the outcome of the last connect of a socket:\n * - `ready`: the last connect succeeded.\n * - `failing`: the last connect failed, or did not end in connect_bound. The agent does not connect\n * again by itself. The next request tries again.\n *\n * A free socket that the agent closes does not change the state. A request on a reused socket that\n * fails records `reuse.failed`, and the state stays. A test records the transitions of the client\n * and replays them against this document.\n */\nexport const HTTP_CLIENT_MACHINE: Machine = {\n format: \"mark1russell7.machine/1\",\n id: \"service.machine.http-client\",\n title: \"The HTTP client of the service, over an http.Agent of Node\",\n layer: \"driver\",\n version: \"0.1.0\",\n sources: [{ ref: \"packages/service/src/http-client (the client implements this document)\", rung: \"implemented\" }],\n states: {\n none: { initial: true, note: \"the client exists, and its agent does not\" },\n connecting: { note: \"the check request of the startup opens the first socket\" },\n ready: { note: \"the last connect succeeded. A dead free socket stays hidden until the free socket timer or a request meets it\" },\n failing: { note: \"the last connect failed. A request that waits for a socket of a full pool does not change the state\" },\n closing: { note: \"the client waits for its calls in flight in close_bound, then destroys each socket of the agent\" },\n end: { final: true, note: \"the service closed the client\" },\n },\n events: {\n \"status.connecting\": { kind: \"local\" },\n join: { kind: \"rx\", note: \"a new socket of the agent connected: the request on it leaves the client (D1)\" },\n \"connect.failed\": { kind: \"local\", note: \"a connect failed: its error, or connect_bound\" },\n \"reuse.failed\": { kind: \"rx\", note: \"a request on a free socket that the agent reused failed: the server closed that socket\" },\n \"command.reissue\": { kind: \"local\" },\n \"close.request\": { kind: \"local\" },\n \"close.bound\": { kind: \"timer\", note: \"a call in flight did not end in close_bound\" },\n \"close.destroy\": { kind: \"local\", note: \"the bound destroys each socket of the agent, and each call in flight is indeterminate\" },\n \"status.end\": { kind: \"local\" },\n },\n knobs: {\n connect_bound: knob(\"connect.bound\"),\n request_timeout: knob(\"request.bound\"),\n pool_idle_expiry: knob(\"timeout\"),\n hint_buffer: knob(\"agentKeepAliveTimeoutBuffer\"),\n keepalive_idle: knob(\"keepAliveMsecs\"),\n close_bound: knob(\"close.bound\"),\n },\n timers: {\n connect_timeout: {\n duration: { knob: \"connect_bound\", unit: \"s\" },\n arms: [\"enter:connecting\"],\n cancels: [\"join\", \"connect.failed\"],\n fires: \"connect.failed\",\n note: \"the timer of the wrapper on the connect of a new socket. The agent has no connect timer\",\n },\n close: closeTimer(),\n },\n transitions: [\n t(\"connect\", \"none\", \"status.connecting\", \"connecting\"),\n t(\"join\", [...SERVING], \"join\", \"ready\"),\n t(\"connect-failed\", [...SERVING], \"connect.failed\", \"failing\", { timer: \"connect_timeout\" }),\n // a request that is in flight at the close can still connect, or fail\n t(\"join-closing\", \"closing\", \"join\", \"closing\"),\n t(\"connect-failed-closing\", \"closing\", \"connect.failed\", \"closing\"),\n ...OPEN.map((state) => t(`reuse-failed-${state}`, state, \"reuse.failed\", state)),\n ...OPEN.map((state) => t(`reissue-${state}`, state, \"command.reissue\", state)),\n t(\"close\", [...SERVING], \"close.request\", \"closing\"),\n t(\"close-bound\", \"closing\", \"close.bound\", \"closing\", { timer: \"close\" }),\n t(\"close-destroy\", \"closing\", \"close.destroy\", \"closing\"),\n t(\"end\", \"closing\", \"status.end\", \"end\"),\n ],\n};\n","import { STARTING_POLL } from \"../client/core.ts\";\nimport type { Poll } from \"../timing.ts\";\nimport type { HttpClientKnobs } from \"./client.ts\";\n\n/**\n * This poll is the poll of the checks that `HTTP_CLIENT_STARTING_KNOBS` fits. It is `STARTING_POLL` of\n * the core: each 5 s with a jitter of 20 %, and a timeout of 4 s (B6, O12). The poll does not depend on the driver.\n */\nexport const HTTP_CLIENT_STARTING_POLL: Poll = STARTING_POLL;\n\n/**\n * These knobs are a declaration to start from. They come from what the HTTP cells measured on the\n * agent of Node 24.21.0 against a Node server at its defaults. The runtime has no defaults: a service gives\n * these knobs, or its own. Each value holds the borders that the cells measured:\n * - `connect_bound` 5 s: the connect timeout of DP44. The agent has no connect timer.\n * - `request_timeout` 9.5 s: in the slowest poll of 10 s (O13). The agent has no timer of a call.\n * A frozen server held the call of undici until its headersTimeout, at 10.187 s.\n * - `pool_idle_expiry` 4 s: the free socket timer. The agent then closes a free socket at 4 s, the\n * hint of 5 s less `hint_buffer`. That is below the idle close of the server.\n * - `hint_buffer` 1 s: the default of the agent.\n * - `server_idle_close` 6 s: a Node server closes an idle connection at its `keepAliveTimeout` of 5 s\n * plus its buffer of 1 s. The cells measured 6.0003 s. O01 then has a margin of 2 s.\n * - `keepalive_idle` 1 s: the default of the agent. Node truncates it to whole seconds.\n * - `max_connection_age` \"infinite\": the agent does not replace a connection at an age.\n * - `close_bound` 2 s: a call in flight behind a black hole holds its socket with no bound.\n * - `retrans_giveup_time` 924.6 s: the give-up of Linux at `tcp_retries2 = 15`.\n */\nexport const HTTP_CLIENT_STARTING_KNOBS: HttpClientKnobs = {\n connect_bound: { value: 5, unit: \"s\" },\n request_timeout: { value: 9.5, unit: \"s\" },\n pool_idle_expiry: { value: 4, unit: \"s\" },\n hint_buffer: { value: 1, unit: \"s\" },\n server_idle_close: { value: 6, unit: \"s\" },\n keepalive_idle: { value: 1, unit: \"s\" },\n max_connection_age: { value: \"infinite\", unit: \"s\" },\n close_bound: { value: 2, unit: \"s\" },\n retrans_giveup_time: { value: 924.6, unit: \"s\" },\n};\n"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { a as backoffDelay, i as backoffBand, n as DelayBand, o as backoffProblems, r as Jitter, t as Backoff } from "./backoff-B-jGWVDm.js";
|
|
2
2
|
import { a as realClock, i as platformClock, n as Clock, r as PlatformClockOptions, t as Cancel } from "./clock-mVqhXD0b.js";
|
|
3
|
-
import { Heartbeat, HeartbeatOptions, detectionTime, heartbeat } from "./heartbeat.js";
|
|
4
3
|
import { S as retryAfterSeconds, _ as freeAt, a as LimitRule, b as limiterProblems, c as LimiterSpec, d as Refused, f as SlidingWindow, g as decide, h as TokenBucket, i as LimitPolicy, l as LimiterStoreSpec, m as TakeStep, n as Decision, o as Limiter, p as StoreFailure, r as KeyState, s as LimiterEviction, t as Allowed, u as RefusalCause, v as idleSpan, x as longestRetryAfter, y as limitRule } from "./policy-G4QJ1ZZq.js";
|
|
5
4
|
import { $ as asWindow, A as LifecycleEvent, B as Gate, C as ProbeAnswer, Ct as Idempotency, D as Outcome, E as CheckFn, F as CooperativeFloor, G as ServiceDescription, H as Phase, I as Coupling, J as FailsBy, K as Step, L as Dependency, M as Recorder, N as Check, O as Result, P as Controller, Q as asDebounce, R as ExitCodes, S as HealthVerdict, St as processPortOf, T as StartOptions, Tt as mayReissue, U as ProgressMarks, V as MessageTrigger, W as RepeatSignal, X as Span, Y as Poll, Z as Timing, _ as RequestOptions, _t as isExitCode, a as PhaseContext, at as RouteRequest, b as CheckVerdict, bt as matchesMessage, c as STAGES, ct as ExitSignal, d as TransportImpl, dt as MessageShape, et as failSpan, f as cooperativeAbortAt, ft as PROCESS_TRIGGERS, g as RequestFn, gt as embeddedProcess, h as RequestEvent, ht as ProcessTrigger, i as LifecycleState, it as RoutePattern, j as MachineName, l as ShutdownOptions, lt as ExitTrigger, m as RequestEnd, mt as ProcessPort, n as HealthServer, nt as recoverSpan, o as PhaseWork, ot as EXIT_SIGNALS, p as stageRank, pt as ProcessHost, q as Transport, r as Lifecycle, rt as OperationOf, s as RunOptions, st as EmbeddedProcess, t as DependencyHandle, tt as heartbeatDetection, u as Stage, ut as HostStream, v as RequestStream, vt as isExitSignal, w as ProbeKind, wt as Operation, x as Health, xt as nodeProcess, y as RequestWork, yt as isMessageShape, z as FORMAT } from "./lifecycle-fgYfxrHK.js";
|
|
6
5
|
import { a as WatchdogSpec, c as Profile, d as kubernetes, f as localDev, i as Watchdog, l as daemon, n as ServiceBuilder, o as watchdog, r as service, s as PodTiming, t as ControllerBuilder, u as ephemeral } from "./builder-DawJhq4p.js";
|
|
6
|
+
import { Heartbeat, HeartbeatOptions, detectionTime, heartbeat } from "./heartbeat.js";
|
|
7
7
|
import { a as keyedLimiter, i as TakeRequest, n as LimiterStore, o as memoryStore, r as MemoryStore, t as KeyedLimiter } from "./store-D5HLgBnW.js";
|
|
8
8
|
import { a as limiterStateOf, i as limiterMachine, n as SLIDING_WINDOW_MACHINE, r as TOKEN_BUCKET_MACHINE, t as LimiterState } from "./limiter-B0849Vhp.js";
|
|
9
|
+
import { a as JobSource, c as QueueKnobs, d as queueDrain, f as redeliverOnce, i as JobSettle, l as RedeliveryPolicy, n as JobContext, o as QueueDrain, p as UnitRequest, r as JobFailure, s as QueueDrainOptions, t as JobAction, u as RequeueCause } from "./queue-snRHK3Jm.js";
|
|
9
10
|
import { Extent, Quantity } from "@mark1russell7/quantity";
|
|
10
11
|
import { replay } from "@mark1russell7/algebra/replay";
|
|
11
12
|
import { Machine } from "@mark1russell7/algebra";
|
|
@@ -162,21 +163,6 @@ export declare const PROGRESS_RULE = "progress";
|
|
|
162
163
|
*/
|
|
163
164
|
export declare const lint: (svc: ServiceDescription, scenario: Scenario, rules: readonly Rule[]) => Finding[];
|
|
164
165
|
//#endregion
|
|
165
|
-
//#region src/units.d.ts
|
|
166
|
-
/**
|
|
167
|
-
* A unit request names the route of each unit of work in the request tracking of the service. Then
|
|
168
|
-
* the service admits, counts, drains and cuts each unit as it does each request (`life.request`).
|
|
169
|
-
* The controller declares the route, thus its idempotency tells if a cut unit can go again.
|
|
170
|
-
*/
|
|
171
|
-
interface UnitRequest<U> {
|
|
172
|
-
/** This name is the controller that declares the route. */
|
|
173
|
-
readonly controller: string;
|
|
174
|
-
/** This name is the route of each unit, or a function that gives it from the unit. A route can match a route pattern. */
|
|
175
|
-
readonly route: string | ((unit: U) => string);
|
|
176
|
-
/** This function gives the detail of a unit, for the function of a route pattern. */
|
|
177
|
-
readonly detail?: ((unit: U) => unknown) | undefined;
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
166
|
//#region src/worker.d.ts
|
|
181
167
|
/**
|
|
182
168
|
* These are the knobs of a worker loop, by the names of the ordering law where the law has them.
|
|
@@ -266,136 +252,6 @@ interface WorkerLoop extends TransportImpl {
|
|
|
266
252
|
*/
|
|
267
253
|
export declare const workerLoop: <S = undefined>(knobs: WorkerKnobs, options: WorkerLoopOptions<S>) => WorkerLoop;
|
|
268
254
|
//#endregion
|
|
269
|
-
//#region src/queue.d.ts
|
|
270
|
-
/**
|
|
271
|
-
* These are the knobs of a queue drain, by the names of the ordering law where the law has them.
|
|
272
|
-
* The drain has no defaults, thus each knob is necessary. A bound that nobody gives is "infinite".
|
|
273
|
-
*/
|
|
274
|
-
interface QueueKnobs {
|
|
275
|
-
/** This time is the wait after a take that gives no job, before the next take. A time without a bound waits for `wake()`. */
|
|
276
|
-
readonly queue_poll_interval: Extent;
|
|
277
|
-
/**
|
|
278
|
-
* This time is the most that one job can take. At it, the drain aborts the signal of the job, and
|
|
279
|
-
* a throw after it is a failure. The law reads it as `unit_processing_time@consumer` (O20).
|
|
280
|
-
*/
|
|
281
|
-
readonly unit_processing_time: Extent;
|
|
282
|
-
/** This time is the delay after the first failed take in sequence, or after a refusal of the service. The delay is two times longer after each one. */
|
|
283
|
-
readonly retry_base: Quantity;
|
|
284
|
-
/** This time is the most that one delay can be, before its jitter. */
|
|
285
|
-
readonly retry_cap: Quantity;
|
|
286
|
-
/** This time bounds the close: the end of the jobs after their abort, and then the close of the source. */
|
|
287
|
-
readonly close_bound: Quantity;
|
|
288
|
-
/** This count (the unit `count`) is the most jobs in flight at one time. A count without a bound takes each job that comes. */
|
|
289
|
-
readonly concurrency: Extent;
|
|
290
|
-
/** The other knobs go to the declaration, for example `lease_duration@broker`. The drain does not read them. */
|
|
291
|
-
readonly [knob: string]: Extent;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* A job source gives the jobs of a queue drain:
|
|
295
|
-
* - A function takes the next job, or gives null when no job is ready. Its signal aborts when the intake stops.
|
|
296
|
-
* - An async iterable gives the jobs in sequence. The drain asks for the next job only when it has a free slot.
|
|
297
|
-
* At the stop of the intake, the drain ends the iterator through `return()`.
|
|
298
|
-
*/
|
|
299
|
-
type JobSource<J> = ((signal: AbortSignal) => J | null | undefined | PromiseLike<J | null | undefined>) | AsyncIterable<J>;
|
|
300
|
-
/**
|
|
301
|
-
* A job action is what the drain does with a job that failed:
|
|
302
|
-
* - `requeue`: the job goes back on the queue.
|
|
303
|
-
* - `reject`: the job leaves the queue without a success, for example to the dead-letter route of the queue.
|
|
304
|
-
* - `ack`: the job leaves the queue, as a poison message does.
|
|
305
|
-
*/
|
|
306
|
-
type JobAction = "requeue" | "reject" | "ack";
|
|
307
|
-
/** A job failure is a job whose handler failed, with the count of its deliveries, 1 at the first. */
|
|
308
|
-
interface JobFailure<J> {
|
|
309
|
-
readonly job: J;
|
|
310
|
-
readonly error: unknown;
|
|
311
|
-
readonly delivery: number;
|
|
312
|
-
}
|
|
313
|
-
/** A redelivery policy gives the action for a job that failed. The queue declares it, and it has no default (S6 A11). */
|
|
314
|
-
type RedeliveryPolicy<J> = (failure: JobFailure<J>) => JobAction;
|
|
315
|
-
/**
|
|
316
|
-
* A requeue cause tells why a job goes back on the queue:
|
|
317
|
-
* - `failed`: the redelivery policy sent it back after a failure.
|
|
318
|
-
* - `unstarted`: the drain took the job and did not start it, because its intake stopped or the service refused it.
|
|
319
|
-
* - `cut`: the shutdown cut the job, and its route declares that it can go again.
|
|
320
|
-
*/
|
|
321
|
-
type RequeueCause = "failed" | "unstarted" | "cut";
|
|
322
|
-
/** A job settle is how the drain tells the queue the end of each job. Each function can give a promise. */
|
|
323
|
-
interface JobSettle<J> {
|
|
324
|
-
/** This function removes the job from the queue: a success, or the action `ack`. */
|
|
325
|
-
ack(job: J): void | PromiseLike<void>;
|
|
326
|
-
/** This function puts the job back on the queue, or hands it back to the queue at the stop. */
|
|
327
|
-
requeue(job: J, cause: RequeueCause): void | PromiseLike<void>;
|
|
328
|
-
/** This function removes the job from the queue without a success: the action `reject`, or a cut job that cannot go again. */
|
|
329
|
-
reject(job: J, error: unknown): void | PromiseLike<void>;
|
|
330
|
-
/**
|
|
331
|
-
* This function gives the count of deliveries of a job from the queue, 1 at the first. The queue
|
|
332
|
-
* knows it, for example `x-delivery-count` of RabbitMQ or `ApproximateReceiveCount` of SQS. A
|
|
333
|
-
* queue in memory counts for itself. The drain has no default, because a broker gives a new object at each delivery.
|
|
334
|
-
*/
|
|
335
|
-
delivery(job: J): number;
|
|
336
|
-
}
|
|
337
|
-
/** A job context is what the drain gives the handler of each job. */
|
|
338
|
-
interface JobContext {
|
|
339
|
-
/** This signal aborts at the cooperative abort of the drain, at the bound of the job, and at the close. */
|
|
340
|
-
readonly signal: AbortSignal;
|
|
341
|
-
/** This signal aborts when the intake stops. */
|
|
342
|
-
readonly intake: AbortSignal;
|
|
343
|
-
/** This count is the delivery of the job, 1 at the first. */
|
|
344
|
-
readonly delivery: number;
|
|
345
|
-
/** This lifecycle is the lifecycle that the drain got at its start. */
|
|
346
|
-
readonly life: Lifecycle;
|
|
347
|
-
}
|
|
348
|
-
/** These options make a queue drain. Each function can give a promise. */
|
|
349
|
-
interface QueueDrainOptions<J> {
|
|
350
|
-
readonly source: JobSource<J>;
|
|
351
|
-
/** This function does one job. A throw is a failure, and the redelivery policy then gives its action. */
|
|
352
|
-
readonly handle: (job: J, context: JobContext) => unknown;
|
|
353
|
-
readonly settle: JobSettle<J>;
|
|
354
|
-
/** This policy gives the action for each failed job. `redeliverOnce` gives the policy of S6 A11. */
|
|
355
|
-
readonly redelivery: RedeliveryPolicy<J>;
|
|
356
|
-
/** This function closes the source at the close, after the jobs end: for example, it closes a channel. */
|
|
357
|
-
readonly close?: (() => void | PromiseLike<void>) | undefined;
|
|
358
|
-
/** This clock is the clock of the lifecycle. The polls, the backoffs and the bounds use it. The default is `realClock`. */
|
|
359
|
-
readonly clock?: Clock | undefined;
|
|
360
|
-
/** This function gives the random numbers of the jitter, from 0 up to 1. The default is `Math.random`. */
|
|
361
|
-
readonly random?: (() => number) | undefined;
|
|
362
|
-
/**
|
|
363
|
-
* This route puts each job through `life.request`: the service then admits it, counts it, drains
|
|
364
|
-
* it and can cut it. The idempotency of the route tells if a cut job goes back on the queue.
|
|
365
|
-
*/
|
|
366
|
-
readonly request?: UnitRequest<J> | undefined;
|
|
367
|
-
/**
|
|
368
|
-
* This function receives each failure: of a take, of a job, of a settle and of a policy. It also
|
|
369
|
-
* receives the end of a source while the intake is open. The job is undefined for a take and for that end.
|
|
370
|
-
*/
|
|
371
|
-
readonly onError?: ((error: unknown, job: J | undefined) => void) | undefined;
|
|
372
|
-
}
|
|
373
|
-
/** A queue drain is a transport of the lifecycle whose work is the jobs of a queue (S6 A11). */
|
|
374
|
-
interface QueueDrain extends TransportImpl {
|
|
375
|
-
/** These knobs go to the declaration of the consumer: `svc.consumer(name, drain.knobs, drain)`. */
|
|
376
|
-
readonly knobs: QueueKnobs;
|
|
377
|
-
/** This method ends the wait of a poll at once, for example when a job comes. It gives false after the intake stops. */
|
|
378
|
-
wake(): boolean;
|
|
379
|
-
/** This method gives the problems of the knobs in a declaration: the close bound and the bound of a job against the caps. */
|
|
380
|
-
problems(description: ServiceDescription): string[];
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* This function gives the redelivery policy of S6 A11. A poison message gets `ack`. Each other job
|
|
384
|
-
* goes back on the queue after its first failure, one time. After a later failure, `then` gives the
|
|
385
|
-
* action: the declared policy of the queue.
|
|
386
|
-
*/
|
|
387
|
-
export declare const redeliverOnce: <J>(then: JobAction | RedeliveryPolicy<J>, poison?: (error: unknown, job: J) => boolean) => RedeliveryPolicy<J>;
|
|
388
|
-
/**
|
|
389
|
-
* This function makes a queue drain as a transport (S6 A11). One taker takes the jobs from the
|
|
390
|
-
* source while a slot is free, and each job operates in its slot.
|
|
391
|
-
* - A success gets `ack`. A failure gets the action of the redelivery policy.
|
|
392
|
-
* - `stopIntake` takes no new job, and a job that the drain took and did not start goes back (`requeue`, `unstarted`).
|
|
393
|
-
* - `drain` waits for each job in flight until the deadline, and gives it the cooperative abort.
|
|
394
|
-
* - `close` aborts each job, waits for its end, and closes the source, all in `close_bound`. A job
|
|
395
|
-
* that does not end in the bound gets `reject`.
|
|
396
|
-
*/
|
|
397
|
-
export declare const queueDrain: <J>(knobs: QueueKnobs, options: QueueDrainOptions<J>) => QueueDrain;
|
|
398
|
-
//#endregion
|
|
399
255
|
//#region src/machines.d.ts
|
|
400
256
|
/**
|
|
401
257
|
* The startup gate opens one time. The lifecycle moves it from `closed` to `starting` while its
|
|
@@ -436,5 +292,5 @@ export declare const drainMachine: (description: ServiceDescription) => Machine;
|
|
|
436
292
|
/** These are the static machines of the runtime, by the name that their events carry. */
|
|
437
293
|
export declare const MACHINES: Readonly<Record<Exclude<MachineName, "drain" | "client">, Machine>>;
|
|
438
294
|
//#endregion
|
|
439
|
-
export { type Allowed, type Backoff, type Binding, type Bound, type Cancel, type Check, type CheckFn, type CheckVerdict, type Clock, type Controller, type ControllerBuilder, type CooperativeFloor, type Coupling, type Decision, type DelayBand, type Dependency, type DependencyHandle, EXIT_SIGNALS, type EmbeddedProcess, type Evaluated, type ExitCodes, type ExitSignal, type ExitTrigger, FORMAT, type FailsBy, type Finding, type Gate, type Health, type HealthServer, type HealthVerdict, type Heartbeat, type HeartbeatOptions, type HostStream, type Idempotency, type Inequality, type IterationContext, type Jitter, type JobAction, type JobContext, type JobFailure, type JobSettle, type JobSource, type KeyState, type KeyedLimiter, type Lifecycle, type LifecycleEvent, type LifecycleState, type LimitPolicy, type LimitRule, type Limiter, type LimiterEviction, type LimiterSpec, type LimiterState, type LimiterStore, type LimiterStoreSpec, type MachineName, type MemoryStore, type MessageShape, type MessageTrigger, type Operation, type OperationOf, type Outcome, PROCESS_TRIGGERS, type Phase, type PhaseContext, type PhaseWork, type PlatformClockOptions, type PodTiming, type Poll, type ProbeAnswer, type ProbeKind, type ProcessHost, type ProcessPort, type ProcessTrigger, type Profile, type ProgressMarks, type QueueDrain, type QueueDrainOptions, type QueueKnobs, type Recorder, type RedeliveryPolicy, type RefusalCause, type Refused, type RepeatSignal, type RequestEnd, type RequestEvent, type RequestFn, type RequestOptions, type RequestStream, type RequestWork, type RequeueCause, type Result, type RoutePattern, type RouteRequest, type Rule, type RunOptions, SLIDING_WINDOW_MACHINE, STAGES, type Scenario, type ServiceBuilder, type ServiceDescription, type ShutdownOptions, type Side, type SlidingWindow, type Span, type Stage, type StartOptions, type Step, type StoreFailure, TOKEN_BUCKET_MACHINE, type TakeRequest, type TakeStep, type Term, type Timing, type TokenBucket, type Transport, type TransportImpl, type UnitRequest, type Verdict, type Watchdog, type WatchdogSpec, type WorkerKnobs, type WorkerLoop, type WorkerLoopOptions, asDebounce, asWindow, backoffBand, backoffDelay, backoffProblems, cooperativeAbortAt, daemon, decide, detectionTime, embeddedProcess, ephemeral, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterMachine, limiterProblems, limiterStateOf, localDev, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, realClock, recoverSpan, replay, retryAfterSeconds, service, stageRank, watchdog };
|
|
295
|
+
export { type Allowed, type Backoff, type Binding, type Bound, type Cancel, type Check, type CheckFn, type CheckVerdict, type Clock, type Controller, type ControllerBuilder, type CooperativeFloor, type Coupling, type Decision, type DelayBand, type Dependency, type DependencyHandle, EXIT_SIGNALS, type EmbeddedProcess, type Evaluated, type ExitCodes, type ExitSignal, type ExitTrigger, FORMAT, type FailsBy, type Finding, type Gate, type Health, type HealthServer, type HealthVerdict, type Heartbeat, type HeartbeatOptions, type HostStream, type Idempotency, type Inequality, type IterationContext, type Jitter, type JobAction, type JobContext, type JobFailure, type JobSettle, type JobSource, type KeyState, type KeyedLimiter, type Lifecycle, type LifecycleEvent, type LifecycleState, type LimitPolicy, type LimitRule, type Limiter, type LimiterEviction, type LimiterSpec, type LimiterState, type LimiterStore, type LimiterStoreSpec, type MachineName, type MemoryStore, type MessageShape, type MessageTrigger, type Operation, type OperationOf, type Outcome, PROCESS_TRIGGERS, type Phase, type PhaseContext, type PhaseWork, type PlatformClockOptions, type PodTiming, type Poll, type ProbeAnswer, type ProbeKind, type ProcessHost, type ProcessPort, type ProcessTrigger, type Profile, type ProgressMarks, type QueueDrain, type QueueDrainOptions, type QueueKnobs, type Recorder, type RedeliveryPolicy, type RefusalCause, type Refused, type RepeatSignal, type RequestEnd, type RequestEvent, type RequestFn, type RequestOptions, type RequestStream, type RequestWork, type RequeueCause, type Result, type RoutePattern, type RouteRequest, type Rule, type RunOptions, SLIDING_WINDOW_MACHINE, STAGES, type Scenario, type ServiceBuilder, type ServiceDescription, type ShutdownOptions, type Side, type SlidingWindow, type Span, type Stage, type StartOptions, type Step, type StoreFailure, TOKEN_BUCKET_MACHINE, type TakeRequest, type TakeStep, type Term, type Timing, type TokenBucket, type Transport, type TransportImpl, type UnitRequest, type Verdict, type Watchdog, type WatchdogSpec, type WorkerKnobs, type WorkerLoop, type WorkerLoopOptions, asDebounce, asWindow, backoffBand, backoffDelay, backoffProblems, cooperativeAbortAt, daemon, decide, detectionTime, embeddedProcess, ephemeral, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterMachine, limiterProblems, limiterStateOf, localDev, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, queueDrain, realClock, recoverSpan, redeliverOnce, replay, retryAfterSeconds, service, stageRank, watchdog };
|
|
440
296
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as mayReissue } from "./operation-DL5RxFds.js";
|
|
2
|
+
import { backoffDelay } from "./backoff.js";
|
|
2
3
|
import { quantity, valueIn } from "@mark1russell7/quantity";
|
|
3
4
|
import { connect } from "node:net";
|
|
4
5
|
//#region src/client/meets.ts
|
|
@@ -123,10 +124,20 @@ const notSent = (error) => ({
|
|
|
123
124
|
/** This function makes the record of the calls in flight of a client. */
|
|
124
125
|
const inFlight = () => {
|
|
125
126
|
const closes = /* @__PURE__ */ new Set();
|
|
127
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
128
|
+
const wake = () => {
|
|
129
|
+
if (closes.size > 0) return;
|
|
130
|
+
for (const waiter of [...waiters]) waiter();
|
|
131
|
+
waiters.clear();
|
|
132
|
+
};
|
|
126
133
|
return {
|
|
127
134
|
get size() {
|
|
128
135
|
return closes.size;
|
|
129
136
|
},
|
|
137
|
+
idle: () => new Promise((resolve) => {
|
|
138
|
+
if (closes.size === 0) resolve();
|
|
139
|
+
else waiters.add(resolve);
|
|
140
|
+
}),
|
|
130
141
|
track(signal, abandoned, start, after) {
|
|
131
142
|
return new Promise((resolve) => {
|
|
132
143
|
let settled = false;
|
|
@@ -137,6 +148,7 @@ const inFlight = () => {
|
|
|
137
148
|
signal?.removeEventListener("abort", aborted);
|
|
138
149
|
after?.(value, cause);
|
|
139
150
|
resolve(value);
|
|
151
|
+
wake();
|
|
140
152
|
};
|
|
141
153
|
const close = (error) => settle({
|
|
142
154
|
outcome: "indeterminate",
|
|
@@ -154,6 +166,25 @@ const inFlight = () => {
|
|
|
154
166
|
};
|
|
155
167
|
};
|
|
156
168
|
/**
|
|
169
|
+
* This function sends one call through the record of the calls in flight, with a timer of the
|
|
170
|
+
* wrapper. A driver with no timer for its call uses it: amqplib, and the agent of Node. At `bound`
|
|
171
|
+
* milliseconds the call is indeterminate with the error `timedOut`, because it left the client.
|
|
172
|
+
* The timer stops when the call settles, before `after` operates.
|
|
173
|
+
*/
|
|
174
|
+
const trackWithTimer = (flight, clock, bound, signal, abandoned, timedOut, start, after) => {
|
|
175
|
+
let stop = () => {};
|
|
176
|
+
return flight.track(signal, abandoned, (settle) => {
|
|
177
|
+
stop = clock.after(bound, () => settle({
|
|
178
|
+
outcome: "indeterminate",
|
|
179
|
+
error: timedOut
|
|
180
|
+
}));
|
|
181
|
+
start(settle);
|
|
182
|
+
}, (value, cause) => {
|
|
183
|
+
stop();
|
|
184
|
+
after?.(value, cause);
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
157
188
|
* This function waits for the work of a close, for `bound` milliseconds at most. It gives true when
|
|
158
189
|
* the work ended in its bound, with a success or a failure. It gives false at the bound. The driver
|
|
159
190
|
* then destroys each socket that stays, because a silent server does not close its half.
|
|
@@ -169,12 +200,147 @@ const withinBound = (clock, bound, work) => {
|
|
|
169
200
|
return ended;
|
|
170
201
|
});
|
|
171
202
|
};
|
|
203
|
+
/** This function makes the ready wait of a client. The function `ready` tells if the client is ready. */
|
|
204
|
+
const readyWait = (clock, ready) => {
|
|
205
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
206
|
+
return {
|
|
207
|
+
wait: (within, signal) => new Promise((resolve) => {
|
|
208
|
+
if (ready()) {
|
|
209
|
+
resolve(true);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
let cancel = () => {};
|
|
213
|
+
const done = (value) => {
|
|
214
|
+
waiters.delete(wake);
|
|
215
|
+
cancel();
|
|
216
|
+
signal?.removeEventListener("abort", aborted);
|
|
217
|
+
resolve(value);
|
|
218
|
+
};
|
|
219
|
+
const wake = () => done(true);
|
|
220
|
+
const aborted = () => done(false);
|
|
221
|
+
waiters.add(wake);
|
|
222
|
+
cancel = clock.after(within, () => done(ready()));
|
|
223
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
224
|
+
}),
|
|
225
|
+
wake() {
|
|
226
|
+
for (const wake of [...waiters]) wake();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* This function gives the backoff of the tries to connect: from `retry_base`, doubling to
|
|
232
|
+
* `retry_cap`, each with a jitter up to the base. The client rounds each delay to a whole millisecond.
|
|
233
|
+
*/
|
|
234
|
+
const retryBackoff = (knobs) => ({
|
|
235
|
+
base: ms(knobs.retry_base),
|
|
236
|
+
factor: 2,
|
|
237
|
+
cap: ms(knobs.retry_cap),
|
|
238
|
+
jitter: {
|
|
239
|
+
kind: "add",
|
|
240
|
+
amount: ms(knobs.retry_base)
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
/** This function makes the retry schedule of a client, from its backoff and its random numbers. RabbitMQ and WebSocket use it. */
|
|
244
|
+
const retrySchedule = (clock, backoff, random) => {
|
|
245
|
+
let tries = 0;
|
|
246
|
+
let timer = null;
|
|
247
|
+
return {
|
|
248
|
+
get tries() {
|
|
249
|
+
return tries;
|
|
250
|
+
},
|
|
251
|
+
schedule(due) {
|
|
252
|
+
tries += 1;
|
|
253
|
+
const delay = Math.round(backoffDelay(backoff, tries, random));
|
|
254
|
+
timer?.();
|
|
255
|
+
timer = clock.after(delay, () => {
|
|
256
|
+
timer = null;
|
|
257
|
+
due();
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
cancel() {
|
|
261
|
+
timer?.();
|
|
262
|
+
timer = null;
|
|
263
|
+
},
|
|
264
|
+
reset() {
|
|
265
|
+
tries = 0;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
/** This function gives the problems of the retry knobs: the cap must not be below the base. */
|
|
270
|
+
const retryProblems = (name, knobs) => ms(knobs.retry_cap) < ms(knobs.retry_base) ? [`${name}'s retry_cap is below its retry_base`] : [];
|
|
271
|
+
/**
|
|
272
|
+
* This function gives the problem of a count of misses that is not a whole number above zero. A
|
|
273
|
+
* client that closes its connection after a count of heartbeat misses uses it. `mechanism` names the heartbeat, for example "watchdog".
|
|
274
|
+
*/
|
|
275
|
+
const missesProblems = (name, misses, mechanism) => Number.isInteger(misses) && misses >= 1 ? [] : [`${name}'s ${mechanism} closes the connection after ${misses} misses, not a whole number above zero`];
|
|
172
276
|
/** 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. */
|
|
173
277
|
const timeProblems = (name, knobs) => {
|
|
174
278
|
const problems = [];
|
|
175
279
|
for (const [knob, amount] of Object.entries(knobs)) if (amount.value !== "infinite" && !(ms(amount) >= 0)) problems.push(`${name}'s ${knob} is not a time of zero or more`);
|
|
176
280
|
return problems;
|
|
177
281
|
};
|
|
282
|
+
/** This function writes a bound for a driver that reads 0 as no timer: "infinite" is 0. */
|
|
283
|
+
const orZero = (amount) => finiteMs(amount) ?? 0;
|
|
284
|
+
/** This function tells if an amount is a whole number of milliseconds. "infinite" is whole. */
|
|
285
|
+
const wholeMs = (amount) => amount.value === "infinite" || Number.isInteger(ms(amount));
|
|
286
|
+
/** This function tells if an amount is a whole number of seconds. "infinite" is whole. */
|
|
287
|
+
const wholeSeconds = (amount) => amount.value === "infinite" || Number.isInteger(valueIn(amount, "s"));
|
|
288
|
+
const knobOf = (knobs, knob) => knobs[knob];
|
|
289
|
+
/**
|
|
290
|
+
* This function gives the problems of the knobs of a driver that reads 0 as no timer. pg and the
|
|
291
|
+
* MongoDB driver read 0 so. A bounded knob must be a finite time above zero. An open knob can be
|
|
292
|
+
* "infinite", and 0 is then a mistake.
|
|
293
|
+
*/
|
|
294
|
+
const zeroProblems = (name, driver, knobs, kinds) => {
|
|
295
|
+
const problems = [];
|
|
296
|
+
for (const knob of kinds.bounded ?? []) {
|
|
297
|
+
const value = ms(knobOf(knobs, knob));
|
|
298
|
+
if (!(value > 0) || !Number.isFinite(value)) problems.push(`${name}'s ${knob} is not a finite time above zero: ${driver} reads 0 as no bound`);
|
|
299
|
+
}
|
|
300
|
+
for (const knob of kinds.open ?? []) {
|
|
301
|
+
const amount = knobOf(knobs, knob);
|
|
302
|
+
if (amount.value !== "infinite" && !(ms(amount) > 0)) problems.push(`${name}'s ${knob} is zero: give "infinite" for no bound`);
|
|
303
|
+
}
|
|
304
|
+
return problems;
|
|
305
|
+
};
|
|
306
|
+
/** This function gives the problems of the knobs that a driver takes in whole milliseconds or in whole seconds. */
|
|
307
|
+
const wholeProblems = (name, knobs, kinds) => [...(kinds.millis ?? []).filter((knob) => !wholeMs(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of milliseconds`), ...(kinds.seconds ?? []).filter((knob) => !wholeSeconds(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of seconds`)];
|
|
308
|
+
/** This function gives the problems of the sizes of a pool: at most `max` connections, and `min` from 0 to `max`. */
|
|
309
|
+
const poolProblems = (name, pool) => {
|
|
310
|
+
const problems = [];
|
|
311
|
+
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`);
|
|
312
|
+
if (!Number.isInteger(pool.min) || pool.min < 0 || pool.min > pool.max) problems.push(`${name}'s pool has a min of ${pool.min}, not a whole number from 0 to its max`);
|
|
313
|
+
return problems;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* This function gives the problem O07 of a client whose server has a deadline, or null. The client
|
|
317
|
+
* must wait longer than the deadline of the server. Then the server ends a slow call with an error
|
|
318
|
+
* first. Otherwise the client gives up first, and the server goes on with the call. `call` names
|
|
319
|
+
* the call of the server, for example a statement.
|
|
320
|
+
*/
|
|
321
|
+
const deadlineProblem = (name, request, deadline, call) => deadline.value !== "infinite" && ms(request) <= ms(deadline) ? `${name}'s request_timeout is not above its statement_timeout: the server goes on with a ${call} that the client gave up (O07)` : null;
|
|
322
|
+
/**
|
|
323
|
+
* This function gives the problem O01 of a client pool, or null. The pool must close an idle
|
|
324
|
+
* connection before its server does. Otherwise a request can go on a connection that the server
|
|
325
|
+
* closed. A server that keeps an idle connection with no bound gives no problem. `knob` names the
|
|
326
|
+
* idle time of the client: `pool_idle_expiry` of a pool, or `client_idle_timeout` of a gRPC channel.
|
|
327
|
+
*/
|
|
328
|
+
const idleCloseProblem = (name, expiry, close, knob = "pool_idle_expiry") => close.value !== "infinite" && !(ms(expiry) < ms(close)) ? `${name}'s ${knob} is not below the idle close of its server: a request can go on a connection that the server closed (O01)` : null;
|
|
329
|
+
/**
|
|
330
|
+
* This function gives the problems of the timers that the wrapper has, because its driver has none.
|
|
331
|
+
* Each one must be a finite time above zero. `bounds` gives what each knob bounds, for example "a call".
|
|
332
|
+
*/
|
|
333
|
+
const wrapperTimerProblems = (name, knobs, bounds) => Object.entries(bounds).filter(([knob]) => {
|
|
334
|
+
const value = ms(knobOf(knobs, knob));
|
|
335
|
+
return !(value > 0) || !Number.isFinite(value);
|
|
336
|
+
}).map(([knob, what]) => `${name}'s ${knob} is not a finite time above zero: the timer of the wrapper is the only bound of ${what}`);
|
|
337
|
+
/** 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. */
|
|
338
|
+
const ageProblems = (name, knobs, reason) => knobs.max_connection_age.value === "infinite" ? [] : [`${name}'s max_connection_age is not "infinite": ${reason}`];
|
|
339
|
+
/** This function gives the problem of a keepalive that is not a finite time above zero, for a driver that always turns keepalive on. */
|
|
340
|
+
const keepaliveOnProblems = (name, knobs, driver) => {
|
|
341
|
+
const value = ms(knobs.keepalive_idle);
|
|
342
|
+
return value > 0 && Number.isFinite(value) ? [] : [`${name}'s keepalive_idle is not a finite time above zero: ${driver} always turns keepalive on`];
|
|
343
|
+
};
|
|
178
344
|
/**
|
|
179
345
|
* This function sends a call, and gives its outcome in three values. It sends again only an
|
|
180
346
|
* idempotent operation, one time at most, after an outcome that is not certain. A reply of the
|
|
@@ -339,6 +505,20 @@ const helloDial = (hello, judge) => (host, port, signal) => new Promise((resolve
|
|
|
339
505
|
socket.once("error", (error) => end(error));
|
|
340
506
|
socket.once("close", () => end(/* @__PURE__ */ new Error("the server closed the dial")));
|
|
341
507
|
});
|
|
508
|
+
/** This function gives the authority of a host and a port for a Host header. An IPv6 address goes in brackets. */
|
|
509
|
+
const authority = (host, port) => `${host.includes(":") ? `[${host}]` : host}:${port}`;
|
|
510
|
+
/**
|
|
511
|
+
* This function makes a judge of the status line of an HTTP/1.1 answer. The judge waits for the end
|
|
512
|
+
* of the line, in the first 8 KiB. Then `accept` reads the status code, and gives null for a good status or an error.
|
|
513
|
+
* The HTTP client accepts each status. The WebSocket client accepts only 101.
|
|
514
|
+
*/
|
|
515
|
+
const statusLineJudge = (accept) => (received) => {
|
|
516
|
+
const end = received.indexOf("\r\n");
|
|
517
|
+
if (end < 0) return received.length > 8192 ? /* @__PURE__ */ new Error("the answer has no status line in its first 8 KiB") : "more";
|
|
518
|
+
const line = received.toString("latin1", 0, end);
|
|
519
|
+
const status = /^HTTP\/1\.[01] (\d{3})/.exec(line);
|
|
520
|
+
return status === null ? /* @__PURE__ */ new Error(`the answer ${JSON.stringify(line)} is not an HTTP/1.1 status line`) : accept(Number(status[1]), line);
|
|
521
|
+
};
|
|
342
522
|
//#endregion
|
|
343
523
|
//#region src/client/machine.ts
|
|
344
524
|
/** This function gives one transition of a machine document. */
|
|
@@ -367,6 +547,6 @@ const closeTimer = () => ({
|
|
|
367
547
|
fires: "close.bound"
|
|
368
548
|
});
|
|
369
549
|
//#endregion
|
|
370
|
-
export {
|
|
550
|
+
export { retrySchedule as A, zeroProblems as B, notSent as C, readyWait as D, poolProblems as E, timeProblems as F, immediate as G, NEVER as H, trackWithTimer as I, keepaliveSilent as J, keepaliveAnswered as K, wholeProblems as L, sendWithReissue as M, socketEnds as N, retryBackoff as O, stepChecks as P, withinBound as R, ms as S, orZero as T, WHAT as U, MANIFESTATIONS as V, firstDetector as W, unwatched as X, kernelGiveUp as Y, inFlight as _, helloDial as a, message as b, ageProblems as c, clientProblems as d, clientRecorder as f, idleCloseProblem as g, finiteMs as h, authority as i, roundTripCheck as j, retryProblems as k, attachClient as l, dialCheck as m, closeTimer as n, statusLineJudge as o, deadlineProblem as p, keepaliveReset as q, transition as r, STARTING_POLL as s, clientKnob as t, clientBounds as u, keepaliveOnProblems as v, operationTable as w, missesProblems as x, localStateCheck as y, wrapperTimerProblems as z };
|
|
371
551
|
|
|
372
|
-
//# sourceMappingURL=machine-
|
|
552
|
+
//# sourceMappingURL=machine-BhTRu3u9.js.map
|