@mark1russell7/service 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -2
- package/build/backoff-B-jGWVDm.d.ts +48 -0
- package/build/backoff.d.ts +2 -0
- package/build/backoff.js +70 -0
- package/build/backoff.js.map +1 -0
- package/build/{builder-BFxOuULH.d.ts → builder-DnfUDW9u.d.ts} +8 -22
- package/build/clock-D-38uH_z.d.ts +37 -0
- package/build/clock.d.ts +2 -0
- package/build/clock.js +30 -0
- package/build/clock.js.map +1 -0
- package/build/heartbeat.d.ts +70 -0
- package/build/heartbeat.js +103 -0
- package/build/heartbeat.js.map +1 -0
- package/build/index.d.ts +6 -9
- package/build/index.js +9 -23
- package/build/index.js.map +1 -1
- package/build/{clock-DlJFFZlX.js → operation-DL5RxFds.js} +2 -16
- package/build/operation-DL5RxFds.js.map +1 -0
- package/build/redis.d.ts +7 -6
- package/build/redis.js +49 -47
- package/build/redis.js.map +1 -1
- package/build/testing.d.ts +2 -1
- package/build/testing.js +1 -1
- package/build/{timing-DxZ4nkQg.js → timing-DGRi3XCI.js} +12 -3
- package/build/timing-DGRi3XCI.js.map +1 -0
- package/package.json +15 -3
- package/build/clock-DlJFFZlX.js.map +0 -1
- package/build/timing-DxZ4nkQg.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/service",
|
|
4
|
-
"version": "0.
|
|
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.",
|
|
4
|
+
"version": "0.4.0",
|
|
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",
|
|
8
8
|
"readiness",
|
|
@@ -41,6 +41,18 @@
|
|
|
41
41
|
"types": "./build/redis.d.ts",
|
|
42
42
|
"default": "./build/redis.js"
|
|
43
43
|
},
|
|
44
|
+
"./clock": {
|
|
45
|
+
"types": "./build/clock.d.ts",
|
|
46
|
+
"default": "./build/clock.js"
|
|
47
|
+
},
|
|
48
|
+
"./heartbeat": {
|
|
49
|
+
"types": "./build/heartbeat.d.ts",
|
|
50
|
+
"default": "./build/heartbeat.js"
|
|
51
|
+
},
|
|
52
|
+
"./backoff": {
|
|
53
|
+
"types": "./build/backoff.d.ts",
|
|
54
|
+
"default": "./build/backoff.js"
|
|
55
|
+
},
|
|
44
56
|
"./package.json": "./package.json"
|
|
45
57
|
},
|
|
46
58
|
"files": [
|
|
@@ -52,7 +64,7 @@
|
|
|
52
64
|
"node": ">=22.19"
|
|
53
65
|
},
|
|
54
66
|
"dependencies": {
|
|
55
|
-
"@mark1russell7/algebra": "^0.
|
|
67
|
+
"@mark1russell7/algebra": "^0.5.0",
|
|
56
68
|
"@mark1russell7/quantity": "^0.2.0"
|
|
57
69
|
},
|
|
58
70
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clock-DlJFFZlX.js","names":[],"sources":["../src/operation.ts","../src/clock.ts"],"sourcesContent":["import type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { compare, convertible } from \"@mark1russell7/quantity\";\n\n/**\n * This type tells if an operation can go again after a failure. It is a property of the\n * operation, not of the dependency. One Redis client sends a GET, which is safe to send again. It\n * also sends an INCR, which counts two times when it goes again after a timeout. Each operation\n * declares its idempotency:\n * - `true`: a second issue has the same effect. Examples are an HTTP GET or PUT, a SELECT, a\n * Redis GET or SET, and a write that the server removes when it is a copy.\n * - `false`: the client does not send it again after it can have reached the server.\n * - keyed: the server keeps a key for a window, as with an `Idempotency-Key` header, and it\n * ignores a copy in that window. A copy after the window is a second operation. Thus the\n * window is a timer, and it must be longer than all retries of the operation.\n */\nexport type Idempotency = boolean | { readonly key: string; readonly window: Extent };\n\n/** An operation is an endpoint that the service calls or serves, a command, or a query. */\nexport interface Operation {\n readonly idempotent: Idempotency;\n}\n\n/**\n * This function tells if an operation can go again `since` its first issue. An idempotent\n * operation can always go again, and other operations cannot. A keyed operation can go again\n * while the server keeps its key.\n */\nexport const mayReissue = (operation: Operation, since: Quantity): boolean => {\n const { idempotent } = operation;\n if (typeof idempotent === \"boolean\") return idempotent;\n return compare(since, idempotent.window) < 0;\n};\n\n/** This function gives the problems in the declaration of an operation that `describe()` refuses. */\nexport const operationProblems = (owner: string, name: string, operation: Operation): string[] => {\n const { idempotent } = operation;\n if (typeof idempotent === \"boolean\") return [];\n const problems: string[] = [];\n if (idempotent.key.trim().length === 0) problems.push(`${owner}'s ${name} names no key`);\n if (!convertible(idempotent.window.unit, \"s\"))\n problems.push(`${owner}'s ${name} holds its key for a span that is not a time`);\n return problems;\n};\n","/** This function stops a timer that `Clock.after` started. A second use does nothing. */\nexport type Cancel = () => void;\n\n/**\n * A clock gives the monotonic time in milliseconds, and it starts a function after a delay. The\n * runtime reads time only through a clock. Thus a test can give a fake clock, and the timing of\n * the runtime is then exact and repeatable.\n */\nexport interface Clock {\n /** This method gives the monotonic time, in milliseconds. A step of the wall clock does not move it. */\n now(): number;\n /** This method starts `run` after `ms` milliseconds, and gives the function that stops it. */\n after(ms: number, run: () => void): Cancel;\n}\n\n/**\n * This clock reads `performance.now()` and uses `setTimeout`. Its timers do not keep a Node\n * process alive, thus a timer that is not stopped cannot stop the exit of the process.\n */\nexport const realClock: Clock = {\n now: () => performance.now(),\n after(ms, run) {\n const timer = setTimeout(run, Math.max(0, ms));\n (timer as { unref?: () => void }).unref?.();\n return () => clearTimeout(timer);\n },\n};\n"],"mappings":";;;;;;;AA2BA,MAAa,cAAc,WAAsB,UAA6B;CAC5E,MAAM,EAAE,eAAe;CACvB,IAAI,OAAO,eAAe,WAAW,OAAO;CAC5C,OAAO,QAAQ,OAAO,WAAW,MAAM,IAAI;AAC7C;;AAGA,MAAa,qBAAqB,OAAe,MAAc,cAAmC;CAChG,MAAM,EAAE,eAAe;CACvB,IAAI,OAAO,eAAe,WAAW,OAAO,CAAC;CAC7C,MAAM,WAAqB,CAAC;CAC5B,IAAI,WAAW,IAAI,KAAK,CAAC,CAAC,WAAW,GAAG,SAAS,KAAK,GAAG,MAAM,KAAK,KAAK,cAAc;CACvF,IAAI,CAAC,YAAY,WAAW,OAAO,MAAM,GAAG,GAC1C,SAAS,KAAK,GAAG,MAAM,KAAK,KAAK,6CAA6C;CAChF,OAAO;AACT;;;;;;;ACvBA,MAAa,YAAmB;CAC9B,WAAW,YAAY,IAAI;CAC3B,MAAM,IAAI,KAAK;EACb,MAAM,QAAQ,WAAW,KAAK,KAAK,IAAI,GAAG,EAAE,CAAC;EAC7C,MAAkC,QAAQ;EAC1C,aAAa,aAAa,KAAK;CACjC;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timing-DxZ4nkQg.js","names":["r"],"sources":["../src/timing.ts"],"sourcesContent":["import type { Quantity, Rational } from \"@mark1russell7/quantity\";\nimport { convertExact, quantity, rationals as r } from \"@mark1russell7/quantity\";\n\n/**\n * All policies have the same timing shape. A timing is a count of polls (a debounce) or a\n * duration (a window), and each converts to the other. A long window is easier to read as a\n * duration, and that is the only difference between the two.\n */\nexport type Timing =\n | {\n readonly type: \"debounce\";\n /** This interval is the time between two polls of the signal. */\n readonly interval: Quantity;\n /** This count is the number of failed polls in sequence before the policy fails. */\n readonly failAfter: number;\n /** This count is the number of passed polls in sequence before the policy recovers. */\n readonly recoverAfter: number;\n }\n | {\n readonly type: \"window\";\n /** This duration is how long the signal fails before the policy fails, on the monotonic clock. */\n readonly failAfter: Quantity;\n /** This duration is how long the signal passes before the policy recovers. Without it, the first pass recovers the policy. */\n readonly recoverAfter?: Quantity | undefined;\n };\n\n/** A poll has an interval, a jitter as a fraction (±20 % is 0.2) and a timeout. */\nexport interface Poll {\n readonly interval: Quantity;\n readonly jitter: number;\n readonly timeout: Quantity;\n}\n\n/** A span is a range of times, from the earliest to the latest. */\nexport interface Span {\n readonly min: Quantity;\n readonly max: Quantity;\n}\n\n/**\n * This type tells how a failed step answers a poll.\n * - An `\"error\"` step refuses at once.\n * - A `\"timeout\"` step (a black hole) answers only when the timeout of the poll ends. Each poll\n * makes a new call, and each call ends at its timeout. Thus the step is slower by that timeout,\n * at the two ends of the span.\n * - A `\"hang\"` step makes a use that does not end. The first poll fails at its timeout. While the\n * use stays pending, the runtime does not start the check again, and each later poll fails at\n * once. Thus a debounce of two polls or more fails as fast as on an error.\n */\nexport type FailsBy = \"error\" | \"timeout\" | \"hang\";\n\nconst seconds = (amount: Quantity): Rational =>\n convertExact(r.fromNumber(amount.value), amount.unit, \"s\");\nconst out = (value: Rational): Quantity => quantity(r.toNumber(value), \"s\");\nconst fraction = (jitter: number): Rational => {\n if (!(jitter >= 0 && jitter < 1))\n throw new RangeError(`jitter is a fraction below 1, not ${jitter}`);\n return r.fromNumber(jitter);\n};\nconst count = (polls: number, what: string): Rational => {\n if (!Number.isInteger(polls) || polls < 1)\n throw new RangeError(`${what} is a whole number of polls, not ${polls}`);\n return r.rational(BigInt(polls));\n};\n\n/** N polls with jitter take from N − 1 short intervals to N long intervals. */\nconst debounced = (polls: Rational, interval: Rational, jitter: Rational): [Rational, Rational] => [\n r.mul(r.sub(polls, r.ONE), r.mul(interval, r.sub(r.ONE, jitter))),\n r.mul(polls, r.mul(interval, r.add(r.ONE, jitter))),\n];\n\n/**\n * This function gives the time that a policy takes to fail after its signal becomes bad. A\n * debounce of N polls takes approximately N intervals, with the jitter. A window takes its\n * duration, plus one interval at most for the poll that starts its sequence. A step that fails by\n * a timeout adds the timeout of the poll. At 3 polls of 5 s ± 20 %, the latch fails in 8–18 s on\n * an error, and in 12–22 s on a 4 s timeout. A step that hangs adds the timeout only where its\n * first failure ends the sequence: a window, or a debounce of one poll.\n */\nexport const failSpan = (timing: Timing, poll: Poll, by: FailsBy = \"error\"): Span => {\n const jitter = fraction(poll.jitter);\n const first = timing.type === \"window\" || timing.failAfter === 1;\n const late =\n by === \"timeout\" || (by === \"hang\" && first) ? seconds(poll.timeout) : r.ZERO;\n if (timing.type === \"debounce\") {\n const [min, max] = debounced(\n count(timing.failAfter, \"failAfter\"),\n seconds(timing.interval),\n jitter,\n );\n return { min: out(r.add(min, late)), max: out(r.add(max, late)) };\n }\n const duration = seconds(timing.failAfter);\n const interval = r.mul(seconds(poll.interval), r.add(r.ONE, jitter));\n return { min: out(r.add(duration, late)), max: out(r.add(r.add(duration, interval), late)) };\n};\n\n/** This function gives the time that a policy takes to recover after its signal becomes good. */\nexport const recoverSpan = (timing: Timing, poll: Poll): Span => {\n const jitter = fraction(poll.jitter);\n if (timing.type === \"debounce\") {\n const [min, max] = debounced(\n count(timing.recoverAfter, \"recoverAfter\"),\n seconds(timing.interval),\n jitter,\n );\n return { min: out(min), max: out(max) };\n }\n const duration = timing.recoverAfter === undefined ? r.ZERO : seconds(timing.recoverAfter);\n const interval = r.mul(seconds(poll.interval), r.add(r.ONE, jitter));\n return { min: out(duration), max: out(r.add(duration, interval)) };\n};\n\n/** This function gives the window that a debounce approximates: N polls of an interval fail after N intervals. */\nexport const asWindow = (timing: Timing): Timing =>\n timing.type === \"window\"\n ? timing\n : {\n type: \"window\",\n failAfter: out(r.mul(count(timing.failAfter, \"failAfter\"), seconds(timing.interval))),\n recoverAfter: out(\n r.mul(count(timing.recoverAfter, \"recoverAfter\"), seconds(timing.interval)),\n ),\n };\n\n/** This function gives the debounce that approximates a window on a poll: the polls that fit, rounded up. */\nexport const asDebounce = (timing: Timing, poll: Poll): Timing => {\n if (timing.type === \"debounce\") return timing;\n const interval = seconds(poll.interval);\n const polls = (duration: Rational | undefined): number =>\n duration === undefined ? 1 : Math.max(1, Number(r.ceil(r.div(duration, interval)).n));\n return {\n type: \"debounce\",\n interval: poll.interval,\n failAfter: polls(seconds(timing.failAfter)),\n recoverAfter: polls(\n timing.recoverAfter === undefined ? undefined : seconds(timing.recoverAfter),\n ),\n };\n};\n"],"mappings":";;AAmDA,MAAM,WAAW,WACf,aAAaA,UAAE,WAAW,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG;AAC3D,MAAM,OAAO,UAA8B,SAASA,UAAE,SAAS,KAAK,GAAG,GAAG;AAC1E,MAAM,YAAY,WAA6B;CAC7C,IAAI,EAAE,UAAU,KAAK,SAAS,IAC5B,MAAM,IAAI,WAAW,qCAAqC,QAAQ;CACpE,OAAOA,UAAE,WAAW,MAAM;AAC5B;AACA,MAAM,SAAS,OAAe,SAA2B;CACvD,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GACtC,MAAM,IAAI,WAAW,GAAG,KAAK,mCAAmC,OAAO;CACzE,OAAOA,UAAE,SAAS,OAAO,KAAK,CAAC;AACjC;;AAGA,MAAM,aAAa,OAAiB,UAAoB,WAA2C,CACjGA,UAAE,IAAIA,UAAE,IAAI,OAAOA,UAAE,GAAG,GAAGA,UAAE,IAAI,UAAUA,UAAE,IAAIA,UAAE,KAAK,MAAM,CAAC,CAAC,GAChEA,UAAE,IAAI,OAAOA,UAAE,IAAI,UAAUA,UAAE,IAAIA,UAAE,KAAK,MAAM,CAAC,CAAC,CACpD;;;;;;;;;AAUA,MAAa,YAAY,QAAgB,MAAY,KAAc,YAAkB;CACnF,MAAM,SAAS,SAAS,KAAK,MAAM;CACnC,MAAM,QAAQ,OAAO,SAAS,YAAY,OAAO,cAAc;CAC/D,MAAM,OACJ,OAAO,aAAc,OAAO,UAAU,QAAS,QAAQ,KAAK,OAAO,IAAIA,UAAE;CAC3E,IAAI,OAAO,SAAS,YAAY;EAC9B,MAAM,CAAC,KAAK,OAAO,UACjB,MAAM,OAAO,WAAW,WAAW,GACnC,QAAQ,OAAO,QAAQ,GACvB,MACF;EACA,OAAO;GAAE,KAAK,IAAIA,UAAE,IAAI,KAAK,IAAI,CAAC;GAAG,KAAK,IAAIA,UAAE,IAAI,KAAK,IAAI,CAAC;EAAE;CAClE;CACA,MAAM,WAAW,QAAQ,OAAO,SAAS;CACzC,MAAM,WAAWA,UAAE,IAAI,QAAQ,KAAK,QAAQ,GAAGA,UAAE,IAAIA,UAAE,KAAK,MAAM,CAAC;CACnE,OAAO;EAAE,KAAK,IAAIA,UAAE,IAAI,UAAU,IAAI,CAAC;EAAG,KAAK,IAAIA,UAAE,IAAIA,UAAE,IAAI,UAAU,QAAQ,GAAG,IAAI,CAAC;CAAE;AAC7F;;AAGA,MAAa,eAAe,QAAgB,SAAqB;CAC/D,MAAM,SAAS,SAAS,KAAK,MAAM;CACnC,IAAI,OAAO,SAAS,YAAY;EAC9B,MAAM,CAAC,KAAK,OAAO,UACjB,MAAM,OAAO,cAAc,cAAc,GACzC,QAAQ,OAAO,QAAQ,GACvB,MACF;EACA,OAAO;GAAE,KAAK,IAAI,GAAG;GAAG,KAAK,IAAI,GAAG;EAAE;CACxC;CACA,MAAM,WAAW,OAAO,iBAAiB,KAAA,IAAYA,UAAE,OAAO,QAAQ,OAAO,YAAY;CACzF,MAAM,WAAWA,UAAE,IAAI,QAAQ,KAAK,QAAQ,GAAGA,UAAE,IAAIA,UAAE,KAAK,MAAM,CAAC;CACnE,OAAO;EAAE,KAAK,IAAI,QAAQ;EAAG,KAAK,IAAIA,UAAE,IAAI,UAAU,QAAQ,CAAC;CAAE;AACnE;;AAGA,MAAa,YAAY,WACvB,OAAO,SAAS,WACZ,SACA;CACE,MAAM;CACN,WAAW,IAAIA,UAAE,IAAI,MAAM,OAAO,WAAW,WAAW,GAAG,QAAQ,OAAO,QAAQ,CAAC,CAAC;CACpF,cAAc,IACZA,UAAE,IAAI,MAAM,OAAO,cAAc,cAAc,GAAG,QAAQ,OAAO,QAAQ,CAAC,CAC5E;AACF;;AAGN,MAAa,cAAc,QAAgB,SAAuB;CAChE,IAAI,OAAO,SAAS,YAAY,OAAO;CACvC,MAAM,WAAW,QAAQ,KAAK,QAAQ;CACtC,MAAM,SAAS,aACb,aAAa,KAAA,IAAY,IAAI,KAAK,IAAI,GAAG,OAAOA,UAAE,KAAKA,UAAE,IAAI,UAAU,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;CACtF,OAAO;EACL,MAAM;EACN,UAAU,KAAK;EACf,WAAW,MAAM,QAAQ,OAAO,SAAS,CAAC;EAC1C,cAAc,MACZ,OAAO,iBAAiB,KAAA,IAAY,KAAA,IAAY,QAAQ,OAAO,YAAY,CAC7E;CACF;AACF"}
|