@azlib/scheduler 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -17
- package/dist/cli-config-DrI08d47.mjs.map +1 -1
- package/dist/http-server-Cme4XKyH.d.mts.map +1 -1
- package/dist/http-server-u1BnbkAK.d.cts.map +1 -1
- package/dist/store-dashboard-C44d7sdh.d.cts.map +1 -1
- package/dist/store-dashboard-Cyf_JILa.mjs.map +1 -1
- package/dist/store-dashboard-D-yIxXxL.d.mts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -17,16 +17,16 @@ Cron-style job scheduler for Node.js, supporting timezone normalization, overlap
|
|
|
17
17
|
|
|
18
18
|
### Core Exports
|
|
19
19
|
|
|
20
|
-
| Export | Type | Description
|
|
21
|
-
| ------------------------------------------------------------------------------- | -------- |
|
|
22
|
-
| `createSchedulerService(options: SchedulerServiceOptions)` | Function | Instantiates a SchedulerService instance and a Handler Registry.
|
|
23
|
-
| `createCronExpression(): CronExpressionBuilder` | Function | Fluent helper to construct standard 5-field cron strings.
|
|
24
|
-
| `bindSchedulerToHost(service: SchedulerService, adapter: SchedulerHostAdapter)` | Function | Automatically starts/stops the scheduler based on custom server bindings.
|
|
25
|
-
| `CronWeekday` | Enum | Monday through Sunday utility enum values.
|
|
26
|
-
| `createSchedulerDashboardService(scheduler, options?)` | Function | Operator API: health, list, pause/resume, run-now, retry, CRUD.
|
|
20
|
+
| Export | Type | Description |
|
|
21
|
+
| ------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------- |
|
|
22
|
+
| `createSchedulerService(options: SchedulerServiceOptions)` | Function | Instantiates a SchedulerService instance and a Handler Registry. |
|
|
23
|
+
| `createCronExpression(): CronExpressionBuilder` | Function | Fluent helper to construct standard 5-field cron strings. |
|
|
24
|
+
| `bindSchedulerToHost(service: SchedulerService, adapter: SchedulerHostAdapter)` | Function | Automatically starts/stops the scheduler based on custom server bindings. |
|
|
25
|
+
| `CronWeekday` | Enum | Monday through Sunday utility enum values. |
|
|
26
|
+
| `createSchedulerDashboardService(scheduler, options?)` | Function | Operator API: health, list, pause/resume, run-now, retry, CRUD. |
|
|
27
27
|
| `createSchedulerDashboardFromPersistence(persistence)` | Function | Sidecar dashboard against the same SQL tables (does not start the engine). |
|
|
28
|
-
| `createSchedulerDashboardServer(options)` | Function | Serves the React UI and JSON API (`@azlib/scheduler/dashboard`).
|
|
29
|
-
| `createSchedulerDashboardNodeHandler(options)` | Function | Same monitor as the standalone server, for Express/`http` hosts.
|
|
28
|
+
| `createSchedulerDashboardServer(options)` | Function | Serves the React UI and JSON API (`@azlib/scheduler/dashboard`). |
|
|
29
|
+
| `createSchedulerDashboardNodeHandler(options)` | Function | Same monitor as the standalone server, for Express/`http` hosts. |
|
|
30
30
|
|
|
31
31
|
### Dashboard
|
|
32
32
|
|
|
@@ -61,14 +61,14 @@ app.use(
|
|
|
61
61
|
|
|
62
62
|
Sidecar CLI (`azlib-scheduler-dashboard`) and Docker (`packages/scheduler/Dockerfile`, `compose.yaml`):
|
|
63
63
|
|
|
64
|
-
| Env
|
|
65
|
-
|
|
|
66
|
-
| `DATABASE_URL`
|
|
67
|
-
| `SCHEDULER_DIALECT`
|
|
68
|
-
| `SCHEDULER_NAMESPACE` | `azlib`
|
|
69
|
-
| `HOST`
|
|
70
|
-
| `PORT`
|
|
71
|
-
| `DASHBOARD_TOKEN`
|
|
64
|
+
| Env | Default | Notes |
|
|
65
|
+
| --------------------- | ---------------------------------- | ------------------------------------------------ |
|
|
66
|
+
| `DATABASE_URL` | required | Same database as the worker |
|
|
67
|
+
| `SCHEDULER_DIALECT` | inferred from URL | `postgres` (default), `mysql`, `sqlite`, `mssql` |
|
|
68
|
+
| `SCHEDULER_NAMESPACE` | `azlib` | Table prefix (`azlib__scheduler_jobs`, …) |
|
|
69
|
+
| `HOST` | `127.0.0.1` | Use `0.0.0.0` in Docker |
|
|
70
|
+
| `PORT` | `9100` | |
|
|
71
|
+
| `DASHBOARD_TOKEN` | required when HOST is not loopback | `Authorization: Bearer …` on `/api/*` |
|
|
72
72
|
|
|
73
73
|
Install the matching SQL driver (`pg`, `mysql2`, `better-sqlite3`, or `mssql`) next to `@azlib/scheduler`. Run-now and retry enqueue only when the worker’s queue is reachable; pause, resume, update, and delete work from SQL alone.
|
|
74
74
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-config-DrI08d47.mjs","names":[],"sources":["../src/dashboard/http-auth.ts","../src/dashboard/http-parse.ts","../src/dashboard/http-path.ts","../src/dashboard/public-dir.ts","../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts","../src/dashboard/sql-drivers.ts","../src/dashboard/cli-config.ts"],"sourcesContent":["export class DashboardHttpError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"DashboardHttpError\";\n this.status = status;\n }\n}\n\nexport function isLoopbackHost(host: string): boolean {\n const normalized = host.trim().toLowerCase();\n return (\n normalized === \"127.0.0.1\" ||\n normalized === \"localhost\" ||\n normalized === \"::1\" ||\n normalized === \"[::1]\"\n );\n}\n\nexport function authorizeDashboardRequest(input: {\n token?: string;\n authorization?: string | string[] | undefined;\n}): void {\n if (!input.token) {\n return;\n }\n const header = Array.isArray(input.authorization)\n ? input.authorization[0]\n : input.authorization;\n const expected = `Bearer ${input.token}`;\n if (header !== expected) {\n throw new DashboardHttpError(401, \"Unauthorized\");\n }\n}\n\nexport function assertDashboardBindSafety(input: {\n host: string;\n token?: string;\n}): void {\n if (!isLoopbackHost(input.host) && !input.token) {\n throw new Error(\n \"DASHBOARD_TOKEN is required when HOST is not a loopback address\",\n );\n }\n}\n","import { asBoolean, asRecord, asString } from \"@azlib/std\";\n\nimport type { SchedulerJobDefinition } from \"../contracts/scheduler-types.js\";\nimport { parseSchedule } from \"../core/schedule-parser.js\";\nimport { DashboardHttpError } from \"./http-auth.js\";\n\nconst DEFAULT_TIMEZONE = \"UTC\";\n\nfunction requiredText(value: unknown, field: string): string {\n const text = asString(value)?.trim();\n if (!text) {\n throw new DashboardHttpError(400, `${field} is required.`);\n }\n return text;\n}\n\nfunction parseConfig(value: unknown): unknown {\n if (value === undefined || value === null || value === \"\") {\n return {};\n }\n if (typeof value === \"string\") {\n try {\n return JSON.parse(value) as unknown;\n } catch {\n throw new DashboardHttpError(400, \"config must be valid JSON.\");\n }\n }\n return value;\n}\n\nfunction parseScheduleFromRecord(\n record: Record<string, unknown>,\n): SchedulerJobDefinition[\"schedule\"] {\n const nested = asRecord(record.schedule);\n const scheduleType =\n asString(record.scheduleType)?.trim() ||\n asString(nested?.scheduleType)?.trim() ||\n \"cron\";\n if (scheduleType !== \"cron\" && scheduleType !== \"once\") {\n throw new DashboardHttpError(400, \"scheduleType must be cron or once.\");\n }\n const expression = requiredText(\n record.expression ?? nested?.expression,\n \"expression\",\n );\n const timezone =\n asString(record.timezone)?.trim() ||\n asString(nested?.timezone)?.trim() ||\n DEFAULT_TIMEZONE;\n try {\n parseSchedule(scheduleType, expression, timezone);\n } catch (error) {\n throw new DashboardHttpError(\n 400,\n error instanceof Error ? error.message : \"Invalid schedule.\",\n );\n }\n return {\n scheduleType,\n expression,\n timezone,\n };\n}\n\nexport function parseCreateJobBody(body: unknown): SchedulerJobDefinition {\n const record = asRecord(body);\n if (!record) {\n throw new DashboardHttpError(400, \"Request body must be a JSON object.\");\n }\n return {\n name: requiredText(record.name, \"name\"),\n handlerKey: requiredText(record.handlerKey, \"handlerKey\"),\n schedule: parseScheduleFromRecord(record),\n config: parseConfig(record.config),\n enabled: asBoolean(record.enabled) ?? true,\n };\n}\n\nexport function parseUpdateJobBody(\n body: unknown,\n): Partial<SchedulerJobDefinition> {\n const record = asRecord(body);\n if (!record) {\n throw new DashboardHttpError(400, \"Request body must be a JSON object.\");\n }\n const patch: Partial<SchedulerJobDefinition> = {};\n if (record.name !== undefined) {\n patch.name = requiredText(record.name, \"name\");\n }\n if (record.handlerKey !== undefined) {\n patch.handlerKey = requiredText(record.handlerKey, \"handlerKey\");\n }\n if (\n record.expression !== undefined ||\n record.timezone !== undefined ||\n record.scheduleType !== undefined ||\n record.schedule !== undefined\n ) {\n patch.schedule = parseScheduleFromRecord(record);\n }\n if (record.config !== undefined) {\n patch.config = parseConfig(record.config);\n }\n if (record.enabled !== undefined) {\n patch.enabled = asBoolean(record.enabled) ?? true;\n }\n return patch;\n}\n","export function normalizeDashboardBasePath(basePath?: string): string {\n const trimmed = basePath?.trim();\n if (!trimmed || trimmed === \"/\") {\n return \"\";\n }\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nexport function stripDashboardBasePath(\n pathname: string,\n basePath?: string,\n): string | null {\n const base = normalizeDashboardBasePath(basePath);\n const path = pathname || \"/\";\n if (!base) {\n return path.startsWith(\"/\") ? path : `/${path}`;\n }\n if (path === base || path === `${base}/`) {\n return \"/\";\n }\n if (path.startsWith(`${base}/`)) {\n const rest = path.slice(base.length);\n return rest.startsWith(\"/\") ? rest : `/${rest}`;\n }\n return null;\n}\n\nexport function resolveDashboardApiBase(pathname: string): string {\n const path = pathname.split(\"?\")[0] || \"/\";\n const withoutIndex = path.replace(/\\/index\\.html$/i, \"/\");\n const withoutAsset = withoutIndex.replace(/\\/[^/]+\\.[a-z0-9]+$/i, \"/\");\n const trimmed = withoutAsset.replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/api\" : `${trimmed}/api`;\n}\n\nexport function requestPathname(\n url: string | undefined,\n originalUrl?: string,\n): string {\n const raw = originalUrl || url || \"/\";\n try {\n return new URL(raw, \"http://127.0.0.1\").pathname;\n } catch {\n return raw.split(\"?\")[0] || \"/\";\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport function resolveDashboardPublicDir(fromFileUrl: string): string {\n const dir = dirname(fileURLToPath(fromFileUrl));\n const nested = join(dir, \"public\");\n const sibling = join(dir, \"dashboard\", \"public\");\n if (existsSync(join(nested, \"index.html\"))) {\n return nested;\n }\n if (existsSync(join(sibling, \"index.html\"))) {\n return sibling;\n }\n return nested;\n}\n","import {\n createReadStream,\n existsSync,\n statSync,\n} from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extname, join, relative, resolve, sep } from \"node:path\";\n\nimport type { SchedulerDashboardService } from \"./dashboard-types.js\";\nimport {\n authorizeDashboardRequest,\n DashboardHttpError,\n} from \"./http-auth.js\";\nimport { parseCreateJobBody, parseUpdateJobBody } from \"./http-parse.js\";\nimport {\n normalizeDashboardBasePath,\n requestPathname,\n stripDashboardBasePath,\n} from \"./http-path.js\";\nimport { resolveDashboardPublicDir } from \"./public-dir.js\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".map\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n};\n\nexport type DashboardAuthorize = (input: {\n method: string;\n pathname: string;\n authorization?: string | string[];\n}) => void | Promise<void>;\n\nexport interface SchedulerDashboardHandlerOptions {\n dashboard: SchedulerDashboardService;\n token?: string;\n authorize?: DashboardAuthorize;\n publicDir?: string;\n basePath?: string;\n}\n\nexport type SchedulerDashboardRequestHandler = (\n req: IncomingMessage,\n res: ServerResponse,\n next?: (err?: unknown) => void,\n) => void;\n\nfunction sendJson(\n res: ServerResponse,\n status: number,\n body: unknown,\n): void {\n res.writeHead(status, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(body));\n}\n\nexport function sendDashboardError(res: ServerResponse, error: unknown): void {\n if (error instanceof DashboardHttpError) {\n sendJson(res, error.status, { error: error.message });\n return;\n }\n const message = error instanceof Error ? error.message : \"Unknown error\";\n const status = message.includes(\"not found\")\n ? 404\n : message.includes(\"Only failed\")\n ? 400\n : message.includes(\"Unknown handlerKey\")\n ? 400\n : 500;\n sendJson(res, status, { error: message });\n}\n\nfunction hasParsedBody(req: IncomingMessage): boolean {\n return Object.prototype.hasOwnProperty.call(req, \"body\");\n}\n\nasync function readJsonBody(req: IncomingMessage): Promise<unknown> {\n if (req.readableEnded && hasParsedBody(req)) {\n return (req as IncomingMessage & { body?: unknown }).body ?? {};\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n if (chunks.length === 0) {\n if (hasParsedBody(req)) {\n return (req as IncomingMessage & { body?: unknown }).body ?? {};\n }\n return {};\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (!raw.trim()) {\n return {};\n }\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new DashboardHttpError(400, \"Request body must be valid JSON.\");\n }\n}\n\nfunction matchPath(\n pathname: string,\n pattern: string,\n): Record<string, string> | null {\n const pathParts = pathname.split(\"/\").filter(Boolean);\n const patternParts = pattern.split(\"/\").filter(Boolean);\n if (pathParts.length !== patternParts.length) {\n return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < patternParts.length; i += 1) {\n const expected = patternParts[i] ?? \"\";\n const actual = pathParts[i] ?? \"\";\n if (expected.startsWith(\":\")) {\n params[expected.slice(1)] = decodeURIComponent(actual);\n continue;\n }\n if (expected !== actual) {\n return null;\n }\n }\n return params;\n}\n\nfunction servePublicFile(\n publicDir: string,\n urlPath: string,\n res: ServerResponse,\n): boolean {\n if (!existsSync(publicDir)) {\n return false;\n }\n const pathname = decodeURIComponent(urlPath.split(\"?\")[0] ?? \"/\");\n const requested = pathname === \"/\" ? \"/index.html\" : pathname;\n const abs = resolve(publicDir, `.${requested}`);\n const rel = relative(resolve(publicDir), abs);\n if (rel.startsWith(\"..\") || rel.includes(`..${sep}`)) {\n return false;\n }\n\n const filePath =\n existsSync(abs) && statSync(abs).isFile()\n ? abs\n : join(publicDir, \"index.html\");\n if (!existsSync(filePath) || !statSync(filePath).isFile()) {\n return false;\n }\n\n const type = MIME_TYPES[extname(filePath)] ?? \"application/octet-stream\";\n res.writeHead(200, { \"Content-Type\": type });\n createReadStream(filePath).pipe(res);\n return true;\n}\n\nasync function authorizeApi(\n options: SchedulerDashboardHandlerOptions,\n method: string,\n pathname: string,\n req: IncomingMessage,\n): Promise<void> {\n if (options.authorize) {\n await options.authorize({\n method,\n pathname,\n authorization: req.headers.authorization,\n });\n return;\n }\n authorizeDashboardRequest({\n token: options.token,\n authorization: req.headers.authorization,\n });\n}\n\nasync function handleApi(\n dashboard: SchedulerDashboardService,\n method: string,\n pathname: string,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n if (method === \"GET\" && pathname === \"/api/health\") {\n sendJson(res, 200, await dashboard.getHealth());\n return;\n }\n\n if (method === \"GET\" && pathname === \"/api/handlers\") {\n sendJson(res, 200, { handlers: dashboard.listHandlerKeys() });\n return;\n }\n\n if (method === \"GET\" && pathname === \"/api/jobs\") {\n sendJson(res, 200, { items: await dashboard.listItems() });\n return;\n }\n\n if (method === \"POST\" && pathname === \"/api/jobs\") {\n const created = await dashboard.createJob(\n parseCreateJobBody(await readJsonBody(req)),\n );\n sendJson(res, 201, created);\n return;\n }\n\n const jobExec = matchPath(pathname, \"/api/jobs/:jobId/executions\");\n if (jobExec && method === \"GET\") {\n sendJson(res, 200, {\n executions: await dashboard.listExecutions(jobExec.jobId ?? \"\"),\n });\n return;\n }\n\n const jobAction = matchPath(pathname, \"/api/jobs/:jobId/:action\");\n if (jobAction && method === \"POST\") {\n const jobId = jobAction.jobId ?? \"\";\n if (jobAction.action === \"pause\") {\n await dashboard.pauseJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n if (jobAction.action === \"resume\") {\n await dashboard.resumeJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n if (jobAction.action === \"run\") {\n await dashboard.runJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n const retry = matchPath(pathname, \"/api/executions/:executionId/retry\");\n if (retry && method === \"POST\") {\n await dashboard.retryExecution(retry.executionId ?? \"\");\n res.writeHead(204);\n res.end();\n return;\n }\n\n const job = matchPath(pathname, \"/api/jobs/:jobId\");\n if (job) {\n const jobId = job.jobId ?? \"\";\n if (method === \"GET\") {\n const item = await dashboard.getJob(jobId);\n if (!item) {\n throw new DashboardHttpError(404, `Job not found: ${jobId}`);\n }\n sendJson(res, 200, item);\n return;\n }\n if (method === \"PATCH\") {\n await dashboard.updateJob(\n jobId,\n parseUpdateJobBody(await readJsonBody(req)),\n );\n res.writeHead(204);\n res.end();\n return;\n }\n if (method === \"DELETE\") {\n await dashboard.deleteJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n sendJson(res, 404, { error: \"Not Found\" });\n}\n\nexport async function handleSchedulerDashboardRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options: SchedulerDashboardHandlerOptions,\n): Promise<boolean> {\n const method = req.method ?? \"GET\";\n const originalUrl =\n \"originalUrl\" in req &&\n typeof (req as { originalUrl?: unknown }).originalUrl === \"string\"\n ? (req as { originalUrl: string }).originalUrl\n : undefined;\n const pathname = requestPathname(req.url, originalUrl);\n const basePath = normalizeDashboardBasePath(options.basePath);\n const relative = stripDashboardBasePath(pathname, basePath);\n if (relative === null) {\n return false;\n }\n\n if (method === \"GET\" && basePath && pathname === basePath) {\n res.writeHead(302, { Location: `${basePath}/` });\n res.end();\n return true;\n }\n\n const publicDir =\n options.publicDir ?? resolveDashboardPublicDir(import.meta.url);\n\n try {\n if (method === \"GET\" && relative === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"ok\");\n return true;\n }\n\n if (relative === \"/api\" || relative.startsWith(\"/api/\")) {\n await authorizeApi(options, method, relative, req);\n await handleApi(options.dashboard, method, relative, req, res);\n return true;\n }\n\n if (method === \"GET\" && servePublicFile(publicDir, relative, res)) {\n return true;\n }\n\n res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"Not Found\");\n return true;\n } catch (error) {\n sendDashboardError(res, error);\n return true;\n }\n}\n\nexport function createSchedulerDashboardNodeHandler(\n options: SchedulerDashboardHandlerOptions,\n): SchedulerDashboardRequestHandler {\n return (req, res, next) => {\n void handleSchedulerDashboardRequest(req, res, options)\n .then((handled) => {\n if (!handled) {\n next?.();\n }\n })\n .catch((error: unknown) => {\n if (next) {\n next(error);\n return;\n }\n if (!res.headersSent) {\n sendDashboardError(res, error);\n }\n });\n };\n}\n","import * as http from \"node:http\";\n\nimport {\n createSchedulerDashboardNodeHandler,\n type SchedulerDashboardHandlerOptions,\n} from \"./http-handler.js\";\nimport { resolveDashboardPublicDir } from \"./public-dir.js\";\n\nexport type {\n DashboardAuthorize,\n SchedulerDashboardHandlerOptions,\n SchedulerDashboardRequestHandler,\n} from \"./http-handler.js\";\nexport {\n createSchedulerDashboardNodeHandler,\n handleSchedulerDashboardRequest,\n} from \"./http-handler.js\";\n\nexport interface SchedulerDashboardServerOptions\n extends SchedulerDashboardHandlerOptions {\n port?: number;\n host?: string;\n}\n\nexport interface SchedulerDashboardServer {\n readonly port: number;\n readonly host: string;\n listen(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function createSchedulerDashboardServer(\n options: SchedulerDashboardServerOptions,\n): SchedulerDashboardServer {\n const port = options.port ?? 9100;\n const host = options.host ?? \"127.0.0.1\";\n const handler = createSchedulerDashboardNodeHandler({\n ...options,\n publicDir:\n options.publicDir ?? resolveDashboardPublicDir(import.meta.url),\n });\n\n const server = http.createServer((req, res) => {\n handler(req, res, () => {\n if (!res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"Not Found\");\n }\n });\n });\n\n return {\n get port() {\n return port;\n },\n get host() {\n return host;\n },\n listen(): Promise<void> {\n return new Promise((resolveListen, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolveListen();\n });\n });\n },\n close(): Promise<void> {\n return new Promise((resolveClose, reject) => {\n server.close((err) => (err ? reject(err) : resolveClose()));\n });\n },\n };\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport {\n createMySqlDialectAdapter,\n createPostgresDialectAdapter,\n createSqlClientAdapter,\n createSqlServerDialectAdapter,\n createSqliteDialectAdapter,\n type SqlDialectAdapter,\n type SqlDriverLike,\n} from \"@azlib/persistence\";\n\nexport type SchedulerDashboardDialect =\n | \"postgres\"\n | \"mysql\"\n | \"sqlite\"\n | \"mssql\";\n\nexport function rewriteQuestionParams(\n sql: string,\n style: \"$n\" | \"?\" | \"@pn\",\n): string {\n if (style === \"?\") {\n return sql;\n }\n let index = 0;\n return sql.replaceAll(\"?\", () => {\n index += 1;\n return style === \"$n\" ? `$${index}` : `@p${index}`;\n });\n}\n\nexport function inferDashboardDialect(\n databaseUrl: string,\n): SchedulerDashboardDialect {\n const scheme = databaseUrl.split(\":\")[0]?.toLowerCase() ?? \"\";\n if (scheme === \"postgres\" || scheme === \"postgresql\") {\n return \"postgres\";\n }\n if (scheme === \"mysql\" || scheme === \"mariadb\") {\n return \"mysql\";\n }\n if (scheme === \"mssql\" || scheme === \"sqlserver\") {\n return \"mssql\";\n }\n if (scheme === \"sqlite\" || scheme === \"file\") {\n return \"sqlite\";\n }\n return \"postgres\";\n}\n\nexport function dialectAdapterFor(\n dialect: SchedulerDashboardDialect,\n): SqlDialectAdapter {\n if (dialect === \"mysql\") {\n return createMySqlDialectAdapter();\n }\n if (dialect === \"sqlite\") {\n return createSqliteDialectAdapter();\n }\n if (dialect === \"mssql\") {\n return createSqlServerDialectAdapter();\n }\n return createPostgresDialectAdapter();\n}\n\nfunction sqlitePathFromUrl(databaseUrl: string): string {\n if (databaseUrl.startsWith(\"file:\")) {\n return databaseUrl.slice(\"file:\".length);\n }\n if (databaseUrl.startsWith(\"sqlite:\")) {\n return databaseUrl.slice(\"sqlite:\".length);\n }\n return databaseUrl;\n}\n\ntype PgQueryable = {\n query: (\n sql: string,\n params?: unknown[],\n ) => Promise<{ rows: unknown[] }>;\n};\n\ntype PgPool = PgQueryable & {\n connect: () => Promise<PgQueryable & { release: () => void }>;\n};\n\ntype MysqlConn = {\n execute: (\n sql: string,\n params?: unknown[],\n ) => Promise<[unknown, unknown]>;\n beginTransaction: () => Promise<void>;\n commit: () => Promise<void>;\n rollback: () => Promise<void>;\n release: () => void;\n};\n\ntype MysqlPool = {\n execute: (\n sql: string,\n params?: unknown[],\n ) => Promise<[unknown, unknown]>;\n getConnection: () => Promise<MysqlConn>;\n};\n\ntype SqliteDatabase = {\n prepare: (sql: string) => {\n all: (...params: unknown[]) => unknown[];\n run: (...params: unknown[]) => unknown;\n };\n};\n\ntype MssqlRequest = {\n input: (name: string, value: unknown) => void;\n query: (sql: string) => Promise<{ recordset: unknown[] }>;\n};\n\ntype MssqlPool = {\n request: () => MssqlRequest;\n};\n\nfunction bindMssqlParams(\n request: MssqlRequest,\n params: readonly unknown[] | undefined,\n): void {\n (params ?? []).forEach((value, index) => {\n request.input(`p${index + 1}`, value);\n });\n}\n\nfunction createPgDriver(pool: PgPool): SqlDriverLike {\n const txStore = new AsyncLocalStorage<PgQueryable>();\n const active = (): PgQueryable => txStore.getStore() ?? pool;\n const runQuery = async (sql: string, params?: readonly unknown[]) =>\n active().query(rewriteQuestionParams(sql, \"$n\"), [...(params ?? [])]);\n\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const result = await runQuery(sql, params);\n return result.rows as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n await runQuery(sql, params);\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => {\n const existing = txStore.getStore();\n if (existing) {\n return run();\n }\n const conn = await pool.connect();\n try {\n await conn.query(\"BEGIN\");\n const result = await txStore.run(conn, run);\n await conn.query(\"COMMIT\");\n return result;\n } catch (error) {\n await conn.query(\"ROLLBACK\");\n throw error;\n } finally {\n conn.release();\n }\n },\n };\n}\n\nfunction createMysqlDriver(pool: MysqlPool): SqlDriverLike {\n const txStore = new AsyncLocalStorage<MysqlConn | MysqlPool>();\n const active = (): MysqlConn | MysqlPool => txStore.getStore() ?? pool;\n\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const [rows] = await active().execute(sql, [...(params ?? [])]);\n return rows as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n await active().execute(sql, [...(params ?? [])]);\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => {\n const existing = txStore.getStore();\n if (existing) {\n return run();\n }\n const conn = await pool.getConnection();\n try {\n await conn.beginTransaction();\n const result = await txStore.run(conn, run);\n await conn.commit();\n return result;\n } catch (error) {\n await conn.rollback();\n throw error;\n } finally {\n conn.release();\n }\n },\n };\n}\n\nexport async function createDashboardSqlDriver(\n databaseUrl: string,\n dialect: SchedulerDashboardDialect,\n): Promise<SqlDriverLike> {\n if (dialect === \"postgres\") {\n const pg = (await import(\"pg\")) as {\n default?: { Pool: new (config: { connectionString: string }) => PgPool };\n Pool?: new (config: { connectionString: string }) => PgPool;\n };\n const Pool = pg.Pool ?? pg.default?.Pool;\n if (!Pool) {\n throw new Error(\"The pg package is required for postgres dashboards.\");\n }\n return createPgDriver(new Pool({ connectionString: databaseUrl }));\n }\n\n if (dialect === \"mysql\") {\n const mysql = (await import(\"mysql2/promise\")) as {\n default?: { createPool: (url: string) => MysqlPool };\n createPool?: (url: string) => MysqlPool;\n };\n const createPool = mysql.createPool ?? mysql.default?.createPool;\n if (!createPool) {\n throw new Error(\"The mysql2 package is required for mysql dashboards.\");\n }\n return createMysqlDriver(createPool(databaseUrl));\n }\n\n if (dialect === \"sqlite\") {\n const sqlite = (await import(\"better-sqlite3\")) as {\n default?: new (path: string) => SqliteDatabase;\n };\n const Database = sqlite.default;\n if (!Database) {\n throw new Error(\n \"The better-sqlite3 package is required for sqlite dashboards.\",\n );\n }\n const db = new Database(sqlitePathFromUrl(databaseUrl));\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) =>\n db.prepare(sql).all(...(params ?? [])) as T[],\n execute: async (sql: string, params?: readonly unknown[]) => {\n db.prepare(sql).run(...(params ?? []));\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => run(),\n };\n }\n\n const mssql = (await import(\"mssql\")) as {\n default?: { connect: (url: string) => Promise<MssqlPool> };\n connect?: (url: string) => Promise<MssqlPool>;\n };\n const connect = mssql.connect ?? mssql.default?.connect;\n if (!connect) {\n throw new Error(\"The mssql package is required for mssql dashboards.\");\n }\n const pool = await connect(databaseUrl);\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const request = pool.request();\n bindMssqlParams(request, params);\n const result = await request.query(rewriteQuestionParams(sql, \"@pn\"));\n return result.recordset as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n const request = pool.request();\n bindMssqlParams(request, params);\n await request.query(rewriteQuestionParams(sql, \"@pn\"));\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => run(),\n };\n}\n\nexport function createDashboardPersistenceConfig(input: {\n driver: SqlDriverLike;\n dialect: SchedulerDashboardDialect;\n namespace?: string;\n}) {\n return {\n client: createSqlClientAdapter(input.driver),\n dialect: dialectAdapterFor(input.dialect),\n namespace: input.namespace,\n };\n}\n","import { asString } from \"@azlib/std\";\n\nimport { assertDashboardBindSafety } from \"./http-auth.js\";\nimport {\n inferDashboardDialect,\n type SchedulerDashboardDialect,\n} from \"./sql-drivers.js\";\n\nexport interface SchedulerDashboardCliConfig {\n databaseUrl: string;\n dialect: SchedulerDashboardDialect;\n namespace: string;\n port: number;\n host: string;\n token?: string;\n}\n\nfunction parsePort(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const port = Number(value);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid PORT: ${value}`);\n }\n return port;\n}\n\nfunction parseDialect(\n value: string | undefined,\n databaseUrl: string,\n): SchedulerDashboardDialect {\n const explicit = value?.trim().toLowerCase();\n if (!explicit) {\n return inferDashboardDialect(databaseUrl);\n }\n if (\n explicit === \"postgres\" ||\n explicit === \"mysql\" ||\n explicit === \"sqlite\" ||\n explicit === \"mssql\"\n ) {\n return explicit;\n }\n throw new Error(\n `Invalid SCHEDULER_DIALECT: ${value}. Use postgres, mysql, sqlite, or mssql.`,\n );\n}\n\nexport function parseDashboardCliEnv(\n env: Record<string, string | undefined>,\n): SchedulerDashboardCliConfig {\n const databaseUrl = asString(env.DATABASE_URL)?.trim();\n if (!databaseUrl) {\n throw new Error(\"DATABASE_URL is required\");\n }\n\n const host = asString(env.HOST)?.trim() || \"127.0.0.1\";\n const token = asString(env.DASHBOARD_TOKEN)?.trim() || undefined;\n assertDashboardBindSafety({ host, token });\n\n return {\n databaseUrl,\n dialect: parseDialect(env.SCHEDULER_DIALECT, databaseUrl),\n namespace: asString(env.SCHEDULER_NAMESPACE)?.trim() || \"azlib\",\n port: parsePort(env.PORT, 9100),\n host,\n token,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-config-DrI08d47.mjs","names":[],"sources":["../src/dashboard/http-auth.ts","../src/dashboard/http-parse.ts","../src/dashboard/http-path.ts","../src/dashboard/public-dir.ts","../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts","../src/dashboard/sql-drivers.ts","../src/dashboard/cli-config.ts"],"sourcesContent":["export class DashboardHttpError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"DashboardHttpError\";\n this.status = status;\n }\n}\n\nexport function isLoopbackHost(host: string): boolean {\n const normalized = host.trim().toLowerCase();\n return (\n normalized === \"127.0.0.1\" ||\n normalized === \"localhost\" ||\n normalized === \"::1\" ||\n normalized === \"[::1]\"\n );\n}\n\nexport function authorizeDashboardRequest(input: {\n token?: string;\n authorization?: string | string[] | undefined;\n}): void {\n if (!input.token) {\n return;\n }\n const header = Array.isArray(input.authorization)\n ? input.authorization[0]\n : input.authorization;\n const expected = `Bearer ${input.token}`;\n if (header !== expected) {\n throw new DashboardHttpError(401, \"Unauthorized\");\n }\n}\n\nexport function assertDashboardBindSafety(input: {\n host: string;\n token?: string;\n}): void {\n if (!isLoopbackHost(input.host) && !input.token) {\n throw new Error(\n \"DASHBOARD_TOKEN is required when HOST is not a loopback address\",\n );\n }\n}\n","import { asBoolean, asRecord, asString } from \"@azlib/std\";\n\nimport type { SchedulerJobDefinition } from \"../contracts/scheduler-types.js\";\nimport { parseSchedule } from \"../core/schedule-parser.js\";\nimport { DashboardHttpError } from \"./http-auth.js\";\n\nconst DEFAULT_TIMEZONE = \"UTC\";\n\nfunction requiredText(value: unknown, field: string): string {\n const text = asString(value)?.trim();\n if (!text) {\n throw new DashboardHttpError(400, `${field} is required.`);\n }\n return text;\n}\n\nfunction parseConfig(value: unknown): unknown {\n if (value === undefined || value === null || value === \"\") {\n return {};\n }\n if (typeof value === \"string\") {\n try {\n return JSON.parse(value) as unknown;\n } catch {\n throw new DashboardHttpError(400, \"config must be valid JSON.\");\n }\n }\n return value;\n}\n\nfunction parseScheduleFromRecord(\n record: Record<string, unknown>,\n): SchedulerJobDefinition[\"schedule\"] {\n const nested = asRecord(record.schedule);\n const scheduleType =\n asString(record.scheduleType)?.trim() ||\n asString(nested?.scheduleType)?.trim() ||\n \"cron\";\n if (scheduleType !== \"cron\" && scheduleType !== \"once\") {\n throw new DashboardHttpError(400, \"scheduleType must be cron or once.\");\n }\n const expression = requiredText(\n record.expression ?? nested?.expression,\n \"expression\",\n );\n const timezone =\n asString(record.timezone)?.trim() ||\n asString(nested?.timezone)?.trim() ||\n DEFAULT_TIMEZONE;\n try {\n parseSchedule(scheduleType, expression, timezone);\n } catch (error) {\n throw new DashboardHttpError(\n 400,\n error instanceof Error ? error.message : \"Invalid schedule.\",\n );\n }\n return {\n scheduleType,\n expression,\n timezone,\n };\n}\n\nexport function parseCreateJobBody(body: unknown): SchedulerJobDefinition {\n const record = asRecord(body);\n if (!record) {\n throw new DashboardHttpError(400, \"Request body must be a JSON object.\");\n }\n return {\n name: requiredText(record.name, \"name\"),\n handlerKey: requiredText(record.handlerKey, \"handlerKey\"),\n schedule: parseScheduleFromRecord(record),\n config: parseConfig(record.config),\n enabled: asBoolean(record.enabled) ?? true,\n };\n}\n\nexport function parseUpdateJobBody(\n body: unknown,\n): Partial<SchedulerJobDefinition> {\n const record = asRecord(body);\n if (!record) {\n throw new DashboardHttpError(400, \"Request body must be a JSON object.\");\n }\n const patch: Partial<SchedulerJobDefinition> = {};\n if (record.name !== undefined) {\n patch.name = requiredText(record.name, \"name\");\n }\n if (record.handlerKey !== undefined) {\n patch.handlerKey = requiredText(record.handlerKey, \"handlerKey\");\n }\n if (\n record.expression !== undefined ||\n record.timezone !== undefined ||\n record.scheduleType !== undefined ||\n record.schedule !== undefined\n ) {\n patch.schedule = parseScheduleFromRecord(record);\n }\n if (record.config !== undefined) {\n patch.config = parseConfig(record.config);\n }\n if (record.enabled !== undefined) {\n patch.enabled = asBoolean(record.enabled) ?? true;\n }\n return patch;\n}\n","export function normalizeDashboardBasePath(basePath?: string): string {\n const trimmed = basePath?.trim();\n if (!trimmed || trimmed === \"/\") {\n return \"\";\n }\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nexport function stripDashboardBasePath(\n pathname: string,\n basePath?: string,\n): string | null {\n const base = normalizeDashboardBasePath(basePath);\n const path = pathname || \"/\";\n if (!base) {\n return path.startsWith(\"/\") ? path : `/${path}`;\n }\n if (path === base || path === `${base}/`) {\n return \"/\";\n }\n if (path.startsWith(`${base}/`)) {\n const rest = path.slice(base.length);\n return rest.startsWith(\"/\") ? rest : `/${rest}`;\n }\n return null;\n}\n\nexport function resolveDashboardApiBase(pathname: string): string {\n const path = pathname.split(\"?\")[0] || \"/\";\n const withoutIndex = path.replace(/\\/index\\.html$/i, \"/\");\n const withoutAsset = withoutIndex.replace(/\\/[^/]+\\.[a-z0-9]+$/i, \"/\");\n const trimmed = withoutAsset.replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/api\" : `${trimmed}/api`;\n}\n\nexport function requestPathname(\n url: string | undefined,\n originalUrl?: string,\n): string {\n const raw = originalUrl || url || \"/\";\n try {\n return new URL(raw, \"http://127.0.0.1\").pathname;\n } catch {\n return raw.split(\"?\")[0] || \"/\";\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport function resolveDashboardPublicDir(fromFileUrl: string): string {\n const dir = dirname(fileURLToPath(fromFileUrl));\n const nested = join(dir, \"public\");\n const sibling = join(dir, \"dashboard\", \"public\");\n if (existsSync(join(nested, \"index.html\"))) {\n return nested;\n }\n if (existsSync(join(sibling, \"index.html\"))) {\n return sibling;\n }\n return nested;\n}\n","import { createReadStream, existsSync, statSync } from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extname, join, relative, resolve, sep } from \"node:path\";\n\nimport type { SchedulerDashboardService } from \"./dashboard-types.js\";\nimport { authorizeDashboardRequest, DashboardHttpError } from \"./http-auth.js\";\nimport { parseCreateJobBody, parseUpdateJobBody } from \"./http-parse.js\";\nimport {\n normalizeDashboardBasePath,\n requestPathname,\n stripDashboardBasePath,\n} from \"./http-path.js\";\nimport { resolveDashboardPublicDir } from \"./public-dir.js\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".map\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n};\n\nexport type DashboardAuthorize = (input: {\n method: string;\n pathname: string;\n authorization?: string | string[];\n}) => void | Promise<void>;\n\nexport interface SchedulerDashboardHandlerOptions {\n dashboard: SchedulerDashboardService;\n token?: string;\n authorize?: DashboardAuthorize;\n publicDir?: string;\n basePath?: string;\n}\n\nexport type SchedulerDashboardRequestHandler = (\n req: IncomingMessage,\n res: ServerResponse,\n next?: (err?: unknown) => void,\n) => void;\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(body));\n}\n\nexport function sendDashboardError(res: ServerResponse, error: unknown): void {\n if (error instanceof DashboardHttpError) {\n sendJson(res, error.status, { error: error.message });\n return;\n }\n const message = error instanceof Error ? error.message : \"Unknown error\";\n const status = message.includes(\"not found\")\n ? 404\n : message.includes(\"Only failed\")\n ? 400\n : message.includes(\"Unknown handlerKey\")\n ? 400\n : 500;\n sendJson(res, status, { error: message });\n}\n\nfunction hasParsedBody(req: IncomingMessage): boolean {\n return Object.prototype.hasOwnProperty.call(req, \"body\");\n}\n\nasync function readJsonBody(req: IncomingMessage): Promise<unknown> {\n if (req.readableEnded && hasParsedBody(req)) {\n return (req as IncomingMessage & { body?: unknown }).body ?? {};\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n if (chunks.length === 0) {\n if (hasParsedBody(req)) {\n return (req as IncomingMessage & { body?: unknown }).body ?? {};\n }\n return {};\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (!raw.trim()) {\n return {};\n }\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new DashboardHttpError(400, \"Request body must be valid JSON.\");\n }\n}\n\nfunction matchPath(\n pathname: string,\n pattern: string,\n): Record<string, string> | null {\n const pathParts = pathname.split(\"/\").filter(Boolean);\n const patternParts = pattern.split(\"/\").filter(Boolean);\n if (pathParts.length !== patternParts.length) {\n return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < patternParts.length; i += 1) {\n const expected = patternParts[i] ?? \"\";\n const actual = pathParts[i] ?? \"\";\n if (expected.startsWith(\":\")) {\n params[expected.slice(1)] = decodeURIComponent(actual);\n continue;\n }\n if (expected !== actual) {\n return null;\n }\n }\n return params;\n}\n\nfunction servePublicFile(\n publicDir: string,\n urlPath: string,\n res: ServerResponse,\n): boolean {\n if (!existsSync(publicDir)) {\n return false;\n }\n const pathname = decodeURIComponent(urlPath.split(\"?\")[0] ?? \"/\");\n const requested = pathname === \"/\" ? \"/index.html\" : pathname;\n const abs = resolve(publicDir, `.${requested}`);\n const rel = relative(resolve(publicDir), abs);\n if (rel.startsWith(\"..\") || rel.includes(`..${sep}`)) {\n return false;\n }\n\n const filePath =\n existsSync(abs) && statSync(abs).isFile()\n ? abs\n : join(publicDir, \"index.html\");\n if (!existsSync(filePath) || !statSync(filePath).isFile()) {\n return false;\n }\n\n const type = MIME_TYPES[extname(filePath)] ?? \"application/octet-stream\";\n res.writeHead(200, { \"Content-Type\": type });\n createReadStream(filePath).pipe(res);\n return true;\n}\n\nasync function authorizeApi(\n options: SchedulerDashboardHandlerOptions,\n method: string,\n pathname: string,\n req: IncomingMessage,\n): Promise<void> {\n if (options.authorize) {\n await options.authorize({\n method,\n pathname,\n authorization: req.headers.authorization,\n });\n return;\n }\n authorizeDashboardRequest({\n token: options.token,\n authorization: req.headers.authorization,\n });\n}\n\nasync function handleApi(\n dashboard: SchedulerDashboardService,\n method: string,\n pathname: string,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n if (method === \"GET\" && pathname === \"/api/health\") {\n sendJson(res, 200, await dashboard.getHealth());\n return;\n }\n\n if (method === \"GET\" && pathname === \"/api/handlers\") {\n sendJson(res, 200, { handlers: dashboard.listHandlerKeys() });\n return;\n }\n\n if (method === \"GET\" && pathname === \"/api/jobs\") {\n sendJson(res, 200, { items: await dashboard.listItems() });\n return;\n }\n\n if (method === \"POST\" && pathname === \"/api/jobs\") {\n const created = await dashboard.createJob(\n parseCreateJobBody(await readJsonBody(req)),\n );\n sendJson(res, 201, created);\n return;\n }\n\n const jobExec = matchPath(pathname, \"/api/jobs/:jobId/executions\");\n if (jobExec && method === \"GET\") {\n sendJson(res, 200, {\n executions: await dashboard.listExecutions(jobExec.jobId ?? \"\"),\n });\n return;\n }\n\n const jobAction = matchPath(pathname, \"/api/jobs/:jobId/:action\");\n if (jobAction && method === \"POST\") {\n const jobId = jobAction.jobId ?? \"\";\n if (jobAction.action === \"pause\") {\n await dashboard.pauseJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n if (jobAction.action === \"resume\") {\n await dashboard.resumeJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n if (jobAction.action === \"run\") {\n await dashboard.runJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n const retry = matchPath(pathname, \"/api/executions/:executionId/retry\");\n if (retry && method === \"POST\") {\n await dashboard.retryExecution(retry.executionId ?? \"\");\n res.writeHead(204);\n res.end();\n return;\n }\n\n const job = matchPath(pathname, \"/api/jobs/:jobId\");\n if (job) {\n const jobId = job.jobId ?? \"\";\n if (method === \"GET\") {\n const item = await dashboard.getJob(jobId);\n if (!item) {\n throw new DashboardHttpError(404, `Job not found: ${jobId}`);\n }\n sendJson(res, 200, item);\n return;\n }\n if (method === \"PATCH\") {\n await dashboard.updateJob(\n jobId,\n parseUpdateJobBody(await readJsonBody(req)),\n );\n res.writeHead(204);\n res.end();\n return;\n }\n if (method === \"DELETE\") {\n await dashboard.deleteJob(jobId);\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n sendJson(res, 404, { error: \"Not Found\" });\n}\n\nexport async function handleSchedulerDashboardRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options: SchedulerDashboardHandlerOptions,\n): Promise<boolean> {\n const method = req.method ?? \"GET\";\n const originalUrl =\n \"originalUrl\" in req &&\n typeof (req as { originalUrl?: unknown }).originalUrl === \"string\"\n ? (req as { originalUrl: string }).originalUrl\n : undefined;\n const pathname = requestPathname(req.url, originalUrl);\n const basePath = normalizeDashboardBasePath(options.basePath);\n const relative = stripDashboardBasePath(pathname, basePath);\n if (relative === null) {\n return false;\n }\n\n if (method === \"GET\" && basePath && pathname === basePath) {\n res.writeHead(302, { Location: `${basePath}/` });\n res.end();\n return true;\n }\n\n const publicDir =\n options.publicDir ?? resolveDashboardPublicDir(import.meta.url);\n\n try {\n if (method === \"GET\" && relative === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"ok\");\n return true;\n }\n\n if (relative === \"/api\" || relative.startsWith(\"/api/\")) {\n await authorizeApi(options, method, relative, req);\n await handleApi(options.dashboard, method, relative, req, res);\n return true;\n }\n\n if (method === \"GET\" && servePublicFile(publicDir, relative, res)) {\n return true;\n }\n\n res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"Not Found\");\n return true;\n } catch (error) {\n sendDashboardError(res, error);\n return true;\n }\n}\n\nexport function createSchedulerDashboardNodeHandler(\n options: SchedulerDashboardHandlerOptions,\n): SchedulerDashboardRequestHandler {\n return (req, res, next) => {\n void handleSchedulerDashboardRequest(req, res, options)\n .then((handled) => {\n if (!handled) {\n next?.();\n }\n })\n .catch((error: unknown) => {\n if (next) {\n next(error);\n return;\n }\n if (!res.headersSent) {\n sendDashboardError(res, error);\n }\n });\n };\n}\n","import * as http from \"node:http\";\n\nimport {\n createSchedulerDashboardNodeHandler,\n type SchedulerDashboardHandlerOptions,\n} from \"./http-handler.js\";\nimport { resolveDashboardPublicDir } from \"./public-dir.js\";\n\nexport type {\n DashboardAuthorize,\n SchedulerDashboardHandlerOptions,\n SchedulerDashboardRequestHandler,\n} from \"./http-handler.js\";\nexport {\n createSchedulerDashboardNodeHandler,\n handleSchedulerDashboardRequest,\n} from \"./http-handler.js\";\n\nexport interface SchedulerDashboardServerOptions extends SchedulerDashboardHandlerOptions {\n port?: number;\n host?: string;\n}\n\nexport interface SchedulerDashboardServer {\n readonly port: number;\n readonly host: string;\n listen(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport function createSchedulerDashboardServer(\n options: SchedulerDashboardServerOptions,\n): SchedulerDashboardServer {\n const port = options.port ?? 9100;\n const host = options.host ?? \"127.0.0.1\";\n const handler = createSchedulerDashboardNodeHandler({\n ...options,\n publicDir: options.publicDir ?? resolveDashboardPublicDir(import.meta.url),\n });\n\n const server = http.createServer((req, res) => {\n handler(req, res, () => {\n if (!res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"Not Found\");\n }\n });\n });\n\n return {\n get port() {\n return port;\n },\n get host() {\n return host;\n },\n listen(): Promise<void> {\n return new Promise((resolveListen, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolveListen();\n });\n });\n },\n close(): Promise<void> {\n return new Promise((resolveClose, reject) => {\n server.close((err) => (err ? reject(err) : resolveClose()));\n });\n },\n };\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport {\n createMySqlDialectAdapter,\n createPostgresDialectAdapter,\n createSqlClientAdapter,\n createSqlServerDialectAdapter,\n createSqliteDialectAdapter,\n type SqlDialectAdapter,\n type SqlDriverLike,\n} from \"@azlib/persistence\";\n\nexport type SchedulerDashboardDialect =\n \"postgres\" | \"mysql\" | \"sqlite\" | \"mssql\";\n\nexport function rewriteQuestionParams(\n sql: string,\n style: \"$n\" | \"?\" | \"@pn\",\n): string {\n if (style === \"?\") {\n return sql;\n }\n let index = 0;\n return sql.replaceAll(\"?\", () => {\n index += 1;\n return style === \"$n\" ? `$${index}` : `@p${index}`;\n });\n}\n\nexport function inferDashboardDialect(\n databaseUrl: string,\n): SchedulerDashboardDialect {\n const scheme = databaseUrl.split(\":\")[0]?.toLowerCase() ?? \"\";\n if (scheme === \"postgres\" || scheme === \"postgresql\") {\n return \"postgres\";\n }\n if (scheme === \"mysql\" || scheme === \"mariadb\") {\n return \"mysql\";\n }\n if (scheme === \"mssql\" || scheme === \"sqlserver\") {\n return \"mssql\";\n }\n if (scheme === \"sqlite\" || scheme === \"file\") {\n return \"sqlite\";\n }\n return \"postgres\";\n}\n\nexport function dialectAdapterFor(\n dialect: SchedulerDashboardDialect,\n): SqlDialectAdapter {\n if (dialect === \"mysql\") {\n return createMySqlDialectAdapter();\n }\n if (dialect === \"sqlite\") {\n return createSqliteDialectAdapter();\n }\n if (dialect === \"mssql\") {\n return createSqlServerDialectAdapter();\n }\n return createPostgresDialectAdapter();\n}\n\nfunction sqlitePathFromUrl(databaseUrl: string): string {\n if (databaseUrl.startsWith(\"file:\")) {\n return databaseUrl.slice(\"file:\".length);\n }\n if (databaseUrl.startsWith(\"sqlite:\")) {\n return databaseUrl.slice(\"sqlite:\".length);\n }\n return databaseUrl;\n}\n\ntype PgQueryable = {\n query: (sql: string, params?: unknown[]) => Promise<{ rows: unknown[] }>;\n};\n\ntype PgPool = PgQueryable & {\n connect: () => Promise<PgQueryable & { release: () => void }>;\n};\n\ntype MysqlConn = {\n execute: (sql: string, params?: unknown[]) => Promise<[unknown, unknown]>;\n beginTransaction: () => Promise<void>;\n commit: () => Promise<void>;\n rollback: () => Promise<void>;\n release: () => void;\n};\n\ntype MysqlPool = {\n execute: (sql: string, params?: unknown[]) => Promise<[unknown, unknown]>;\n getConnection: () => Promise<MysqlConn>;\n};\n\ntype SqliteDatabase = {\n prepare: (sql: string) => {\n all: (...params: unknown[]) => unknown[];\n run: (...params: unknown[]) => unknown;\n };\n};\n\ntype MssqlRequest = {\n input: (name: string, value: unknown) => void;\n query: (sql: string) => Promise<{ recordset: unknown[] }>;\n};\n\ntype MssqlPool = {\n request: () => MssqlRequest;\n};\n\nfunction bindMssqlParams(\n request: MssqlRequest,\n params: readonly unknown[] | undefined,\n): void {\n (params ?? []).forEach((value, index) => {\n request.input(`p${index + 1}`, value);\n });\n}\n\nfunction createPgDriver(pool: PgPool): SqlDriverLike {\n const txStore = new AsyncLocalStorage<PgQueryable>();\n const active = (): PgQueryable => txStore.getStore() ?? pool;\n const runQuery = async (sql: string, params?: readonly unknown[]) =>\n active().query(rewriteQuestionParams(sql, \"$n\"), [...(params ?? [])]);\n\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const result = await runQuery(sql, params);\n return result.rows as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n await runQuery(sql, params);\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => {\n const existing = txStore.getStore();\n if (existing) {\n return run();\n }\n const conn = await pool.connect();\n try {\n await conn.query(\"BEGIN\");\n const result = await txStore.run(conn, run);\n await conn.query(\"COMMIT\");\n return result;\n } catch (error) {\n await conn.query(\"ROLLBACK\");\n throw error;\n } finally {\n conn.release();\n }\n },\n };\n}\n\nfunction createMysqlDriver(pool: MysqlPool): SqlDriverLike {\n const txStore = new AsyncLocalStorage<MysqlConn | MysqlPool>();\n const active = (): MysqlConn | MysqlPool => txStore.getStore() ?? pool;\n\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const [rows] = await active().execute(sql, [...(params ?? [])]);\n return rows as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n await active().execute(sql, [...(params ?? [])]);\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => {\n const existing = txStore.getStore();\n if (existing) {\n return run();\n }\n const conn = await pool.getConnection();\n try {\n await conn.beginTransaction();\n const result = await txStore.run(conn, run);\n await conn.commit();\n return result;\n } catch (error) {\n await conn.rollback();\n throw error;\n } finally {\n conn.release();\n }\n },\n };\n}\n\nexport async function createDashboardSqlDriver(\n databaseUrl: string,\n dialect: SchedulerDashboardDialect,\n): Promise<SqlDriverLike> {\n if (dialect === \"postgres\") {\n const pg = (await import(\"pg\")) as {\n default?: { Pool: new (config: { connectionString: string }) => PgPool };\n Pool?: new (config: { connectionString: string }) => PgPool;\n };\n const Pool = pg.Pool ?? pg.default?.Pool;\n if (!Pool) {\n throw new Error(\"The pg package is required for postgres dashboards.\");\n }\n return createPgDriver(new Pool({ connectionString: databaseUrl }));\n }\n\n if (dialect === \"mysql\") {\n const mysql = (await import(\"mysql2/promise\")) as {\n default?: { createPool: (url: string) => MysqlPool };\n createPool?: (url: string) => MysqlPool;\n };\n const createPool = mysql.createPool ?? mysql.default?.createPool;\n if (!createPool) {\n throw new Error(\"The mysql2 package is required for mysql dashboards.\");\n }\n return createMysqlDriver(createPool(databaseUrl));\n }\n\n if (dialect === \"sqlite\") {\n const sqlite = (await import(\"better-sqlite3\")) as {\n default?: new (path: string) => SqliteDatabase;\n };\n const Database = sqlite.default;\n if (!Database) {\n throw new Error(\n \"The better-sqlite3 package is required for sqlite dashboards.\",\n );\n }\n const db = new Database(sqlitePathFromUrl(databaseUrl));\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) =>\n db.prepare(sql).all(...(params ?? [])) as T[],\n execute: async (sql: string, params?: readonly unknown[]) => {\n db.prepare(sql).run(...(params ?? []));\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => run(),\n };\n }\n\n const mssql = (await import(\"mssql\")) as {\n default?: { connect: (url: string) => Promise<MssqlPool> };\n connect?: (url: string) => Promise<MssqlPool>;\n };\n const connect = mssql.connect ?? mssql.default?.connect;\n if (!connect) {\n throw new Error(\"The mssql package is required for mssql dashboards.\");\n }\n const pool = await connect(databaseUrl);\n return {\n query: async <T = unknown>(sql: string, params?: readonly unknown[]) => {\n const request = pool.request();\n bindMssqlParams(request, params);\n const result = await request.query(rewriteQuestionParams(sql, \"@pn\"));\n return result.recordset as T[];\n },\n execute: async (sql: string, params?: readonly unknown[]) => {\n const request = pool.request();\n bindMssqlParams(request, params);\n await request.query(rewriteQuestionParams(sql, \"@pn\"));\n },\n transaction: async <T>(run: () => Promise<T>): Promise<T> => run(),\n };\n}\n\nexport function createDashboardPersistenceConfig(input: {\n driver: SqlDriverLike;\n dialect: SchedulerDashboardDialect;\n namespace?: string;\n}) {\n return {\n client: createSqlClientAdapter(input.driver),\n dialect: dialectAdapterFor(input.dialect),\n namespace: input.namespace,\n };\n}\n","import { asString } from \"@azlib/std\";\n\nimport { assertDashboardBindSafety } from \"./http-auth.js\";\nimport {\n inferDashboardDialect,\n type SchedulerDashboardDialect,\n} from \"./sql-drivers.js\";\n\nexport interface SchedulerDashboardCliConfig {\n databaseUrl: string;\n dialect: SchedulerDashboardDialect;\n namespace: string;\n port: number;\n host: string;\n token?: string;\n}\n\nfunction parsePort(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const port = Number(value);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid PORT: ${value}`);\n }\n return port;\n}\n\nfunction parseDialect(\n value: string | undefined,\n databaseUrl: string,\n): SchedulerDashboardDialect {\n const explicit = value?.trim().toLowerCase();\n if (!explicit) {\n return inferDashboardDialect(databaseUrl);\n }\n if (\n explicit === \"postgres\" ||\n explicit === \"mysql\" ||\n explicit === \"sqlite\" ||\n explicit === \"mssql\"\n ) {\n return explicit;\n }\n throw new Error(\n `Invalid SCHEDULER_DIALECT: ${value}. Use postgres, mysql, sqlite, or mssql.`,\n );\n}\n\nexport function parseDashboardCliEnv(\n env: Record<string, string | undefined>,\n): SchedulerDashboardCliConfig {\n const databaseUrl = asString(env.DATABASE_URL)?.trim();\n if (!databaseUrl) {\n throw new Error(\"DATABASE_URL is required\");\n }\n\n const host = asString(env.HOST)?.trim() || \"127.0.0.1\";\n const token = asString(env.DASHBOARD_TOKEN)?.trim() || undefined;\n assertDashboardBindSafety({ host, token });\n\n return {\n databaseUrl,\n dialect: parseDialect(env.SCHEDULER_DIALECT, databaseUrl),\n namespace: asString(env.SCHEDULER_NAMESPACE)?.trim() || \"azlib\",\n port: parsePort(env.PORT, 9100),\n host,\n token,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server-Cme4XKyH.d.mts","names":[],"sources":["../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"http-server-Cme4XKyH.d.mts","names":[],"sources":["../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts"],"mappings":";;;KAwBY,sBAAsB;EAChC;EACA;EACA;aACW;UAEI;EACf,WAAW;EACX;EACA,YAAY;EACZ;EACA;;KAGU,oCACV,KAAK,iBACL,KAAK,gBACL,QAAQ;iBAmOY,gCACpB,KAAK,iBACL,KAAK,gBACL,SAAS,mCACR;iBAiDa,oCACd,SAAS,mCACR;;;UCjTc,wCAAwC;EACvD;EACA;;UAGe;WACN;WACA;EACT,UAAU;EACV,SAAS;;iBAGK,+BACd,SAAS,kCACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server-u1BnbkAK.d.cts","names":[],"sources":["../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"http-server-u1BnbkAK.d.cts","names":[],"sources":["../src/dashboard/http-handler.ts","../src/dashboard/http-server.ts"],"mappings":";;;KAwBY,sBAAsB;EAChC;EACA;EACA;aACW;UAEI;EACf,WAAW;EACX;EACA,YAAY;EACZ;EACA;;KAGU,oCACV,KAAK,iBACL,KAAK,gBACL,QAAQ;iBAmOY,gCACpB,KAAK,iBACL,KAAK,gBACL,SAAS,mCACR;iBAiDa,oCACd,SAAS,mCACR;;;UCjTc,wCAAwC;EACvD;EACA;;UAGe;WACN;WACA;EACT,UAAU;EACV,SAAS;;iBAGK,+BACd,SAAS,kCACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-dashboard-C44d7sdh.d.cts","names":[],"sources":["../src/dashboard/queue-dashboard-service.ts","../src/core/job-execution-store.ts","../src/core/schedule-cursor-store.ts","../src/core/scheduler-job-store.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"mappings":";;;iBAKgB,gCACd,WAAW,kBACX;EAAY,WAAW,KAAK;IAC3B;;;UCNc;EACf,OAAO,QAAQ,2BAA2B,QAAQ;EAClD,OACE,qBACA,OAAO,QAAQ,KAAK,4CACnB,QAAQ;EACX,UAAU,gBAAgB,QAAQ;;;;UCRnB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf,IACE,eACA,mBACA,cACC,QAAQ;EACX,cACE,eACA,sBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,OAAO,gBAAgB;;;;UCdR;EACf,OACE,eACA,YAAY,yBACX,QAAQ;EACX,OACE,eACA,OAAO,QAAQ,0BACd,QAAQ;EACX,WACE,eACA,mBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,QAAQ,QAAQ;EAChB,OAAO,gBAAgB;;;;UCyLR;EACf,UAAU;EACV,aAAa;EACb,gBAAgB;;iBAGF,2BACd,QAAQ,6BACP;;;iBCzJa,mCACd,QAAQ,sBACR;EACE,eAAe;EACf;IAED;
|
|
1
|
+
{"version":3,"file":"store-dashboard-C44d7sdh.d.cts","names":[],"sources":["../src/dashboard/queue-dashboard-service.ts","../src/core/job-execution-store.ts","../src/core/schedule-cursor-store.ts","../src/core/scheduler-job-store.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"mappings":";;;iBAKgB,gCACd,WAAW,kBACX;EAAY,WAAW,KAAK;IAC3B;;;UCNc;EACf,OAAO,QAAQ,2BAA2B,QAAQ;EAClD,OACE,qBACA,OAAO,QAAQ,KAAK,4CACnB,QAAQ;EACX,UAAU,gBAAgB,QAAQ;;;;UCRnB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf,IACE,eACA,mBACA,cACC,QAAQ;EACX,cACE,eACA,sBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,OAAO,gBAAgB;;;;UCdR;EACf,OACE,eACA,YAAY,yBACX,QAAQ;EACX,OACE,eACA,OAAO,QAAQ,0BACd,QAAQ;EACX,WACE,eACA,mBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,QAAQ,QAAQ;EAChB,OAAO,gBAAgB;;;;UCyLR;EACf,UAAU;EACV,aAAa;EACb,gBAAgB;;iBAGF,2BACd,QAAQ,6BACP;;;iBCzJa,mCACd,QAAQ,sBACR;EACE,eAAe;EACf;IAED;iBA0Ia,wCACd,aAAa,4BACb;EACE,eAAe;EACf;IAED"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-dashboard-Cyf_JILa.mjs","names":[],"sources":["../src/core/schedule-parser.ts","../src/core/scheduler-job-trigger.ts","../src/core/scheduler-service-retry.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"sourcesContent":["import type { ScheduleType } from \"../contracts/scheduler-types.js\";\n\nexport interface ParsedSchedule {\n scheduleType: ScheduleType;\n expression: string;\n timezone: string;\n}\n\nconst CRON_FIELD_PATTERN = /^\\*|\\*\\/\\d+|\\d+|\\d+-\\d+|\\d+(?:,\\d+)*$/;\n\nexport function assertValidTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: timezone }).format(new Date());\n } catch {\n throw new Error(`Invalid timezone: ${timezone}`);\n }\n}\n\nfunction assertValidCronExpression(expression: string): void {\n const fields = expression.trim().split(/\\s+/);\n if (fields.length !== 5) {\n throw new Error(\"Cron expression must contain 5 fields\");\n }\n\n for (const field of fields) {\n if (!CRON_FIELD_PATTERN.test(field)) {\n throw new Error(`Invalid cron field: ${field}`);\n }\n }\n}\n\nfunction assertValidOnceExpression(expression: string): void {\n const date = new Date(expression);\n if (Number.isNaN(date.getTime())) {\n throw new Error(\"Once schedule expression must be a valid ISO datetime\");\n }\n}\n\nexport function parseSchedule(\n scheduleType: ScheduleType,\n expression: string,\n timezone: string,\n): ParsedSchedule {\n assertValidTimezone(timezone);\n\n if (scheduleType === \"cron\") {\n assertValidCronExpression(expression);\n } else {\n assertValidOnceExpression(expression);\n }\n\n return {\n scheduleType,\n expression: expression.trim(),\n timezone,\n };\n}\n\nexport function computeNextRunAt(\n parsed: ParsedSchedule,\n now = new Date(),\n): string {\n if (parsed.scheduleType === \"once\") {\n return new Date(parsed.expression).toISOString();\n }\n\n const next = new Date(now);\n next.setUTCSeconds(0, 0);\n next.setUTCMinutes(next.getUTCMinutes() + 1);\n return next.toISOString();\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerExecutionRecord,\n SchedulerJobRecord,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\n\nexport async function dispatchJobExecution(input: {\n job: SchedulerJobRecord;\n executionStore: JobExecutionStore;\n queueService?: QueueService;\n scheduledFor: string;\n attemptCount?: number;\n idempotencyKey: string;\n}): Promise<SchedulerExecutionRecord> {\n const executionId = `exec_${randomUUID()}`;\n const created: SchedulerExecutionRecord = {\n executionId,\n jobId: input.job.jobId,\n scheduledFor: input.scheduledFor,\n triggeredAt: new Date().toISOString(),\n status: \"queued\",\n attemptCount: input.attemptCount ?? 0,\n };\n await input.executionStore.create(created);\n\n if (!input.queueService) {\n return created;\n }\n\n const accepted = await input.queueService.enqueue({\n idempotencyKey: input.idempotencyKey,\n payloadRef: {\n executionId,\n handlerKey: input.job.handlerKey,\n config: input.job.config,\n },\n });\n const withTask = await input.executionStore.update(executionId, {\n queueTaskId: accepted.taskId,\n });\n return withTask ?? { ...created, queueTaskId: accepted.taskId };\n}\n","import type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerExecutionRecord,\n SchedulerJobRecord,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\nimport { dispatchJobExecution } from \"./scheduler-job-trigger.js\";\n\nexport function findRetryableExecution(\n allExecutions: SchedulerExecutionRecord[],\n executionId: string,\n): SchedulerExecutionRecord {\n const target = allExecutions.find((item) => item.executionId === executionId);\n if (!target) {\n throw new Error(`Execution not found: ${executionId}`);\n }\n if (target.status !== \"failed\" && target.status !== \"dead-letter\") {\n throw new Error(\"Only failed or dead-letter executions can be retried\");\n }\n return target;\n}\n\nexport async function retryFailedExecutionById(\n allExecutions: SchedulerExecutionRecord[],\n jobs: readonly SchedulerJobRecord[],\n executionStore: JobExecutionStore,\n queueService: QueueService | undefined,\n executionId: string,\n): Promise<SchedulerExecutionRecord> {\n const target = findRetryableExecution(allExecutions, executionId);\n const job = jobs.find((item) => item.jobId === target.jobId);\n if (!job) {\n throw new Error(`Job not found: ${target.jobId}`);\n }\n\n return dispatchJobExecution({\n job,\n executionStore,\n queueService,\n scheduledFor: target.scheduledFor,\n attemptCount: target.attemptCount + 1,\n idempotencyKey: `scheduler:${job.jobId}:retry:${executionId}`,\n });\n}\n","import type { SqlClientAdapter, SqlDialectAdapter } from \"@azlib/persistence\";\n\nimport type { SchedulerPersistenceConfig } from \"../contracts/scheduler-types.js\";\nimport type { ScheduleCursorRecord } from \"./schedule-cursor-store.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\nimport type { ScheduleCursorStore } from \"./schedule-cursor-store.js\";\nimport type { SchedulerJobStore } from \"./scheduler-job-store.js\";\n\ninterface JobRow {\n jobId: string;\n name: string;\n handlerKey: string;\n scheduleType: \"once\" | \"cron\";\n expression: string;\n timezone: string;\n overlapPolicy: string | null;\n missedRunPolicy: string | null;\n configJson: string;\n enabled: number;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface CursorRow {\n jobId: string;\n lastEvaluatedAt: string;\n lastTriggeredAt: string | null;\n nextRunAt: string;\n version: number;\n}\n\ninterface ExecutionRow {\n executionId: string;\n jobId: string;\n scheduledFor: string;\n triggeredAt: string;\n status: string;\n queueTaskId: string | null;\n attemptCount: number;\n lastError: string | null;\n}\n\nfunction namespaceFor(config: SchedulerPersistenceConfig): string {\n return config.namespace?.trim() || \"azlib\";\n}\n\nfunction tableNames(namespace: string) {\n return {\n jobs: `${namespace}__scheduler_jobs`,\n cursors: `${namespace}__scheduler_cursors`,\n executions: `${namespace}__scheduler_executions`,\n };\n}\n\nfunction quoted(dialect: SqlDialectAdapter, tableName: string): string {\n return dialect.quoteIdentifier(tableName);\n}\n\nasync function bootstrapSchedulerPersistence(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n): Promise<void> {\n await client.transaction(async (tx) => {\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.jobs)} (\n job_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n handler_key TEXT NOT NULL,\n schedule_type TEXT NOT NULL,\n expression TEXT NOT NULL,\n timezone TEXT NOT NULL,\n overlap_policy TEXT,\n missed_run_policy TEXT,\n config_json TEXT NOT NULL,\n enabled INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n )`);\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.cursors)} (\n job_id TEXT PRIMARY KEY,\n last_evaluated_at TEXT NOT NULL,\n last_triggered_at TEXT,\n next_run_at TEXT NOT NULL,\n version INTEGER NOT NULL\n )`);\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.executions)} (\n execution_id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL,\n scheduled_for TEXT NOT NULL,\n triggered_at TEXT NOT NULL,\n status TEXT NOT NULL,\n queue_task_id TEXT,\n attempt_count INTEGER NOT NULL,\n last_error TEXT\n )`);\n });\n}\n\nfunction toJobRow<TConfig>(\n jobId: string,\n definition: import(\"../contracts/scheduler-types.js\").SchedulerJobDefinition<TConfig>,\n): JobRow {\n const now = new Date().toISOString();\n return {\n jobId,\n name: definition.name,\n handlerKey: definition.handlerKey,\n scheduleType: definition.schedule.scheduleType,\n expression: definition.schedule.expression,\n timezone: definition.schedule.timezone,\n overlapPolicy: definition.schedule.overlapPolicy ?? null,\n missedRunPolicy: definition.schedule.missedRunPolicy ?? null,\n configJson: JSON.stringify(definition.config),\n enabled: (definition.enabled ?? true) ? 1 : 0,\n createdAt: now,\n updatedAt: now,\n };\n}\n\nfunction rowToJobRecord<TConfig>(\n row: JobRow,\n): import(\"../contracts/scheduler-types.js\").SchedulerJobRecord<TConfig> {\n return {\n jobId: row.jobId,\n name: row.name,\n handlerKey: row.handlerKey,\n schedule: {\n scheduleType: row.scheduleType,\n expression: row.expression,\n timezone: row.timezone,\n overlapPolicy:\n row.overlapPolicy === null ? undefined : (row.overlapPolicy as any),\n missedRunPolicy:\n row.missedRunPolicy === null ? undefined : (row.missedRunPolicy as any),\n },\n config: JSON.parse(row.configJson) as TConfig,\n enabled: Boolean(row.enabled),\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nasync function readJobRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n jobId: string,\n) {\n const rows = await client.query<JobRow>(\n `SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt\n FROM ${quoted(dialect, names.jobs)} WHERE job_id = ? LIMIT 1`,\n [jobId],\n );\n return rows[0] ? rowToJobRecord(rows[0]) : null;\n}\n\nasync function readCursorRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n jobId: string,\n): Promise<ScheduleCursorRecord | null> {\n const rows = await client.query<CursorRow>(\n `SELECT job_id AS jobId, last_evaluated_at AS lastEvaluatedAt, last_triggered_at AS lastTriggeredAt, next_run_at AS nextRunAt, version\n FROM ${quoted(dialect, names.cursors)} WHERE job_id = ? LIMIT 1`,\n [jobId],\n );\n const row = rows[0];\n return row\n ? {\n jobId: row.jobId,\n lastEvaluatedAt: row.lastEvaluatedAt,\n lastTriggeredAt: row.lastTriggeredAt ?? undefined,\n nextRunAt: row.nextRunAt,\n version: row.version,\n }\n : null;\n}\n\nasync function readExecutionRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n executionId: string,\n) {\n const rows = await client.query<ExecutionRow>(\n `SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError\n FROM ${quoted(dialect, names.executions)} WHERE execution_id = ? LIMIT 1`,\n [executionId],\n );\n const row = rows[0];\n return row\n ? {\n executionId: row.executionId,\n jobId: row.jobId,\n scheduledFor: row.scheduledFor,\n triggeredAt: row.triggeredAt,\n status: row.status as any,\n queueTaskId: row.queueTaskId ?? undefined,\n attemptCount: row.attemptCount,\n lastError: row.lastError ?? undefined,\n }\n : null;\n}\n\nexport interface SchedulerPersistence {\n jobStore: SchedulerJobStore;\n cursorStore: ScheduleCursorStore;\n executionStore: JobExecutionStore;\n}\n\nexport function createSchedulerPersistence(\n config: SchedulerPersistenceConfig,\n): SchedulerPersistence {\n const namespace = namespaceFor(config);\n const names = tableNames(namespace);\n let bootstrapPromise: Promise<void> | null = null;\n\n async function ensureBootstrapped() {\n if (!bootstrapPromise) {\n bootstrapPromise = bootstrapSchedulerPersistence(\n config.client,\n config.dialect,\n names,\n );\n }\n await bootstrapPromise;\n }\n\n return {\n jobStore: {\n async create(jobId, definition) {\n await ensureBootstrapped();\n const row = toJobRow(jobId, definition);\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n row.jobId,\n row.name,\n row.handlerKey,\n row.scheduleType,\n row.expression,\n row.timezone,\n row.overlapPolicy,\n row.missedRunPolicy,\n row.configJson,\n row.enabled,\n row.createdAt,\n row.updatedAt,\n ],\n );\n });\n return rowToJobRecord(row);\n },\n async update(jobId, patch) {\n const current = await readJobRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next: import(\"../contracts/scheduler-types.js\").SchedulerJobRecord =\n {\n ...current,\n ...patch,\n schedule: {\n ...current.schedule,\n ...patch.schedule,\n },\n updatedAt: new Date().toISOString(),\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.name,\n next.handlerKey,\n next.schedule.scheduleType,\n next.schedule.expression,\n next.schedule.timezone,\n next.schedule.overlapPolicy ?? null,\n next.schedule.missedRunPolicy ?? null,\n JSON.stringify(next.config),\n next.enabled ? 1 : 0,\n next.createdAt,\n next.updatedAt,\n ],\n );\n });\n return next;\n },\n async setEnabled(jobId, enabled) {\n const current = await readJobRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next = {\n ...current,\n enabled,\n updatedAt: new Date().toISOString(),\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.name,\n next.handlerKey,\n next.schedule.scheduleType,\n next.schedule.expression,\n next.schedule.timezone,\n next.schedule.overlapPolicy ?? null,\n next.schedule.missedRunPolicy ?? null,\n JSON.stringify(next.config),\n next.enabled ? 1 : 0,\n next.createdAt,\n next.updatedAt,\n ],\n );\n });\n return next;\n },\n async get(jobId) {\n await ensureBootstrapped();\n return readJobRow(config.client, config.dialect, names, jobId);\n },\n async list() {\n await ensureBootstrapped();\n const rows = await config.client.query<JobRow>(\n `SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt\n FROM ${quoted(config.dialect, names.jobs)} ORDER BY created_at ASC`,\n [],\n );\n return rows.map(rowToJobRecord);\n },\n async remove(jobId) {\n await ensureBootstrapped();\n await config.client.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n return true;\n },\n },\n cursorStore: {\n async set(jobId, nextRunAt, now) {\n await ensureBootstrapped();\n const current = await readCursorRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n const next: CursorRow = {\n jobId,\n lastEvaluatedAt: now,\n lastTriggeredAt: current?.lastTriggeredAt ?? null,\n nextRunAt,\n version: (current?.version ?? 0) + 1,\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.cursors)} (job_id, last_evaluated_at, last_triggered_at, next_run_at, version) VALUES (?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.lastEvaluatedAt,\n next.lastTriggeredAt,\n next.nextRunAt,\n next.version,\n ],\n );\n });\n return {\n jobId: next.jobId,\n lastEvaluatedAt: next.lastEvaluatedAt,\n lastTriggeredAt: next.lastTriggeredAt ?? undefined,\n nextRunAt: next.nextRunAt,\n version: next.version,\n };\n },\n async markTriggered(jobId, triggeredAt) {\n await ensureBootstrapped();\n const current = await readCursorRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next: CursorRow = {\n ...current,\n lastTriggeredAt: triggeredAt,\n version: current.version + 1,\n };\n await config.client.execute(\n `UPDATE ${quoted(config.dialect, names.cursors)} SET last_triggered_at = ?, version = ? WHERE job_id = ?`,\n [next.lastTriggeredAt, next.version, jobId],\n );\n return {\n jobId: next.jobId,\n lastEvaluatedAt: next.lastEvaluatedAt,\n lastTriggeredAt: next.lastTriggeredAt ?? undefined,\n nextRunAt: next.nextRunAt,\n version: next.version,\n };\n },\n async get(jobId) {\n await ensureBootstrapped();\n return readCursorRow(config.client, config.dialect, names, jobId);\n },\n async remove(jobId) {\n await ensureBootstrapped();\n await config.client.execute(\n `DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`,\n [jobId],\n );\n return true;\n },\n },\n executionStore: {\n async create(record) {\n await ensureBootstrapped();\n await config.client.execute(\n `INSERT INTO ${quoted(config.dialect, names.executions)} (execution_id, job_id, scheduled_for, triggered_at, status, queue_task_id, attempt_count, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n record.executionId,\n record.jobId,\n record.scheduledFor,\n record.triggeredAt,\n record.status,\n record.queueTaskId ?? null,\n record.attemptCount,\n record.lastError ?? null,\n ],\n );\n return record;\n },\n async update(executionId, patch) {\n const current = await readExecutionRow(\n config.client,\n config.dialect,\n names,\n executionId,\n );\n if (!current) {\n return null;\n }\n const next = {\n ...current,\n ...patch,\n };\n await config.client.execute(\n `UPDATE ${quoted(config.dialect, names.executions)} SET job_id = ?, scheduled_for = ?, triggered_at = ?, status = ?, queue_task_id = ?, attempt_count = ?, last_error = ? WHERE execution_id = ?`,\n [\n next.jobId,\n next.scheduledFor,\n next.triggeredAt,\n next.status,\n next.queueTaskId ?? null,\n next.attemptCount,\n next.lastError ?? null,\n executionId,\n ],\n );\n return next;\n },\n async listByJob(jobId) {\n await ensureBootstrapped();\n const rows = await config.client.query<ExecutionRow>(\n `SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError\n FROM ${quoted(config.dialect, names.executions)} WHERE job_id = ? ORDER BY triggered_at ASC`,\n [jobId],\n );\n return rows.map((row) => ({\n executionId: row.executionId,\n jobId: row.jobId,\n scheduledFor: row.scheduledFor,\n triggeredAt: row.triggeredAt,\n status: row.status as any,\n queueTaskId: row.queueTaskId ?? undefined,\n attemptCount: row.attemptCount,\n lastError: row.lastError ?? undefined,\n }));\n },\n },\n };\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerJobDefinition,\n SchedulerPersistenceConfig,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"../core/job-execution-store.js\";\nimport {\n createSchedulerPersistence,\n type SchedulerPersistence,\n} from \"../core/persistence.js\";\nimport { computeNextRunAt, parseSchedule } from \"../core/schedule-parser.js\";\nimport type { ScheduleCursorStore } from \"../core/schedule-cursor-store.js\";\nimport type { SchedulerJobStore } from \"../core/scheduler-job-store.js\";\nimport { dispatchJobExecution } from \"../core/scheduler-job-trigger.js\";\nimport { retryFailedExecutionById } from \"../core/scheduler-service-retry.js\";\nimport type {\n SchedulerDashboardItem,\n SchedulerDashboardService,\n} from \"./dashboard-types.js\";\n\nfunction nextRunForJob(\n job: {\n schedule: {\n scheduleType: \"once\" | \"cron\";\n expression: string;\n timezone: string;\n };\n },\n now = new Date(),\n): string {\n const parsed = parseSchedule(\n job.schedule.scheduleType,\n job.schedule.expression,\n job.schedule.timezone,\n );\n return computeNextRunAt(parsed, now);\n}\n\nasync function toDashboardItem(\n jobStore: SchedulerJobStore,\n cursorStore: ScheduleCursorStore,\n executionStore: JobExecutionStore,\n jobId: string,\n): Promise<SchedulerDashboardItem | null> {\n const job = await jobStore.get(jobId);\n if (!job) {\n return null;\n }\n const cursor = await cursorStore.get(jobId);\n const executions = await executionStore.listByJob(jobId);\n return {\n ...job,\n nextRunAt: cursor?.nextRunAt,\n executions,\n };\n}\n\nexport function createSchedulerDashboardFromStores(\n stores: SchedulerPersistence,\n options?: {\n queueService?: QueueService;\n handlerKeys?: readonly string[];\n },\n): SchedulerDashboardService {\n const { jobStore, cursorStore, executionStore } = stores;\n const handlerKeys = [...(options?.handlerKeys ?? [])];\n\n return {\n async getHealth() {\n const jobs = await jobStore.list();\n const executions = (\n await Promise.all(jobs.map((job) => executionStore.listByJob(job.jobId)))\n ).flat();\n const failed = executions.filter(\n (item) => item.status === \"failed\" || item.status === \"dead-letter\",\n );\n return {\n totalJobs: jobs.length,\n enabledJobs: jobs.filter((job) => job.enabled).length,\n pausedJobs: jobs.filter((job) => !job.enabled).length,\n failedExecutions: failed.length,\n };\n },\n\n async listItems() {\n const jobs = await jobStore.list();\n return Promise.all(\n jobs.map(async (job) => {\n const item = await toDashboardItem(\n jobStore,\n cursorStore,\n executionStore,\n job.jobId,\n );\n return item as SchedulerDashboardItem;\n }),\n );\n },\n\n async getJob(jobId) {\n return toDashboardItem(jobStore, cursorStore, executionStore, jobId);\n },\n\n async createJob<TConfig>(job: SchedulerJobDefinition<TConfig>) {\n parseSchedule(\n job.schedule.scheduleType,\n job.schedule.expression,\n job.schedule.timezone,\n );\n const jobId = `job_${randomUUID()}`;\n const created = await jobStore.create(jobId, job);\n const nextRunAt = nextRunForJob(created);\n await cursorStore.set(jobId, nextRunAt, new Date().toISOString());\n return { jobId };\n },\n\n async updateJob<TConfig>(\n jobId: string,\n patch: Partial<SchedulerJobDefinition<TConfig>>,\n ) {\n const updated = await jobStore.update(\n jobId,\n patch as Partial<SchedulerJobDefinition>,\n );\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n const nextRunAt = nextRunForJob(updated);\n await cursorStore.set(jobId, nextRunAt, new Date().toISOString());\n },\n\n async pauseJob(jobId) {\n const updated = await jobStore.setEnabled(jobId, false);\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async resumeJob(jobId) {\n const updated = await jobStore.setEnabled(jobId, true);\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async deleteJob(jobId) {\n const removed = await jobStore.remove(jobId);\n await cursorStore.remove(jobId);\n if (!removed) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async runJob(jobId) {\n const job = await jobStore.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n const scheduledFor = new Date().toISOString();\n await dispatchJobExecution({\n job,\n executionStore,\n queueService: options?.queueService,\n scheduledFor,\n attemptCount: 0,\n idempotencyKey: `scheduler:${job.jobId}:manual:${scheduledFor}`,\n });\n },\n\n async listExecutions(jobId) {\n const job = await jobStore.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n return executionStore.listByJob(jobId);\n },\n\n async retryExecution(executionId) {\n const jobs = await jobStore.list();\n const all = (\n await Promise.all(jobs.map((job) => executionStore.listByJob(job.jobId)))\n ).flat();\n await retryFailedExecutionById(\n all,\n jobs,\n executionStore,\n options?.queueService,\n executionId,\n );\n },\n\n listHandlerKeys() {\n return handlerKeys;\n },\n };\n}\n\nexport function createSchedulerDashboardFromPersistence(\n persistence: SchedulerPersistenceConfig,\n options?: {\n queueService?: QueueService;\n handlerKeys?: readonly string[];\n },\n): SchedulerDashboardService {\n return createSchedulerDashboardFromStores(\n createSchedulerPersistence(persistence),\n options,\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store-dashboard-Cyf_JILa.mjs","names":[],"sources":["../src/core/schedule-parser.ts","../src/core/scheduler-job-trigger.ts","../src/core/scheduler-service-retry.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"sourcesContent":["import type { ScheduleType } from \"../contracts/scheduler-types.js\";\n\nexport interface ParsedSchedule {\n scheduleType: ScheduleType;\n expression: string;\n timezone: string;\n}\n\nconst CRON_FIELD_PATTERN = /^\\*|\\*\\/\\d+|\\d+|\\d+-\\d+|\\d+(?:,\\d+)*$/;\n\nexport function assertValidTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: timezone }).format(new Date());\n } catch {\n throw new Error(`Invalid timezone: ${timezone}`);\n }\n}\n\nfunction assertValidCronExpression(expression: string): void {\n const fields = expression.trim().split(/\\s+/);\n if (fields.length !== 5) {\n throw new Error(\"Cron expression must contain 5 fields\");\n }\n\n for (const field of fields) {\n if (!CRON_FIELD_PATTERN.test(field)) {\n throw new Error(`Invalid cron field: ${field}`);\n }\n }\n}\n\nfunction assertValidOnceExpression(expression: string): void {\n const date = new Date(expression);\n if (Number.isNaN(date.getTime())) {\n throw new Error(\"Once schedule expression must be a valid ISO datetime\");\n }\n}\n\nexport function parseSchedule(\n scheduleType: ScheduleType,\n expression: string,\n timezone: string,\n): ParsedSchedule {\n assertValidTimezone(timezone);\n\n if (scheduleType === \"cron\") {\n assertValidCronExpression(expression);\n } else {\n assertValidOnceExpression(expression);\n }\n\n return {\n scheduleType,\n expression: expression.trim(),\n timezone,\n };\n}\n\nexport function computeNextRunAt(\n parsed: ParsedSchedule,\n now = new Date(),\n): string {\n if (parsed.scheduleType === \"once\") {\n return new Date(parsed.expression).toISOString();\n }\n\n const next = new Date(now);\n next.setUTCSeconds(0, 0);\n next.setUTCMinutes(next.getUTCMinutes() + 1);\n return next.toISOString();\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerExecutionRecord,\n SchedulerJobRecord,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\n\nexport async function dispatchJobExecution(input: {\n job: SchedulerJobRecord;\n executionStore: JobExecutionStore;\n queueService?: QueueService;\n scheduledFor: string;\n attemptCount?: number;\n idempotencyKey: string;\n}): Promise<SchedulerExecutionRecord> {\n const executionId = `exec_${randomUUID()}`;\n const created: SchedulerExecutionRecord = {\n executionId,\n jobId: input.job.jobId,\n scheduledFor: input.scheduledFor,\n triggeredAt: new Date().toISOString(),\n status: \"queued\",\n attemptCount: input.attemptCount ?? 0,\n };\n await input.executionStore.create(created);\n\n if (!input.queueService) {\n return created;\n }\n\n const accepted = await input.queueService.enqueue({\n idempotencyKey: input.idempotencyKey,\n payloadRef: {\n executionId,\n handlerKey: input.job.handlerKey,\n config: input.job.config,\n },\n });\n const withTask = await input.executionStore.update(executionId, {\n queueTaskId: accepted.taskId,\n });\n return withTask ?? { ...created, queueTaskId: accepted.taskId };\n}\n","import type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerExecutionRecord,\n SchedulerJobRecord,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\nimport { dispatchJobExecution } from \"./scheduler-job-trigger.js\";\n\nexport function findRetryableExecution(\n allExecutions: SchedulerExecutionRecord[],\n executionId: string,\n): SchedulerExecutionRecord {\n const target = allExecutions.find((item) => item.executionId === executionId);\n if (!target) {\n throw new Error(`Execution not found: ${executionId}`);\n }\n if (target.status !== \"failed\" && target.status !== \"dead-letter\") {\n throw new Error(\"Only failed or dead-letter executions can be retried\");\n }\n return target;\n}\n\nexport async function retryFailedExecutionById(\n allExecutions: SchedulerExecutionRecord[],\n jobs: readonly SchedulerJobRecord[],\n executionStore: JobExecutionStore,\n queueService: QueueService | undefined,\n executionId: string,\n): Promise<SchedulerExecutionRecord> {\n const target = findRetryableExecution(allExecutions, executionId);\n const job = jobs.find((item) => item.jobId === target.jobId);\n if (!job) {\n throw new Error(`Job not found: ${target.jobId}`);\n }\n\n return dispatchJobExecution({\n job,\n executionStore,\n queueService,\n scheduledFor: target.scheduledFor,\n attemptCount: target.attemptCount + 1,\n idempotencyKey: `scheduler:${job.jobId}:retry:${executionId}`,\n });\n}\n","import type { SqlClientAdapter, SqlDialectAdapter } from \"@azlib/persistence\";\n\nimport type { SchedulerPersistenceConfig } from \"../contracts/scheduler-types.js\";\nimport type { ScheduleCursorRecord } from \"./schedule-cursor-store.js\";\nimport type { JobExecutionStore } from \"./job-execution-store.js\";\nimport type { ScheduleCursorStore } from \"./schedule-cursor-store.js\";\nimport type { SchedulerJobStore } from \"./scheduler-job-store.js\";\n\ninterface JobRow {\n jobId: string;\n name: string;\n handlerKey: string;\n scheduleType: \"once\" | \"cron\";\n expression: string;\n timezone: string;\n overlapPolicy: string | null;\n missedRunPolicy: string | null;\n configJson: string;\n enabled: number;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface CursorRow {\n jobId: string;\n lastEvaluatedAt: string;\n lastTriggeredAt: string | null;\n nextRunAt: string;\n version: number;\n}\n\ninterface ExecutionRow {\n executionId: string;\n jobId: string;\n scheduledFor: string;\n triggeredAt: string;\n status: string;\n queueTaskId: string | null;\n attemptCount: number;\n lastError: string | null;\n}\n\nfunction namespaceFor(config: SchedulerPersistenceConfig): string {\n return config.namespace?.trim() || \"azlib\";\n}\n\nfunction tableNames(namespace: string) {\n return {\n jobs: `${namespace}__scheduler_jobs`,\n cursors: `${namespace}__scheduler_cursors`,\n executions: `${namespace}__scheduler_executions`,\n };\n}\n\nfunction quoted(dialect: SqlDialectAdapter, tableName: string): string {\n return dialect.quoteIdentifier(tableName);\n}\n\nasync function bootstrapSchedulerPersistence(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n): Promise<void> {\n await client.transaction(async (tx) => {\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.jobs)} (\n job_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n handler_key TEXT NOT NULL,\n schedule_type TEXT NOT NULL,\n expression TEXT NOT NULL,\n timezone TEXT NOT NULL,\n overlap_policy TEXT,\n missed_run_policy TEXT,\n config_json TEXT NOT NULL,\n enabled INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n )`);\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.cursors)} (\n job_id TEXT PRIMARY KEY,\n last_evaluated_at TEXT NOT NULL,\n last_triggered_at TEXT,\n next_run_at TEXT NOT NULL,\n version INTEGER NOT NULL\n )`);\n await tx.execute(`CREATE TABLE IF NOT EXISTS ${quoted(dialect, names.executions)} (\n execution_id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL,\n scheduled_for TEXT NOT NULL,\n triggered_at TEXT NOT NULL,\n status TEXT NOT NULL,\n queue_task_id TEXT,\n attempt_count INTEGER NOT NULL,\n last_error TEXT\n )`);\n });\n}\n\nfunction toJobRow<TConfig>(\n jobId: string,\n definition: import(\"../contracts/scheduler-types.js\").SchedulerJobDefinition<TConfig>,\n): JobRow {\n const now = new Date().toISOString();\n return {\n jobId,\n name: definition.name,\n handlerKey: definition.handlerKey,\n scheduleType: definition.schedule.scheduleType,\n expression: definition.schedule.expression,\n timezone: definition.schedule.timezone,\n overlapPolicy: definition.schedule.overlapPolicy ?? null,\n missedRunPolicy: definition.schedule.missedRunPolicy ?? null,\n configJson: JSON.stringify(definition.config),\n enabled: (definition.enabled ?? true) ? 1 : 0,\n createdAt: now,\n updatedAt: now,\n };\n}\n\nfunction rowToJobRecord<TConfig>(\n row: JobRow,\n): import(\"../contracts/scheduler-types.js\").SchedulerJobRecord<TConfig> {\n return {\n jobId: row.jobId,\n name: row.name,\n handlerKey: row.handlerKey,\n schedule: {\n scheduleType: row.scheduleType,\n expression: row.expression,\n timezone: row.timezone,\n overlapPolicy:\n row.overlapPolicy === null ? undefined : (row.overlapPolicy as any),\n missedRunPolicy:\n row.missedRunPolicy === null ? undefined : (row.missedRunPolicy as any),\n },\n config: JSON.parse(row.configJson) as TConfig,\n enabled: Boolean(row.enabled),\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nasync function readJobRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n jobId: string,\n) {\n const rows = await client.query<JobRow>(\n `SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt\n FROM ${quoted(dialect, names.jobs)} WHERE job_id = ? LIMIT 1`,\n [jobId],\n );\n return rows[0] ? rowToJobRecord(rows[0]) : null;\n}\n\nasync function readCursorRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n jobId: string,\n): Promise<ScheduleCursorRecord | null> {\n const rows = await client.query<CursorRow>(\n `SELECT job_id AS jobId, last_evaluated_at AS lastEvaluatedAt, last_triggered_at AS lastTriggeredAt, next_run_at AS nextRunAt, version\n FROM ${quoted(dialect, names.cursors)} WHERE job_id = ? LIMIT 1`,\n [jobId],\n );\n const row = rows[0];\n return row\n ? {\n jobId: row.jobId,\n lastEvaluatedAt: row.lastEvaluatedAt,\n lastTriggeredAt: row.lastTriggeredAt ?? undefined,\n nextRunAt: row.nextRunAt,\n version: row.version,\n }\n : null;\n}\n\nasync function readExecutionRow(\n client: SqlClientAdapter,\n dialect: SqlDialectAdapter,\n names: ReturnType<typeof tableNames>,\n executionId: string,\n) {\n const rows = await client.query<ExecutionRow>(\n `SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError\n FROM ${quoted(dialect, names.executions)} WHERE execution_id = ? LIMIT 1`,\n [executionId],\n );\n const row = rows[0];\n return row\n ? {\n executionId: row.executionId,\n jobId: row.jobId,\n scheduledFor: row.scheduledFor,\n triggeredAt: row.triggeredAt,\n status: row.status as any,\n queueTaskId: row.queueTaskId ?? undefined,\n attemptCount: row.attemptCount,\n lastError: row.lastError ?? undefined,\n }\n : null;\n}\n\nexport interface SchedulerPersistence {\n jobStore: SchedulerJobStore;\n cursorStore: ScheduleCursorStore;\n executionStore: JobExecutionStore;\n}\n\nexport function createSchedulerPersistence(\n config: SchedulerPersistenceConfig,\n): SchedulerPersistence {\n const namespace = namespaceFor(config);\n const names = tableNames(namespace);\n let bootstrapPromise: Promise<void> | null = null;\n\n async function ensureBootstrapped() {\n if (!bootstrapPromise) {\n bootstrapPromise = bootstrapSchedulerPersistence(\n config.client,\n config.dialect,\n names,\n );\n }\n await bootstrapPromise;\n }\n\n return {\n jobStore: {\n async create(jobId, definition) {\n await ensureBootstrapped();\n const row = toJobRow(jobId, definition);\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n row.jobId,\n row.name,\n row.handlerKey,\n row.scheduleType,\n row.expression,\n row.timezone,\n row.overlapPolicy,\n row.missedRunPolicy,\n row.configJson,\n row.enabled,\n row.createdAt,\n row.updatedAt,\n ],\n );\n });\n return rowToJobRecord(row);\n },\n async update(jobId, patch) {\n const current = await readJobRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next: import(\"../contracts/scheduler-types.js\").SchedulerJobRecord =\n {\n ...current,\n ...patch,\n schedule: {\n ...current.schedule,\n ...patch.schedule,\n },\n updatedAt: new Date().toISOString(),\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.name,\n next.handlerKey,\n next.schedule.scheduleType,\n next.schedule.expression,\n next.schedule.timezone,\n next.schedule.overlapPolicy ?? null,\n next.schedule.missedRunPolicy ?? null,\n JSON.stringify(next.config),\n next.enabled ? 1 : 0,\n next.createdAt,\n next.updatedAt,\n ],\n );\n });\n return next;\n },\n async setEnabled(jobId, enabled) {\n const current = await readJobRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next = {\n ...current,\n enabled,\n updatedAt: new Date().toISOString(),\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.jobs)} (job_id, name, handler_key, schedule_type, expression, timezone, overlap_policy, missed_run_policy, config_json, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.name,\n next.handlerKey,\n next.schedule.scheduleType,\n next.schedule.expression,\n next.schedule.timezone,\n next.schedule.overlapPolicy ?? null,\n next.schedule.missedRunPolicy ?? null,\n JSON.stringify(next.config),\n next.enabled ? 1 : 0,\n next.createdAt,\n next.updatedAt,\n ],\n );\n });\n return next;\n },\n async get(jobId) {\n await ensureBootstrapped();\n return readJobRow(config.client, config.dialect, names, jobId);\n },\n async list() {\n await ensureBootstrapped();\n const rows = await config.client.query<JobRow>(\n `SELECT job_id AS jobId, name, handler_key AS handlerKey, schedule_type AS scheduleType, expression, timezone, overlap_policy AS overlapPolicy, missed_run_policy AS missedRunPolicy, config_json AS configJson, enabled, created_at AS createdAt, updated_at AS updatedAt\n FROM ${quoted(config.dialect, names.jobs)} ORDER BY created_at ASC`,\n [],\n );\n return rows.map(rowToJobRecord);\n },\n async remove(jobId) {\n await ensureBootstrapped();\n await config.client.execute(\n `DELETE FROM ${quoted(config.dialect, names.jobs)} WHERE job_id = ?`,\n [jobId],\n );\n return true;\n },\n },\n cursorStore: {\n async set(jobId, nextRunAt, now) {\n await ensureBootstrapped();\n const current = await readCursorRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n const next: CursorRow = {\n jobId,\n lastEvaluatedAt: now,\n lastTriggeredAt: current?.lastTriggeredAt ?? null,\n nextRunAt,\n version: (current?.version ?? 0) + 1,\n };\n await config.client.transaction(async (tx) => {\n await tx.execute(\n `DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`,\n [jobId],\n );\n await tx.execute(\n `INSERT INTO ${quoted(config.dialect, names.cursors)} (job_id, last_evaluated_at, last_triggered_at, next_run_at, version) VALUES (?, ?, ?, ?, ?)`,\n [\n next.jobId,\n next.lastEvaluatedAt,\n next.lastTriggeredAt,\n next.nextRunAt,\n next.version,\n ],\n );\n });\n return {\n jobId: next.jobId,\n lastEvaluatedAt: next.lastEvaluatedAt,\n lastTriggeredAt: next.lastTriggeredAt ?? undefined,\n nextRunAt: next.nextRunAt,\n version: next.version,\n };\n },\n async markTriggered(jobId, triggeredAt) {\n await ensureBootstrapped();\n const current = await readCursorRow(\n config.client,\n config.dialect,\n names,\n jobId,\n );\n if (!current) {\n return null;\n }\n const next: CursorRow = {\n ...current,\n lastTriggeredAt: triggeredAt,\n version: current.version + 1,\n };\n await config.client.execute(\n `UPDATE ${quoted(config.dialect, names.cursors)} SET last_triggered_at = ?, version = ? WHERE job_id = ?`,\n [next.lastTriggeredAt, next.version, jobId],\n );\n return {\n jobId: next.jobId,\n lastEvaluatedAt: next.lastEvaluatedAt,\n lastTriggeredAt: next.lastTriggeredAt ?? undefined,\n nextRunAt: next.nextRunAt,\n version: next.version,\n };\n },\n async get(jobId) {\n await ensureBootstrapped();\n return readCursorRow(config.client, config.dialect, names, jobId);\n },\n async remove(jobId) {\n await ensureBootstrapped();\n await config.client.execute(\n `DELETE FROM ${quoted(config.dialect, names.cursors)} WHERE job_id = ?`,\n [jobId],\n );\n return true;\n },\n },\n executionStore: {\n async create(record) {\n await ensureBootstrapped();\n await config.client.execute(\n `INSERT INTO ${quoted(config.dialect, names.executions)} (execution_id, job_id, scheduled_for, triggered_at, status, queue_task_id, attempt_count, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n record.executionId,\n record.jobId,\n record.scheduledFor,\n record.triggeredAt,\n record.status,\n record.queueTaskId ?? null,\n record.attemptCount,\n record.lastError ?? null,\n ],\n );\n return record;\n },\n async update(executionId, patch) {\n const current = await readExecutionRow(\n config.client,\n config.dialect,\n names,\n executionId,\n );\n if (!current) {\n return null;\n }\n const next = {\n ...current,\n ...patch,\n };\n await config.client.execute(\n `UPDATE ${quoted(config.dialect, names.executions)} SET job_id = ?, scheduled_for = ?, triggered_at = ?, status = ?, queue_task_id = ?, attempt_count = ?, last_error = ? WHERE execution_id = ?`,\n [\n next.jobId,\n next.scheduledFor,\n next.triggeredAt,\n next.status,\n next.queueTaskId ?? null,\n next.attemptCount,\n next.lastError ?? null,\n executionId,\n ],\n );\n return next;\n },\n async listByJob(jobId) {\n await ensureBootstrapped();\n const rows = await config.client.query<ExecutionRow>(\n `SELECT execution_id AS executionId, job_id AS jobId, scheduled_for AS scheduledFor, triggered_at AS triggeredAt, status, queue_task_id AS queueTaskId, attempt_count AS attemptCount, last_error AS lastError\n FROM ${quoted(config.dialect, names.executions)} WHERE job_id = ? ORDER BY triggered_at ASC`,\n [jobId],\n );\n return rows.map((row) => ({\n executionId: row.executionId,\n jobId: row.jobId,\n scheduledFor: row.scheduledFor,\n triggeredAt: row.triggeredAt,\n status: row.status as any,\n queueTaskId: row.queueTaskId ?? undefined,\n attemptCount: row.attemptCount,\n lastError: row.lastError ?? undefined,\n }));\n },\n },\n };\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { QueueService } from \"@azlib/queue\";\n\nimport type {\n SchedulerJobDefinition,\n SchedulerPersistenceConfig,\n} from \"../contracts/scheduler-types.js\";\nimport type { JobExecutionStore } from \"../core/job-execution-store.js\";\nimport {\n createSchedulerPersistence,\n type SchedulerPersistence,\n} from \"../core/persistence.js\";\nimport { computeNextRunAt, parseSchedule } from \"../core/schedule-parser.js\";\nimport type { ScheduleCursorStore } from \"../core/schedule-cursor-store.js\";\nimport type { SchedulerJobStore } from \"../core/scheduler-job-store.js\";\nimport { dispatchJobExecution } from \"../core/scheduler-job-trigger.js\";\nimport { retryFailedExecutionById } from \"../core/scheduler-service-retry.js\";\nimport type {\n SchedulerDashboardItem,\n SchedulerDashboardService,\n} from \"./dashboard-types.js\";\n\nfunction nextRunForJob(\n job: {\n schedule: {\n scheduleType: \"once\" | \"cron\";\n expression: string;\n timezone: string;\n };\n },\n now = new Date(),\n): string {\n const parsed = parseSchedule(\n job.schedule.scheduleType,\n job.schedule.expression,\n job.schedule.timezone,\n );\n return computeNextRunAt(parsed, now);\n}\n\nasync function toDashboardItem(\n jobStore: SchedulerJobStore,\n cursorStore: ScheduleCursorStore,\n executionStore: JobExecutionStore,\n jobId: string,\n): Promise<SchedulerDashboardItem | null> {\n const job = await jobStore.get(jobId);\n if (!job) {\n return null;\n }\n const cursor = await cursorStore.get(jobId);\n const executions = await executionStore.listByJob(jobId);\n return {\n ...job,\n nextRunAt: cursor?.nextRunAt,\n executions,\n };\n}\n\nexport function createSchedulerDashboardFromStores(\n stores: SchedulerPersistence,\n options?: {\n queueService?: QueueService;\n handlerKeys?: readonly string[];\n },\n): SchedulerDashboardService {\n const { jobStore, cursorStore, executionStore } = stores;\n const handlerKeys = [...(options?.handlerKeys ?? [])];\n\n return {\n async getHealth() {\n const jobs = await jobStore.list();\n const executions = (\n await Promise.all(\n jobs.map((job) => executionStore.listByJob(job.jobId)),\n )\n ).flat();\n const failed = executions.filter(\n (item) => item.status === \"failed\" || item.status === \"dead-letter\",\n );\n return {\n totalJobs: jobs.length,\n enabledJobs: jobs.filter((job) => job.enabled).length,\n pausedJobs: jobs.filter((job) => !job.enabled).length,\n failedExecutions: failed.length,\n };\n },\n\n async listItems() {\n const jobs = await jobStore.list();\n return Promise.all(\n jobs.map(async (job) => {\n const item = await toDashboardItem(\n jobStore,\n cursorStore,\n executionStore,\n job.jobId,\n );\n return item as SchedulerDashboardItem;\n }),\n );\n },\n\n async getJob(jobId) {\n return toDashboardItem(jobStore, cursorStore, executionStore, jobId);\n },\n\n async createJob<TConfig>(job: SchedulerJobDefinition<TConfig>) {\n parseSchedule(\n job.schedule.scheduleType,\n job.schedule.expression,\n job.schedule.timezone,\n );\n const jobId = `job_${randomUUID()}`;\n const created = await jobStore.create(jobId, job);\n const nextRunAt = nextRunForJob(created);\n await cursorStore.set(jobId, nextRunAt, new Date().toISOString());\n return { jobId };\n },\n\n async updateJob<TConfig>(\n jobId: string,\n patch: Partial<SchedulerJobDefinition<TConfig>>,\n ) {\n const updated = await jobStore.update(\n jobId,\n patch as Partial<SchedulerJobDefinition>,\n );\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n const nextRunAt = nextRunForJob(updated);\n await cursorStore.set(jobId, nextRunAt, new Date().toISOString());\n },\n\n async pauseJob(jobId) {\n const updated = await jobStore.setEnabled(jobId, false);\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async resumeJob(jobId) {\n const updated = await jobStore.setEnabled(jobId, true);\n if (!updated) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async deleteJob(jobId) {\n const removed = await jobStore.remove(jobId);\n await cursorStore.remove(jobId);\n if (!removed) {\n throw new Error(`Job not found: ${jobId}`);\n }\n },\n\n async runJob(jobId) {\n const job = await jobStore.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n const scheduledFor = new Date().toISOString();\n await dispatchJobExecution({\n job,\n executionStore,\n queueService: options?.queueService,\n scheduledFor,\n attemptCount: 0,\n idempotencyKey: `scheduler:${job.jobId}:manual:${scheduledFor}`,\n });\n },\n\n async listExecutions(jobId) {\n const job = await jobStore.get(jobId);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n return executionStore.listByJob(jobId);\n },\n\n async retryExecution(executionId) {\n const jobs = await jobStore.list();\n const all = (\n await Promise.all(\n jobs.map((job) => executionStore.listByJob(job.jobId)),\n )\n ).flat();\n await retryFailedExecutionById(\n all,\n jobs,\n executionStore,\n options?.queueService,\n executionId,\n );\n },\n\n listHandlerKeys() {\n return handlerKeys;\n },\n };\n}\n\nexport function createSchedulerDashboardFromPersistence(\n persistence: SchedulerPersistenceConfig,\n options?: {\n queueService?: QueueService;\n handlerKeys?: readonly string[];\n },\n): SchedulerDashboardService {\n return createSchedulerDashboardFromStores(\n createSchedulerPersistence(persistence),\n options,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-dashboard-D-yIxXxL.d.mts","names":[],"sources":["../src/dashboard/queue-dashboard-service.ts","../src/core/job-execution-store.ts","../src/core/schedule-cursor-store.ts","../src/core/scheduler-job-store.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"mappings":";;;iBAKgB,gCACd,WAAW,kBACX;EAAY,WAAW,KAAK;IAC3B;;;UCNc;EACf,OAAO,QAAQ,2BAA2B,QAAQ;EAClD,OACE,qBACA,OAAO,QAAQ,KAAK,4CACnB,QAAQ;EACX,UAAU,gBAAgB,QAAQ;;;;UCRnB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf,IACE,eACA,mBACA,cACC,QAAQ;EACX,cACE,eACA,sBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,OAAO,gBAAgB;;;;UCdR;EACf,OACE,eACA,YAAY,yBACX,QAAQ;EACX,OACE,eACA,OAAO,QAAQ,0BACd,QAAQ;EACX,WACE,eACA,mBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,QAAQ,QAAQ;EAChB,OAAO,gBAAgB;;;;UCyLR;EACf,UAAU;EACV,aAAa;EACb,gBAAgB;;iBAGF,2BACd,QAAQ,6BACP;;;iBCzJa,mCACd,QAAQ,sBACR;EACE,eAAe;EACf;IAED;
|
|
1
|
+
{"version":3,"file":"store-dashboard-D-yIxXxL.d.mts","names":[],"sources":["../src/dashboard/queue-dashboard-service.ts","../src/core/job-execution-store.ts","../src/core/schedule-cursor-store.ts","../src/core/scheduler-job-store.ts","../src/core/persistence.ts","../src/dashboard/store-dashboard.ts"],"mappings":";;;iBAKgB,gCACd,WAAW,kBACX;EAAY,WAAW,KAAK;IAC3B;;;UCNc;EACf,OAAO,QAAQ,2BAA2B,QAAQ;EAClD,OACE,qBACA,OAAO,QAAQ,KAAK,4CACnB,QAAQ;EACX,UAAU,gBAAgB,QAAQ;;;;UCRnB;EACf;EACA;EACA;EACA;EACA;;UAGe;EACf,IACE,eACA,mBACA,cACC,QAAQ;EACX,cACE,eACA,sBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,OAAO,gBAAgB;;;;UCdR;EACf,OACE,eACA,YAAY,yBACX,QAAQ;EACX,OACE,eACA,OAAO,QAAQ,0BACd,QAAQ;EACX,WACE,eACA,mBACC,QAAQ;EACX,IAAI,gBAAgB,QAAQ;EAC5B,QAAQ,QAAQ;EAChB,OAAO,gBAAgB;;;;UCyLR;EACf,UAAU;EACV,aAAa;EACb,gBAAgB;;iBAGF,2BACd,QAAQ,6BACP;;;iBCzJa,mCACd,QAAQ,sBACR;EACE,eAAe;EACf;IAED;iBA0Ia,wCACd,aAAa,4BACb;EACE,eAAe;EACf;IAED"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azlib/scheduler",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@azlib/cache": "0.2.2",
|
|
34
34
|
"@azlib/logger": "0.2.2",
|
|
35
|
-
"@azlib/std": "0.5.0",
|
|
36
35
|
"@azlib/persistence": "0.5.0",
|
|
37
|
-
"@azlib/queue": "1.0.5"
|
|
36
|
+
"@azlib/queue": "1.0.5",
|
|
37
|
+
"@azlib/std": "0.5.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"better-sqlite3": ">=9.0.0",
|