@dudousxd/nestjs-durable-dashboard 0.22.2 → 0.22.4
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/LICENSE +21 -0
- package/dist/client/durable-client.d.ts +3 -3
- package/dist/client/durable-client.d.ts.map +1 -1
- package/dist/client/durable-client.js.map +1 -1
- package/dist/client/group-subprocesses.d.ts +5 -5
- package/dist/client/group-subprocesses.d.ts.map +1 -1
- package/dist/server/index.cjs +695 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +160 -0
- package/dist/server/index.d.ts +160 -5
- package/dist/server/index.js +662 -4
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/{index-B0Nn98HA.js → index-D8irnyfw.js} +2 -2
- package/dist/spa/index.html +1 -1
- package/package.json +14 -7
- package/dist/server/attr-filter.d.ts +0 -8
- package/dist/server/attr-filter.d.ts.map +0 -1
- package/dist/server/attr-filter.js +0 -30
- package/dist/server/attr-filter.js.map +0 -1
- package/dist/server/dashboard.service.d.ts +0 -70
- package/dist/server/dashboard.service.d.ts.map +0 -1
- package/dist/server/dashboard.service.js +0 -157
- package/dist/server/dashboard.service.js.map +0 -1
- package/dist/server/durable-api.controller.d.ts +0 -44
- package/dist/server/durable-api.controller.d.ts.map +0 -1
- package/dist/server/durable-api.controller.js +0 -197
- package/dist/server/durable-api.controller.js.map +0 -1
- package/dist/server/durable-dashboard.module.d.ts +0 -26
- package/dist/server/durable-dashboard.module.d.ts.map +0 -1
- package/dist/server/durable-dashboard.module.js +0 -60
- package/dist/server/durable-dashboard.module.js.map +0 -1
- package/dist/server/durable-ui.controller.d.ts +0 -19
- package/dist/server/durable-ui.controller.d.ts.map +0 -1
- package/dist/server/durable-ui.controller.js +0 -99
- package/dist/server/durable-ui.controller.js.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../../src/server/dashboard.service.ts","../../src/server/durable-api.controller.ts","../../src/server/attr-filter.ts","../../src/server/durable-dashboard.module.ts","../../src/server/durable-ui.controller.ts"],"sourcesContent":["import {\n type EngineEvent,\n type GroupHealth,\n type MetricsCollector,\n type RunQuery,\n type RunResult,\n STATE_STORE_CANONICAL,\n type StateStore,\n type StepCheckpoint,\n type UpdateResult,\n WorkflowEngine,\n type WorkflowRun,\n collectMetrics,\n} from '@dudousxd/nestjs-durable-core';\nimport { Inject, Injectable } from '@nestjs/common';\nimport { Observable } from 'rxjs';\n\nexport interface RunDetail {\n run: WorkflowRun;\n /** Steps in execution order — the end-to-end timeline (local + remote). */\n timeline: StepCheckpoint[];\n /** Ids of runs this run spawned (ctx.child / ctx.startChild) — the parent→children tree. */\n children: string[];\n}\n\n/** Read-model and actions backing the control-plane UI. */\n@Injectable()\nexport class DashboardService {\n /** Prometheus counters accumulated from engine events since boot (per process). */\n private readonly metricsCollector: MetricsCollector;\n\n constructor(\n @Inject(STATE_STORE_CANONICAL) private readonly store: StateStore,\n private readonly engine: WorkflowEngine,\n ) {\n this.metricsCollector = collectMetrics(this.engine);\n }\n\n /**\n * Prometheus-text metrics for a `/metrics` scrape: the event-counters (runs/steps by outcome,\n * per-workflow counts) plus live **backlog gauges** queried at scrape time — `durable_pending_runs`\n * (the dispatch backlog: the key health signal of the dispatch model) and `durable_dead_runs` (DLQ\n * size). Capped per status so the scrape can't load an unbounded result set.\n */\n async metrics(): Promise<string> {\n const cap = 10_000;\n const [pending, running, dead] = await Promise.all([\n this.store.listRuns({ status: 'pending', limit: cap }),\n this.store.listRuns({ status: 'running', limit: cap }),\n this.store.listRuns({ status: 'dead', limit: cap }),\n ]);\n const gauges = [\n '# TYPE durable_pending_runs gauge',\n `durable_pending_runs ${pending.length}`,\n '# TYPE durable_running_runs gauge',\n `durable_running_runs ${running.length}`,\n '# TYPE durable_dead_runs gauge',\n `durable_dead_runs ${dead.length}`,\n ];\n // Per-group worker health: backlog vs. live workers. `depth>0 && live==0` is the alert (work\n // piling up with no consumer) — expressible as a Prometheus rule on these two series.\n const health = await this.engine.workerHealth();\n if (health.length > 0) {\n gauges.push('# TYPE durable_group_queue_depth gauge');\n for (const h of health) {\n gauges.push(`durable_group_queue_depth{group=\"${h.group}\"} ${h.depth}`);\n }\n gauges.push('# TYPE durable_group_live_workers gauge');\n for (const h of health) {\n gauges.push(`durable_group_live_workers{group=\"${h.group}\"} ${h.liveWorkers.length}`);\n }\n }\n return `${this.metricsCollector.prometheus()}${gauges.join('\\n')}\\n`;\n }\n\n listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n return this.store.listRuns(query);\n }\n\n /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. The\n * alert state a row turns red on is `depth > 0 && liveWorkers.length === 0`. */\n workerHealth(): Promise<GroupHealth[]> {\n return this.engine.workerHealth();\n }\n\n async getRunDetail(runId: string): Promise<RunDetail | null> {\n const run = await this.store.getRun(runId);\n if (!run) return null;\n const [timeline, children] = await Promise.all([\n this.store.listCheckpoints(runId),\n this.engine.getRunChildren(runId), // canonical parent→children edge (shared with cancel cascade)\n ]);\n return { run, timeline, children };\n }\n\n /** Fix-and-replay a dead/failed run with a corrected input — a fresh linked run. Returns its id. */\n retryWithInput(runId: string, input: unknown): Promise<{ runId: string } | null> {\n return this.engine.retryWithInput(runId, input);\n }\n\n /** Re-run a failed/incomplete run; completed steps replay from their checkpoints. */\n retry(runId: string): Promise<RunResult | null> {\n // Re-enqueue (dispatch model) instead of resuming inline, so the request can't hang on workflow\n // execution — a worker picks the run up and replays it.\n return this.engine.requeue(runId);\n }\n\n cancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null> {\n return this.engine.cancel(runId, opts);\n }\n\n /**\n * Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow) — e.g. \"retry\n * every `dead` run tagged `type:mel`\". Capped at 500 runs per call; runs that can't be acted on\n * (already terminal) are skipped. Returns how many matched and how many the action applied to.\n */\n async bulk(\n action: 'retry' | 'cancel',\n filter: Pick<RunQuery, 'status' | 'tag' | 'workflow' | 'attributes'>,\n opts?: { compensate?: boolean },\n ): Promise<{ matched: number; applied: number }> {\n const runs = await this.store.listRuns({ ...filter, limit: 500 });\n let applied = 0;\n for (const r of runs) {\n try {\n if (action === 'retry') await this.engine.requeue(r.id);\n else await this.engine.cancel(r.id, opts);\n applied += 1;\n } catch {\n // Skip a run that can't take the action (e.g. already terminal) — keep going.\n }\n }\n return { matched: runs.length, applied };\n }\n\n /** Resume a run paused at a `ctx.breakpoint` (the \"continue\" button). */\n continue(runId: string): Promise<RunResult | null> {\n return this.engine.continue(runId);\n }\n\n /**\n * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the\n * waiting run is parked on. Returns the run result, or `null` if no run waits on that token (a\n * stale/duplicate callback) — a safe no-op the controller maps to 404.\n */\n deliverWebhook(token: string, body: unknown): Promise<RunResult | null> {\n return this.engine.signal(token, body);\n }\n\n /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). */\n getEvent(runId: string, key: string): Promise<unknown> {\n return this.engine.getEvent(runId, key);\n }\n\n /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). */\n update(runId: string, name: string, arg: unknown): Promise<UpdateResult> {\n return this.engine.update(runId, name, arg);\n }\n\n /**\n * Live stream of a run's lifecycle events for SSE. Backed by `engine.subscribe`, which — when the\n * transport has a control plane — receives events from EVERY instance, so a dashboard-only pod\n * tails a run executing on a worker pod. Without a control plane it only sees same-process events.\n */\n streamRun(runId: string): Observable<{ data: EngineEvent }> {\n return new Observable<{ data: EngineEvent }>((subscriber) => {\n const off = this.engine.subscribe((event) => {\n if (event.runId === runId) subscriber.next({ data: event });\n });\n return () => off();\n });\n }\n}\n","import type { RunStatus } from '@dudousxd/nestjs-durable-core';\nimport {\n Body,\n Controller,\n Get,\n Header,\n NotFoundException,\n Param,\n Post,\n Query,\n Sse,\n} from '@nestjs/common';\nimport { parseAttrFilters } from './attr-filter.js';\nimport { DashboardService } from './dashboard.service.js';\n\n/** JSON API consumed by the control-plane SPA. Mounted at `apiBasePath` (set by RouterModule). */\n@Controller()\nexport class DurableApiController {\n constructor(private readonly dashboard: DashboardService) {}\n\n @Get('runs')\n runs(\n @Query('status') status?: RunStatus,\n @Query('workflow') workflow?: string,\n @Query('tag') tag?: string,\n @Query('attr') attr?: string | string[],\n ) {\n return this.dashboard.listRuns({ status, workflow, tag, attributes: parseAttrFilters(attr) });\n }\n\n /** Prometheus-text metrics (runs/steps by outcome, per-workflow counts) for a scrape. */\n @Get('metrics')\n @Header('Content-Type', 'text/plain; version=0.0.4')\n metrics() {\n return this.dashboard.metrics();\n }\n\n /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. */\n @Get('workers')\n workers() {\n return this.dashboard.workerHealth();\n }\n\n @Get('runs/:id')\n async run(@Param('id') id: string) {\n const detail = await this.dashboard.getRunDetail(id);\n if (!detail) throw new NotFoundException(`run ${id} not found`);\n return detail;\n }\n\n /** Server-Sent Events stream of a run's live lifecycle events — the dashboard tails it instead\n * of polling. Cross-pod when the transport has a control plane (see DashboardService.streamRun). */\n @Sse('runs/:id/stream')\n stream(@Param('id') id: string) {\n return this.dashboard.streamRun(id);\n }\n\n @Post('runs/:id/retry')\n retry(@Param('id') id: string) {\n return this.dashboard.retry(id);\n }\n\n /** Fix-and-replay: re-run a dead/failed run with a corrected input (a fresh linked run). */\n @Post('runs/:id/retry-with-input')\n retryWithInput(@Param('id') id: string, @Body() body: { input: unknown }) {\n return this.dashboard.retryWithInput(id, body?.input);\n }\n\n /** Bulk retry/cancel every run matching a filter (status / tag / workflow). */\n @Post('bulk/:action')\n bulk(\n @Param('action') action: 'retry' | 'cancel',\n @Query('status') status?: RunStatus,\n @Query('tag') tag?: string,\n @Query('workflow') workflow?: string,\n @Query('attr') attr?: string | string[],\n @Query('compensate') compensate?: string,\n ) {\n return this.dashboard.bulk(\n action === 'cancel' ? 'cancel' : 'retry',\n { status, tag, workflow, attributes: parseAttrFilters(attr) },\n { compensate: compensate === 'true' },\n );\n }\n\n @Post('runs/:id/cancel')\n async cancel(@Param('id') id: string, @Query('compensate') compensate?: string) {\n const result = await this.dashboard.cancel(id, { compensate: compensate === 'true' });\n if (!result) throw new NotFoundException(`run ${id} not found`);\n return result;\n }\n\n @Post('runs/:id/continue')\n async continue(@Param('id') id: string) {\n const result = await this.dashboard.continue(id);\n if (!result) throw new NotFoundException(`run ${id} is not paused at a breakpoint`);\n return result;\n }\n\n /**\n * Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),\n * and the body resumes the waiting run. NOTE: this is reachable by external systems — protect it\n * by treating the token as a secret (it embeds runId:seq) and/or fronting it with signature\n * verification in your own middleware.\n */\n @Post('webhooks/:token')\n async webhook(@Param('token') token: string, @Body() body: unknown) {\n const result = await this.dashboard.deliverWebhook(token, body);\n if (!result) throw new NotFoundException(`no run is waiting on webhook ${token}`);\n return result;\n }\n\n /** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */\n @Get('runs/:id/events/:key')\n event(@Param('id') id: string, @Param('key') key: string) {\n return this.dashboard.getEvent(id, key);\n }\n\n /** Deliver a validated `ctx.onUpdate` to a run; the body is the update argument. */\n @Post('runs/:id/updates/:name')\n update(@Param('id') id: string, @Param('name') name: string, @Body() body: unknown) {\n return this.dashboard.update(id, name, body);\n }\n}\n","import type { AttributeFilter, AttributeOp } from '@dudousxd/nestjs-durable-core';\n\nconst ATTR_OPS = new Set<AttributeOp>(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']);\n\n/** Coerce a query-string value: `true`/`false` → boolean, numeric → number, else the raw string. */\nfunction coerce(v: string): string | number | boolean {\n if (v === 'true') return true;\n if (v === 'false') return false;\n if (v !== '' && !Number.isNaN(Number(v))) return Number(v);\n return v;\n}\n\n/**\n * Parse `attr=key:op:value` query params (repeatable, ANDed) into {@link AttributeFilter}s — e.g.\n * `?attr=amount:gte:200&attr=tier:eq:pro`. `op` must be a known operator; a colon in the value is\n * preserved (only the first two colons delimit). Malformed entries are skipped.\n */\nexport function parseAttrFilters(attr?: string | string[]): AttributeFilter[] | undefined {\n if (!attr) return undefined;\n const raw = Array.isArray(attr) ? attr : [attr];\n const filters: AttributeFilter[] = [];\n for (const entry of raw) {\n const [key, op, ...rest] = entry.split(':');\n if (!key || !ATTR_OPS.has(op as AttributeOp) || rest.length === 0) continue;\n filters.push({ key, op: op as AttributeOp, value: coerce(rest.join(':')) });\n }\n return filters.length ? filters : undefined;\n}\n","import { type DynamicModule, Module } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { DashboardService } from './dashboard.service.js';\nimport { DurableApiController } from './durable-api.controller.js';\nimport {\n DASHBOARD_API_PATH,\n DASHBOARD_BASE_PATH,\n DurableUiController,\n} from './durable-ui.controller.js';\n\nexport interface DurableDashboardOptions {\n /**\n * Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an\n * `/api` prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/durable` — so the API inherits the app's auth/proxy rules\n * while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/** Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. */\n@Module({\n controllers: [DurableApiController],\n providers: [DashboardService],\n exports: [DashboardService],\n})\nexport class DurableApiModule {}\n\n/**\n * Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`\n * (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` alongside\n * `DurableModule` (global), so it resolves the engine and store. Front the routes with a guard.\n */\n@Module({})\nexport class DurableDashboardModule {\n static forRoot(options: DurableDashboardOptions = {}): DynamicModule {\n const basePath = normalize(options.basePath ?? '/durable');\n const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);\n return {\n module: DurableDashboardModule,\n imports: [\n DurableApiModule,\n RouterModule.register([\n { path: basePath, module: DurableDashboardModule }, // the UI controller below\n { path: apiBasePath, module: DurableApiModule },\n ]),\n ],\n controllers: [DurableUiController],\n providers: [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n ],\n // Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).\n exports: [DurableApiModule],\n };\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n StreamableFile,\n} from '@nestjs/common';\n\n/** DI token carrying the UI mount base (e.g. `/durable`). */\nexport const DASHBOARD_BASE_PATH = Symbol('DASHBOARD_BASE_PATH');\n/** DI token carrying the JSON API base the SPA fetches from (e.g. `/api/durable`). */\nexport const DASHBOARD_API_PATH = Symbol('DASHBOARD_API_PATH');\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/durable';\n\n/** dist/server/durable-ui.controller.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled control-plane SPA at the configured base (+ hashed assets at `<base>/assets`).\n * The path comes from `RouterModule` (set by `DurableDashboardModule.forRoot({ basePath })`), so the\n * controller routes are relative.\n */\n@Controller()\nexport class DurableUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the\n // classic \"stuck loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(): string {\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Dashboard is not built. Run the package build.');\n }\n // The bundle was built with Vite base `/durable/`; rewrite asset URLs to the configured base so\n // the SPA loads from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n // __DURABLE_BASE__ = where assets load; __DURABLE_API__ = where the SPA fetches the JSON API.\n const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n"],"mappings":"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","names":["STATE_STORE_CANONICAL","WorkflowEngine","collectMetrics","Inject","Injectable","Observable","DashboardService","metricsCollector","store","engine","collectMetrics","metrics","cap","pending","running","dead","Promise","all","listRuns","status","limit","gauges","length","health","workerHealth","push","h","group","depth","liveWorkers","prometheus","join","query","getRunDetail","runId","run","getRun","timeline","children","listCheckpoints","getRunChildren","retryWithInput","input","retry","requeue","cancel","opts","bulk","action","filter","runs","applied","r","id","matched","continue","deliverWebhook","token","body","signal","getEvent","key","update","name","arg","streamRun","Observable","subscriber","off","subscribe","event","next","data","Body","Controller","Get","Header","NotFoundException","Param","Post","Query","Sse","ATTR_OPS","Set","coerce","v","Number","isNaN","parseAttrFilters","attr","undefined","raw","Array","isArray","filters","entry","key","op","rest","split","has","length","push","value","join","DurableApiController","dashboard","runs","status","workflow","tag","attr","listRuns","attributes","parseAttrFilters","metrics","workers","workerHealth","run","id","detail","getRunDetail","NotFoundException","stream","streamRun","retry","retryWithInput","body","input","bulk","action","compensate","cancel","result","continue","webhook","token","deliverWebhook","event","key","getEvent","update","name","Module","RouterModule","existsSync","readFileSync","basename","extname","join","resolve","sep","fileURLToPath","Controller","Get","Header","Inject","NotFoundException","Param","StreamableFile","DASHBOARD_BASE_PATH","Symbol","DASHBOARD_API_PATH","BUILD_BASE","spaDir","fileURLToPath","URL","url","CONTENT_TYPES","DurableUiController","dir","basePath","apiBasePath","index","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","includes","replace","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","normalize","path","replace","DurableApiModule","controllers","DurableApiController","providers","DashboardService","exports","DurableDashboardModule","forRoot","options","basePath","apiBasePath","module","imports","RouterModule","register","DurableUiController","provide","DASHBOARD_BASE_PATH","useValue","DASHBOARD_API_PATH"]}
|