{"_id":"@alexalves87/opentimestamps-client","_rev":"7-b78461d1692f97f7b139cc334394a7a9","name":"@alexalves87/opentimestamps-client","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.0":{"name":"@alexalves87/opentimestamps-client","version":"1.0.0","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.0","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"e56e0e44f01464fa4f1b780081838b22cc8a37ff","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.0.tgz","fileCount":7,"integrity":"sha512-bH9/Hirbd7degWRXE3YEvhcXguDOGKFOyIfRQiCa8RBOfAe+1mFbKn3wyExPOoHdLpLCgACJf65NX1jX8jmq1w==","signatures":[{"sig":"MEYCIQCSdK4mZjBC4QPTtDF2cRTv5O6t1uxbBkFhq5VGDvYBowIhAP40SO1wdSRIp97b2nUcorV5p48CBn7Y25zP0U24nOac","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81277},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.0_1760996039804_0.2686448550640308","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"},"1.0.1":{"name":"@alexalves87/opentimestamps-client","version":"1.0.1","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.1","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"6d62497075c65568d72a0b118b9cf401d3677b76","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.1.tgz","fileCount":7,"integrity":"sha512-KU6K+tNhGKi2eKIeFmWue+oFY4RoPVwDQmXq8jKgGLsH3sU+Gn0Z6heGWKJsWEufYiaINIQzvH+Dcot/Zj34Zg==","signatures":[{"sig":"MEUCIFHBUz6XJNIHGWlDIR5uzS58DJYa72QqF1HA7SEVbiatAiEAvB4eJbpInaOnkF1YmdBcfpcSpTEvPfdknTwzZXb6QKQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81317},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.1_1761069813745_0.5677915509969182","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"},"1.0.2":{"name":"@alexalves87/opentimestamps-client","version":"1.0.2","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.2","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"865641aa2ea56cff690a0116b9dde99e9a392862","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.2.tgz","fileCount":7,"integrity":"sha512-ZB23P4YRa9J2AL7IM7GLM/Ifl9XREABUY+n+LYavnwya/52koths1Ahgsg4S9DVN+GRsN0E0nkkdOgtSdFn+rQ==","signatures":[{"sig":"MEUCIQDqJgU8W13n1/O+OJUPs96qg1ooZczxf01zy/3qYf/ygwIgC5oqa/TNqo7b8f5OewVmo/dG6H6gqjk4pYOJTg9yJ1g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81317},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.2_1761070378942_0.8774597352664615","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"},"1.0.3":{"name":"@alexalves87/opentimestamps-client","version":"1.0.3","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.3","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"2ed3b8671d342f656041135c757d318c2ffbf20b","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.3.tgz","fileCount":7,"integrity":"sha512-pQCVztzMRY8bM+eytIXneH1Nk6oHyyqpodovG0MBn2rbNcmr2usJTRutjVmXFaYvJcAXk+9hqsp3SOL6dt05jg==","signatures":[{"sig":"MEUCIQCTg84ruM+w6KrZU1OmOhlh/4IpDmlispHiF9uh7zgjfQIgLPgQ8iHO5iKQhLSbV0Igc+X1R/4ZwpYInmFMEl/jywM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":84885},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.3_1761075446481_0.8379044619566707","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"},"1.0.4":{"name":"@alexalves87/opentimestamps-client","version":"1.0.4","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.4","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"c4ef4c04859ebc63cfc3bd08c7e6dcd3791a7fa7","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.4.tgz","fileCount":7,"integrity":"sha512-h5vhcA4nu4yAlEzSaAxjZ1wl5RUFO1u6lXO9LniSQ3HKLjgiFhl9R1kPlLHJTVZx4W19GhxK+irqN6LB7FNwbA==","signatures":[{"sig":"MEUCIQDK6rLyhjjk44lRGvpFCoSNaRvROF6v5KY99tBJazVW0QIgbQ/ljP3xDvN08Z1iRcOL+uNW4ijKVG+s+MP8VV1boGw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":85084},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.4_1761086537551_0.47586400848051635","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"},"1.0.5":{"name":"@alexalves87/opentimestamps-client","version":"1.0.5","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"author":{"name":"alexalves87"},"license":"MIT","_id":"@alexalves87/opentimestamps-client@1.0.5","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"homepage":"https://github.com/alexalves87/opentimestamps-client#readme","bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"dist":{"shasum":"ca7b3362fb7c89b04ddec7603a7f88a4138da55c","tarball":"https://registry.npmjs.org/@alexalves87/opentimestamps-client/-/opentimestamps-client-1.0.5.tgz","fileCount":7,"integrity":"sha512-02N7TNjW+9v6pCDN6MzuVttBkS2aE5rP0cm0nTbDcUIpkwASByZa4iyEzsT2LgY7Io2G2YYf9E8pq2nQd2RoQw==","signatures":[{"sig":"MEUCIFQm3zBLjLZu2YOOCHxQ+czho2Zd4gwzVxCYrLIDsK6+AiEA1JYs/ccd4KkP8KQ7HzLoGpBtMACUxEFb+wszC4QP1FQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":87022},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup src/index.ts --format esm,cjs --dts --watch","lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --clean","format":"prettier --write \"src/**/*.ts\"","test:watch":"vitest","semantic-release":"semantic-release"},"_npmUser":{"name":"alexalves87","email":"catabego@hotmail.com"},"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"_npmVersion":"10.8.2","description":"Official client SDK for OpenTimestamps calendars with resilience patterns","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"@alexalves87/opentimestamps":"^1.0.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.11.6","tsup":"^8.3.5","eslint":"^9.14.0","vitest":"^2.1.4","prettier":"^3.3.3","fast-check":"^4.3.0","playwright":"^1.56.1","typescript":"^5.6.3","@types/node":"^22.8.7","@vitest/browser":"^2.1.9","semantic-release":"^24.2.0","@vitest/coverage-v8":"^2.1.9","@semantic-release/git":"^10.0.1","@semantic-release/changelog":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/opentimestamps-client_1.0.5_1761230014052_0.06372630382571964","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @otskit/client instead. npm install @otskit/client"}},"time":{"created":"2025-10-20T21:33:59.702Z","modified":"2026-06-03T20:07:07.150Z","1.0.0":"2025-10-20T21:34:00.007Z","1.0.1":"2025-10-21T18:03:33.920Z","1.0.2":"2025-10-21T18:12:59.112Z","1.0.3":"2025-10-21T19:37:26.663Z","1.0.4":"2025-10-21T22:42:17.729Z","1.0.5":"2025-10-23T14:33:34.257Z"},"bugs":{"url":"https://github.com/alexalves87/opentimestamps-client/issues"},"author":{"name":"alexalves87"},"license":"MIT","homepage":"https://github.com/alexalves87/opentimestamps-client#readme","keywords":["opentimestamps","ots","blockchain","bitcoin","timestamp","proof","client","sdk"],"repository":{"url":"git+https://github.com/alexalves87/opentimestamps-client.git","type":"git"},"description":"Official client SDK for OpenTimestamps calendars with resilience patterns","maintainers":[{"name":"alexalves87","email":"catabego@hotmail.com"}],"readme":"# OpenTimestamps Client SDK\n\n> Official TypeScript/JavaScript client for OpenTimestamps with enterprise-grade resilience patterns\n\n[![npm version](https://img.shields.io/npm/v/@alexalves87/opentimestamps-client.svg)](https://www.npmjs.com/package/@alexalves87/opentimestamps-client)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)\n[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)\n\n## Features\n\n( **Complete OpenTimestamps Operations**\n- `stamp()` - Create timestamp proofs by submitting to calendar servers\n- `upgrade()` - Query calendars for Bitcoin confirmations\n- `verify()` - Verify proofs against the Bitcoin blockchain\n\n=�\u000f **Enterprise-Grade Resilience**\n- **Circuit Breaker** - Isolate failures per calendar (prevents cascading failures)\n- **Exponential Backoff** - Configurable retry strategies with jitter\n- **Timeout Management** - Per-attempt and total operation timeouts\n- **Threshold-based Submissions** - Require minimum successful submissions (default: 2/4 calendars)\n\n=' **Developer Experience**\n- **TypeScript First** - Full type safety with strict mode enabled\n- **Multi-Runtime** - Works in Node.js 18+, browsers, and edge runtimes\n- **Tree-Shakeable** - Dual ESM/CJS build with zero dependencies (except core OTS library)\n- **Abort Support** - Native AbortController integration for all operations\n- **Observable** - Optional logger interface for monitoring and debugging\n\n## Installation\n\n```bash\nnpm install @alexalves87/opentimestamps-client\n```\n\nOr with other package managers:\n\n```bash\nyarn add @alexalves87/opentimestamps-client\npnpm add @alexalves87/opentimestamps-client\n```\n\n## Quick Start\n\n### Create a Timestamp\n\n```typescript\nimport { OpenTimestampsClient } from '@alexalves87/opentimestamps-client'\nimport { createHash } from 'crypto'\nimport { readFileSync } from 'fs'\n\n// Initialize client (uses default OpenTimestamps calendars)\nconst client = new OpenTimestampsClient()\n\n// Hash your file\nconst fileContent = readFileSync('document.pdf')\nconst hash = createHash('sha256').update(fileContent).digest()\n\n// Create timestamp proof\nconst otsProof = await client.stamp(hash)\n\n// Save the .ots file\nwriteFileSync('document.pdf.ots', otsProof)\n```\n\n### Upgrade to Bitcoin Confirmation\n\n```typescript\n// Later, query calendars for Bitcoin confirmation\nconst upgradedProof = await client.upgrade(otsProof)\n\nif (upgradedProof !== otsProof) {\n  console.log('Proof upgraded with Bitcoin confirmation!')\n  writeFileSync('document.pdf.ots', upgradedProof)\n}\n```\n\n### Verify a Timestamp\n\n```typescript\n// Verify the timestamp proof\nconst result = await client.verify(upgradedProof, hash)\n\nif (result.valid) {\n  console.log(`\u0005 Timestamp verified!`)\n  console.log(`   Block: ${result.blockHeight}`)\n  console.log(`   Time:  ${result.timestamp}`)\n} else {\n  console.error(`L Verification failed: ${result.error}`)\n}\n```\n\n## Advanced Usage\n\n### Custom Calendars\n\n```typescript\nconst client = new OpenTimestampsClient({\n  calendars: [\n    'https://alice.btc.calendar.opentimestamps.org',\n    'https://bob.btc.calendar.opentimestamps.org',\n    'https://finney.calendar.eternitywall.com',\n  ],\n  minimumSuccessfulSubmissions: 2, // Require 2/3 calendars to succeed\n})\n```\n\n### Resilience Configuration\n\n```typescript\nconst client = new OpenTimestampsClient({\n  resilience: {\n    // Timeout settings\n    timeout: {\n      totalMs: 30000,      // 30s total operation timeout\n      perAttemptMs: 5000,  // 5s per attempt timeout\n    },\n\n    // Retry configuration\n    retries: {\n      enabled: true,\n      maxAttempts: 3,\n      backoff: {\n        strategy: 'exponential', // 'exponential' | 'linear' | 'constant'\n        initialDelayMs: 1000,\n        maxDelayMs: 10000,\n        jitter: 'full',  // 'full' | 'equal' | 'none'\n      },\n    },\n\n    // Circuit breaker settings\n    circuitBreaker: {\n      enabled: true,\n      failureThreshold: 5,        // Open circuit after 5 failures\n      recoveryTimeoutMs: 15000,   // Try recovery after 15s\n      halfOpenMaxAttempts: 1,     // Test with 1 request when half-open\n    },\n  },\n})\n```\n\n### Cancellation Support\n\n```typescript\nconst controller = new AbortController()\n\n// Cancel operation after 5 seconds\nsetTimeout(() => controller.abort(), 5000)\n\ntry {\n  const proof = await client.stamp(hash, { signal: controller.signal })\n} catch (error) {\n  if (error.name === 'AbortError') {\n    console.log('Operation cancelled')\n  }\n}\n```\n\n### Monitoring with Logger\n\n```typescript\nconst logger = {\n  debug: (msg) => console.log(`[DEBUG] ${msg}`),\n  info: (msg) => console.log(`[INFO] ${msg}`),\n  warn: (msg) => console.warn(`[WARN] ${msg}`),\n  error: (msg) => console.error(`[ERROR] ${msg}`),\n}\n\nconst client = new OpenTimestampsClient({ logger })\n\n// Monitor circuit breaker state\nconst state = client.getCircuitState('https://alice.btc.calendar.opentimestamps.org')\nconsole.log(`Circuit state: ${state}`) // 'CLOSED' | 'OPEN' | 'HALF_OPEN'\n\n// Manually reset circuit if needed\nclient.resetCircuit('https://alice.btc.calendar.opentimestamps.org')\n```\n\n## API Reference\n\n### `OpenTimestampsClient`\n\n#### Constructor Options\n\n```typescript\ninterface ClientOptions {\n  // Calendar servers to use (defaults to official OpenTimestamps calendars)\n  calendars?: string[]\n\n  // Minimum number of successful submissions required for stamp()\n  minimumSuccessfulSubmissions?: number // default: 2\n\n  // Resilience configuration (timeout, retry, circuit breaker)\n  resilience?: ResilienceOptions\n\n  // Optional logger for observability\n  logger?: Logger\n\n  // Global AbortSignal for all operations\n  signal?: AbortSignal\n}\n```\n\n#### Methods\n\n##### `stamp(hash, options?)`\n\nCreate a timestamp by submitting to calendar servers.\n\n```typescript\nasync stamp(\n  hash: Buffer | string,  // SHA-256 hash (32 bytes or 64 hex chars)\n  options?: OperationOptions\n): Promise<Buffer>  // .ots proof with pending attestations\n```\n\n**Throws:**\n- `ValidationError` - Invalid hash format\n- `StampError` - Insufficient successful submissions\n- `NetworkError` - Network/timeout errors\n\n##### `upgrade(proof, options?)`\n\nQuery calendars for Bitcoin confirmation.\n\n```typescript\nasync upgrade(\n  proof: Buffer,  // Existing .ots proof\n  options?: OperationOptions\n): Promise<Buffer>  // Upgraded .ots proof (or original if no upgrade available)\n```\n\n**Throws:**\n- `ValidationError` - Invalid proof format\n- `UpgradeError` - Upgrade operation failed\n\n##### `verify(proof, hash, options?)`\n\nVerify timestamp proof against Bitcoin blockchain.\n\n```typescript\nasync verify(\n  proof: Buffer,    // .ots proof file\n  hash: Buffer | string,  // Original file hash\n  options?: OperationOptions\n): Promise<VerificationResult>\n\ninterface VerificationResult {\n  valid: boolean\n  blockHeight?: number\n  timestamp?: Date\n  error?: string\n}\n```\n\n##### `getCircuitState(calendar)`\n\nGet current circuit breaker state for a calendar.\n\n```typescript\ngetCircuitState(calendar: string): 'CLOSED' | 'OPEN' | 'HALF_OPEN' | undefined\n```\n\n##### `resetCircuit(calendar)`\n\nManually reset circuit breaker for a calendar.\n\n```typescript\nresetCircuit(calendar: string): void\n```\n\n## Architecture\n\nThe SDK implements a layered resilience architecture:\n\n```\n\f\u0010\n\u0002   Public API (stamp/upgrade/verify) \u0002\n\u0014,\u0018\n                 \u0002\n\f�\u0010\n\u0002   Orchestration Layer                \u0002\n\u0002   - Validation                       \u0002\n\u0002   - Parallel/Sequential coordination \u0002\n\u0014,\u0018\n                 \u0002\n\f�\u0010\n\u0002   Resilient Network Layer            \u0002\n\u0002   - Timeout (total + per-attempt)    \u0002\n\u0002   - Retry (exponential backoff)      \u0002\n\u0002   - Circuit Breaker (per-calendar)   \u0002\n\u0014,\u0018\n                 \u0002\n\f�\u0010\n\u0002   Fetch Adapter (multi-runtime)     \u0002\n\u0014\u0018\n```\n\n### Key Design Decisions\n\n- **Per-Calendar Circuit Breakers**: Each calendar has independent failure tracking, so one failing calendar doesn't affect others\n- **Threshold-based Submissions**: `stamp()` requires N/M calendars to succeed (default: 2/4), providing resilience through redundancy\n- **Sequential Upgrade Queries**: `upgrade()` stops at the first confirmed calendar, minimizing unnecessary API calls\n- **4xx = Fail Fast, 5xx = Retry**: HTTP semantics are respected for efficient error handling\n\n## Error Handling\n\nThe SDK provides detailed error context:\n\n```typescript\ntry {\n  await client.stamp(hash)\n} catch (error) {\n  if (error instanceof StampError) {\n    console.log(`${error.successfulSubmissions.length} calendars succeeded`)\n    console.log(`${error.failedSubmissions.length} calendars failed`)\n\n    error.failedSubmissions.forEach(({ calendar, error }) => {\n      console.error(`${calendar}: ${error.message}`)\n    })\n  }\n}\n```\n\n## Browser Usage\n\nThe SDK works in browsers with native `fetch` support:\n\n```html\n<script type=\"module\">\n  import { OpenTimestampsClient } from '@alexalves87/opentimestamps-client'\n\n  const client = new OpenTimestampsClient()\n\n  // Hash from file input\n  const file = document.querySelector('input[type=\"file\"]').files[0]\n  const buffer = await file.arrayBuffer()\n  const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)\n  const hash = Array.from(new Uint8Array(hashBuffer))\n    .map(b => b.toString(16).padStart(2, '0'))\n    .join('')\n\n  // Create timestamp\n  const proof = await client.stamp(hash)\n  console.log('Timestamp created!', proof)\n</script>\n```\n\n## Testing\n\nThe SDK has comprehensive test coverage:\n\n```bash\n# Run tests\nnpm test\n\n# Run tests with coverage\nnpm test -- --coverage\n\n# Run tests in watch mode\nnpm run test:watch\n```\n\n**Test Stats:**\n- 83+ tests (unit + integration)\n- 80%+ code coverage\n- MSW-based integration tests with realistic scenarios\n- Property-based testing with fast-check\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build (ESM + CJS)\nnpm run build\n\n# Development watch mode\nnpm run dev\n\n# Lint\nnpm run lint\n\n# Format\nnpm run format\n```\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.\n\n## Security\n\nFor security issues, please see [SECURITY.md](./.github/SECURITY.md).\n\n## License\n\nMIT � alexalves87\n\n---\n\n## FAQ\n\n### How does this differ from the core `opentimestamps` library?\n\nThe core `@alexalves87/opentimestamps` library handles OTS file format serialization/deserialization and cryptographic operations. This client SDK adds:\n- High-level API for calendar operations\n- Enterprise resilience patterns (circuit breaker, retry, timeout)\n- Multi-calendar coordination\n- TypeScript-first developer experience\n\n### Why require 2/4 calendars by default?\n\nRequiring multiple successful submissions provides resilience:\n- Protects against single calendar outages\n- Ensures timestamp validity even if some calendars are compromised\n- Default threshold (2/4) balances reliability vs. speed\n\n### Can I use my own calendar servers?\n\nYes! Just pass custom calendars to the constructor:\n\n```typescript\nconst client = new OpenTimestampsClient({\n  calendars: ['https://my-calendar.example.com'],\n  minimumSuccessfulSubmissions: 1,\n})\n```\n\n### How do I handle offline scenarios?\n\nUse timeout configuration and catch network errors:\n\n```typescript\nconst client = new OpenTimestampsClient({\n  resilience: {\n    timeout: { totalMs: 5000 },  // Fail fast\n    retries: { enabled: false },  // Don't retry\n  },\n})\n\ntry {\n  await client.stamp(hash)\n} catch (error) {\n  // Store hash locally for retry later\n  await queueForLater(hash)\n}\n```\n\n### Is this production-ready?\n\nYes! The SDK is designed for production use with:\n- Comprehensive test coverage (83+ tests)\n- Battle-tested resilience patterns\n- Semantic versioning\n- Clear error messages and debugging support\n\n## Links\n\n- [OpenTimestamps Website](https://opentimestamps.org)\n- [Core Library](https://github.com/alexalves87/opentimestamps)\n- [Issue Tracker](https://github.com/alexalves87/opentimestamps-client/issues)\n- [npm Package](https://www.npmjs.com/package/@alexalves87/opentimestamps-client)\n\n## Acknowledgments\n\nBuilt with the OpenTimestamps protocol created by Peter Todd.\n","readmeFilename":"README.md"}