{"_id":"@alexpopovme/deadline-timer","name":"@alexpopovme/deadline-timer","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@alexpopovme/deadline-timer","version":"0.1.0","description":"A small TypeScript utility for counting down to an absolute date based on server time","repository":{"type":"git","url":"https://github.com/alexpopovme/deadline-timer.git"},"homepage":"https://github.com/alexpopovme/deadline-timer#readme","bugs":{"url":"https://github.com/alexpopovme/deadline-timer/issues"},"keywords":["deadline","timer","countdown","server-time","typescript"],"license":"MIT","type":"module","sideEffects":false,"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./package.json":"./package.json"},"engines":{"node":">=22.13.0"},"publishConfig":{"access":"public"},"devDependencies":{"@arethetypeswrong/cli":"0.18.5","@eslint/js":"10.0.1","@vitest/coverage-v8":"4.1.10","eslint":"10.8.0","eslint-config-prettier":"10.1.8","prettier":"3.9.6","publint":"0.3.23","typescript":"6.0.3","typescript-eslint":"8.66.0","unplugin-dts":"1.0.3","vite":"8.2.1","vitest":"4.1.10"},"scripts":{"build":"vite build","typecheck":"tsc -p tsconfig.lib.json --noEmit && tsc -p tsconfig.json --noEmit","lint":"eslint . --max-warnings=0","lint:fix":"eslint . --fix","format":"prettier . --write","format:check":"prettier . --check","test":"vitest","test:run":"vitest run","test:coverage":"vitest run --coverage","package:check":"pnpm build && publint --strict && attw --pack --profile esm-only .","check":"pnpm typecheck && pnpm lint && pnpm format:check && pnpm test:run && pnpm package:check"},"_nodeVersion":"24.11.1","_id":"@alexpopovme/deadline-timer@0.1.0","dist":{"integrity":"sha512-AyqaqUlzNPV0SzBSiLRDvUsrc3QO8Zg7Z9TbcHDNua96R1fsXHLGwSQ8N3bZZb2JXiLAJimmHrDhS57yk74abg==","shasum":"7ee4f6eb392e8c1172f63bbaf0170985e73ddf93","tarball":"https://registry.npmjs.org/@alexpopovme/deadline-timer/-/deadline-timer-0.1.0.tgz","fileCount":14,"unpackedSize":28133,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGtQ5zrBUWfah+Cad8QjqjgzJJoIUtroEudEkqVBjktOAiEA36r3kNMgwem0R98/ZSQw7nPSTv6GTwFfsHhoZkbrkSg="}]},"_npmUser":{"name":"alexpopovme","email":"alexpopov.me@gmail.com"},"directories":{},"maintainers":[{"name":"alexpopovme","email":"alexpopov.me@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/deadline-timer_0.1.0_1787738424135_0.000053144978143793153"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-26T10:00:23.854Z","0.1.0":"2026-08-26T10:00:24.268Z","modified":"2026-08-26T10:00:24.560Z"},"maintainers":[{"name":"alexpopovme","email":"alexpopov.me@gmail.com"}],"description":"A small TypeScript utility for counting down to an absolute date based on server time","homepage":"https://github.com/alexpopovme/deadline-timer#readme","keywords":["deadline","timer","countdown","server-time","typescript"],"repository":{"type":"git","url":"https://github.com/alexpopovme/deadline-timer.git"},"bugs":{"url":"https://github.com/alexpopovme/deadline-timer/issues"},"license":"MIT","readme":"# DeadlineTimer\n\n[English](README.md) | [Русский](README.ru.md)\n\nA small TypeScript utility that counts down to an absolute date using server time. It is UI-framework-agnostic and makes no network requests.\n\n## Installation\n\n```bash\npnpm add @alexpopovme/deadline-timer\n```\n\nThe package is distributed as ESM and has no runtime dependencies.\n\n## Usage\n\n```ts\nimport { createDeadlineTimer } from '@alexpopovme/deadline-timer'\n\nconst timer = createDeadlineTimer({\n  expiresAt: '2026-08-06T12:30:00Z',\n  serverNow: '2026-08-06T12:10:00Z',\n})\n\nif (timer) {\n  const unsubscribe = timer.subscribe((snapshot) => {\n    console.log(snapshot.remainingSeconds)\n    console.log(snapshot.status)\n  })\n\n  // Когда подписка больше не нужна\n  unsubscribe()\n}\n```\n\n## Input\n\n- `expiresAt` — the absolute expiration date or `null`. When it is `null`, the factory returns `null`.\n- `serverNow` — the server time received together with the deadline. You do not need to pass the device's local time.\n- `expirationSafetySeconds` — ends the countdown slightly early to account for delay between the server and the client. It defaults to `5` seconds; `0` disables the margin.\n\nDates are parsed with `Date.parse`. Always specify the time zone explicitly; the recommended format is `2026-08-14T07:38:51.923Z`. An invalid object, type, date, or option causes a synchronous `TypeError`. Additional properties in input objects are allowed.\n\nThe backend must still verify whether an action is available.\n\n## Snapshot\n\n```ts\ntype DeadlineTimerSnapshot = Readonly<{\n  remainingSeconds: number\n  status: 'active' | 'expired'\n}>\n```\n\n`remainingSeconds` is the non-negative number of seconds remaining, rounded up. The status is `active` when the remaining time is positive and `expired` when it reaches zero. Previously returned snapshot objects are not mutated.\n\n## Methods\n\n- `getSnapshot()` returns the current state without starting updates or notifying subscribers.\n- `subscribe(listener)` immediately calls the listener synchronously with the current state. It then calls the listener only when the remaining time or status changes.\n- The returned function removes only its own subscription and is safe to call repeatedly. Updates stop after the last subscription is removed.\n- For an already expired timer, the listener is called once. The listener must be a function and must not throw.\n\n## How time is calculated\n\nThe timer stores `serverNow` when it is created and adds the actual elapsed time to it. After a delay or returning to the tab, it therefore calculates the current remaining time immediately without replaying missed values.\n\nThe timer does not contact the server again. If the backend provides new `expiresAt` or `serverNow` values, create a new instance. The server remains the source of truth for deciding whether an action is available.\n\n## API\n\n```ts\ntype DeadlineTimerInput = Readonly<{\n  expiresAt: string | null\n  serverNow: string\n}>\n\ntype DeadlineTimerOptions = Readonly<{\n  expirationSafetySeconds?: number\n}>\n\ntype DeadlineTimerStatus = 'active' | 'expired'\n\ntype DeadlineTimerSnapshot = Readonly<{\n  remainingSeconds: number\n  status: DeadlineTimerStatus\n}>\n\ntype DeadlineTimerListener = (snapshot: DeadlineTimerSnapshot) => void\n\ntype DeadlineTimer = Readonly<{\n  getSnapshot(): DeadlineTimerSnapshot\n  subscribe(listener: DeadlineTimerListener): () => void\n}>\n\ndeclare const createDeadlineTimer: (\n  input: DeadlineTimerInput,\n  options?: DeadlineTimerOptions,\n) => DeadlineTimer | null\n```\n\n## Specifications\n\nThe normative project specifications are currently available in Russian:\n\n- [`DeadlineTimer` specification](specs/ru/deadline-timer.md)\n- [Specification writing rules](specs/ru/spec-writing.md)\n","readmeFilename":"","_rev":"1-2e351aafc846a628b529b3b7d4005ae8"}