{"_id":"@acegalaxy/scheduler-runtime","_rev":"3-6144df9bfa3462d9aef086dd9d4b6842","name":"@acegalaxy/scheduler-runtime","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@acegalaxy/scheduler-runtime","version":"0.1.0","keywords":["scheduler","cron","notion","catalog","status-tracker","pm2"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","_id":"@acegalaxy/scheduler-runtime@0.1.0","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"homepage":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs#readme","bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs/issues"},"dist":{"shasum":"769f459d73ccd5dfc56cf0f78b2207d87b2bfaec","tarball":"https://registry.npmjs.org/@acegalaxy/scheduler-runtime/-/scheduler-runtime-0.1.0.tgz","fileCount":26,"integrity":"sha512-IkfviJFOtBt1Hkgq/y4umlWeydPlINkEX/Wh/1MnMMKrlDvxrRf/Nl7ncPtiSynL+Ev/9WJsrzFIviAYocvyZw==","signatures":[{"sig":"MEQCIDR14mVZY7njFvGgjt80vUVFzsOzZBRvYhtilhnmyHm2AiBwiOljbkech3T/NUouJ0YRBJQwPbWONjWNXEZlkpbLQQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49542},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"}},"gitHead":"c3e6455c49c2617716e31f146b4058ae4c87e8ac","private":false,"scripts":{"test":"node --test test/*.test.js","build":"tsc","clean":"rm -rf dist","pretest":"npm run build","prepublishOnly":"npm run build"},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"repository":{"url":"git+https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs.git","type":"git"},"_npmVersion":"11.12.1","description":"Notion-backed cron catalog for Node.js — see all your scheduled jobs in one Notion table, with auto-tracked status, last run, and errors. Pluggable cron adapter + reporter + status tracker.","directories":{},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/scheduler-runtime_0.1.0_1778086022111_0.03157010354214851","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@acegalaxy/scheduler-runtime","version":"0.1.1","keywords":["scheduler","cron","notion","catalog","status-tracker","pm2"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","_id":"@acegalaxy/scheduler-runtime@0.1.1","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"homepage":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs#readme","bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs/issues"},"dist":{"shasum":"42cb370504708fe4ce3a680070353fd6223463df","tarball":"https://registry.npmjs.org/@acegalaxy/scheduler-runtime/-/scheduler-runtime-0.1.1.tgz","fileCount":26,"integrity":"sha512-jBCyJvKLj+3Z9G349t1QizMlMHNfoZru49eK3FizJhHBBJYmi0C+zUxlxeZ/Og4P/dKqVV0w4VO79veqpNoaPA==","signatures":[{"sig":"MEUCIHlkdL2NPlTq2oCHDQGylu3MM0zczkKZHFTPKMFeCUQuAiEAl/fjRuev6oZzfgMO0Oq+tpBcRh6iIMt5wWnhuMHYScA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49542},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"}},"gitHead":"8f7de53cac571231f4547374d44f32ccdc0c2ae9","private":false,"scripts":{"test":"node --test test/*.test.js","build":"tsc","clean":"rm -rf dist","pretest":"npm run build","prepublishOnly":"npm run build"},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"repository":{"url":"git+https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs.git","type":"git"},"_npmVersion":"11.12.1","description":"Notion-backed cron catalog for Node.js — see all your scheduled jobs in one Notion table, with auto-tracked status, last run, and errors. Pluggable cron adapter + reporter + status tracker.","directories":{},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/scheduler-runtime_0.1.1_1778088192946_0.19391383037784138","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@acegalaxy/scheduler-runtime","version":"0.1.2","description":"Notion-backed cron catalog for Node.js — see all your scheduled jobs in one Notion table, with auto-tracked status, last run, and errors. Pluggable cron adapter + reporter + status tracker.","main":"dist/index.js","scripts":{"test":"node --test test/*.test.js","build":"tsc","prepublishOnly":"npm run build","clean":"rm -rf dist","pretest":"npm run build"},"keywords":["scheduler","cron","notion","catalog","status-tracker","pm2"],"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","private":false,"engines":{"node":">=20"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs.git"},"bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs/issues"},"homepage":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs#readme","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./dist/*.d.ts","default":"./dist/*.js"},"./package.json":"./package.json"},"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.0.0"},"gitHead":"8f7de53cac571231f4547374d44f32ccdc0c2ae9","_id":"@acegalaxy/scheduler-runtime@0.1.2","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-jSrLoCqYfL9Pc2L20vYXVY9EuvT5UQvIOW4+cv2qtbop+cpDbAhHXStkIhfq2lIuMxM9kLNXMrptetqDF2EE4g==","shasum":"8a621f225ea1c61fb303cbfe287f1bdcd9ad74b8","tarball":"https://registry.npmjs.org/@acegalaxy/scheduler-runtime/-/scheduler-runtime-0.1.2.tgz","fileCount":26,"unpackedSize":49582,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFNZgHm3x9M1Epo32RUrMgn01IIqLMMwlJSQQ7v6UzB+AiBRa3UkvW03aeT+KzMXfn0Nx51HW7bjIljKTLgbzToXuQ=="}]},"_npmUser":{"name":"kanelr","email":"lanhnk@acegalaxy.co"},"directories":{},"maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scheduler-runtime_0.1.2_1778088278101_0.36213155070857095"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-06T16:47:02.033Z","modified":"2026-05-06T17:24:38.432Z","0.1.0":"2026-05-06T16:47:02.258Z","0.1.1":"2026-05-06T17:23:13.113Z","0.1.2":"2026-05-06T17:24:38.299Z"},"bugs":{"url":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs/issues"},"author":{"name":"ACE Galaxy","email":"hello@acegalaxy.co"},"license":"MIT","homepage":"https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs#readme","keywords":["scheduler","cron","notion","catalog","status-tracker","pm2"],"repository":{"type":"git","url":"git+https://github.com/acegalaxy-co/ace_commons-scheduler-runtime-nodejs.git"},"description":"Notion-backed cron catalog for Node.js — see all your scheduled jobs in one Notion table, with auto-tracked status, last run, and errors. Pluggable cron adapter + reporter + status tracker.","maintainers":[{"name":"kanelr","email":"lanhnk@acegalaxy.co"}],"readme":"# @acegalaxy/scheduler-runtime\n\n[![npm version](https://img.shields.io/npm/v/@acegalaxy%2Fscheduler-runtime.svg)](https://www.npmjs.com/package/@acegalaxy/scheduler-runtime)\n[![npm downloads](https://img.shields.io/npm/dm/@acegalaxy%2Fscheduler-runtime.svg)](https://www.npmjs.com/package/@acegalaxy/scheduler-runtime)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Node](https://img.shields.io/node/v/@acegalaxy%2Fscheduler-runtime.svg)](https://nodejs.org)\n\n\n> **Notion-backed cron catalog** — see all your scheduled jobs in one Notion table, with status, last run, and errors auto-tracked. An alternative to BullMQ-only / Inngest when you want a human-readable, ops-friendly source of truth that lives where your team already works.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](package.json)\n\n## Why\n\n- **Visibility for ops** — every cron in the company shows up in one Notion DB. PM, ops, and on-call all see the same row.\n- **No new dashboard.** Status / last run / errors flow into Notion fields you can filter, sort, comment on.\n- **Bring-your-own cron.** Works on top of `node-cron`, PM2 `cron_restart`, or any adapter exposing `.schedule()`.\n- **Fire-and-forget catalog sync.** Notion failures NEVER bubble into your jobs.\n- **Project-agnostic.** Cron adapter, error reporter, status tracker, Notion DB id + token are ALL injected. The runtime hardcodes nothing.\n\n## What's in the box\n\n- **Overlap lock** — same-job re-entry skipped\n- **Report lock** — critical reports pause background jobs\n- **Pluggable error reporter** — project injects Telegram routing / cooldown\n- **Pluggable status tracker** — project injects file/DB persistence\n- **Notion catalog auto-sync** — fire-and-forget upsert per registration\n\n## Install\n\n```bash\nnpm install @acegalaxy/scheduler-runtime node-cron\n```\n\n## Quick start\n\n**1. Register a cron job**\n\n```js\nconst cron = require(\"node-cron\");\nconst runtime = require(\"@acegalaxy/scheduler-runtime\");\n\n// One-time configuration\nruntime.configure({\n  cronAdapter: cron,                                  // node-cron or compatible\n  reporter: (label, err) => myTelegramAlert(label, err),\n  statusTracker: (name, status, ms) => myStatusFile(name, status, ms),\n  catalog: {\n    token: process.env.NOTION_API_KEY,\n    dbId: process.env.NOTION_DB_SCHEDULER_CATALOG,\n    project: \"nexus\",                                 // slug for filtering\n    host: \"PROD\",                                     // or \"LOCAL\"\n    tz: \"Asia/Ho_Chi_Minh\",\n    enabled: () => process.env.DOCKER_CONTAINER === \"1\",\n    alertOnce: async (kind, name, msg) => myTelegramAlert(`Catalog ${kind}`, msg),\n  },\n});\n\n// Register schedulers\nruntime.scheduleJob(\"morning-check\", \"30 8 * * *\", runMorningCheck);\nruntime.scheduleJob(\"daily-report\",  \"0 18 * * *\", runDailyReport, { isReport: true });\n```\n\n**2. View status in Notion**\n\nOpen your catalog DB. Each registered job appears as a row with `Name`, `Cron`, `Project`, `Host`, `Source File`, `Schedule TZ`, and `Last Reviewed` auto-populated. Add views per project / per host as needed.\n\n**3. Get alerted on failure**\n\nYour injected `reporter(label, err)` is called whenever a wrapped job throws. Pipe it to Telegram, Slack, PagerDuty — whatever your team already uses. The status tracker also flips the row to `failed` so on-call can triage from Notion directly.\n\n## API\n\n### `configure(opts)`\n\n| Field | Type | Notes |\n|---|---|---|\n| `cronAdapter` | object | Must expose `.schedule(expr, fn, opts)`. Required for `scheduleJob()`. |\n| `reporter` | `(label, err) => void` | Called when wrapped fn throws. Default = `console.error`. |\n| `statusTracker` | `(name, status, durationMs?) => void` | `status` is `\"running\"` / `\"done\"` / `\"failed\"`. Default = no-op. |\n| `catalog` | object | See catalog config below. Skipped if missing token/dbId/project. |\n\n### `catalog` config\n\n| Field | Type | Notes |\n|---|---|---|\n| `token` | string | Notion integration bearer. |\n| `dbId` | string | Notion DB ID (with or without dashes). |\n| `project` | string | Project slug, e.g. `\"nexus\"`. Used for `Project` select column. |\n| `host` | string | `\"PROD\"` or `\"LOCAL\"`. Project decides. |\n| `tz` | string | Default `\"Asia/Ho_Chi_Minh\"`. |\n| `enabled` | `() => boolean` | Gating predicate. Default `() => true`. Use to PROD-gate. |\n| `alertOnce` | `async (kind, name, msg) => void` | Optional. Called once per error kind to surface drift. |\n\n### `scheduleJob(name, schedule, fn, options?)`\n\nWraps `fn`, registers via `cronAdapter`, fires catalog upsert.\n\n- `options.isReport` — if `true`, uses report-lock wrapper (pauses background jobs)\n- `options.timezone` — default `\"Asia/Ho_Chi_Minh\"`\n\nReturns whatever the cron adapter's `schedule()` returns.\n\n### `createBackgroundJob(name, fn)` / `createReportJob(name, fn)`\n\nStandalone wrappers for direct use (e.g. PM2 cron_restart one-shots that don't go through node-cron). Returns `async () => void`.\n\n### `isReportRunning()`\n\nBoolean — true while a report-job is in-flight.\n\n## Notion DB schema\n\nProject must create a Notion DB with these properties (auto-managed):\n\n| Property | Type | Auto/Manual |\n|---|---|---|\n| `Name` | title | auto |\n| `Project` | select | auto |\n| `Cron` | rich_text | auto |\n| `Host` | select (PROD/LOCAL) | auto |\n| `Source File` | rich_text | auto |\n| `Schedule TZ` | select | auto |\n| `Last Reviewed` | date | auto |\n| `Status` | select (Active/Paused/...) | auto on create only |\n| `Description` | any | manual (preserved) |\n| `Type` | any | manual (preserved) |\n| `Notes` | any | manual (preserved) |\n| `Target` | any | manual (preserved) |\n\nThe DB is **shared across projects** — `Project` column distinguishes rows. Filter views per project as needed.\n\n## Error handling philosophy\n\n- Wrapped fn errors → `reporter(label, err)` + `statusTracker(name, \"failed\", ms)` + lock released\n- Catalog upsert errors → logged + `alertOnce(kind, name, msg)` if configured. NEVER bubbles into caller.\n- Reporter / tracker errors → swallowed (logged to stderr). Never crash the scheduler.\n\n## Testing\n\n```bash\nnpm test\n```\n\n15 unit tests cover wrappers, locks, catalog upsert/patch/skip, error classification.\n","readmeFilename":"README.md"}