{"id":51319436,"url":"https://github.com/rrulenet/events","last_synced_at":"2026-07-01T11:30:45.486Z","repository":{"id":368270347,"uuid":"1284334889","full_name":"rrulenet/events","owner":"rrulenet","description":"Event-set projection layer for contextual scheduling with @rrulenet/recurrence.","archived":false,"fork":false,"pushed_at":"2026-06-29T19:42:38.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T21:18:38.845Z","etag":null,"topics":["calendar","deno","event-scheduling","events","recurrence","rrule","scheduling","temporal","typescript"],"latest_commit_sha":null,"homepage":"https://rrule.net","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rrulenet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-29T19:00:16.000Z","updated_at":"2026-06-29T19:42:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rrulenet/events","commit_stats":null,"previous_names":["rrulenet/events"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/rrulenet/events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrulenet%2Fevents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrulenet%2Fevents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrulenet%2Fevents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrulenet%2Fevents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rrulenet","download_url":"https://codeload.github.com/rrulenet/events/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrulenet%2Fevents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35005408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","deno","event-scheduling","events","recurrence","rrule","scheduling","temporal","typescript"],"created_at":"2026-07-01T11:30:44.919Z","updated_at":"2026-07-01T11:30:45.476Z","avatar_url":"https://github.com/rrulenet.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rrule.net\"\u003e\n    \u003cimg src=\"./assets/avatar.svg\" alt=\"rrule.net\" width=\"96\" height=\"96\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e@rrulenet/events\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Event-set projection layer for contextual scheduling with \u003ccode\u003e@rrulenet/recurrence\u003c/code\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rrule.net\"\u003errule.net\u003c/a\u003e •\n  \u003cstrong\u003e@rrulenet ecosystem\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ccode\u003e@rrulenet/rrule\u003c/code\u003e ·\n  \u003ccode\u003e@rrulenet/recurrence\u003c/code\u003e ·\n  \u003ccode\u003e@rrulenet/core\u003c/code\u003e ·\n  \u003ccode\u003e@rrulenet/events\u003c/code\u003e ·\n  \u003ccode\u003e@rrulenet/cli\u003c/code\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@rrulenet/events\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/%40rrulenet%2Fevents\" alt=\"npm version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://jsr.io/@rrulenet/events\"\u003e\u003cimg src=\"https://img.shields.io/jsr/v/%40rrulenet%2Fevents\" alt=\"JSR version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://rrulenet.github.io/events/coverage.json\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?url=https://rrulenet.github.io/events/coverage.json\" alt=\"Coverage\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/license-MIT-2563EB\" alt=\"MIT License\"\u003e\n\u003c/p\u003e\n\n`@rrulenet/events` is a small event-set projection layer. It combines explicit event sets supplied by an application with transforms that either project a `Recurrence` from `@rrulenet/recurrence` during or around those events, or generate event-relative triggers from those events.\n\nIt is not an event catalogue, a data client, or an event-discovery service. Applications own event sourcing, persistence, metadata, permissions, and product-specific availability rules.\n\n## Install\n\n```bash\nnpm install @rrulenet/events @rrulenet/recurrence\n```\n\n## Role\n\nThe package answers this question:\n\n```text\ngiven explicit event dates/windows + a transform,\nwhich occurrences should run?\n```\n\nFor example:\n\n```text\nEvery day at 09:00 during Black Friday and Cyber Monday\n```\n\nor:\n\n```text\nEvery day at 09:00 from 10 days before Black Friday through 2 days after Cyber Monday\n```\n\nor:\n\n```text\nTrigger each regional audience 7 days before a maintenance window at 09:00 local time\n```\n\n## Boundary With `@rrulenet/recurrence`\n\n`@rrulenet/recurrence` remains the recurrence engine. It owns recurrence parsing, recurrence JSON, point occurrence generation, and recurrence algebra.\n\n`@rrulenet/events` does not extend that API. It depends on `@rrulenet/recurrence` and uses `Recurrence` as the engine for `during` and `window` transforms. Event-relative transforms are not recurrence filters; they generate explicit local trigger occurrences from event anchor dates.\n\nThe event layer owns only:\n\n- event-set JSON validation\n- date, point, and interval member semantics\n- projection of recurrence occurrences onto event dates or event windows\n- event-relative trigger occurrences in a target timezone\n- deterministic serialization of event schedules\n\n## Data Boundary\n\nRuntime event data does not live in this package.\n\nApplications pass explicit `EventSetJson` objects into the library. Event ids, versions, sources, metadata, curation status, persistence, permissions, and availability rules belong to the application layer.\n\nThis package does not fetch event data and does not ship a runtime event catalogue.\n\nTests include deterministic fixtures such as `black_friday_2026` and `cyber_monday_2026`. They are fixtures, not bundled catalogue data.\n\n## JSON Model\n\n`EventSetJson` contains explicit members:\n\n```ts\ntype EventSetMemberJson =\n  | { kind: 'date'; id: string; date: '2026-11-27' }\n  | { kind: 'point'; id: string; at: '2026-11-27T09:00:00Z' }\n  | { kind: 'interval'; id: string; start: string; end: string };\n```\n\nFor recurrence projection, the schedule combines events, recurrence, and transform:\n\n```ts\ntype EventScheduleJson = {\n  kind: 'event-schedule';\n  timezone: string;\n  events: EventSetJson;\n  recurrence: RecurrenceJson;\n  transform:\n    | { kind: 'during' }\n    | { kind: 'window'; before?: { days?: number }; after?: { days?: number } };\n};\n```\n\nFor event-relative triggers, `recurrence` is intentionally absent:\n\n```ts\ntype EventScheduleJson = {\n  kind: 'event-schedule';\n  timezone: string;\n  events: EventSetJson;\n  transform: {\n    kind: 'event-relative';\n    anchor?: 'start';\n    triggers: Array\u003c{\n      before: { days: number };\n      time: '09:00';\n    } | {\n      after: { days: number };\n      time: '09:00';\n    }\u003e;\n  };\n};\n```\n\nDate members are calendar dates. They are projected into the schedule timezone as half-open local-day windows:\n\n```text\n[YYYY-MM-DDT00:00, next local midnight)\n```\n\nPoint members are exact temporal points. Interval members are half-open windows:\n\n```text\n[start, end)\n```\n\nEvent-relative transforms use the event member's local anchor date in the schedule timezone:\n\n- date members anchor on their date\n- point members anchor on the point instant converted to the schedule timezone\n- interval `before` triggers anchor on the interval start instant converted to the schedule timezone\n- interval `after` triggers anchor on the interval end instant converted to the schedule timezone\n\nEach trigger applies calendar days to the local anchor date, then combines the resulting date with the supplied local wall-clock time. A trigger must contain exactly one of `before` or `after`.\n\n## Example\n\n```js\nimport { Temporal } from 'temporal-polyfill';\nimport { Recurrence } from '@rrulenet/recurrence';\nimport { EventSchedule } from '@rrulenet/events';\n\nconst recurrence = Recurrence.rule({\n  freq: 'DAILY',\n  byHour: [9],\n  start: Temporal.ZonedDateTime.from('2026-11-01T09:00:00[America/New_York]'),\n  until: Temporal.ZonedDateTime.from('2026-12-10T09:00:00[America/New_York]'),\n});\n\nconst schedule = EventSchedule.fromJSON({\n  kind: 'event-schedule',\n  timezone: 'America/New_York',\n  events: {\n    kind: 'event-set',\n    id: 'campaign_events_2026',\n    version: 'app-supplied-v1',\n    members: [\n      { kind: 'date', id: 'black_friday_2026', date: '2026-11-27' },\n      { kind: 'date', id: 'cyber_monday_2026', date: '2026-11-30' },\n    ],\n  },\n  recurrence: recurrence.toJSON(),\n  transform: {\n    kind: 'window',\n    before: { days: 10 },\n    after: { days: 2 },\n  },\n});\n\nconsole.log(schedule.occurrences().map((value) =\u003e value.toString()));\n```\n\n## Event-Relative Example\n\n```js\nimport { EventSchedule } from '@rrulenet/events';\n\nconst schedule = EventSchedule.fromJSON({\n  kind: 'event-schedule',\n  timezone: 'Australia/Perth',\n  events: {\n    kind: 'event-set',\n    id: 'inline.maintenance.2026-09-15',\n    version: 'user-snapshot-1',\n    source: 'inline',\n    timezone: 'UTC',\n    members: [\n      {\n        kind: 'interval',\n        id: 'maintenance-window',\n        start: '2026-09-15T02:00:00Z',\n        end: '2026-09-15T04:00:00Z',\n      },\n    ],\n  },\n  transform: {\n    kind: 'event-relative',\n    triggers: [\n      { before: { days: 7 }, time: '09:00' },\n      { after: { days: 2 }, time: '09:00' },\n    ],\n  },\n});\n\nconsole.log(schedule.occurrences().map((value) =\u003e value.toString()));\n// [\n//   '2026-09-08T09:00:00+08:00[Australia/Perth]',\n//   '2026-09-17T09:00:00+08:00[Australia/Perth]'\n// ]\n```\n\n## Future VEVENT Relationship\n\n`@rrulenet/vevent` is intentionally out of scope for this package.\n\nThis package keeps date sets, point sets, and interval/window semantics explicit, so a future VEVENT-oriented package can map richer event components into event sets later without forcing VEVENT concepts into this smaller projection layer.\n\n## Development\n\n```bash\nnpm install\nnpm test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrulenet%2Fevents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frrulenet%2Fevents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrulenet%2Fevents/lists"}