{"_id":"@c1m-public/resilience","name":"@c1m-public/resilience","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@c1m-public/resilience","version":"0.1.0","type":"module","license":"MIT","description":"Framework-neutral failure classification and retry decisions shared by the C1M clients and applications","repository":{"type":"git","url":"git+https://github.com/cloud1mediaLtd/mml-js.git","directory":"packages/resilience"},"homepage":"https://github.com/cloud1mediaLtd/mml-js/tree/main/packages/resilience","bugs":{"url":"https://github.com/cloud1mediaLtd/mml-js/issues"},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"engines":{"node":">=20"},"publishConfig":{"access":"public"},"devDependencies":{"@types/node":"^22","typescript":"5.9.3","vitest":"^4.1.4"},"scripts":{"clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","build":"pnpm clean && tsc -p tsconfig.build.json","lint":"tsc -p tsconfig.json --noEmit","test":"vitest","test:run":"vitest run","type-check":"tsc -p tsconfig.json --noEmit"},"_id":"@c1m-public/resilience@0.1.0","_integrity":"sha512-9iEldQ/pXlVS5hzG8rHwmBbFzTcYZHA0cbDiDSeY5STDVMkKuRHTKiGgLmMXMcVCJzrmah63Js7hG9RiNKZAEg==","_resolved":"/tmp/c1m-public-resilience-0.1.0.tgz","_from":"file:/tmp/c1m-public-resilience-0.1.0.tgz","_nodeVersion":"22.23.2","_npmVersion":"12.0.1","dist":{"integrity":"sha512-9iEldQ/pXlVS5hzG8rHwmBbFzTcYZHA0cbDiDSeY5STDVMkKuRHTKiGgLmMXMcVCJzrmah63Js7hG9RiNKZAEg==","shasum":"d847e73ff0c9eae13b6d875aaa45ac8a3489a529","tarball":"https://registry.npmjs.org/@c1m-public/resilience/-/resilience-0.1.0.tgz","fileCount":4,"unpackedSize":15869,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDiMDHlI3VmkizO1JshmmoLxdjTlP79/Vc5uFdEkcPmRwIhAIBVDfZKZy+Z+UOEDi/KrctpWesS17+y3rJDaaCYqg3n"}]},"_npmUser":{"name":"c1m-npm","email":"admin@cloud1media.com"},"directories":{},"maintainers":[{"name":"c1m-npm","email":"admin@cloud1media.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/resilience_0.1.0_1789160174168_0.4169165011182909"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-11T20:56:13.968Z","0.1.0":"2026-09-11T20:56:14.321Z","modified":"2026-09-11T20:56:14.659Z"},"maintainers":[{"name":"c1m-npm","email":"admin@cloud1media.com"}],"description":"Framework-neutral failure classification and retry decisions shared by the C1M clients and applications","homepage":"https://github.com/cloud1mediaLtd/mml-js/tree/main/packages/resilience","repository":{"type":"git","url":"git+https://github.com/cloud1mediaLtd/mml-js.git","directory":"packages/resilience"},"bugs":{"url":"https://github.com/cloud1mediaLtd/mml-js/issues"},"license":"MIT","readme":"# @c1m-public/resilience\n\nFailure classification and retry decisions, shared by the published C1M clients\nand by the applications that call the API directly.\n\nPure functions and string-literal types only — no `fetch`, no envelope\nparsing, no framework. The request performer stays private to\n`packages/transport`, where it is coupled to each client's error vocabulary.\n\n## Why it exists\n\nThese decisions were already made, well, once — inside the private transport\ncore the published clients inline. So the commissioned headless sites have had\njittered backoff and `Retry-After` handling all along, while SaaSDash, the\nworkspace app and Platform Admin had no retry at all and classified failures by\nmatching substrings in error messages. This is that logic, published, so all of\nthem share one copy.\n\n## Classify on the server\n\n`FailureKind` is a string because classification cannot happen where the\nfailure is displayed. A production React Flight build serializes only an\nerror's `digest`; name, message, status and every custom field are gone by the\ntime a client error boundary runs, so branching on the error there works in\ndevelopment and silently does nothing in production.\n\nClassify on the server, where the real error still exists, and pass the string.\n\n```ts\nimport { classifyFailure, type FailureKind } from \"@c1m-public/resilience\";\n\n// server\ntry {\n  return { ok: true, data: await load() };\n} catch (error) {\n  return { ok: false, kind: classifyFailure(error) };  // serializable\n}\n```\n\n## Retrying\n\n```ts\nimport {\n  DEFAULT_RETRY_POLICY, RETRYABLE_STATUSES, isRetryableRequest, retryDelay, wait,\n} from \"@c1m-public/resilience\";\n```\n\nTwo rules are inherited rather than configurable, because both are about\nsafety:\n\n- **GET always; a write only with an `Idempotency-Key`.** A keyed write is safe\n  to replay by definition — the server dedupes on the key. A keyless write is\n  never retried.\n- **A server's `Retry-After` beats our backoff**, capped. It knows when it will\n  be ready and we do not.\n\n`DEFAULT_RETRY_POLICY` is two extra attempts capped at 4s. That does not cover\na ~30s single-replica deploy — nothing in a request's lifetime does — so the\nUI still needs an unavailable state. What it buys is the common case: the\none-or-two-second window while a container is replaced.\n\n## Two copies is fine\n\nThe clients inline this package; the applications depend on it. Two copies can\ntherefore exist in one process, which is why everything here is a pure function\nor a literal type. Nothing may rely on class identity or `instanceof` across\nthat boundary.\n","readmeFilename":"README.md","_rev":"1-c3ea7fc81c56b6f9a2ac6d4c65b3d5d5"}