@dudousxd/nestjs-durable-dashboard 0.56.0 → 0.58.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../src/server/console-authorize.ts","../../src/server/dashboard.service.ts","../../src/server/durable-api.controller.ts","../../src/server/durable-run.ts","../../src/server/not-modified.ts","../../src/server/run-list-row.ts","../../src/server/run.filter.ts","../../src/server/attr-filter.ts","../../src/server/run-query.adapter.ts","../../src/server/run-query-draft.ts","../../src/server/durable-dashboard.module.ts","../../src/server/controller-decorators.ts","../../src/server/durable-ui.controller.ts"],"sourcesContent":["export * from './console-authorize.js';\nexport * from './dashboard.service.js';\nexport * from './durable-api.controller.js';\nexport * from './durable-dashboard.module.js';\nexport * from './durable-ui.controller.js';\n","import 'reflect-metadata';\nimport { createHash, timingSafeEqual } from 'node:crypto';\nimport {\n type ArgumentsHost,\n type CanActivate,\n Catch,\n type ExceptionFilter,\n type ExecutionContext,\n HttpException,\n Inject,\n Injectable,\n Logger,\n type Type,\n UseFilters,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { HttpAdapterHost, type ModuleRef } from '@nestjs/core';\n\n// ---------------------------------------------------------------------------------------------\n// The console access contract. Every Aviary console declares these LOCALLY with identical names\n// and shapes (no cross-lib import): TypeScript is structural, so one authorizer fits every console.\n// ---------------------------------------------------------------------------------------------\n\nexport type ConsoleAuthorizeResult =\n | boolean\n | { allowed: true }\n | { allowed: false; reason?: 'unauthenticated' | 'forbidden' }\n | { redirectTo: string };\n\n/** Class form: an `@Injectable()` provider (constructor injection works). */\nexport interface ConsoleAuthorizer {\n authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;\n}\nexport type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;\n\n/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */\nexport interface ConsolePolicyCheck {\n policy: Type<unknown>;\n ability: string;\n}\n\nexport type ConsoleAuthorize =\n | ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>)\n | ConsoleAuthorizerClass\n | (() => Promise<{ default: ConsoleAuthorizerClass }>)\n | ConsolePolicyCheck\n | string;\n\nexport type ConsoleDecision =\n | { allowed: true }\n | { allowed: false; reason: 'unauthenticated' | 'forbidden'; redirectTo?: string };\n\n/** What a resolved `authorize` is: called once per request, never throws. */\nexport type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;\n\n/**\n * The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).\n * A registry symbol, so it is found without depending on that package.\n */\nexport const CONSOLE_GATE = Symbol.for('@dudousxd/aviary:console-gate');\nexport interface ConsoleGate {\n allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;\n}\n\nconst PACKAGE = '@dudousxd/nestjs-durable-dashboard';\nconst MODULE = 'DurableDashboardModule';\n\n/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */\nexport const CONSOLE_AUTHORIZE_MISSING_WARNING = `[${PACKAGE}] console has no \\`authorize\\` configured: it is open outside production and denies every request in production. Set \\`authorize\\` in ${MODULE}.forRoot().`;\n\n// Factories, not shared constants: every decision is a fresh object, so a caller mutating one can\n// never change the answer for the next request.\nconst allow = (): ConsoleDecision => ({ allowed: true });\nconst forbidden = (): ConsoleDecision => ({ allowed: false, reason: 'forbidden' });\nconst unauthenticated = (): ConsoleDecision => ({ allowed: false, reason: 'unauthenticated' });\n\nfunction isProduction(): boolean {\n return (process.env.NODE_ENV ?? '').trim().toLowerCase() === 'production';\n}\n\n/**\n * A class with `authorize()`: on the prototype, or (an instance-property arrow function,\n * `authorize = (ctx) => ...`) only on instances, so any `class`.\n */\nfunction isAuthorizerClass(value: unknown): value is ConsoleAuthorizerClass {\n if (typeof value !== 'function') return false;\n if (\n typeof (value as { prototype?: { authorize?: unknown } }).prototype?.authorize === 'function'\n ) {\n return true;\n }\n return /^class[\\s{]/.test(Function.prototype.toString.call(value));\n}\n\nfunction isPolicyCheck(value: unknown): value is ConsolePolicyCheck {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ConsolePolicyCheck).policy === 'function' &&\n typeof (value as ConsolePolicyCheck).ability === 'string'\n );\n}\n\nfunction isLazyModule(value: unknown): value is { default: ConsoleAuthorizerClass } {\n return (\n typeof value === 'object' &&\n value !== null &&\n isAuthorizerClass((value as { default?: unknown }).default)\n );\n}\n\n/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */\nexport function normalizeConsoleResult(result: unknown): ConsoleDecision {\n if (result === true) return allow();\n if (typeof result !== 'object' || result === null) return forbidden();\n const shape = result as { allowed?: unknown; reason?: unknown; redirectTo?: unknown };\n if (typeof shape.redirectTo === 'string' && shape.allowed !== true) {\n return { allowed: false, reason: 'unauthenticated', redirectTo: shape.redirectTo };\n }\n if (shape.allowed === true) return allow();\n return shape.reason === 'unauthenticated' ? unauthenticated() : forbidden();\n}\n\n/** `null`, `undefined` and `false` mean \"nobody signed in\", like `@nestjs/authorization`'s guard. */\nfunction hasUser(user: unknown): boolean {\n return user !== null && user !== undefined && user !== false;\n}\n\nasync function policyDecider(\n check: ConsolePolicyCheck,\n moduleRef: ModuleRef,\n): Promise<ConsoleDecide> {\n let pkg: typeof import('@nestjs/authorization');\n try {\n pkg = await import('@nestjs/authorization');\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs @nestjs/authorization installed, with AuthorizationModule.forRoot() imported in the app; or pass a function, a class, or a gate name.`,\n );\n }\n const { AuthorizationService, AUTHORIZATION_MODULE_OPTIONS, defaultGetUser } = pkg;\n try {\n moduleRef.get(AuthorizationService, { strict: false });\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app.`,\n );\n }\n // The user the official `@Can()` sees: the module's `getUser` when one is configured, else the\n // package default (`request.user`). Exactly one of them answers; `undefined`, `null` and `false`\n // all mean a guest, as in `AuthorizationGuard`.\n let getUser = defaultGetUser as (context: ExecutionContext) => unknown;\n try {\n const options = moduleRef.get<{ getUser?: (context: ExecutionContext) => unknown }>(\n AUTHORIZATION_MODULE_OPTIONS,\n { strict: false },\n );\n if (typeof options?.getUser === 'function') getUser = options.getUser;\n } catch {\n // No options registered: the package default reads `request.user`.\n }\n return async (context) => {\n const authorization = moduleRef.get(AuthorizationService, { strict: false });\n const user = await getUser(context);\n const can = authorization.can as (\n policy: unknown,\n ability: string,\n user: unknown,\n ) => Promise<boolean>;\n if (await can.call(authorization, check.policy, check.ability, hasUser(user) ? user : null)) {\n return allow();\n }\n return hasUser(user) ? forbidden() : unauthenticated();\n };\n}\n\nfunction gateDecider(name: string, moduleRef: ModuleRef): ConsoleDecide {\n try {\n moduleRef.get(CONSOLE_GATE, { strict: false });\n } catch {\n throw new Error(\n `\\`authorize: '${name}'\\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }`,\n );\n }\n return async (context) => {\n const gate = moduleRef.get<ConsoleGate>(CONSOLE_GATE, { strict: false });\n return normalizeConsoleResult(await gate.allows(name, context));\n };\n}\n\nfunction classDecider(moduleRef: ModuleRef): (Class: ConsoleAuthorizerClass) => ConsoleDecide {\n const instances = new Map<ConsoleAuthorizerClass, Promise<ConsoleAuthorizer>>();\n const instanceOf = (Class: ConsoleAuthorizerClass): Promise<ConsoleAuthorizer> => {\n const cached = instances.get(Class);\n if (cached) return cached;\n const pending = (async (): Promise<ConsoleAuthorizer> => {\n try {\n // A registered provider is used as is; otherwise one instance per registration.\n const registered = moduleRef.get(Class, { strict: false });\n if (registered) return registered;\n } catch {\n // Not a provider: create it below.\n }\n return moduleRef.create(Class);\n })();\n // A failed construction is retried on the next request instead of denying forever.\n pending.catch(() => instances.delete(Class));\n instances.set(Class, pending);\n return pending;\n };\n return (Class) => async (context) =>\n normalizeConsoleResult(await (await instanceOf(Class)).authorize(context));\n}\n\n/**\n * Resolves `authorize` into one function the guard calls per request. Resolution errors that are\n * configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)\n * throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.\n *\n * `authorize` omitted: open outside production, `forbidden` for every request in production (read\n * when this runs), and one warning logged (unless `warnIfMissing: false`).\n */\nexport async function resolveConsoleAuthorize(\n authorize: ConsoleAuthorize | undefined,\n moduleRef: ModuleRef,\n options: { logger?: Logger; warnIfMissing?: boolean } = {},\n): Promise<ConsoleDecide> {\n const logger = options.logger ?? new Logger(MODULE);\n if (authorize === undefined) {\n if (options.warnIfMissing !== false) logger.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);\n const production = isProduction();\n return async () => (production ? forbidden() : allow());\n }\n\n let decide: ConsoleDecide;\n if (typeof authorize === 'string') {\n decide = gateDecider(authorize, moduleRef);\n } else if (isPolicyCheck(authorize)) {\n decide = await policyDecider(authorize, moduleRef);\n } else if (isAuthorizerClass(authorize)) {\n decide = classDecider(moduleRef)(authorize);\n } else if (typeof authorize === 'function') {\n const fromClass = classDecider(moduleRef);\n let lazy: ConsoleDecide | undefined;\n const fn = authorize as (context: ExecutionContext) => unknown;\n decide = async (context) => {\n if (lazy) return lazy(context);\n const result = await fn(context);\n if (!isLazyModule(result)) return normalizeConsoleResult(result);\n // A lazy import: remember the class, so the import runs once per registration.\n lazy = fromClass(result.default);\n return lazy(context);\n };\n } else {\n throw new Error(\n `${MODULE}: \\`authorize\\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`,\n );\n }\n\n return async (context) => {\n try {\n return await decide(context);\n } catch (error) {\n logger.warn(\n `console \\`authorize\\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`,\n );\n return forbidden();\n }\n };\n}\n\n// ---------------------------------------------------------------------------------------------\n// authorizeByToken\n// ---------------------------------------------------------------------------------------------\n\nconst sha256 = (value: string): Buffer => createHash('sha256').update(value).digest();\n\n/**\n * Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else\n * is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is\n * empty, so a missing env var fails at config time instead of opening the console.\n */\nexport function authorizeByToken(options: { token: string | undefined }): ConsoleAuthorize {\n const token = options.token;\n if (typeof token !== 'string' || token.length === 0) {\n throw new Error('authorizeByToken: `token` is empty; set it (e.g. from an env var).');\n }\n const expected = sha256(token);\n return (context: ExecutionContext): ConsoleAuthorizeResult => {\n const header = context.switchToHttp().getRequest<{ headers?: Record<string, unknown> }>()\n ?.headers?.authorization;\n const match = typeof header === 'string' ? /^Bearer\\s+(.+)$/i.exec(header.trim()) : null;\n if (match?.[1] && timingSafeEqual(sha256(match[1]), expected)) return true;\n return { allowed: false, reason: 'unauthenticated' };\n };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Enforcement: a guard (decides) + a filter scoped to the console controllers (answers).\n// ---------------------------------------------------------------------------------------------\n\n/** DI token of the resolved {@link ConsoleDecide} the guard runs. */\nexport const CONSOLE_AUTHORIZE_DECIDE = Symbol(`${PACKAGE}:console-authorize`);\n\n/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */\nexport const CONSOLE_PAGE_METADATA = `${PACKAGE}:console-page`;\nexport const ConsolePage = (): ClassDecorator => (target) => {\n Reflect.defineMetadata(CONSOLE_PAGE_METADATA, true, target);\n};\n\n/**\n * The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly\n * where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.\n */\nexport class ConsoleAccessDenied extends HttpException {\n constructor(\n readonly decision: Extract<ConsoleDecision, { allowed: false }>,\n readonly page: boolean,\n ) {\n const unauthenticated = decision.reason === 'unauthenticated';\n super(\n unauthenticated\n ? {\n error: 'unauthenticated',\n ...(decision.redirectTo ? { redirectTo: decision.redirectTo } : {}),\n }\n : { error: 'forbidden' },\n unauthenticated ? 401 : 403,\n );\n }\n}\n\n@Injectable()\nexport class ConsoleAuthorizeGuard implements CanActivate {\n constructor(@Inject(CONSOLE_AUTHORIZE_DECIDE) private readonly decide: ConsoleDecide) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const decision = await this.decide(context);\n if (decision.allowed) return true;\n throw new ConsoleAccessDenied(decision, isPageNavigation(context));\n }\n}\n\nfunction isPageNavigation(context: ExecutionContext): boolean {\n if (context.getType() !== 'http') return false;\n if (Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) !== true) return false;\n const request = context\n .switchToHttp()\n .getRequest<{ method?: string; headers?: Record<string, unknown> }>();\n const accept = request?.headers?.accept;\n return request?.method === 'GET' && typeof accept === 'string' && accept.includes('text/html');\n}\n\n/** The minimal page answered to a denied page navigation: same markup for 401 and 403. */\nconst consolePage = (title: string, message: string): string =>\n `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title}</title></head><body style=\"font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0\"><main style=\"text-align:center\"><h1>${title}</h1><p>${message}</p></main></body></html>`;\nconst DENIED_PAGE = consolePage('Access denied', 'You do not have access to this console.');\nconst SIGN_IN_PAGE = consolePage('Sign in required', 'Sign in to open this console.');\n\n@Catch(ConsoleAccessDenied)\nexport class ConsoleAuthorizeFilter implements ExceptionFilter {\n constructor(private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void {\n const adapter = this.adapterHost.httpAdapter;\n const response = host.switchToHttp().getResponse();\n const { decision, page } = exception;\n if (page && decision.redirectTo) {\n adapter.redirect(response, 302, decision.redirectTo);\n return;\n }\n if (page) {\n const forbiddenPage = decision.reason === 'forbidden';\n adapter.setHeader(response, 'Content-Type', 'text/html; charset=utf-8');\n adapter.reply(\n response,\n forbiddenPage ? DENIED_PAGE : SIGN_IN_PAGE,\n forbiddenPage ? 403 : 401,\n );\n return;\n }\n adapter.reply(response, exception.getResponse(), exception.getStatus());\n }\n}\n\n/**\n * The guard and its filter as one class decorator, for a console you mount yourself\n * (`routes: false`). List it ABOVE your authentication guards, so it runs after them.\n */\nexport const UseConsoleAuthorize = (): ClassDecorator & MethodDecorator =>\n applyDecorators(UseGuards(ConsoleAuthorizeGuard), UseFilters(ConsoleAuthorizeFilter));\n","import {\n type DurableTopology,\n type EngineEvent,\n type FanPage,\n type FanPageQuery,\n type GroupHealth,\n type MetricsCollector,\n type RepublishLifecycleResult,\n type RunDetail,\n type RunDetailOptions,\n type RunFacetQuery,\n type RunFacetRow,\n RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\n STATE_STORE_CANONICAL,\n type ScheduleDescription,\n type ScheduleListQuery,\n type StateStore,\n TERMINAL_RUN_STATUSES,\n type UpdateResult,\n WorkflowEngine,\n type WorkflowLifecycleLinks,\n collectMetrics,\n parseWebhookToken,\n} from '@dudousxd/nestjs-durable-core';\nimport { Inject, Injectable, Optional } from '@nestjs/common';\nimport { Observable } from 'rxjs';\n\n// Single source of truth: core owns `RunDetail` (`run-gateway.ts`) — re-exported here (not\n// re-declared) so this package's existing public API (`import type { RunDetail } from\n// '@dudousxd/nestjs-durable-dashboard'`) keeps working unchanged.\nexport type { RunDetail };\n\n/**\n * Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/\n * continue/retryWithInput/stream/bulk/workerHealth) route through the injected {@link RUN_GATEWAY}\n * port, which the lib binds on both topologies (a store-backed impl on the control plane, a proxy\n * over the transport on a store-less tenant) — so those ops work regardless of whether `store`/\n * `engine` are present (`workerHealth` is scoped to the tenant's own groups over the proxy).\n * Operator-only ops (metrics/deliverWebhook/getEvent/update) still need direct `store`/`engine`\n * access; they stay `@Optional()` and throw a clear error when absent.\n */\n/**\n * A workflow's lifecycle links as `GET workflows/:name/lifecycle` answers them, plus `triggerCounts`:\n * per subscriber of `startsOnSettle`, how many runs it was started for (`triggered`) and how many\n * times starting it failed (`failed`), counted by this process's metrics since it started.\n */\nexport interface WorkflowLifecyclePage extends WorkflowLifecycleLinks {\n triggerCounts?: Record<string, { triggered: number; failed: number }> | undefined;\n}\n\n@Injectable()\nexport class DashboardService {\n /** Prometheus counters accumulated from engine events since boot (per process); undefined on a\n * store-less tenant (no engine to accumulate from). */\n private readonly metricsCollector: MetricsCollector | undefined;\n\n constructor(\n private readonly gateway: RunGateway,\n @Optional() @Inject(STATE_STORE_CANONICAL) private readonly store?: StateStore,\n @Optional() private readonly engine?: WorkflowEngine,\n ) {\n // Only the control plane (which has the canonical store) has a FULL engine to accumulate from;\n // on a tenant `this.engine` is a store-less start client with no `subscribe`, so guard on store.\n this.metricsCollector = this.store && this.engine ? collectMetrics(this.engine) : undefined;\n }\n\n /**\n * The operator-only ops need the FULL engine + canonical store, which only the control plane has.\n * On a tenant the `WorkflowEngine` token is a store-less start client that implements none of these\n * methods, so presence of the canonical store is the reliable discriminator — gate on it (never\n * on engine presence, which is truthy on a tenant too).\n */\n private controlPlane(): { store: StateStore; engine: WorkflowEngine } {\n if (!this.store || !this.engine) {\n throw new Error(\n 'This durable dashboard operation requires the control plane (not available on a tenant deployment).',\n );\n }\n return { store: this.store, engine: this.engine };\n }\n\n /**\n * The persisted schedules (`engine.schedules`), soonest-due first. Empty — not an error — on a\n * tenant deployment or a store that doesn't persist schedules, so the console can simply hide the\n * panel instead of special-casing either.\n */\n async listSchedules(query: ScheduleListQuery): Promise<ScheduleDescription[]> {\n if (!this.store || !this.engine?.schedules?.supported) return [];\n return this.engine.schedules.list(query);\n }\n\n /** Pause a persisted schedule (control plane only). */\n pauseSchedule(id: string): Promise<ScheduleDescription> {\n return this.controlPlane().engine.schedules.pause(id);\n }\n\n /** Resume a persisted schedule from its next window (control plane only). */\n resumeSchedule(id: string): Promise<ScheduleDescription> {\n return this.controlPlane().engine.schedules.resume(id);\n }\n\n /** Start one run of a persisted schedule now, outside its cadence (control plane only). */\n triggerSchedule(id: string): Promise<RunResult> {\n return this.controlPlane().engine.schedules.trigger(id);\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. Control-plane-only.\n */\n async metrics(): Promise<string> {\n const { store, engine } = this.controlPlane();\n const cap = 10_000;\n const [pending, running, dead] = await Promise.all([\n store.listRuns({ status: 'pending', limit: cap }),\n store.listRuns({ status: 'running', limit: cap }),\n 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 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<RunListItem[]> {\n return this.gateway.listRuns(query);\n }\n\n /** `(status, origin)` counts over the same predicates {@link listRuns} pages — what keeps the\n * console's chips exact while its list is bounded to a page. */\n runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n return this.gateway.runFacets(query);\n }\n\n /** The distinct values of one filter axis over the same predicates — what fills the console's\n * tenant/tag/attribute pickers, so an operator picks a value that exists instead of typing one\n * blind. `[]` when the store cannot enumerate them; the pickers then fall back to free text. */\n runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.gateway.runValueFacets(axis, query, opts);\n }\n\n /** This deployment's durable role (control plane vs tenant) + tenant name — for the dashboard header\n * badge. Routes through the gateway, which knows its own topology (store-backed = operator, proxy =\n * tenant). Works on both topologies (no store/engine needed). */\n topology(): DurableTopology {\n return this.gateway.topology();\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`. Routes through the\n * gateway so it works on BOTH topologies: the control plane returns every group; a tenant proxies\n * to the operator, which scopes the result to the tenant's own `@<tenant>` groups. */\n async workerHealth(): Promise<GroupHealth[]> {\n return this.gateway.workerHealth();\n }\n\n getRunDetail(runId: string, options?: RunDetailOptions): Promise<RunDetail | null> {\n return this.gateway.getRunDetail(runId, options);\n }\n\n /** A page of one fan's members, or null when the run is unknown or the gateway can't page fans. */\n async getFanMembers(runId: string, group: string, query: FanPageQuery): Promise<FanPage | null> {\n return (await this.gateway.getFanMembers?.(runId, group, query)) ?? null;\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.gateway.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 return this.gateway.retry(runId);\n }\n\n cancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null> {\n return this.gateway.cancel(runId, opts);\n }\n\n /**\n * Publish a settled run's lifecycle event again, to its subscribers that have not started (or to\n * `subscriber` alone). Undefined when the gateway cannot republish (a tenant proxy).\n */\n async republishLifecycle(\n runId: string,\n subscriber?: string,\n ): Promise<RepublishLifecycleResult | undefined> {\n return this.gateway.republishLifecycle?.({\n runId,\n ...(subscriber !== undefined ? { subscriber } : {}),\n });\n }\n\n /**\n * Who starts whom through lifecycle events, for `workflow`, plus `triggerCounts` when this process\n * counts the engine's events (the control plane) and the workflow has subscribers. Undefined when\n * the gateway cannot answer.\n */\n async workflowLifecycle(workflow: string): Promise<WorkflowLifecyclePage | undefined> {\n const links = await this.gateway.workflowLifecycle?.(workflow);\n if (!links) return links;\n const counts = this.triggerCountsOf(links);\n return counts ? { ...links, triggerCounts: counts } : links;\n }\n\n /**\n * How many runs each subscriber of `links.workflow` was started for and how many times starting it\n * failed, counted by this process's metrics since it started. Undefined without metrics (a tenant)\n * or without subscribers.\n */\n private triggerCountsOf(\n links: WorkflowLifecycleLinks,\n ): Record<string, { triggered: number; failed: number }> | undefined {\n const snapshot = this.metricsCollector?.snapshot();\n if (!snapshot || links.startsOnSettle.length === 0) return undefined;\n const triggered = snapshot.triggeredBySubscriber[links.workflow] ?? {};\n const failed = snapshot.triggerFailedBySubscriber[links.workflow] ?? {};\n const counts: Record<string, { triggered: number; failed: number }> = {};\n for (const { workflow } of links.startsOnSettle) {\n counts[workflow] = { triggered: triggered[workflow] ?? 0, failed: failed[workflow] ?? 0 };\n }\n return counts;\n }\n\n /**\n * Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow / namespace /\n * origin) — e.g. \"retry every `dead` run tagged `type:mel`\". The filter is the same one the run\n * list uses, so a bulk action can never reach wider than the list it was launched from — but note\n * an ABSENT predicate still means \"every value on that axis\" (an omitted `namespace` is every\n * tenant), which is what makes passing the operator's full filter through mandatory, not optional.\n * 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' | 'namespace' | 'origin'>,\n opts?: { compensate?: boolean },\n ): Promise<{ matched: number; applied: number }> {\n const runs = await this.gateway.listRuns({ ...filter, limit: 500 });\n let applied = 0;\n for (const r of runs) {\n try {\n if (action === 'retry') await this.gateway.retry(r.id);\n else await this.gateway.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.gateway.continue(runId);\n }\n\n /** Re-dispatch a run's stuck `pending` remote steps — recovery for a lost step dispatch. */\n redispatch(runId: string) {\n return this.gateway.redispatchPending(runId);\n }\n\n /**\n * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the\n * run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url\n * was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so\n * the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a\n * token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already\n * resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk\n * rows behind or signal non-webhook tokens. Control-plane-only.\n */\n async deliverWebhook(token: string, body: unknown): Promise<RunResult | null> {\n const { store, engine } = this.controlPlane();\n const parsed = parseWebhookToken(token);\n if (!parsed) return null;\n const run = await store.getRun(parsed.runId);\n if (!run || TERMINAL_RUN_STATUSES.includes(run.status)) return null;\n const resolved = await store.getCheckpoint(parsed.runId, parsed.seq);\n if (resolved?.status === 'completed') return null;\n const delivered = await engine.signal(token, body);\n if (delivered) return delivered;\n // Buffered. A concurrent duplicate may have resolved the wait between the check above and our\n // buffer write: then nothing will ever read this row, so take it back and answer 404. (If the\n // take finds nothing, the waiter consumed our payload itself — a real delivery.)\n const now = await store.getCheckpoint(parsed.runId, parsed.seq);\n if (now?.status === 'completed' && (await store.takeBufferedSignal(token))) return null;\n const fresh = await store.getRun(parsed.runId);\n return { runId: run.id, status: fresh?.status ?? run.status };\n }\n\n /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */\n async getEvent(runId: string, key: string): Promise<unknown> {\n return this.controlPlane().engine.getEvent(runId, key);\n }\n\n /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). Control-plane-only. */\n async update(runId: string, name: string, arg: unknown): Promise<UpdateResult> {\n return this.controlPlane().engine.update(runId, name, arg);\n }\n\n /**\n * Live stream of a run's lifecycle events for SSE. Backed by `gateway.subscribe`, which — on the\n * control plane — receives events from EVERY instance (so a dashboard-only pod tails a run\n * executing on a worker pod), and on a tenant round-trips over the transport. Either way the\n * gateway already filters to `runId`; the dashboard just wires the events through.\n */\n streamRun(runId: string): Observable<{ data: EngineEvent }> {\n return new Observable<{ data: EngineEvent }>((subscriber) => {\n const off = this.gateway.subscribe(runId, (event) => subscriber.next({ data: event }));\n return () => off();\n });\n }\n}\n","import { ApplyFilter, ApplyFilterInterceptor, FilterRunner } from '@dudousxd/nestjs-filter';\nimport {\n BadRequestException,\n Body,\n Controller,\n Get,\n Header,\n Headers,\n NotFoundException,\n NotImplementedException,\n Param,\n Post,\n Query,\n Res,\n Sse,\n UseFilters,\n UseInterceptors,\n} from '@nestjs/common';\nimport { DashboardService } from './dashboard.service.js';\n\n/** Fans with more members than this come back summarized in a run's detail. */\nconst FAN_INLINE_MAX = 50;\nimport { DurableRun } from './durable-run.js';\nimport { NotModifiedException, NotModifiedFilter, setEtag } from './not-modified.js';\nimport { type RunListRow, toRunListRow } from './run-list-row.js';\nimport type { RunQueryDraft } from './run-query-draft.js';\nimport { RunFilter } from './run.filter.js';\n\n/**\n * JSON API consumed by the control-plane SPA. The mounted {@link DurableApiController} sits at\n * `apiBasePath` (set by RouterModule).\n *\n * This base is what a host EXTENDS to serve the API from a controller of its own (with\n * `forRoot({ routes: false, apiBasePath })`): `@Controller('<apiBasePath>') class MyConsoleApi\n * extends DurableApiControllerBase {}`. It carries no guards and no `@nestjs/authorization` mark,\n * so a subclass is protected only by what the host puts on it.\n *\n * The filter interceptor (what resolves `@ApplyFilter` into a query) is bound HERE rather than as\n * the app-wide `APP_INTERCEPTOR` that `FilterModule.forRoot()` registers: a library must not add a\n * global interceptor to its host, and on an app that already has one this would be the second copy,\n * running every filter in the app twice.\n */\n@Controller()\n@UseInterceptors(ApplyFilterInterceptor)\n@UseFilters(NotModifiedFilter)\nexport class DurableApiControllerBase {\n constructor(\n private readonly dashboard: DashboardService,\n private readonly runner: FilterRunner,\n ) {}\n\n /**\n * The run list, filtered by whatever the operator asked for.\n *\n * The predicates are parsed, validated and translated by `@dudousxd/nestjs-filter` (see\n * `RunFilter` for the two spellings it accepts, and `RunQueryAdapter` for how they become a\n * `RunQuery`). Every predicate is OPTIONAL and an omitted one means \"don't narrow on this axis\" —\n * in particular `namespace`, whose absence keeps the historical, deliberate behaviour that read\n * paths are NOT namespace-scoped (core, `WorkflowRun.namespace`): the console shows every tenant's\n * runs until an operator chooses one.\n */\n @Get('runs')\n async runs(@ApplyFilter(RunFilter) draft: RunQueryDraft): Promise<RunListRow[]> {\n const runs = await this.dashboard.listRuns(draft.query);\n // Rows only — the three payload fields are the bulk of a large listing and belong to `runs/:id`.\n return runs.map(toRunListRow);\n }\n\n /**\n * `(status, origin)` counts for the runs matching the same tag/tenant/attribute predicates as\n * `GET runs`. This is what lets that listing be PAGED at all: the page bounds what the console\n * renders, this bounds nothing and keeps its status and origin chips exact.\n *\n * `status`, `origin` and the page bounds are accepted (the route takes the same filter) but\n * deliberately DROPPED before counting — they are the axes being counted, so narrowing by them\n * would report the answer back to itself.\n */\n @Get('runs/facets')\n facets(@ApplyFilter(RunFilter) draft: RunQueryDraft) {\n return this.dashboard.runFacets(draft.facetQuery());\n }\n\n /**\n * The distinct values one filter field takes across the runs matching every OTHER active\n * predicate, with counts — what the console's tenant/tag/attribute pickers list.\n *\n * `?groupByCount[field]=tag&groupByCount[limit]=20`, with the filters in the structured\n * `filter[where][...]` form (what `filterQuery()` builds). Scoping the values to the active\n * filters is the point: picking a tenant leaves the tag picker offering only that tenant's tags,\n * so a picker never offers a value whose result set is empty.\n *\n * `limit` matters rather than being a nicety — tag and attribute cardinality grows with the data\n * (a `singleton:<key>` tag is minted per key), so the unbounded answer is a listing. `offset` and\n * `search` are what make that bound livable: a picker pages as it scrolls and narrows as the\n * operator types, both server-side, so a value outside the first page is still reachable.\n */\n @Get('runs/values')\n values(@Query() query: Record<string, unknown>) {\n return this.runner.groupByCount(DurableRun, query, { filterClass: RunFilter });\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 /**\n * The persisted schedules (`engine.schedules`), soonest-due first, optionally narrowed by\n * `namespace`/`workflow`/`tag`/`paused`. Empty on a tenant or a store without schedules.\n */\n @Get('schedules')\n schedules(\n @Query('namespace') namespace?: string,\n @Query('workflow') workflow?: string,\n @Query('tag') tag?: string,\n @Query('paused') paused?: string,\n @Query('limit') limit?: string,\n ) {\n return this.dashboard.listSchedules({\n ...(namespace ? { namespace } : {}),\n ...(workflow ? { workflow } : {}),\n ...(tag ? { tag } : {}),\n ...(paused === 'true' || paused === 'false' ? { paused: paused === 'true' } : {}),\n limit: Math.min(Number(limit) || 200, 1_000),\n });\n }\n\n @Post('schedules/:id/pause')\n pauseSchedule(@Param('id') id: string) {\n return this.dashboard.pauseSchedule(id).catch(notFound);\n }\n\n @Post('schedules/:id/resume')\n resumeSchedule(@Param('id') id: string) {\n return this.dashboard.resumeSchedule(id).catch(notFound);\n }\n\n /** Start one run of the schedule now, outside its cadence. */\n @Post('schedules/:id/trigger')\n triggerSchedule(@Param('id') id: string) {\n return this.dashboard.triggerSchedule(id).catch(notFound);\n }\n\n /**\n * Who starts whom through lifecycle events, for one workflow: the workflows that start when it\n * settles, and the ones whose settling starts it. What the console's workflow page lists.\n */\n @Get('workflows/:name/lifecycle')\n async workflowLifecycle(@Param('name') name: string) {\n const links = await this.dashboard.workflowLifecycle(name).catch(notSupportedByServer);\n if (!links)\n throw new NotImplementedException('workflow lifecycle links need the control plane');\n return links;\n }\n\n /** This deployment's durable role (control plane vs tenant) + tenant name — for the header badge. */\n @Get('topology')\n topology() {\n return this.dashboard.topology();\n }\n\n /**\n * A run's detail. Fans wider than {@link FAN_INLINE_MAX} come back as one `FanSummary` each, their\n * members paged by `runs/:id/fans/:group`; `?full=true` returns every checkpoint instead.\n *\n * When the gateway can version the detail without reading it, the response carries an `ETag`, and\n * a poll whose `If-None-Match` still names it gets a 304 before the timeline is read. The browser\n * sends that header itself (the response is `no-cache`), so a console poll needs no code for it.\n */\n @Get('runs/:id')\n async run(\n @Param('id') id: string,\n @Query('full') full?: string,\n @Headers('if-none-match') ifNoneMatch?: string,\n @Res({ passthrough: true }) response?: unknown,\n ) {\n const detail = await this.dashboard.getRunDetail(\n id,\n full === 'true'\n ? undefined\n : { summarizeFansOver: FAN_INLINE_MAX, ...(ifNoneMatch && { ifNoneMatch }) },\n );\n if (!detail) throw new NotFoundException(`run ${id} not found`);\n const { etag, notModified, ...body } = detail;\n if (etag) {\n if (notModified) throw new NotModifiedException(etag);\n setEtag(response, etag);\n }\n return body;\n }\n\n /** One page of a summarized fan's members, in seq order. Versioned like {@link run}. */\n @Get('runs/:id/fans/:group')\n async fanMembers(\n @Param('id') id: string,\n @Param('group') group: string,\n @Query('offset') offset?: string,\n @Query('limit') limit?: string,\n @Query('failed') failed?: string,\n @Headers('if-none-match') ifNoneMatch?: string,\n @Res({ passthrough: true }) response?: unknown,\n ) {\n const page = await this.dashboard.getFanMembers(id, group, {\n offset: offset === undefined ? undefined : Number(offset),\n limit: limit === undefined ? undefined : Number(limit),\n failedOnly: failed === 'true',\n ...(ifNoneMatch && { ifNoneMatch }),\n });\n if (!page) throw new NotFoundException(`fan ${group} of run ${id} not found`);\n const { etag, notModified, ...body } = page;\n if (etag) {\n if (notModified) throw new NotModifiedException(etag);\n setEtag(response, etag);\n }\n return body;\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 /**\n * Bulk retry/cancel every run matching a filter (status / tag / workflow / namespace / origin).\n * The filter takes the SAME params as `GET runs` and is parsed identically, so \"retry all\" acts on\n * exactly the set the operator was looking at rather than a wider one.\n */\n @Post('bulk/:action')\n bulk(\n @Param('action') action: 'retry' | 'cancel',\n @ApplyFilter(RunFilter) draft: RunQueryDraft,\n @Query('compensate') compensate?: string,\n ) {\n // The operator's page window is dropped, and only it: `bulk` acts on the whole matching SET (it\n // applies its own bound), so carrying a `limit` of 100 over from the list would silently retry\n // the first page and report success for the rest.\n const { limit, offset, ...filter } = draft.query;\n return this.dashboard.bulk(action === 'cancel' ? 'cancel' : 'retry', filter, {\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 /**\n * Publish a settled run's lifecycle event again, to the subscribers that have not started from it\n * (or only `subscriber`). Same gate as retry/cancel.\n */\n @Post('runs/:id/republish-lifecycle')\n async republishLifecycle(@Param('id') id: string, @Body() body?: { subscriber?: string }) {\n let result: Awaited<ReturnType<DashboardService['republishLifecycle']>>;\n try {\n result = await this.dashboard.republishLifecycle(id, body?.subscriber || undefined);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (/^run .* not found$/.test(message)) throw new NotFoundException(message);\n if (/^run .* has not settled/.test(message)) throw new BadRequestException(message);\n if ((error as { code?: unknown } | null)?.code === 'bad-request') {\n throw new BadRequestException(message);\n }\n return notSupportedByServer(error);\n }\n if (!result) {\n throw new NotImplementedException('republishing lifecycle events needs the control plane');\n }\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 /** Re-dispatch a run's stuck `pending` remote steps — recovery for a lost step dispatch. */\n @Post('runs/:id/redispatch')\n async redispatch(@Param('id') id: string) {\n const result = await this.dashboard.redispatch(id);\n if (!result) throw new NotFoundException(`run ${id} not found`);\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 live run is waiting for 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\n/** Map core's `ScheduleNotFoundError` to a 404; rethrow anything else. */\nfunction notFound(error: unknown): never {\n if (error instanceof Error && error.name === 'ScheduleNotFoundError') {\n throw new NotFoundException(error.message);\n }\n throw error;\n}\n\n/**\n * A tenant proxy's control plane that is older than the verb (a `RunGatewayError` with code\n * `unsupported-request`) as 501, every other error as it is.\n */\nfunction notSupportedByServer(error: unknown): never {\n if ((error as { code?: unknown } | null)?.code === 'unsupported-request') {\n throw new NotImplementedException(error instanceof Error ? error.message : String(error));\n }\n throw error;\n}\n\n/**\n * The JSON API controller `DurableDashboardModule.forRoot(...)` mounts at `apiBasePath`. The module\n * puts the `@nestjs/authorization` open mark on THIS class (and `decorators` on a per-registration\n * subclass of it), never on {@link DurableApiControllerBase}, so a host's own subclass of the base\n * inherits none of them.\n */\n@Controller()\nexport class DurableApiController extends DurableApiControllerBase {}\n","import type { RunStatus, RunValueAxis, SearchAttributes } from '@dudousxd/nestjs-durable-core';\nimport type { EntityFieldInfo } from '@dudousxd/nestjs-filter';\n\n/**\n * The filterable surface of a run, declared as a class because that is the shape `@dudousxd/nestjs-filter`\n * works in: `@Filterable({ entity })`, its field validation and its codegen all key off a constructor.\n * Nothing ever instantiates it — the console has no ORM entity to point at, its rows come from the\n * `RunGateway` — so this stands in for one and carries the field list.\n *\n * It declares ONLY what {@link RunQuery} can filter by. That is the load-bearing part: a field absent\n * here is rejected by the runner before it reaches the adapter, which lets the adapter fail loudly on\n * the field/operator combinations it cannot express rather than dropping them into a query that comes\n * back looking successful.\n */\nexport class DurableRun {\n workflow!: string;\n status!: RunStatus;\n /** `null` for a run no package claims — see `WorkflowRun.origin`. */\n origin!: string | null;\n /** Tenant / worker-pool partition. */\n namespace!: string;\n /**\n * ONE of the run's tags. Singular deliberately: a run carries a SET of tags, so a predicate here\n * asks about membership (`tag in [etl, nightly]` = runs carrying either), never equality with the\n * whole set.\n */\n tag!: string;\n /** Typed search attributes, addressed per key as `attr.<key>` — a JSON sub-path, not a column. */\n attr!: SearchAttributes;\n}\n\n/** The `attr` head segment, which the adapter resolves as a JSON path rather than a column. */\nexport const ATTRIBUTE_FIELD = 'attr';\n\n/**\n * What the filter runner validates client field names against. `columnName` is cosmetic here (there\n * is no SQL behind this adapter) but the `type` is not: it is what lets the runner tell a JSON\n * sub-path from a typo, and so what makes `attr.tier` resolve while `status.tier` stays unknown.\n */\nexport const DURABLE_RUN_FIELDS: EntityFieldInfo[] = [\n { name: 'workflow', columnName: 'workflow', type: 'string' },\n { name: 'status', columnName: 'status', type: 'string' },\n { name: 'origin', columnName: 'origin', type: 'string' },\n { name: 'namespace', columnName: 'namespace', type: 'string' },\n { name: 'tag', columnName: 'tags', type: 'string' },\n { name: ATTRIBUTE_FIELD, columnName: 'search_attributes', type: 'json' },\n];\n\n/**\n * The value axis a filter FIELD enumerates, or `null` for a field with no enumerable values.\n *\n * This is the join between the two libraries: the filter lib's `groupByCount('namespace')` asks\n * \"what values does this field take\", and durable answers it with `runValueFacets`. `attr` alone\n * lists the search-attribute KEYS in use; `attr.<key>` lists the values recorded under one of them.\n */\nexport function runValueAxisFor(field: string): RunValueAxis | null {\n if (field === ATTRIBUTE_FIELD) return { field: 'attributeKey' };\n if (field.startsWith(`${ATTRIBUTE_FIELD}.`)) {\n const key = field.slice(ATTRIBUTE_FIELD.length + 1);\n return key ? { field: 'attributeValue', key } : null;\n }\n if (\n field === 'workflow' ||\n field === 'status' ||\n field === 'origin' ||\n field === 'namespace' ||\n field === 'tag'\n ) {\n return { field };\n }\n return null;\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter, HttpException } from '@nestjs/common';\n\ninterface RawResponse {\n statusCode: number;\n setHeader(name: string, value: string): unknown;\n end(): unknown;\n}\n\nfunction isRawResponse(value: unknown): value is RawResponse {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return typeof candidate.setHeader === 'function' && typeof candidate.end === 'function';\n}\n\n/**\n * The writable Node `ServerResponse` behind a platform response: Express' response IS it, Fastify's\n * reply wraps it on `.raw`. One write path serves both without a platform-specific dependency.\n */\nfunction rawResponse(response: unknown): RawResponse | null {\n if (isRawResponse(response)) return response;\n const raw = (response as { raw?: unknown } | null)?.raw;\n return isRawResponse(raw) ? raw : null;\n}\n\n/** Set one header on the raw response (Express or Fastify). */\nfunction setRawHeader(response: unknown, name: string, value: string): void {\n rawResponse(response)?.setHeader(name, value);\n}\n\n/**\n * Write a bodiless `304 Not Modified` carrying its validator, and END the response. Its only caller\n * is an exception filter, the terminal step of the request, so there is no second writer to race.\n */\nfunction writeNotModifiedRaw(response: unknown, etag: string): void {\n const raw = rawResponse(response);\n if (!raw) return;\n raw.statusCode = 304;\n raw.setHeader('etag', etag);\n raw.setHeader('cache-control', 'private, no-cache');\n raw.end();\n}\n\n/**\n * How a detail endpoint answers a poll whose `If-None-Match` still names the current version.\n * Thrown rather than returned because Nest sets a handler's status after the handler returns; an\n * {@link NotModifiedFilter} writes the bodiless 304 itself.\n */\nexport class NotModifiedException extends HttpException {\n constructor(readonly etag: string) {\n super('Not Modified', 304);\n }\n}\n\n/** Writes a {@link NotModifiedException} as a bare 304, on Express and Fastify alike. */\n@Catch(NotModifiedException)\nexport class NotModifiedFilter implements ExceptionFilter<NotModifiedException> {\n catch(exception: NotModifiedException, host: ArgumentsHost): void {\n writeNotModifiedRaw(host.switchToHttp().getResponse(), exception.etag);\n }\n}\n\n/**\n * Mark a response as revalidatable: `no-cache` makes the browser ask again on every poll, sending\n * the `ETag` back as `If-None-Match`, so an unchanged run costs a 304 instead of a body.\n */\nexport function setEtag(response: unknown, etag: string): void {\n setRawHeader(response, 'etag', etag);\n setRawHeader(response, 'cache-control', 'private, no-cache');\n}\n","import type { RunListItem } from '@dudousxd/nestjs-durable-core';\n\n/**\n * A run as the CONSOLE'S LIST needs it: every field a row or its derived state reads, and none of the\n * payloads only the detail view opens.\n *\n * `input`, `output` and `error` are dropped. On a real control plane they dominate the response —\n * measured against a 9.5k-run deployment, `error` alone (stack traces on every failed run) was 63% of\n * a 12 MB listing — while no list row, and no `deriveRunState` input, ever reads them. The detail\n * endpoint (`GET runs/:id`) still returns the whole run, which is where those three are actually\n * rendered.\n */\nexport type RunListRow = Omit<RunListItem, 'input' | 'output' | 'error'>;\n\n/** Project a gateway run down to {@link RunListRow}. Deletes rather than re-lists the kept fields, so\n * a field added to `WorkflowRun` reaches the console automatically instead of being silently\n * dropped by a projection nobody remembered to update. */\nexport function toRunListRow(run: RunListItem): RunListRow {\n const { input: _input, output: _output, error: _error, ...row } = run;\n return row;\n}\n","import type { RunStatus } from '@dudousxd/nestjs-durable-core';\nimport { BaseFilter, FilterFor, Filterable } from '@dudousxd/nestjs-filter';\nimport { Injectable } from '@nestjs/common';\nimport { parseAttrFilters } from './attr-filter.js';\nimport { DurableRun } from './durable-run.js';\nimport type { RunQueryDraft } from './run-query-draft.js';\nimport { RUN_QUERY_ADAPTER } from './run-query.adapter.js';\n\n/**\n * A repeated query param arrives as an array and a single one as a scalar; both mean a list. An\n * absent or blank entry is dropped rather than stringified: `?namespace=` is what a cleared filter\n * box sends, and `String(undefined)` would filter for a tenant literally named \"undefined\".\n */\nfunction list(value: unknown): string[] {\n return (Array.isArray(value) ? value : [value])\n .filter((v) => v !== undefined && v !== null && v !== '')\n .map(String);\n}\n\n/** A bound from the query string. A non-numeric or negative value is treated as absent rather than\n * coerced to 0, which would return an empty page and read as \"there are no runs\". */\nfunction bound(value: unknown): number | undefined {\n const n = Number(value);\n return value !== undefined && value !== '' && Number.isInteger(n) && n >= 0 ? n : undefined;\n}\n\n/**\n * The console's run filter.\n *\n * Every predicate is reachable two ways, and both are the same request to this class: the flat form\n * the console has always sent (`?status=failed&tag=etl`, repeatable for a set) is dispatched to the\n * `@FilterFor` methods below, and the structured form (`filter[where][...]`, what\n * `@dudousxd/nestjs-filter-client`'s `filterQuery()` builds) goes through the adapter's\n * `applyColumnFilters`. Keeping the flat spelling working is not politeness to old callers — it is\n * what lets a run row's tag chip stay a plain link.\n *\n * `autoFields: false`: every key is declared here or refused. The fields exist for a REASON that\n * auto-application cannot know — `tag` is set membership, `origin` has an absent bucket, `attr.*` is\n * typed — and a key silently auto-applied as `equals` would be wrong for three of the five.\n */\n@Injectable()\n@Filterable({ entity: DurableRun, adapter: RUN_QUERY_ADAPTER, autoFields: false })\nexport class RunFilter extends BaseFilter<RunQueryDraft> {\n /** One status narrows; several match ANY of them — how a caller asks for a SET (the console's\n * in-flight sibling query, for one). */\n @FilterFor('status')\n applyStatus(value: unknown): void {\n const values = list(value) as RunStatus[];\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { status: values[0] } : { statuses: values });\n }\n\n @FilterFor('workflow')\n applyWorkflow(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { workflow: values[0] } : { workflows: values });\n }\n\n /** A blank param (`?namespace=`, from a cleared filter box) is the same as an absent one; passing\n * `''` through would be an exact match on a tenant nobody has, i.e. a silently empty console. */\n @FilterFor('namespace')\n applyNamespace(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { namespace: values[0] } : { namespaces: values });\n }\n\n @FilterFor('tag')\n applyTag(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { tag: values[0] } : { tags: values });\n }\n\n /**\n * Origin has THREE states rather than two — a named package, the unattributed bucket, or no\n * restriction — and the bucket rides on its own `unattributed` param rather than on a reserved\n * `origin` value, because any reserved string is a package name someone can legitimately have and a\n * console that quietly reinterpreted it would show the wrong runs.\n *\n * Both keys resolve through one function, called from either, reading the RAW input each time:\n * the answer depends on both params, and `@FilterFor` dispatch follows the order the client's keys\n * happen to arrive in. Recomputing from the input makes the result the same either way, where\n * accumulating onto the draft would make `?origin=x&unattributed=true` mean different things\n * depending on key order.\n *\n * Sent together they are a UNION, not a contradiction: `origins` carries the absent bucket\n * alongside named packages, which is the \"this package plus the runs nothing claims\" view that a\n * single `origin` value cannot express at all.\n */\n @FilterFor('origin')\n applyOrigin(): void {\n this.resolveOrigin();\n }\n\n @FilterFor('unattributed')\n applyUnattributed(): void {\n this.resolveOrigin();\n }\n\n private resolveOrigin(): void {\n const named = list(this.$input.origin);\n const raw = this.$input.unattributed;\n const wantsUnattributed = raw === 'true' || raw === '1' || raw === true;\n const values: Array<string | null> = [...named, ...(wantsUnattributed ? [null] : [])];\n if (values.length === 0) return;\n if (values.length === 1) {\n const only = values[0] as string | null;\n this.$query.narrow({ origin: only });\n return;\n }\n this.$query.narrow({ origins: values });\n }\n\n /** Comma/repeat-separated `key:op:value` predicates (e.g. `amount:gte:200`), ANDed. The structured\n * spelling of the same thing is `filter[where][attr.amount][gte]=200`. */\n @FilterFor('attr')\n applyAttributes(value: unknown): void {\n for (const filter of parseAttrFilters(value as string | string[]) ?? []) {\n this.$query.attribute(filter);\n }\n }\n\n @FilterFor('limit')\n applyLimit(value: unknown): void {\n const limit = bound(value);\n if (limit !== undefined) this.$query.narrow({ limit });\n }\n\n @FilterFor('offset')\n applyOffset(value: unknown): void {\n const offset = bound(value);\n if (offset !== undefined) this.$query.narrow({ offset });\n }\n}\n","import type { AttributeFilter, AttributeOp, AttributeValue } from '@dudousxd/nestjs-durable-core';\n\nconst SCALAR_OPS = new Set<AttributeOp>(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']);\n\n/** Separates the operands of an `in` predicate. `,` already separates whole predicates and `:`\n * already delimits the parts, so the set needs a third character that neither can be. */\nconst IN_SEPARATOR = '|';\n\n/** Coerce a query-string value: `true`/`false` → boolean, numeric → number, else the raw string. */\nfunction coerce(v: string): AttributeValue {\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 *\n * `in` takes a `|`-separated SET — `?attr=tier:in:pro|enterprise` — which is the flat spelling of the\n * predicate a multi-select produces. It needs its own operator because two `eq` predicates on one key\n * are ANDed like every other pair, and no run satisfies both.\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 || rest.length === 0) continue;\n const operand = rest.join(':');\n if (op === 'in') {\n const values = operand.split(IN_SEPARATOR).filter(Boolean).map(coerce);\n if (values.length) filters.push({ key, op: 'in', values });\n continue;\n }\n if (!SCALAR_OPS.has(op as AttributeOp)) continue;\n filters.push({ key, op: op as Exclude<AttributeOp, 'in'>, value: coerce(operand) });\n }\n return filters.length ? filters : undefined;\n}\n","import {\n type AttributeOp,\n type AttributeValue,\n RUN_VALUE_FACET_LIMIT,\n RunGateway,\n type RunListItem,\n type RunQuery,\n type RunStatus,\n} from '@dudousxd/nestjs-durable-core';\nimport type {\n ColumnFilter,\n EntityFieldInfo,\n FilterAdapter,\n GroupByCountField,\n SortItem,\n} from '@dudousxd/nestjs-filter';\nimport { BadRequestException, Injectable, type Type } from '@nestjs/common';\nimport { ATTRIBUTE_FIELD, DURABLE_RUN_FIELDS, DurableRun, runValueAxisFor } from './durable-run.js';\nimport { RunQueryDraft } from './run-query-draft.js';\n\n/** DI token for {@link RunQueryAdapter} — named on `@Filterable({ adapter })` so the run filter uses\n * THIS adapter even in a host application whose global filter adapter is its own ORM's. */\nexport const RUN_QUERY_ADAPTER = Symbol('DURABLE_RUN_QUERY_ADAPTER');\n\n/** The only ordering a run listing has (`createdAt DESC`, newest first), fixed by the store adapters. */\nconst FIXED_ORDER: SortItem = { field: 'createdAt', direction: 'desc' };\n\n/**\n * Coerce a query-string operand: `true`/`false` → boolean, numeric → number, else the raw string.\n * Search attributes are typed (a `gte` on a number must compare numerically), and a GET route\n * delivers every operand as text.\n */\nfunction coerce(value: unknown): AttributeValue {\n if (typeof value === 'number' || typeof value === 'boolean') return value;\n const raw = String(value);\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw !== '' && !Number.isNaN(Number(raw))) return Number(raw);\n return raw;\n}\n\n/** A filter operand as a list: the `in` family carries an array, every other operator one value. */\nfunction operands(clause: ColumnFilter): unknown[] {\n return Array.isArray(clause.value) ? clause.value : [clause.value];\n}\n\n/** The error every unsupported translation raises, naming what was asked and what this backend can\n * answer — the alternative (dropping the clause) returns a wider result set that reads as success. */\nfunction unsupported(clause: ColumnFilter, supported: string): never {\n throw new BadRequestException(\n `The durable run list cannot filter \\`${clause.field}\\` with \\`${clause.operator}\\`. Supported here: ${supported}.`,\n );\n}\n\n/**\n * A {@link FilterAdapter} over the durable **run gateway** rather than over an ORM.\n *\n * This is what lets the console's filters be written with `@dudousxd/nestjs-filter` — its wire format,\n * its operator allowlist, its `groupByCount` — while the data still comes from `RunGateway`, and so\n * still works on every store adapter AND on a tenant deployment that has no store at all, only a\n * proxy over the transport.\n *\n * The adapter's \"query builder\" is a {@link RunQueryDraft} (see it for why), and translation is\n * TOTAL rather than best-effort: `RunQuery` has a fixed set of predicates, so a field/operator pair\n * outside it raises {@link BadRequestException} instead of being ignored. That is the difference\n * between a console that says \"I can't filter by that\" and one that quietly shows every run.\n */\n@Injectable()\nexport class RunQueryAdapter implements FilterAdapter {\n constructor(private readonly gateway: RunGateway) {}\n\n createQueryBuilder<E>(entity: Type<E>): unknown {\n this.assertDurableRun(entity);\n return new RunQueryDraft();\n }\n\n getEntityFields(entity: Type<unknown>): EntityFieldInfo[] | null {\n return entity === DurableRun ? DURABLE_RUN_FIELDS : null;\n }\n\n /** No relations: a run is a flat row here, and the one nested-looking address (`attr.<key>`) is a\n * JSON path. Returning `[]` (not null) tells the runner the answer is \"none\", not \"can't say\". */\n getEntityRelations(): [] {\n return [];\n }\n\n /**\n * Which addresses resolve, and as what. `attr` and `attr.<key>` are JSON paths — that classification\n * is what routes a flat `?attr.tier=pro` through the same translation as a structured\n * `where[attr.tier]`, instead of it being dropped as an unknown key.\n */\n resolveFieldPath(entity: Type<unknown>, path: string): 'field' | 'relation' | 'json' | null {\n if (entity !== DurableRun) return null;\n if (path === ATTRIBUTE_FIELD || path.startsWith(`${ATTRIBUTE_FIELD}.`)) return 'json';\n return DURABLE_RUN_FIELDS.some((f) => f.name === path) ? 'field' : null;\n }\n\n applyColumnFilters(qb: unknown, filters: ColumnFilter[], entity?: Type<unknown>): void {\n if (entity) this.assertDurableRun(entity);\n const draft = this.draft(qb);\n for (const clause of filters) {\n // A run query is a flat AND of named predicates — there is no nesting to put a group INSIDE.\n if (clause.AND || clause.OR) {\n throw new BadRequestException(\n 'The durable run list cannot filter with nested AND/OR groups: its predicates are a flat conjunction.',\n );\n }\n this.applyClause(draft, clause);\n }\n }\n\n private applyClause(draft: RunQueryDraft, clause: ColumnFilter): void {\n const { field, operator } = clause;\n if (field === ATTRIBUTE_FIELD || field.startsWith(`${ATTRIBUTE_FIELD}.`)) {\n this.applyAttributeClause(draft, clause);\n return;\n }\n const anyOf = operator === 'in' || operator === 'isAnyOf';\n switch (field) {\n case 'status':\n if (operator === 'equals') {\n draft.narrow({ status: String(clause.value) as RunStatus });\n } else if (anyOf) {\n draft.narrow({ statuses: operands(clause).map((v) => String(v) as RunStatus) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'workflow':\n if (operator === 'equals') {\n draft.narrow({ workflow: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ workflows: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'namespace':\n if (operator === 'equals') {\n draft.narrow({ namespace: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ namespaces: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'tag':\n // Membership, not equality with the set — see `DurableRun.tag`.\n if (operator === 'equals') {\n draft.narrow({ tag: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ tags: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'origin':\n // `null` is a real value on this axis (the unattributed bucket), so it survives translation\n // instead of being coerced to the string \"null\".\n if (operator === 'isNull') {\n draft.narrow({ origin: null });\n } else if (operator === 'equals') {\n draft.narrow({ origin: clause.value === null ? null : String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ origins: operands(clause).map((v) => (v === null ? null : String(v))) });\n } else {\n // `isNotNull` is deliberately absent: \"any origin at all\" is not a predicate `RunQuery`\n // has, and answering it by listing every known origin would silently miss the ones with no\n // runs.\n unsupported(clause, 'equals, in, isNull');\n }\n return;\n default:\n unsupported(clause, `one of ${DURABLE_RUN_FIELDS.map((f) => f.name).join(', ')}`);\n }\n }\n\n /** `attr.<key>` → one {@link AttributeFilter}. The operator set is the store's, so the mapping is\n * 1:1 and anything outside it is rejected rather than approximated. */\n private applyAttributeClause(draft: RunQueryDraft, clause: ColumnFilter): void {\n const key = clause.field.slice(ATTRIBUTE_FIELD.length + 1);\n if (!key) {\n throw new BadRequestException(\n 'Filtering search attributes needs a key: `attr.<key>`, e.g. `attr.tier`.',\n );\n }\n const scalarOps: Record<string, Exclude<AttributeOp, 'in'>> = {\n equals: 'eq',\n notEquals: 'ne',\n gt: 'gt',\n gte: 'gte',\n lt: 'lt',\n lte: 'lte',\n };\n const scalar = scalarOps[clause.operator as string];\n if (scalar) {\n draft.attribute({ key, op: scalar, value: coerce(clause.value) });\n return;\n }\n if (clause.operator === 'in' || clause.operator === 'isAnyOf') {\n draft.attribute({ key, op: 'in', values: operands(clause).map(coerce) });\n return;\n }\n unsupported(clause, 'equals, notEquals, gt, gte, lt, lte, in');\n }\n\n applyOffsetPagination(qb: unknown, page: number, size: number): void {\n this.draft(qb).narrow({ limit: size, offset: (page - 1) * size });\n }\n\n /**\n * A run listing has ONE order — newest first — because that is what every store adapter's\n * `listRuns` emits and what the console's paging depends on. A request for any other sort is\n * refused rather than accepted and ignored: silently returning a differently-ordered page is how a\n * paginated list starts duplicating and skipping rows.\n */\n applySort(_qb: unknown, sorts: SortItem[]): void {\n for (const sort of sorts) {\n if (sort.field !== FIXED_ORDER.field || sort.direction !== FIXED_ORDER.direction) {\n throw new BadRequestException(\n `The durable run list is always ordered \\`${FIXED_ORDER.field} ${FIXED_ORDER.direction}\\` and cannot be sorted by \\`${sort.field} ${sort.direction}\\`.`,\n );\n }\n }\n }\n\n getResult(qb: unknown): Promise<RunListItem[]> {\n return this.gateway.listRuns(this.draft(qb).query);\n }\n\n /**\n * A page plus the size of the whole matching set. The total comes from the facet aggregate rather\n * than from a second listing — that is the entire reason durable exposes one, and it keeps the\n * count exact while the page stays bounded.\n */\n async getResultAndCount(qb: unknown): Promise<{ rows: unknown[]; total: number }> {\n const draft = this.draft(qb);\n const [rows, facets] = await Promise.all([\n this.gateway.listRuns(draft.query),\n this.gateway.runFacets(draft.facetQuery()),\n ]);\n const { status, statuses, origin, origins } = draft.query;\n const total = facets\n .filter((cell) => {\n if (status && cell.status !== status) return false;\n if (statuses && !statuses.includes(cell.status)) return false;\n if (origin !== undefined && cell.origin !== origin) return false;\n if (origins && !origins.includes(cell.origin)) return false;\n return true;\n })\n .reduce((sum, cell) => sum + cell.count, 0);\n return { rows, total };\n }\n\n /**\n * The distinct values of one field over the runs the active predicates select — the console's\n * pickers. `limit` is honoured because it has to be: tag and search-attribute cardinality grows\n * with the data, so the unbounded answer is a listing rather than an aggregate. `offset` and\n * `search` are what keep that bound from becoming a ceiling — the picker pages as it scrolls and\n * narrows as the operator types, both against the whole matching set rather than the fetched page.\n */\n async groupByCount(\n qb: unknown,\n field: GroupByCountField,\n entity: Type<unknown>,\n opts?: { bucket?: number; limit?: number; offset?: number; search?: string },\n ): Promise<Array<{ value: unknown; count: number }>> {\n this.assertDurableRun(entity);\n if (typeof field !== 'string') {\n throw new BadRequestException(\n 'The durable run list has no computed fields to group by — group by one of its own fields.',\n );\n }\n if (opts?.bucket !== undefined) {\n throw new BadRequestException(\n 'The durable run list cannot bucket a group-by-count: none of its filterable fields is numeric.',\n );\n }\n const axis = runValueAxisFor(field);\n if (!axis) {\n throw new BadRequestException(\n `The durable run list cannot enumerate values of \\`${field}\\`.`,\n );\n }\n const rows = await this.gateway.runValueFacets(axis, this.draft(qb).facetQuery(), {\n limit: opts?.limit ?? RUN_VALUE_FACET_LIMIT,\n ...(opts?.offset !== undefined && { offset: opts.offset }),\n ...(opts?.search !== undefined && { search: opts.search }),\n });\n return rows.map((row) => ({ value: row.value, count: row.count }));\n }\n\n getPrimaryKey(): string {\n return 'id';\n }\n\n private draft(qb: unknown): RunQueryDraft {\n if (!(qb instanceof RunQueryDraft)) {\n throw new Error(\n 'The durable run adapter was handed a query builder it did not create. A filter using it must declare `@Filterable({ entity: DurableRun, adapter: RUN_QUERY_ADAPTER })`.',\n );\n }\n return qb;\n }\n\n private assertDurableRun(entity: Type<unknown>): void {\n if (entity !== DurableRun) {\n throw new Error(\n `The durable run adapter only answers for DurableRun, not ${entity?.name ?? String(entity)}.`,\n );\n }\n }\n}\n\n/** Re-exported for a host that wants to narrow the query itself (see `RunQueryDraft`). */\nexport type { RunQuery };\n","import type { AttributeFilter, RunFacetQuery, RunQuery } from '@dudousxd/nestjs-durable-core';\n\n/**\n * What this adapter hands the filter runner in place of a query builder: a {@link RunQuery} under\n * construction.\n *\n * The runner never inspects it — it only passes it back to the adapter's own methods — so the shape\n * is ours to choose, and a plain predicate bag is the honest one. There is no SQL to append to here;\n * a run query is a fixed set of named predicates that the gateway resolves, so \"building\" it is\n * filling fields in, and everything the filter lib can express but `RunQuery` cannot is rejected at\n * the point of translation rather than accumulated and silently dropped.\n */\nexport class RunQueryDraft {\n readonly query: RunQuery = {};\n\n /** Narrow by one or more predicates. Later calls overwrite the same field — the runner applies one\n * clause per field, so the last one wins, exactly as a repeated `where` on a builder would. */\n narrow(patch: RunQuery): void {\n Object.assign(this.query, patch);\n }\n\n /** Add one search-attribute predicate. These ACCUMULATE (unlike {@link narrow}) because\n * `RunQuery.attributes` is an ANDed list — two predicates on different keys are both meant. */\n attribute(filter: AttributeFilter): void {\n this.query.attributes = [...(this.query.attributes ?? []), filter];\n }\n\n /** The predicates minus the page window and the axes a facet count reports on — what\n * `runFacets`/`runValueFacets` are taken over. */\n facetQuery(): RunFacetQuery {\n const { status, statuses, origin, origins, limit, offset, ...facetQuery } = this.query;\n return facetQuery;\n }\n}\n","import 'reflect-metadata';\nimport { ApplyFilterInterceptor, FilterRunner } from '@dudousxd/nestjs-filter';\nimport { type DynamicModule, Module, type Provider, type Type } from '@nestjs/common';\nimport { ModuleRef, RouterModule } from '@nestjs/core';\nimport {\n CONSOLE_AUTHORIZE_DECIDE,\n type ConsoleAuthorize,\n UseConsoleAuthorize,\n resolveConsoleAuthorize,\n} from './console-authorize.js';\nimport { controllerFor, markOpenToAuthorization } from './controller-decorators.js';\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';\nimport { RUN_QUERY_ADAPTER, RunQueryAdapter } from './run-query.adapter.js';\nimport { RunFilter } from './run.filter.js';\n\n// Every controller this module mounts is open to `@nestjs/authorization`'s global guard, which\n// denies by default: access is decided by `authorize` (the console's own guard), and a `@Can()`\n// passed in `decorators` is still enforced. The `*Base` classes a host extends for `routes: false`\n// are NOT marked, so the official guard's deny-by-default applies to a self-mounted console until it\n// declares a `@Can()` (or `@Can.Anyone()`).\nmarkOpenToAuthorization(DurableApiController);\nmarkOpenToAuthorization(DurableUiController);\n\nexport interface DurableDashboardOptions {\n /**\n * Who may use the console (the SPA, its JSON API, the run stream and the webhook route). One of:\n *\n * - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);\n * - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;\n * its dependencies resolve from {@link imports} or global modules);\n * - a lazy import `() => import('./console-access.js')` whose default export is such a class;\n * - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;\n * - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.\n *\n * `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in\n * {@link decorators}, so `request.user` is already set.\n *\n * Omitted: the console is open outside production and answers `403` to every request when\n * `NODE_ENV=production`, and one warning is logged at boot.\n */\n authorize?: ConsoleAuthorize;\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 * Extra `imports` merged into the dashboard's dynamic module: the DI resolution path for a guard\n * passed through {@link decorators} (`UseGuards(...)`), whose dependencies resolve from the module\n * that hosts the controller. Typically the host's own auth module, unless it is global.\n */\n imports?: DynamicModule['imports'];\n /**\n * Class decorators applied to BOTH controllers this module mounts (the SPA and its JSON API), the\n * way `@nestjs/mcp`'s own `decorators` option works. Use it to hand the console to the official NestJS tooling, e.g.\n * `[UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]` with `@nestjs/authentication`\n * and `@nestjs/authorization`.\n *\n * Each registration gets fresh subclasses of the controllers, so decorators never leak into\n * another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves\n * its dependencies from {@link imports} (or a global module).\n *\n * `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs\n * after these. Use `decorators` for what must run first, typically authentication.\n *\n * The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard\n * otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still\n * enforced.\n */\n decorators?: ClassDecorator[];\n /**\n * `false` mounts NO controllers: the SPA and its JSON API are not registered,\n * while every provider still is (the run read/action service, the run filter, the SPA's base\n * paths). Serve the console from controllers of your own instead, by extending\n * {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them\n * with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),\n * or protect them with ordinary Nest decorators:\n *\n * ```ts\n * @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)\n * class ConsoleUi extends DurableUiControllerBase {}\n * @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)\n * class ConsoleApi extends DurableApiControllerBase {}\n * // in the module that declares them:\n * imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]\n * ```\n *\n * `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and\n * the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.\n *\n * `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request\n * through outside production and answers `403` in production, and the boot warning is logged.\n * The `*Base` classes carry no\n * guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass\n * without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than\n * being silently ignored.\n *\n * Default `true`. Named `routes` rather than `path: false` because `basePath`/`apiBasePath` are\n * strings that keep their meaning (where the console lives) whether or not this module mounts it.\n */\n routes?: boolean;\n}\n\n/** `routes: false` mounts nothing, so `decorators` would decorate nothing. Refuse it at boot. */\nfunction assertNothingToDecorate(options: DurableDashboardOptions): void {\n if (options.decorators === undefined) return;\n throw new Error(\n [\n 'DurableDashboardModule.forRoot({ routes: false }) mounts no controllers, so `decorators`',\n 'would protect nothing. Put your protection on the controllers you mount (extend',\n 'DurableUiControllerBase / DurableApiControllerBase and decorate them with @UseGuards /',\n '@Can), and drop `decorators`.',\n ].join(' '),\n );\n}\n\n/** The app's `decorators` first, then the console guard, so `authorize` sees `request.user`. */\nfunction consoleController<T extends Type>(base: T, decorators: ClassDecorator[] | undefined): T {\n return controllerFor(base, [...(decorators ?? []), UseConsoleAuthorize()]);\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/**\n * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own\n * dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their\n * controller's host module, so this module, not the outer wrapper, must carry the host's `imports`.\n */\n@Module({})\nexport class DurableApiModule {\n static register(options: {\n imports?: DynamicModule['imports'];\n /** `false`: register the providers only, no controller (see `routes`). */\n routes?: boolean;\n /** See {@link DurableDashboardOptions.decorators}. */\n decorators?: ClassDecorator[];\n /** See {@link DurableDashboardOptions.authorize}. */\n authorize?: ConsoleAuthorize;\n }): DynamicModule {\n const routes = options.routes !== false;\n return {\n module: DurableApiModule,\n imports: [...(options.imports ?? [])],\n controllers: routes ? [consoleController(DurableApiController, options.decorators)] : [],\n providers: [\n DashboardService,\n // Resolved at boot: a gate name without nestjs-authz, or a policy without\n // @nestjs/authorization, fails here rather than on the first request.\n {\n provide: CONSOLE_AUTHORIZE_DECIDE,\n // Also with `routes: false`: `@UseConsoleAuthorize()` still applies the production 403.\n useFactory: (moduleRef: ModuleRef) =>\n resolveConsoleAuthorize(options.authorize, moduleRef),\n inject: [ModuleRef],\n },\n // `@dudousxd/nestjs-filter` is wired PRIVATELY here rather than through\n // `FilterModule.forRoot()`, which is a global registration. A library calling that would\n // fight the host app over one set of module options and register a second app-wide\n // interceptor — and it cannot tell at module-definition time whether the host already\n // called it. Everything below is scoped to this module, so the console composes with an app\n // that uses the filter library, and with one that has never heard of it, identically.\n //\n // `validation: 'off'`: the run filter's inputs are validated by their own translation —\n // every field/operator pair either maps onto a `RunQuery` predicate or is rejected by\n // name — so there is nothing left for class-validator to check, and requiring it would put\n // a peer dependency on every consumer of this dashboard for no gain.\n //\n // The `null` adapter is the GLOBAL one, deliberately absent: `RunFilter` names its own via\n // `@Filterable({ adapter })`, so the console runs on the durable run gateway while the host\n // app's filters keep running on whatever adapter it registered.\n {\n provide: FilterRunner,\n useFactory: (moduleRef: ModuleRef) =>\n new FilterRunner(moduleRef, { validation: 'off' }, null),\n inject: [ModuleRef],\n },\n ApplyFilterInterceptor,\n RunQueryAdapter,\n { provide: RUN_QUERY_ADAPTER, useExisting: RunQueryAdapter },\n RunFilter,\n ],\n // Self-mounted (`routes: false`): the host's own subclass of `DurableApiControllerBase` is\n // hosted in the HOST's module, so what its constructor injects must be exported. `FilterRunner`\n // is only exported then: exporting it from a mounted console would shadow a host's own\n // `FilterModule` runner in whatever module imports the dashboard.\n exports: routes\n ? [DashboardService, CONSOLE_AUTHORIZE_DECIDE]\n : [DashboardService, FilterRunner, CONSOLE_AUTHORIZE_DECIDE],\n };\n }\n}\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.\n *\n * Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open\n * outside production and denies every request in production, with one warning at boot.\n * `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see\n * {@link DurableDashboardOptions.routes}).\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 const pathProviders: Provider[] = [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n ];\n if (options.routes === false) {\n assertNothingToDecorate(options);\n const api = DurableApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n routes: false,\n });\n return {\n module: DurableDashboardModule,\n imports: [...(options.imports ?? []), api],\n controllers: [],\n providers: pathProviders,\n // What a host's subclasses of `DurableUiControllerBase` / `DurableApiControllerBase` inject:\n // the SPA's paths, the read/action service, the filter runner and the resolved `authorize`\n // (for `@UseConsoleAuthorize()`) via the API module.\n exports: [DurableApiModule, DASHBOARD_BASE_PATH, DASHBOARD_API_PATH],\n };\n }\n return {\n module: DurableDashboardModule,\n imports: [\n ...(options.imports ?? []),\n // Host imports must reach the API controller's HOST module: enhancers resolve from their\n // controller's own module, never from a parent (see DurableApiModule.register).\n // Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.\n DurableApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.decorators ? { decorators: options.decorators } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n }),\n RouterModule.register([\n { path: basePath, module: DurableDashboardModule }, // the UI controller below\n { path: apiBasePath, module: DurableApiModule },\n ]),\n ],\n controllers: [consoleController(DurableUiController, options.decorators)],\n providers: pathProviders,\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 'reflect-metadata';\nimport type { Type } from '@nestjs/common';\n\n/**\n * `@nestjs/authorization`'s `CAN_ANYONE_METADATA` key (what `@Can.Anyone()` writes), INLINED: the\n * constant is not exported, and this package takes no dependency on `@nestjs/authorization`. A drift\n * spec asserts that `Can.Anyone()` still writes exactly this key.\n */\nexport const CAN_ANYONE_METADATA = 'authorization:can-anyone';\n\n/**\n * Marks a controller as open to `@nestjs/authorization`'s global `AuthorizationGuard`, which\n * otherwise denies every route that declares no `@Can()`. A `@Can()` passed through `decorators` is\n * still enforced: the official guard only treats `can-anyone` as open when the route declares no\n * requirement.\n */\nexport function markOpenToAuthorization(controller: Type): void {\n Reflect.defineMetadata(CAN_ANYONE_METADATA, true, controller);\n}\n\n/**\n * The controller one registration mounts. With no `decorators`, the shared class itself (unchanged\n * behaviour). With `decorators`, a FRESH subclass made for this registration only, so decorators\n * given to one `forRoot(...)` never stick to the next registration, another app in the same\n * process, or the exported class (the approach `@nestjs/mcp`'s `createMcpController` takes).\n *\n * The subclass inherits routes, filters, interceptors and constructor injection from the base.\n * Decorators are applied in array order, like `applyDecorators`.\n */\nexport function controllerFor<T extends Type>(\n base: T,\n decorators: readonly ClassDecorator[] | undefined,\n): T {\n if (decorators === undefined || decorators.length === 0) return base;\n const derived = class extends base {};\n // Nest logs, `AuthorizationGuard` denial events and route tables read the class name.\n Object.defineProperty(derived, 'name', { value: base.name });\n markOpenToAuthorization(derived);\n let decorated: T = derived;\n for (const decorator of decorators) {\n // A class decorator may return a replacement class, as with `@Decorator() class X {}`.\n decorated = (decorator(decorated) as T | undefined) ?? decorated;\n }\n return decorated;\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';\nimport { ConsolePage } from './console-authorize.js';\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 controller routes are relative: the mounted {@link DurableUiController} gets its path from\n * `RouterModule` (set by `DurableDashboardModule.forRoot({ basePath })`).\n *\n * This base is what a host EXTENDS to serve the console from a controller of its own (with\n * `forRoot({ routes: false, basePath })`): `@Controller('<basePath>') class MyConsole extends\n * DurableUiControllerBase {}`, protected with the host's own decorators. It carries no guards and\n * no `@nestjs/authorization` mark, so a subclass is protected only by what the host puts on it (and,\n * under `AuthorizationModule`'s global guard, is denied until it declares a `@Can()`).\n */\n@Controller()\n@ConsolePage()\nexport class DurableUiControllerBase {\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\n/**\n * The SPA controller `DurableDashboardModule.forRoot(...)` mounts at `basePath`. The module puts\n * the `@nestjs/authorization` open mark on THIS class (and `decorators` on a per-registration\n * subclass of it), never on {@link DurableUiControllerBase}, so a host's own subclass of the base\n * inherits none of them.\n */\n@Controller()\nexport class DurableUiController extends DurableUiControllerBase {}\n"],"mappings":"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","names":["CONSOLE_GATE","Symbol","for","PACKAGE","MODULE","CONSOLE_AUTHORIZE_MISSING_WARNING","allow","allowed","forbidden","reason","unauthenticated","isProduction","process","env","NODE_ENV","trim","toLowerCase","isAuthorizerClass","value","prototype","authorize","test","Function","toString","call","isPolicyCheck","policy","ability","isLazyModule","default","normalizeConsoleResult","result","shape","redirectTo","hasUser","user","undefined","policyDecider","check","moduleRef","pkg","Error","AuthorizationService","AUTHORIZATION_MODULE_OPTIONS","defaultGetUser","get","strict","getUser","options","context","authorization","can","gateDecider","name","gate","allows","classDecider","instances","Map","instanceOf","Class","cached","pending","registered","create","catch","delete","set","resolveConsoleAuthorize","logger","Logger","warnIfMissing","warn","production","decide","fromClass","lazy","fn","error","message","String","sha256","createHash","update","digest","authorizeByToken","token","length","expected","header","switchToHttp","getRequest","headers","match","exec","timingSafeEqual","CONSOLE_AUTHORIZE_DECIDE","CONSOLE_PAGE_METADATA","ConsolePage","target","Reflect","defineMetadata","ConsoleAccessDenied","HttpException","decision","page","ConsoleAuthorizeGuard","canActivate","isPageNavigation","getType","getMetadata","getClass","request","accept","method","includes","consolePage","title","DENIED_PAGE","SIGN_IN_PAGE","ConsoleAuthorizeFilter","adapterHost","exception","host","adapter","httpAdapter","response","getResponse","redirect","forbiddenPage","setHeader","reply","getStatus","UseConsoleAuthorize","applyDecorators","UseGuards","UseFilters","import_common","DashboardService","metricsCollector","gateway","store","engine","collectMetrics","undefined","controlPlane","Error","listSchedules","query","schedules","supported","list","pauseSchedule","id","pause","resumeSchedule","resume","triggerSchedule","trigger","metrics","cap","pending","running","dead","Promise","all","listRuns","status","limit","gauges","length","health","workerHealth","push","h","group","depth","liveWorkers","prometheus","join","runFacets","runValueFacets","axis","opts","topology","getRunDetail","runId","options","getFanMembers","retryWithInput","input","retry","cancel","republishLifecycle","subscriber","workflowLifecycle","workflow","links","counts","triggerCountsOf","triggerCounts","snapshot","startsOnSettle","triggered","triggeredBySubscriber","failed","triggerFailedBySubscriber","bulk","action","filter","runs","applied","r","matched","continue","redispatch","redispatchPending","deliverWebhook","token","body","parsed","parseWebhookToken","run","getRun","TERMINAL_RUN_STATUSES","includes","resolved","getCheckpoint","seq","delivered","signal","now","takeBufferedSignal","fresh","getEvent","key","update","name","arg","streamRun","Observable","off","subscribe","event","next","data","import_nestjs_filter","import_common","DurableRun","workflow","status","origin","namespace","tag","attr","ATTRIBUTE_FIELD","DURABLE_RUN_FIELDS","name","columnName","type","runValueAxisFor","field","startsWith","key","slice","length","import_common","isRawResponse","value","candidate","setHeader","end","rawResponse","response","raw","setRawHeader","name","writeNotModifiedRaw","etag","statusCode","NotModifiedException","HttpException","NotModifiedFilter","catch","exception","host","switchToHttp","getResponse","setEtag","toRunListRow","run","input","_input","output","_output","error","_error","row","import_common","SCALAR_OPS","Set","IN_SEPARATOR","coerce","v","Number","isNaN","parseAttrFilters","attr","undefined","raw","Array","isArray","filters","entry","key","op","rest","split","length","operand","join","values","filter","Boolean","map","push","has","value","import_nestjs_durable_core","import_common","RunQueryDraft","query","narrow","patch","Object","assign","attribute","filter","attributes","facetQuery","status","statuses","origin","origins","limit","offset","RUN_QUERY_ADAPTER","Symbol","FIXED_ORDER","field","direction","coerce","value","raw","String","Number","isNaN","operands","clause","Array","isArray","unsupported","supported","BadRequestException","operator","RunQueryAdapter","gateway","createQueryBuilder","entity","assertDurableRun","RunQueryDraft","getEntityFields","DurableRun","DURABLE_RUN_FIELDS","getEntityRelations","resolveFieldPath","path","ATTRIBUTE_FIELD","startsWith","some","f","name","applyColumnFilters","qb","filters","draft","AND","OR","applyClause","applyAttributeClause","anyOf","narrow","status","statuses","map","v","workflow","workflows","namespace","namespaces","tag","tags","origin","origins","join","key","slice","length","scalarOps","equals","notEquals","gt","gte","lt","lte","scalar","attribute","op","values","applyOffsetPagination","page","size","limit","offset","applySort","_qb","sorts","sort","getResult","listRuns","query","getResultAndCount","rows","facets","Promise","all","runFacets","facetQuery","total","filter","cell","includes","undefined","reduce","sum","count","groupByCount","opts","bucket","axis","runValueAxisFor","runValueFacets","RUN_VALUE_FACET_LIMIT","search","row","getPrimaryKey","Error","list","value","Array","isArray","filter","v","undefined","map","String","bound","n","Number","isInteger","RunFilter","BaseFilter","applyStatus","values","length","$query","narrow","status","statuses","applyWorkflow","workflow","workflows","applyNamespace","namespace","namespaces","applyTag","tag","tags","applyOrigin","resolveOrigin","applyUnattributed","named","$input","origin","raw","unattributed","wantsUnattributed","only","origins","applyAttributes","parseAttrFilters","attribute","applyLimit","limit","applyOffset","offset","entity","DurableRun","adapter","RUN_QUERY_ADAPTER","autoFields","FAN_INLINE_MAX","DurableApiControllerBase","dashboard","runner","runs","draft","listRuns","query","map","toRunListRow","facets","runFacets","facetQuery","values","groupByCount","DurableRun","filterClass","RunFilter","metrics","workers","workerHealth","schedules","namespace","workflow","tag","paused","limit","listSchedules","Math","min","Number","pauseSchedule","id","catch","notFound","resumeSchedule","triggerSchedule","workflowLifecycle","name","links","notSupportedByServer","NotImplementedException","topology","run","full","ifNoneMatch","response","detail","getRunDetail","undefined","summarizeFansOver","NotFoundException","etag","notModified","body","NotModifiedException","setEtag","fanMembers","group","offset","failed","page","getFanMembers","failedOnly","stream","streamRun","retry","retryWithInput","input","bulk","action","compensate","filter","cancel","result","republishLifecycle","subscriber","error","message","Error","String","test","BadRequestException","code","continue","redispatch","webhook","token","deliverWebhook","event","key","getEvent","update","passthrough","DurableApiController","import_reflect_metadata","import_nestjs_filter","import_common","import_core","import_reflect_metadata","CAN_ANYONE_METADATA","markOpenToAuthorization","controller","Reflect","defineMetadata","controllerFor","base","decorators","undefined","length","derived","Object","defineProperty","value","name","decorated","decorator","import_common","DASHBOARD_BASE_PATH","Symbol","DASHBOARD_API_PATH","BUILD_BASE","spaDir","fileURLToPath","URL","CONTENT_TYPES","DurableUiControllerBase","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","DurableUiController","markOpenToAuthorization","DurableApiController","DurableUiController","assertNothingToDecorate","options","decorators","undefined","Error","join","consoleController","base","controllerFor","UseConsoleAuthorize","normalize","path","replace","DurableApiModule","register","routes","module","imports","controllers","providers","DashboardService","provide","CONSOLE_AUTHORIZE_DECIDE","useFactory","moduleRef","resolveConsoleAuthorize","authorize","inject","ModuleRef","FilterRunner","validation","ApplyFilterInterceptor","RunQueryAdapter","RUN_QUERY_ADAPTER","useExisting","RunFilter","exports","DurableDashboardModule","forRoot","basePath","apiBasePath","pathProviders","DASHBOARD_BASE_PATH","useValue","DASHBOARD_API_PATH","api","RouterModule"]}
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/server/console-authorize.ts","../../src/server/dashboard.service.ts","../../src/server/durable-api.controller.ts","../../src/server/durable-run.ts","../../src/server/not-modified.ts","../../src/server/run-list-row.ts","../../src/server/run.filter.ts","../../src/server/attr-filter.ts","../../src/server/run-query.adapter.ts","../../src/server/run-query-draft.ts","../../src/server/durable-dashboard.module.ts","../../src/server/console-switcher.ts","../../src/server/tokens.ts","../../src/server/controller-decorators.ts","../../src/server/dev-tools.ts","../../src/server/durable-ui.controller.ts"],"sourcesContent":["export * from './console-authorize.js';\nexport * from './dashboard.service.js';\nexport * from './durable-api.controller.js';\nexport * from './durable-dashboard.module.js';\nexport * from './durable-ui.controller.js';\n// Developer tools over MCP (the agent's dev endpoint) and the console switcher.\nexport {\n DEV_TOOLS_SLOT,\n MAX_RUN_IDS,\n DurableDevToolsRegistration,\n durableDevTools,\n type DevToolCall,\n type DevToolInputProperty,\n type DevToolInputSchema,\n type DevToolsDecision,\n type DurableDevTool,\n} from './dev-tools.js';\nexport {\n AVIARY_CONSOLES_SLOT,\n CONSOLE_PROBE_TIMEOUT_MS,\n DURABLE_CONSOLE_ID,\n DurableConsoleRegistration,\n DurableConsolesController,\n listVisibleConsoles,\n registerAviaryConsole,\n type AviaryConsoleDecision,\n type AviaryConsoleEntry,\n type AviaryConsoleIcon,\n type AviaryConsoleRegistry,\n type VisibleConsole,\n type VisibleConsoles,\n} from './console-switcher.js';\n","import 'reflect-metadata';\nimport { createHash, timingSafeEqual } from 'node:crypto';\nimport {\n type ArgumentsHost,\n type CanActivate,\n Catch,\n type ExceptionFilter,\n type ExecutionContext,\n HttpException,\n Inject,\n Injectable,\n Logger,\n type Type,\n UseFilters,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { HttpAdapterHost, ModuleRef } from '@nestjs/core';\n\n// ---------------------------------------------------------------------------------------------\n// The console access contract. Every Aviary console declares these LOCALLY with identical names\n// and shapes (no cross-lib import): TypeScript is structural, so one authorizer fits every console.\n// ---------------------------------------------------------------------------------------------\n\n/**\n * What an `authorize` check answers. An allow may name who it let in (`caller`, for example\n * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret\n * in it.\n */\nexport type ConsoleAuthorizeResult =\n | boolean\n | { allowed: true; caller?: string }\n | { allowed: false; reason?: 'unauthenticated' | 'forbidden' }\n | { redirectTo: string };\n\n/** Class form: an `@Injectable()` provider (constructor injection works). */\nexport interface ConsoleAuthorizer {\n authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;\n}\nexport type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;\n\n/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */\nexport interface ConsolePolicyCheck {\n policy: Type<unknown>;\n ability: string;\n}\n\nexport type ConsoleAuthorize =\n | ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>)\n | ConsoleAuthorizerClass\n | (() => Promise<{ default: ConsoleAuthorizerClass }>)\n | ConsolePolicyCheck\n | string;\n\nexport type ConsoleDecision =\n | { allowed: true; caller?: string }\n | { allowed: false; reason: 'unauthenticated' | 'forbidden'; redirectTo?: string };\n\n/** What a resolved `authorize` is: called once per request, never throws. */\nexport type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;\n\n/**\n * The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).\n * A registry symbol, so it is found without depending on that package.\n */\nexport const CONSOLE_GATE = Symbol.for('@dudousxd/aviary:console-gate');\nexport interface ConsoleGate {\n allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;\n}\n\nconst PACKAGE = '@dudousxd/nestjs-durable-dashboard';\nconst MODULE = 'DurableDashboardModule';\n\n/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */\nexport const CONSOLE_AUTHORIZE_MISSING_WARNING = `[${PACKAGE}] console has no \\`authorize\\` configured: it is open outside production and denies every request in production. Set \\`authorize\\` in ${MODULE}.forRoot().`;\n\n// Factories, not shared constants: every decision is a fresh object, so a caller mutating one can\n// never change the answer for the next request.\nconst allow = (caller?: string): ConsoleDecision =>\n caller !== undefined && caller !== '' ? { allowed: true, caller } : { allowed: true };\nconst forbidden = (): ConsoleDecision => ({ allowed: false, reason: 'forbidden' });\nconst unauthenticated = (): ConsoleDecision => ({ allowed: false, reason: 'unauthenticated' });\n\nfunction isProduction(): boolean {\n return (process.env.NODE_ENV ?? '').trim().toLowerCase() === 'production';\n}\n\n/**\n * A class with `authorize()`: on the prototype, or (an instance-property arrow function,\n * `authorize = (ctx) => ...`) only on instances, so any `class`.\n */\nfunction isAuthorizerClass(value: unknown): value is ConsoleAuthorizerClass {\n if (typeof value !== 'function') return false;\n if (\n typeof (value as { prototype?: { authorize?: unknown } }).prototype?.authorize === 'function'\n ) {\n return true;\n }\n return /^class[\\s{]/.test(Function.prototype.toString.call(value));\n}\n\nfunction isPolicyCheck(value: unknown): value is ConsolePolicyCheck {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ConsolePolicyCheck).policy === 'function' &&\n typeof (value as ConsolePolicyCheck).ability === 'string'\n );\n}\n\nfunction isLazyModule(value: unknown): value is { default: ConsoleAuthorizerClass } {\n return (\n typeof value === 'object' &&\n value !== null &&\n isAuthorizerClass((value as { default?: unknown }).default)\n );\n}\n\n/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */\nexport function normalizeConsoleResult(result: unknown): ConsoleDecision {\n if (result === true) return allow();\n if (typeof result !== 'object' || result === null) return forbidden();\n const shape = result as {\n allowed?: unknown;\n reason?: unknown;\n redirectTo?: unknown;\n caller?: unknown;\n };\n if (typeof shape.redirectTo === 'string' && shape.allowed !== true) {\n return { allowed: false, reason: 'unauthenticated', redirectTo: shape.redirectTo };\n }\n if (shape.allowed === true) {\n return allow(typeof shape.caller === 'string' ? shape.caller : undefined);\n }\n return shape.reason === 'unauthenticated' ? unauthenticated() : forbidden();\n}\n\n/** `null`, `undefined` and `false` mean \"nobody signed in\", like `@nestjs/authorization`'s guard. */\nfunction hasUser(user: unknown): boolean {\n return user !== null && user !== undefined && user !== false;\n}\n\nasync function policyDecider(\n check: ConsolePolicyCheck,\n moduleRef: ModuleRef,\n): Promise<ConsoleDecide> {\n let pkg: typeof import('@nestjs/authorization');\n try {\n pkg = await import('@nestjs/authorization');\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs @nestjs/authorization installed, with AuthorizationModule.forRoot() imported in the app; or pass a function, a class, or a gate name.`,\n );\n }\n const { AuthorizationService, AUTHORIZATION_MODULE_OPTIONS, defaultGetUser } = pkg;\n try {\n moduleRef.get(AuthorizationService, { strict: false });\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app.`,\n );\n }\n // The user the official `@Can()` sees: the module's `getUser` when one is configured, else the\n // package default (`request.user`). Exactly one of them answers; `undefined`, `null` and `false`\n // all mean a guest, as in `AuthorizationGuard`.\n let getUser = defaultGetUser as (context: ExecutionContext) => unknown;\n try {\n const options = moduleRef.get<{ getUser?: (context: ExecutionContext) => unknown }>(\n AUTHORIZATION_MODULE_OPTIONS,\n { strict: false },\n );\n if (typeof options?.getUser === 'function') getUser = options.getUser;\n } catch {\n // No options registered: the package default reads `request.user`.\n }\n return async (context) => {\n const authorization = moduleRef.get(AuthorizationService, { strict: false });\n const user = await getUser(context);\n const can = authorization.can as (\n policy: unknown,\n ability: string,\n user: unknown,\n ) => Promise<boolean>;\n if (await can.call(authorization, check.policy, check.ability, hasUser(user) ? user : null)) {\n return allow();\n }\n return hasUser(user) ? forbidden() : unauthenticated();\n };\n}\n\nfunction gateDecider(name: string, moduleRef: ModuleRef): ConsoleDecide {\n try {\n moduleRef.get(CONSOLE_GATE, { strict: false });\n } catch {\n throw new Error(\n `\\`authorize: '${name}'\\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }`,\n );\n }\n return async (context) => {\n const gate = moduleRef.get<ConsoleGate>(CONSOLE_GATE, { strict: false });\n return normalizeConsoleResult(await gate.allows(name, context));\n };\n}\n\nfunction classDecider(moduleRef: ModuleRef): (Class: ConsoleAuthorizerClass) => ConsoleDecide {\n const instances = new Map<ConsoleAuthorizerClass, Promise<ConsoleAuthorizer>>();\n const instanceOf = (Class: ConsoleAuthorizerClass): Promise<ConsoleAuthorizer> => {\n const cached = instances.get(Class);\n if (cached) return cached;\n const pending = (async (): Promise<ConsoleAuthorizer> => {\n try {\n // A registered provider is used as is; otherwise one instance per registration.\n const registered = moduleRef.get(Class, { strict: false });\n if (registered) return registered;\n } catch {\n // Not a provider: create it below.\n }\n return moduleRef.create(Class);\n })();\n // A failed construction is retried on the next request instead of denying forever.\n pending.catch(() => instances.delete(Class));\n instances.set(Class, pending);\n return pending;\n };\n return (Class) => async (context) =>\n normalizeConsoleResult(await (await instanceOf(Class)).authorize(context));\n}\n\n/**\n * Resolves `authorize` into one function the guard calls per request. Resolution errors that are\n * configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)\n * throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.\n *\n * `authorize` omitted: open outside production, `forbidden` for every request in production (read\n * when this runs), and one warning logged (unless `warnIfMissing: false`).\n */\nexport async function resolveConsoleAuthorize(\n authorize: ConsoleAuthorize | undefined,\n moduleRef: ModuleRef,\n options: { logger?: Logger; warnIfMissing?: boolean } = {},\n): Promise<ConsoleDecide> {\n const logger = options.logger ?? new Logger(MODULE);\n if (authorize === undefined) {\n if (options.warnIfMissing !== false) logger.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);\n const production = isProduction();\n return async () => (production ? forbidden() : allow('dev-open'));\n }\n\n let decide: ConsoleDecide;\n if (typeof authorize === 'string') {\n decide = gateDecider(authorize, moduleRef);\n } else if (isPolicyCheck(authorize)) {\n decide = await policyDecider(authorize, moduleRef);\n } else if (isAuthorizerClass(authorize)) {\n decide = classDecider(moduleRef)(authorize);\n } else if (typeof authorize === 'function') {\n const fromClass = classDecider(moduleRef);\n let lazy: ConsoleDecide | undefined;\n const fn = authorize as (context: ExecutionContext) => unknown;\n decide = async (context) => {\n if (lazy) return lazy(context);\n const result = await fn(context);\n if (!isLazyModule(result)) return normalizeConsoleResult(result);\n // A lazy import: remember the class, so the import runs once per registration.\n lazy = fromClass(result.default);\n return lazy(context);\n };\n } else {\n throw new Error(\n `${MODULE}: \\`authorize\\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`,\n );\n }\n\n return async (context) => {\n try {\n return await decide(context);\n } catch (error) {\n logger.warn(\n `console \\`authorize\\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`,\n );\n return forbidden();\n }\n };\n}\n\n// ---------------------------------------------------------------------------------------------\n// authorizeByToken\n// ---------------------------------------------------------------------------------------------\n\nconst sha256 = (value: string): Buffer => createHash('sha256').update(value).digest();\n\n/**\n * Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else\n * is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is\n * empty, so a missing env var fails at config time instead of opening the console. The allow names\n * its caller `token:<first 12 hex of sha256(token)>`, never the token.\n */\nexport function authorizeByToken(options: { token: string | undefined }): ConsoleAuthorize {\n const token = options.token;\n if (typeof token !== 'string' || token.length === 0) {\n throw new Error('authorizeByToken: `token` is empty; set it (e.g. from an env var).');\n }\n const expected = sha256(token);\n const caller = `token:${expected.toString('hex').slice(0, 12)}`;\n return (context: ExecutionContext): ConsoleAuthorizeResult => {\n const header = context.switchToHttp().getRequest<{ headers?: Record<string, unknown> }>()\n ?.headers?.authorization;\n const match = typeof header === 'string' ? /^Bearer\\s+(.+)$/i.exec(header.trim()) : null;\n if (match?.[1] && timingSafeEqual(sha256(match[1]), expected)) {\n return { allowed: true, caller };\n }\n return { allowed: false, reason: 'unauthenticated' };\n };\n}\n\n/**\n * One `authorize` from several: allowed when any form allows (checked in order, stopping at the\n * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so\n * (keeping the first `redirectTo`), else `forbidden`. Each form is any `authorize` value: a\n * function, a class, a lazy import, `{ policy, ability }` or a gate name.\n *\n * ```ts\n * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)\n * ```\n */\nexport function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass {\n if (forms.length === 0) throw new Error('anyAuthorize: pass at least one authorize form.');\n class AnyConsoleAuthorizer implements ConsoleAuthorizer {\n #decides: Promise<ConsoleDecide[]> | undefined;\n\n constructor(private readonly moduleRef: ModuleRef) {}\n\n async authorize(context: ExecutionContext): Promise<ConsoleAuthorizeResult> {\n this.#decides ??= Promise.all(\n forms.map((form) =>\n resolveConsoleAuthorize(form, this.moduleRef, { warnIfMissing: false }),\n ),\n );\n // A failed resolution is retried on the next request, not cached as a permanent denial.\n this.#decides.catch(() => {\n this.#decides = undefined;\n });\n let unauthenticated: ConsoleDecision | undefined;\n for (const decide of await this.#decides) {\n const decision = await decide(context);\n if (decision.allowed) return decision;\n if (decision.reason === 'unauthenticated') unauthenticated ??= decision;\n }\n if (unauthenticated === undefined) return { allowed: false, reason: 'forbidden' };\n return 'redirectTo' in unauthenticated && unauthenticated.redirectTo !== undefined\n ? { redirectTo: unauthenticated.redirectTo }\n : { allowed: false, reason: 'unauthenticated' };\n }\n }\n // `ModuleRef` by explicit metadata: the class is created with DI whatever the build emits.\n Reflect.defineMetadata('design:paramtypes', [ModuleRef], AnyConsoleAuthorizer);\n return AnyConsoleAuthorizer;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Enforcement: a guard (decides) + a filter scoped to the console controllers (answers).\n// ---------------------------------------------------------------------------------------------\n\n/** DI token of the resolved {@link ConsoleDecide} the guard runs. */\nexport const CONSOLE_AUTHORIZE_DECIDE = Symbol(`${PACKAGE}:console-authorize`);\n\n/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */\nexport const CONSOLE_PAGE_METADATA = `${PACKAGE}:console-page`;\nexport const ConsolePage = (): ClassDecorator => (target) => {\n Reflect.defineMetadata(CONSOLE_PAGE_METADATA, true, target);\n};\n\n/**\n * The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly\n * where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.\n */\nexport class ConsoleAccessDenied extends HttpException {\n constructor(\n readonly decision: Extract<ConsoleDecision, { allowed: false }>,\n readonly page: boolean,\n ) {\n const unauthenticated = decision.reason === 'unauthenticated';\n super(\n unauthenticated\n ? {\n error: 'unauthenticated',\n ...(decision.redirectTo ? { redirectTo: decision.redirectTo } : {}),\n }\n : { error: 'forbidden' },\n unauthenticated ? 401 : 403,\n );\n }\n}\n\n/**\n * Marks a console route that is not part of the console: `authorize` does not apply to it. Used on\n * the `ctx.webhook()` callback, which third parties call with the token as the secret.\n */\nexport const CONSOLE_PUBLIC_METADATA = `${PACKAGE}:console-public`;\nexport const ConsolePublic = (): MethodDecorator => (_target, _key, descriptor) => {\n Reflect.defineMetadata(CONSOLE_PUBLIC_METADATA, true, descriptor.value as object);\n};\n\n@Injectable()\nexport class ConsoleAuthorizeGuard implements CanActivate {\n constructor(@Inject(CONSOLE_AUTHORIZE_DECIDE) private readonly decide: ConsoleDecide) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n if (Reflect.getMetadata(CONSOLE_PUBLIC_METADATA, context.getHandler()) === true) return true;\n const decision = await this.decide(context);\n if (decision.allowed) return true;\n throw new ConsoleAccessDenied(decision, isPageNavigation(context));\n }\n}\n\nfunction isPageNavigation(context: ExecutionContext): boolean {\n if (context.getType() !== 'http') return false;\n if (Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) !== true) return false;\n const request = context\n .switchToHttp()\n .getRequest<{ method?: string; headers?: Record<string, unknown> }>();\n const accept = request?.headers?.accept;\n return request?.method === 'GET' && typeof accept === 'string' && accept.includes('text/html');\n}\n\n/** The minimal page answered to a denied page navigation: same markup for 401 and 403. */\nconst consolePage = (title: string, message: string): string =>\n `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title}</title></head><body style=\"font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0\"><main style=\"text-align:center\"><h1>${title}</h1><p>${message}</p></main></body></html>`;\nconst DENIED_PAGE = consolePage('Access denied', 'You do not have access to this console.');\nconst SIGN_IN_PAGE = consolePage('Sign in required', 'Sign in to open this console.');\n\n@Catch(ConsoleAccessDenied)\nexport class ConsoleAuthorizeFilter implements ExceptionFilter {\n constructor(private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void {\n const adapter = this.adapterHost.httpAdapter;\n const response = host.switchToHttp().getResponse();\n const { decision, page } = exception;\n if (page && decision.redirectTo) {\n adapter.redirect(response, 302, decision.redirectTo);\n return;\n }\n if (page) {\n const forbiddenPage = decision.reason === 'forbidden';\n adapter.setHeader(response, 'Content-Type', 'text/html; charset=utf-8');\n adapter.reply(\n response,\n forbiddenPage ? DENIED_PAGE : SIGN_IN_PAGE,\n forbiddenPage ? 403 : 401,\n );\n return;\n }\n adapter.reply(response, exception.getResponse(), exception.getStatus());\n }\n}\n\n/**\n * The guard and its filter as one class decorator, for a console you mount yourself\n * (`routes: false`). List it ABOVE your authentication guards, so it runs after them.\n */\nexport const UseConsoleAuthorize = (): ClassDecorator & MethodDecorator =>\n applyDecorators(UseGuards(ConsoleAuthorizeGuard), UseFilters(ConsoleAuthorizeFilter));\n","import {\n type DurableTopology,\n type EngineEvent,\n type FanPage,\n type FanPageQuery,\n type GroupHealth,\n type MetricsCollector,\n type RepublishLifecycleResult,\n type RunDetail,\n type RunDetailOptions,\n type RunFacetQuery,\n type RunFacetRow,\n RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\n STATE_STORE_CANONICAL,\n type ScheduleDescription,\n type ScheduleListQuery,\n type StateStore,\n TERMINAL_RUN_STATUSES,\n type UpdateResult,\n WorkflowEngine,\n type WorkflowLifecycleLinks,\n collectMetrics,\n parseWebhookToken,\n} from '@dudousxd/nestjs-durable-core';\nimport { Inject, Injectable, NotImplementedException, Optional } from '@nestjs/common';\nimport { Observable } from 'rxjs';\n\n// Single source of truth: core owns `RunDetail` (`run-gateway.ts`) — re-exported here (not\n// re-declared) so this package's existing public API (`import type { RunDetail } from\n// '@dudousxd/nestjs-durable-dashboard'`) keeps working unchanged.\nexport type { RunDetail };\n\n/**\n * Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/\n * continue/retryWithInput/stream/bulk/workerHealth) route through the injected {@link RUN_GATEWAY}\n * port, which the lib binds on both topologies (a store-backed impl on the control plane, a proxy\n * over the transport on a store-less tenant) — so those ops work regardless of whether `store`/\n * `engine` are present (`workerHealth` is scoped to the tenant's own groups over the proxy).\n * Operator-only ops (metrics/deliverWebhook/getEvent/update) still need direct `store`/`engine`\n * access; they stay `@Optional()` and throw a clear error when absent.\n */\n/**\n * A workflow's lifecycle links as `GET workflows/:name/lifecycle` answers them, plus `triggerCounts`:\n * per subscriber of `startsOnSettle`, how many runs it was started for (`triggered`) and how many\n * times starting it failed (`failed`), counted by this process's metrics since it started.\n */\nexport interface WorkflowLifecyclePage extends WorkflowLifecycleLinks {\n triggerCounts?: Record<string, { triggered: number; failed: number }> | undefined;\n}\n\n@Injectable()\nexport class DashboardService {\n /** Prometheus counters accumulated from engine events since boot (per process); undefined on a\n * store-less tenant (no engine to accumulate from). */\n private readonly metricsCollector: MetricsCollector | undefined;\n\n constructor(\n private readonly gateway: RunGateway,\n @Optional() @Inject(STATE_STORE_CANONICAL) private readonly store?: StateStore,\n @Optional() private readonly engine?: WorkflowEngine,\n ) {\n // Only the control plane (which has the canonical store) has a FULL engine to accumulate from;\n // on a tenant `this.engine` is a store-less start client with no `subscribe`, so guard on store.\n this.metricsCollector = this.store && this.engine ? collectMetrics(this.engine) : undefined;\n }\n\n /**\n * The operator-only ops need the FULL engine + canonical store, which only the control plane has.\n * On a tenant the `WorkflowEngine` token is a store-less start client that implements none of these\n * methods, so presence of the canonical store is the reliable discriminator — gate on it (never\n * on engine presence, which is truthy on a tenant too).\n */\n private controlPlane(): { store: StateStore; engine: WorkflowEngine } {\n if (!this.store || !this.engine) {\n // A 501, not a 500: a tenant is a supported deployment that simply doesn't serve this\n // operation (a third-party webhook POSTed to a tenant must go to the control plane).\n throw new NotImplementedException(\n 'This durable dashboard operation requires the control plane (not available on a tenant deployment).',\n );\n }\n return { store: this.store, engine: this.engine };\n }\n\n /**\n * The persisted schedules (`engine.schedules`), soonest-due first. Empty — not an error — on a\n * tenant deployment or a store that doesn't persist schedules, so the console can simply hide the\n * panel instead of special-casing either.\n */\n async listSchedules(query: ScheduleListQuery): Promise<ScheduleDescription[]> {\n if (!this.store || !this.engine?.schedules?.supported) return [];\n return this.engine.schedules.list(query);\n }\n\n /** Pause a persisted schedule (control plane only). */\n pauseSchedule(id: string): Promise<ScheduleDescription> {\n return this.controlPlane().engine.schedules.pause(id);\n }\n\n /** Resume a persisted schedule from its next window (control plane only). */\n resumeSchedule(id: string): Promise<ScheduleDescription> {\n return this.controlPlane().engine.schedules.resume(id);\n }\n\n /** Start one run of a persisted schedule now, outside its cadence (control plane only). */\n triggerSchedule(id: string): Promise<RunResult> {\n return this.controlPlane().engine.schedules.trigger(id);\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. Control-plane-only.\n */\n async metrics(): Promise<string> {\n const { store, engine } = this.controlPlane();\n const cap = 10_000;\n const [pending, running, dead] = await Promise.all([\n store.listRuns({ status: 'pending', limit: cap }),\n store.listRuns({ status: 'running', limit: cap }),\n 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 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<RunListItem[]> {\n return this.gateway.listRuns(query);\n }\n\n /** `(status, origin)` counts over the same predicates {@link listRuns} pages — what keeps the\n * console's chips exact while its list is bounded to a page. */\n runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n return this.gateway.runFacets(query);\n }\n\n /** The distinct values of one filter axis over the same predicates — what fills the console's\n * tenant/tag/attribute pickers, so an operator picks a value that exists instead of typing one\n * blind. `[]` when the store cannot enumerate them; the pickers then fall back to free text. */\n runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.gateway.runValueFacets(axis, query, opts);\n }\n\n /** This deployment's durable role (control plane vs tenant) + tenant name — for the dashboard header\n * badge. Routes through the gateway, which knows its own topology (store-backed = operator, proxy =\n * tenant). Works on both topologies (no store/engine needed). */\n topology(): DurableTopology {\n return this.gateway.topology();\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`. Routes through the\n * gateway so it works on BOTH topologies: the control plane returns every group; a tenant proxies\n * to the operator, which scopes the result to the tenant's own `@<tenant>` groups. */\n async workerHealth(): Promise<GroupHealth[]> {\n return this.gateway.workerHealth();\n }\n\n getRunDetail(runId: string, options?: RunDetailOptions): Promise<RunDetail | null> {\n return this.gateway.getRunDetail(runId, options);\n }\n\n /** A page of one fan's members, or null when the run is unknown or the gateway can't page fans. */\n async getFanMembers(runId: string, group: string, query: FanPageQuery): Promise<FanPage | null> {\n return (await this.gateway.getFanMembers?.(runId, group, query)) ?? null;\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.gateway.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 return this.gateway.retry(runId);\n }\n\n cancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null> {\n return this.gateway.cancel(runId, opts);\n }\n\n /**\n * Publish a settled run's lifecycle event again, to its subscribers that have not started (or to\n * `subscriber` alone). Undefined when the gateway cannot republish (a tenant proxy).\n */\n async republishLifecycle(\n runId: string,\n subscriber?: string,\n ): Promise<RepublishLifecycleResult | undefined> {\n return this.gateway.republishLifecycle?.({\n runId,\n ...(subscriber !== undefined ? { subscriber } : {}),\n });\n }\n\n /**\n * Who starts whom through lifecycle events, for `workflow`, plus `triggerCounts` when this process\n * counts the engine's events (the control plane) and the workflow has subscribers. Undefined when\n * the gateway cannot answer.\n */\n async workflowLifecycle(workflow: string): Promise<WorkflowLifecyclePage | undefined> {\n const links = await this.gateway.workflowLifecycle?.(workflow);\n if (!links) return links;\n const counts = this.triggerCountsOf(links);\n return counts ? { ...links, triggerCounts: counts } : links;\n }\n\n /**\n * How many runs each subscriber of `links.workflow` was started for and how many times starting it\n * failed, counted by this process's metrics since it started. Undefined without metrics (a tenant)\n * or without subscribers.\n */\n private triggerCountsOf(\n links: WorkflowLifecycleLinks,\n ): Record<string, { triggered: number; failed: number }> | undefined {\n const snapshot = this.metricsCollector?.snapshot();\n if (!snapshot || links.startsOnSettle.length === 0) return undefined;\n const triggered = snapshot.triggeredBySubscriber[links.workflow] ?? {};\n const failed = snapshot.triggerFailedBySubscriber[links.workflow] ?? {};\n const counts: Record<string, { triggered: number; failed: number }> = {};\n for (const { workflow } of links.startsOnSettle) {\n counts[workflow] = { triggered: triggered[workflow] ?? 0, failed: failed[workflow] ?? 0 };\n }\n return counts;\n }\n\n /**\n * Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow / namespace /\n * origin) — e.g. \"retry every `dead` run tagged `type:mel`\". The filter is the same one the run\n * list uses, so a bulk action can never reach wider than the list it was launched from — but note\n * an ABSENT predicate still means \"every value on that axis\" (an omitted `namespace` is every\n * tenant), which is what makes passing the operator's full filter through mandatory, not optional.\n * 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' | 'namespace' | 'origin'>,\n opts?: { compensate?: boolean },\n ): Promise<{ matched: number; applied: number }> {\n const runs = await this.gateway.listRuns({ ...filter, limit: 500 });\n let applied = 0;\n for (const r of runs) {\n try {\n if (action === 'retry') await this.gateway.retry(r.id);\n else await this.gateway.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.gateway.continue(runId);\n }\n\n /** Re-dispatch a run's stuck `pending` remote steps — recovery for a lost step dispatch. */\n redispatch(runId: string) {\n return this.gateway.redispatchPending(runId);\n }\n\n /**\n * Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the\n * run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url\n * was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so\n * the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a\n * token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already\n * resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk\n * rows behind or signal non-webhook tokens. Control-plane-only.\n */\n async deliverWebhook(token: string, body: unknown): Promise<RunResult | null> {\n const { store, engine } = this.controlPlane();\n const parsed = parseWebhookToken(token);\n if (!parsed) return null;\n const run = await store.getRun(parsed.runId);\n if (!run || TERMINAL_RUN_STATUSES.includes(run.status)) return null;\n const resolved = await store.getCheckpoint(parsed.runId, parsed.seq);\n if (resolved?.status === 'completed') return null;\n const delivered = await engine.signal(token, body);\n if (delivered) return delivered;\n // Buffered. A concurrent duplicate may have resolved the wait between the check above and our\n // buffer write: then nothing will ever read this row, so take it back and answer 404. (If the\n // take finds nothing, the waiter consumed our payload itself — a real delivery.)\n const now = await store.getCheckpoint(parsed.runId, parsed.seq);\n if (now?.status === 'completed' && (await store.takeBufferedSignal(token))) return null;\n const fresh = await store.getRun(parsed.runId);\n return { runId: run.id, status: fresh?.status ?? run.status };\n }\n\n /**\n * What this deployment can serve: `controlPlane` (signals, run events, updates, metrics) and\n * `schedules` (persisted schedules). Both are false on a store-less tenant.\n */\n capabilities(): { controlPlane: boolean; schedules: boolean } {\n const controlPlane = this.store !== undefined && this.engine !== undefined;\n return { controlPlane, schedules: controlPlane && this.engine?.schedules?.supported === true };\n }\n\n /** Whether `runId` names a run the caller may see (tenancy applies). */\n async runExists(runId: string): Promise<boolean> {\n if (this.gateway.getRun) return (await this.gateway.getRun(runId)) !== null;\n return (await this.gateway.getRunDetail(runId, { summarizeFansOver: 0 })) !== null;\n }\n\n /**\n * Deliver `payload` to the run waiting on `token` (`engine.signal`). Null when no run the caller\n * may see waits on it yet: the engine then buffers the payload for the run that waits next.\n * Control-plane-only.\n */\n signal(token: string, payload: unknown): Promise<RunResult | null> {\n return this.controlPlane().engine.signal(token, payload);\n }\n\n /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */\n async getEvent(runId: string, key: string): Promise<unknown> {\n return this.controlPlane().engine.getEvent(runId, key);\n }\n\n /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). Control-plane-only. */\n async update(runId: string, name: string, arg: unknown): Promise<UpdateResult> {\n return this.controlPlane().engine.update(runId, name, arg);\n }\n\n /**\n * Live stream of a run's lifecycle events for SSE. Backed by `gateway.subscribe`, which — on the\n * control plane — receives events from EVERY instance (so a dashboard-only pod tails a run\n * executing on a worker pod), and on a tenant round-trips over the transport. Either way the\n * gateway already filters to `runId`; the dashboard just wires the events through.\n */\n streamRun(runId: string): Observable<{ data: EngineEvent }> {\n return new Observable<{ data: EngineEvent }>((subscriber) => {\n const off = this.gateway.subscribe(runId, (event) => subscriber.next({ data: event }));\n return () => off();\n });\n }\n}\n","import { ApplyFilter, ApplyFilterInterceptor, FilterRunner } from '@dudousxd/nestjs-filter';\nimport {\n BadRequestException,\n Body,\n Controller,\n Get,\n Header,\n Headers,\n NotFoundException,\n NotImplementedException,\n Param,\n Post,\n Query,\n Res,\n Sse,\n UseFilters,\n UseInterceptors,\n} from '@nestjs/common';\nimport { ConsolePublic } from './console-authorize.js';\nimport { DashboardService } from './dashboard.service.js';\n\n/** Fans with more members than this come back summarized in a run's detail. */\nconst FAN_INLINE_MAX = 50;\nimport { DurableRun } from './durable-run.js';\nimport { NotModifiedException, NotModifiedFilter, setEtag } from './not-modified.js';\nimport { type RunListRow, toRunListRow } from './run-list-row.js';\nimport type { RunQueryDraft } from './run-query-draft.js';\nimport { RunFilter } from './run.filter.js';\n\n/**\n * JSON API consumed by the control-plane SPA. The mounted {@link DurableApiController} sits at\n * `apiBasePath` (set by RouterModule).\n *\n * This base is what a host EXTENDS to serve the API from a controller of its own (with\n * `forRoot({ routes: false, apiBasePath })`): `@Controller('<apiBasePath>') class MyConsoleApi\n * extends DurableApiControllerBase {}`. It carries no guards and no `@nestjs/authorization` mark,\n * so a subclass is protected only by what the host puts on it.\n *\n * The filter interceptor (what resolves `@ApplyFilter` into a query) is bound HERE rather than as\n * the app-wide `APP_INTERCEPTOR` that `FilterModule.forRoot()` registers: a library must not add a\n * global interceptor to its host, and on an app that already has one this would be the second copy,\n * running every filter in the app twice.\n */\n@Controller()\n@UseInterceptors(ApplyFilterInterceptor)\n@UseFilters(NotModifiedFilter)\nexport class DurableApiControllerBase {\n constructor(\n private readonly dashboard: DashboardService,\n private readonly runner: FilterRunner,\n ) {}\n\n /**\n * The run list, filtered by whatever the operator asked for.\n *\n * The predicates are parsed, validated and translated by `@dudousxd/nestjs-filter` (see\n * `RunFilter` for the two spellings it accepts, and `RunQueryAdapter` for how they become a\n * `RunQuery`). Every predicate is OPTIONAL and an omitted one means \"don't narrow on this axis\" —\n * in particular `namespace`, whose absence keeps the historical, deliberate behaviour that read\n * paths are NOT namespace-scoped (core, `WorkflowRun.namespace`): the console shows every tenant's\n * runs until an operator chooses one.\n */\n @Get('runs')\n async runs(@ApplyFilter(RunFilter) draft: RunQueryDraft): Promise<RunListRow[]> {\n const runs = await this.dashboard.listRuns(draft.query);\n // Rows only — the three payload fields are the bulk of a large listing and belong to `runs/:id`.\n return runs.map(toRunListRow);\n }\n\n /**\n * `(status, origin)` counts for the runs matching the same tag/tenant/attribute predicates as\n * `GET runs`. This is what lets that listing be PAGED at all: the page bounds what the console\n * renders, this bounds nothing and keeps its status and origin chips exact.\n *\n * `status`, `origin` and the page bounds are accepted (the route takes the same filter) but\n * deliberately DROPPED before counting — they are the axes being counted, so narrowing by them\n * would report the answer back to itself.\n */\n @Get('runs/facets')\n facets(@ApplyFilter(RunFilter) draft: RunQueryDraft) {\n return this.dashboard.runFacets(draft.facetQuery());\n }\n\n /**\n * The distinct values one filter field takes across the runs matching every OTHER active\n * predicate, with counts — what the console's tenant/tag/attribute pickers list.\n *\n * `?groupByCount[field]=tag&groupByCount[limit]=20`, with the filters in the structured\n * `filter[where][...]` form (what `filterQuery()` builds). Scoping the values to the active\n * filters is the point: picking a tenant leaves the tag picker offering only that tenant's tags,\n * so a picker never offers a value whose result set is empty.\n *\n * `limit` matters rather than being a nicety — tag and attribute cardinality grows with the data\n * (a `singleton:<key>` tag is minted per key), so the unbounded answer is a listing. `offset` and\n * `search` are what make that bound livable: a picker pages as it scrolls and narrows as the\n * operator types, both server-side, so a value outside the first page is still reachable.\n */\n @Get('runs/values')\n values(@Query() query: Record<string, unknown>) {\n return this.runner.groupByCount(DurableRun, query, { filterClass: RunFilter });\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 /**\n * The persisted schedules (`engine.schedules`), soonest-due first, optionally narrowed by\n * `namespace`/`workflow`/`tag`/`paused`. Empty on a tenant or a store without schedules.\n */\n @Get('schedules')\n schedules(\n @Query('namespace') namespace?: string,\n @Query('workflow') workflow?: string,\n @Query('tag') tag?: string,\n @Query('paused') paused?: string,\n @Query('limit') limit?: string,\n ) {\n return this.dashboard.listSchedules({\n ...(namespace ? { namespace } : {}),\n ...(workflow ? { workflow } : {}),\n ...(tag ? { tag } : {}),\n ...(paused === 'true' || paused === 'false' ? { paused: paused === 'true' } : {}),\n limit: Math.min(Number(limit) || 200, 1_000),\n });\n }\n\n @Post('schedules/:id/pause')\n pauseSchedule(@Param('id') id: string) {\n return this.dashboard.pauseSchedule(id).catch(notFound);\n }\n\n @Post('schedules/:id/resume')\n resumeSchedule(@Param('id') id: string) {\n return this.dashboard.resumeSchedule(id).catch(notFound);\n }\n\n /** Start one run of the schedule now, outside its cadence. */\n @Post('schedules/:id/trigger')\n triggerSchedule(@Param('id') id: string) {\n return this.dashboard.triggerSchedule(id).catch(notFound);\n }\n\n /**\n * Who starts whom through lifecycle events, for one workflow: the workflows that start when it\n * settles, and the ones whose settling starts it. What the console's workflow page lists.\n */\n @Get('workflows/:name/lifecycle')\n async workflowLifecycle(@Param('name') name: string) {\n const links = await this.dashboard.workflowLifecycle(name).catch(notSupportedByServer);\n if (!links)\n throw new NotImplementedException('workflow lifecycle links need the control plane');\n return links;\n }\n\n /** This deployment's durable role (control plane vs tenant) + tenant name — for the header badge. */\n @Get('topology')\n topology() {\n return this.dashboard.topology();\n }\n\n /**\n * A run's detail. Fans wider than {@link FAN_INLINE_MAX} come back as one `FanSummary` each, their\n * members paged by `runs/:id/fans/:group`; `?full=true` returns every checkpoint instead.\n *\n * When the gateway can version the detail without reading it, the response carries an `ETag`, and\n * a poll whose `If-None-Match` still names it gets a 304 before the timeline is read. The browser\n * sends that header itself (the response is `no-cache`), so a console poll needs no code for it.\n */\n @Get('runs/:id')\n async run(\n @Param('id') id: string,\n @Query('full') full?: string,\n @Headers('if-none-match') ifNoneMatch?: string,\n @Res({ passthrough: true }) response?: unknown,\n ) {\n const detail = await this.dashboard.getRunDetail(\n id,\n full === 'true'\n ? undefined\n : { summarizeFansOver: FAN_INLINE_MAX, ...(ifNoneMatch && { ifNoneMatch }) },\n );\n if (!detail) throw new NotFoundException(`run ${id} not found`);\n const { etag, notModified, ...body } = detail;\n if (etag) {\n if (notModified) throw new NotModifiedException(etag);\n setEtag(response, etag);\n }\n return body;\n }\n\n /** One page of a summarized fan's members, in seq order. Versioned like {@link run}. */\n @Get('runs/:id/fans/:group')\n async fanMembers(\n @Param('id') id: string,\n @Param('group') group: string,\n @Query('offset') offset?: string,\n @Query('limit') limit?: string,\n @Query('failed') failed?: string,\n @Headers('if-none-match') ifNoneMatch?: string,\n @Res({ passthrough: true }) response?: unknown,\n ) {\n const page = await this.dashboard.getFanMembers(id, group, {\n offset: offset === undefined ? undefined : Number(offset),\n limit: limit === undefined ? undefined : Number(limit),\n failedOnly: failed === 'true',\n ...(ifNoneMatch && { ifNoneMatch }),\n });\n if (!page) throw new NotFoundException(`fan ${group} of run ${id} not found`);\n const { etag, notModified, ...body } = page;\n if (etag) {\n if (notModified) throw new NotModifiedException(etag);\n setEtag(response, etag);\n }\n return body;\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 /**\n * Bulk retry/cancel every run matching a filter (status / tag / workflow / namespace / origin).\n * The filter takes the SAME params as `GET runs` and is parsed identically, so \"retry all\" acts on\n * exactly the set the operator was looking at rather than a wider one.\n */\n @Post('bulk/:action')\n bulk(\n @Param('action') action: 'retry' | 'cancel',\n @ApplyFilter(RunFilter) draft: RunQueryDraft,\n @Query('compensate') compensate?: string,\n ) {\n // The operator's page window is dropped, and only it: `bulk` acts on the whole matching SET (it\n // applies its own bound), so carrying a `limit` of 100 over from the list would silently retry\n // the first page and report success for the rest.\n const { limit, offset, ...filter } = draft.query;\n return this.dashboard.bulk(action === 'cancel' ? 'cancel' : 'retry', filter, {\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 /**\n * Publish a settled run's lifecycle event again, to the subscribers that have not started from it\n * (or only `subscriber`). Same gate as retry/cancel.\n */\n @Post('runs/:id/republish-lifecycle')\n async republishLifecycle(@Param('id') id: string, @Body() body?: { subscriber?: string }) {\n let result: Awaited<ReturnType<DashboardService['republishLifecycle']>>;\n try {\n result = await this.dashboard.republishLifecycle(id, body?.subscriber || undefined);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (/^run .* not found$/.test(message)) throw new NotFoundException(message);\n if (/^run .* has not settled/.test(message)) throw new BadRequestException(message);\n if ((error as { code?: unknown } | null)?.code === 'bad-request') {\n throw new BadRequestException(message);\n }\n return notSupportedByServer(error);\n }\n if (!result) {\n throw new NotImplementedException('republishing lifecycle events needs the control plane');\n }\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 /** Re-dispatch a run's stuck `pending` remote steps — recovery for a lost step dispatch. */\n @Post('runs/:id/redispatch')\n async redispatch(@Param('id') id: string) {\n const result = await this.dashboard.redispatch(id);\n if (!result) throw new NotFoundException(`run ${id} not found`);\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. Not a console route: the console's `authorize` does not\n * apply, the token is the secret. The app's `decorators` still do. Treat the token as a secret\n * and/or front it with signature verification in your own middleware.\n */\n @ConsolePublic()\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 live run is waiting for 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\n/** Map core's `ScheduleNotFoundError` to a 404; rethrow anything else. */\nfunction notFound(error: unknown): never {\n if (error instanceof Error && error.name === 'ScheduleNotFoundError') {\n throw new NotFoundException(error.message);\n }\n throw error;\n}\n\n/**\n * A tenant proxy's control plane that is older than the verb (a `RunGatewayError` with code\n * `unsupported-request`) as 501, every other error as it is.\n */\nfunction notSupportedByServer(error: unknown): never {\n if ((error as { code?: unknown } | null)?.code === 'unsupported-request') {\n throw new NotImplementedException(error instanceof Error ? error.message : String(error));\n }\n throw error;\n}\n\n/**\n * The JSON API controller `DurableDashboardModule.forRoot(...)` mounts at `apiBasePath`. The module\n * puts the `@nestjs/authorization` open mark on THIS class (and `decorators` on a per-registration\n * subclass of it), never on {@link DurableApiControllerBase}, so a host's own subclass of the base\n * inherits none of them.\n */\n@Controller()\nexport class DurableApiController extends DurableApiControllerBase {}\n","import type { RunStatus, RunValueAxis, SearchAttributes } from '@dudousxd/nestjs-durable-core';\nimport type { EntityFieldInfo } from '@dudousxd/nestjs-filter';\n\n/**\n * The filterable surface of a run, declared as a class because that is the shape `@dudousxd/nestjs-filter`\n * works in: `@Filterable({ entity })`, its field validation and its codegen all key off a constructor.\n * Nothing ever instantiates it — the console has no ORM entity to point at, its rows come from the\n * `RunGateway` — so this stands in for one and carries the field list.\n *\n * It declares ONLY what {@link RunQuery} can filter by. That is the load-bearing part: a field absent\n * here is rejected by the runner before it reaches the adapter, which lets the adapter fail loudly on\n * the field/operator combinations it cannot express rather than dropping them into a query that comes\n * back looking successful.\n */\nexport class DurableRun {\n workflow!: string;\n status!: RunStatus;\n /** `null` for a run no package claims — see `WorkflowRun.origin`. */\n origin!: string | null;\n /** Tenant / worker-pool partition. */\n namespace!: string;\n /**\n * ONE of the run's tags. Singular deliberately: a run carries a SET of tags, so a predicate here\n * asks about membership (`tag in [etl, nightly]` = runs carrying either), never equality with the\n * whole set.\n */\n tag!: string;\n /** Typed search attributes, addressed per key as `attr.<key>` — a JSON sub-path, not a column. */\n attr!: SearchAttributes;\n}\n\n/** The `attr` head segment, which the adapter resolves as a JSON path rather than a column. */\nexport const ATTRIBUTE_FIELD = 'attr';\n\n/**\n * What the filter runner validates client field names against. `columnName` is cosmetic here (there\n * is no SQL behind this adapter) but the `type` is not: it is what lets the runner tell a JSON\n * sub-path from a typo, and so what makes `attr.tier` resolve while `status.tier` stays unknown.\n */\nexport const DURABLE_RUN_FIELDS: EntityFieldInfo[] = [\n { name: 'workflow', columnName: 'workflow', type: 'string' },\n { name: 'status', columnName: 'status', type: 'string' },\n { name: 'origin', columnName: 'origin', type: 'string' },\n { name: 'namespace', columnName: 'namespace', type: 'string' },\n { name: 'tag', columnName: 'tags', type: 'string' },\n { name: ATTRIBUTE_FIELD, columnName: 'search_attributes', type: 'json' },\n];\n\n/**\n * The value axis a filter FIELD enumerates, or `null` for a field with no enumerable values.\n *\n * This is the join between the two libraries: the filter lib's `groupByCount('namespace')` asks\n * \"what values does this field take\", and durable answers it with `runValueFacets`. `attr` alone\n * lists the search-attribute KEYS in use; `attr.<key>` lists the values recorded under one of them.\n */\nexport function runValueAxisFor(field: string): RunValueAxis | null {\n if (field === ATTRIBUTE_FIELD) return { field: 'attributeKey' };\n if (field.startsWith(`${ATTRIBUTE_FIELD}.`)) {\n const key = field.slice(ATTRIBUTE_FIELD.length + 1);\n return key ? { field: 'attributeValue', key } : null;\n }\n if (\n field === 'workflow' ||\n field === 'status' ||\n field === 'origin' ||\n field === 'namespace' ||\n field === 'tag'\n ) {\n return { field };\n }\n return null;\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter, HttpException } from '@nestjs/common';\n\ninterface RawResponse {\n statusCode: number;\n setHeader(name: string, value: string): unknown;\n end(): unknown;\n}\n\nfunction isRawResponse(value: unknown): value is RawResponse {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return typeof candidate.setHeader === 'function' && typeof candidate.end === 'function';\n}\n\n/**\n * The writable Node `ServerResponse` behind a platform response: Express' response IS it, Fastify's\n * reply wraps it on `.raw`. One write path serves both without a platform-specific dependency.\n */\nfunction rawResponse(response: unknown): RawResponse | null {\n if (isRawResponse(response)) return response;\n const raw = (response as { raw?: unknown } | null)?.raw;\n return isRawResponse(raw) ? raw : null;\n}\n\n/** Set one header on the raw response (Express or Fastify). */\nfunction setRawHeader(response: unknown, name: string, value: string): void {\n rawResponse(response)?.setHeader(name, value);\n}\n\n/**\n * Write a bodiless `304 Not Modified` carrying its validator, and END the response. Its only caller\n * is an exception filter, the terminal step of the request, so there is no second writer to race.\n */\nfunction writeNotModifiedRaw(response: unknown, etag: string): void {\n const raw = rawResponse(response);\n if (!raw) return;\n raw.statusCode = 304;\n raw.setHeader('etag', etag);\n raw.setHeader('cache-control', 'private, no-cache');\n raw.end();\n}\n\n/**\n * How a detail endpoint answers a poll whose `If-None-Match` still names the current version.\n * Thrown rather than returned because Nest sets a handler's status after the handler returns; an\n * {@link NotModifiedFilter} writes the bodiless 304 itself.\n */\nexport class NotModifiedException extends HttpException {\n constructor(readonly etag: string) {\n super('Not Modified', 304);\n }\n}\n\n/** Writes a {@link NotModifiedException} as a bare 304, on Express and Fastify alike. */\n@Catch(NotModifiedException)\nexport class NotModifiedFilter implements ExceptionFilter<NotModifiedException> {\n catch(exception: NotModifiedException, host: ArgumentsHost): void {\n writeNotModifiedRaw(host.switchToHttp().getResponse(), exception.etag);\n }\n}\n\n/**\n * Mark a response as revalidatable: `no-cache` makes the browser ask again on every poll, sending\n * the `ETag` back as `If-None-Match`, so an unchanged run costs a 304 instead of a body.\n */\nexport function setEtag(response: unknown, etag: string): void {\n setRawHeader(response, 'etag', etag);\n setRawHeader(response, 'cache-control', 'private, no-cache');\n}\n","import type { RunListItem } from '@dudousxd/nestjs-durable-core';\n\n/**\n * A run as the CONSOLE'S LIST needs it: every field a row or its derived state reads, and none of the\n * payloads only the detail view opens.\n *\n * `input`, `output` and `error` are dropped. On a real control plane they dominate the response —\n * measured against a 9.5k-run deployment, `error` alone (stack traces on every failed run) was 63% of\n * a 12 MB listing — while no list row, and no `deriveRunState` input, ever reads them. The detail\n * endpoint (`GET runs/:id`) still returns the whole run, which is where those three are actually\n * rendered.\n */\nexport type RunListRow = Omit<RunListItem, 'input' | 'output' | 'error'>;\n\n/** Project a gateway run down to {@link RunListRow}. Deletes rather than re-lists the kept fields, so\n * a field added to `WorkflowRun` reaches the console automatically instead of being silently\n * dropped by a projection nobody remembered to update. */\nexport function toRunListRow(run: RunListItem): RunListRow {\n const { input: _input, output: _output, error: _error, ...row } = run;\n return row;\n}\n","import type { RunStatus } from '@dudousxd/nestjs-durable-core';\nimport { BaseFilter, FilterFor, Filterable } from '@dudousxd/nestjs-filter';\nimport { Injectable } from '@nestjs/common';\nimport { parseAttrFilters } from './attr-filter.js';\nimport { DurableRun } from './durable-run.js';\nimport type { RunQueryDraft } from './run-query-draft.js';\nimport { RUN_QUERY_ADAPTER } from './run-query.adapter.js';\n\n/**\n * A repeated query param arrives as an array and a single one as a scalar; both mean a list. An\n * absent or blank entry is dropped rather than stringified: `?namespace=` is what a cleared filter\n * box sends, and `String(undefined)` would filter for a tenant literally named \"undefined\".\n */\nfunction list(value: unknown): string[] {\n return (Array.isArray(value) ? value : [value])\n .filter((v) => v !== undefined && v !== null && v !== '')\n .map(String);\n}\n\n/** A bound from the query string. A non-numeric or negative value is treated as absent rather than\n * coerced to 0, which would return an empty page and read as \"there are no runs\". */\nfunction bound(value: unknown): number | undefined {\n const n = Number(value);\n return value !== undefined && value !== '' && Number.isInteger(n) && n >= 0 ? n : undefined;\n}\n\n/**\n * The console's run filter.\n *\n * Every predicate is reachable two ways, and both are the same request to this class: the flat form\n * the console has always sent (`?status=failed&tag=etl`, repeatable for a set) is dispatched to the\n * `@FilterFor` methods below, and the structured form (`filter[where][...]`, what\n * `@dudousxd/nestjs-filter-client`'s `filterQuery()` builds) goes through the adapter's\n * `applyColumnFilters`. Keeping the flat spelling working is not politeness to old callers — it is\n * what lets a run row's tag chip stay a plain link.\n *\n * `autoFields: false`: every key is declared here or refused. The fields exist for a REASON that\n * auto-application cannot know — `tag` is set membership, `origin` has an absent bucket, `attr.*` is\n * typed — and a key silently auto-applied as `equals` would be wrong for three of the five.\n */\n@Injectable()\n@Filterable({ entity: DurableRun, adapter: RUN_QUERY_ADAPTER, autoFields: false })\nexport class RunFilter extends BaseFilter<RunQueryDraft> {\n /** One status narrows; several match ANY of them — how a caller asks for a SET (the console's\n * in-flight sibling query, for one). */\n @FilterFor('status')\n applyStatus(value: unknown): void {\n const values = list(value) as RunStatus[];\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { status: values[0] } : { statuses: values });\n }\n\n @FilterFor('workflow')\n applyWorkflow(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { workflow: values[0] } : { workflows: values });\n }\n\n /** A blank param (`?namespace=`, from a cleared filter box) is the same as an absent one; passing\n * `''` through would be an exact match on a tenant nobody has, i.e. a silently empty console. */\n @FilterFor('namespace')\n applyNamespace(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { namespace: values[0] } : { namespaces: values });\n }\n\n @FilterFor('tag')\n applyTag(value: unknown): void {\n const values = list(value);\n if (values.length === 0) return;\n this.$query.narrow(values.length === 1 ? { tag: values[0] } : { tags: values });\n }\n\n /**\n * Origin has THREE states rather than two — a named package, the unattributed bucket, or no\n * restriction — and the bucket rides on its own `unattributed` param rather than on a reserved\n * `origin` value, because any reserved string is a package name someone can legitimately have and a\n * console that quietly reinterpreted it would show the wrong runs.\n *\n * Both keys resolve through one function, called from either, reading the RAW input each time:\n * the answer depends on both params, and `@FilterFor` dispatch follows the order the client's keys\n * happen to arrive in. Recomputing from the input makes the result the same either way, where\n * accumulating onto the draft would make `?origin=x&unattributed=true` mean different things\n * depending on key order.\n *\n * Sent together they are a UNION, not a contradiction: `origins` carries the absent bucket\n * alongside named packages, which is the \"this package plus the runs nothing claims\" view that a\n * single `origin` value cannot express at all.\n */\n @FilterFor('origin')\n applyOrigin(): void {\n this.resolveOrigin();\n }\n\n @FilterFor('unattributed')\n applyUnattributed(): void {\n this.resolveOrigin();\n }\n\n private resolveOrigin(): void {\n const named = list(this.$input.origin);\n const raw = this.$input.unattributed;\n const wantsUnattributed = raw === 'true' || raw === '1' || raw === true;\n const values: Array<string | null> = [...named, ...(wantsUnattributed ? [null] : [])];\n if (values.length === 0) return;\n if (values.length === 1) {\n const only = values[0] as string | null;\n this.$query.narrow({ origin: only });\n return;\n }\n this.$query.narrow({ origins: values });\n }\n\n /** Comma/repeat-separated `key:op:value` predicates (e.g. `amount:gte:200`), ANDed. The structured\n * spelling of the same thing is `filter[where][attr.amount][gte]=200`. */\n @FilterFor('attr')\n applyAttributes(value: unknown): void {\n for (const filter of parseAttrFilters(value as string | string[]) ?? []) {\n this.$query.attribute(filter);\n }\n }\n\n @FilterFor('limit')\n applyLimit(value: unknown): void {\n const limit = bound(value);\n if (limit !== undefined) this.$query.narrow({ limit });\n }\n\n @FilterFor('offset')\n applyOffset(value: unknown): void {\n const offset = bound(value);\n if (offset !== undefined) this.$query.narrow({ offset });\n }\n}\n","import type { AttributeFilter, AttributeOp, AttributeValue } from '@dudousxd/nestjs-durable-core';\n\nconst SCALAR_OPS = new Set<AttributeOp>(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']);\n\n/** Separates the operands of an `in` predicate. `,` already separates whole predicates and `:`\n * already delimits the parts, so the set needs a third character that neither can be. */\nconst IN_SEPARATOR = '|';\n\n/** Coerce a query-string value: `true`/`false` → boolean, numeric → number, else the raw string. */\nfunction coerce(v: string): AttributeValue {\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 *\n * `in` takes a `|`-separated SET — `?attr=tier:in:pro|enterprise` — which is the flat spelling of the\n * predicate a multi-select produces. It needs its own operator because two `eq` predicates on one key\n * are ANDed like every other pair, and no run satisfies both.\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 || rest.length === 0) continue;\n const operand = rest.join(':');\n if (op === 'in') {\n const values = operand.split(IN_SEPARATOR).filter(Boolean).map(coerce);\n if (values.length) filters.push({ key, op: 'in', values });\n continue;\n }\n if (!SCALAR_OPS.has(op as AttributeOp)) continue;\n filters.push({ key, op: op as Exclude<AttributeOp, 'in'>, value: coerce(operand) });\n }\n return filters.length ? filters : undefined;\n}\n","import {\n type AttributeOp,\n type AttributeValue,\n RUN_VALUE_FACET_LIMIT,\n RunGateway,\n type RunListItem,\n type RunQuery,\n type RunStatus,\n} from '@dudousxd/nestjs-durable-core';\nimport type {\n ColumnFilter,\n EntityFieldInfo,\n FilterAdapter,\n GroupByCountField,\n SortItem,\n} from '@dudousxd/nestjs-filter';\nimport { BadRequestException, Injectable, type Type } from '@nestjs/common';\nimport { ATTRIBUTE_FIELD, DURABLE_RUN_FIELDS, DurableRun, runValueAxisFor } from './durable-run.js';\nimport { RunQueryDraft } from './run-query-draft.js';\n\n/** DI token for {@link RunQueryAdapter} — named on `@Filterable({ adapter })` so the run filter uses\n * THIS adapter even in a host application whose global filter adapter is its own ORM's. */\nexport const RUN_QUERY_ADAPTER = Symbol('DURABLE_RUN_QUERY_ADAPTER');\n\n/** The only ordering a run listing has (`createdAt DESC`, newest first), fixed by the store adapters. */\nconst FIXED_ORDER: SortItem = { field: 'createdAt', direction: 'desc' };\n\n/**\n * Coerce a query-string operand: `true`/`false` → boolean, numeric → number, else the raw string.\n * Search attributes are typed (a `gte` on a number must compare numerically), and a GET route\n * delivers every operand as text.\n */\nfunction coerce(value: unknown): AttributeValue {\n if (typeof value === 'number' || typeof value === 'boolean') return value;\n const raw = String(value);\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw !== '' && !Number.isNaN(Number(raw))) return Number(raw);\n return raw;\n}\n\n/** A filter operand as a list: the `in` family carries an array, every other operator one value. */\nfunction operands(clause: ColumnFilter): unknown[] {\n return Array.isArray(clause.value) ? clause.value : [clause.value];\n}\n\n/** The error every unsupported translation raises, naming what was asked and what this backend can\n * answer — the alternative (dropping the clause) returns a wider result set that reads as success. */\nfunction unsupported(clause: ColumnFilter, supported: string): never {\n throw new BadRequestException(\n `The durable run list cannot filter \\`${clause.field}\\` with \\`${clause.operator}\\`. Supported here: ${supported}.`,\n );\n}\n\n/**\n * A {@link FilterAdapter} over the durable **run gateway** rather than over an ORM.\n *\n * This is what lets the console's filters be written with `@dudousxd/nestjs-filter` — its wire format,\n * its operator allowlist, its `groupByCount` — while the data still comes from `RunGateway`, and so\n * still works on every store adapter AND on a tenant deployment that has no store at all, only a\n * proxy over the transport.\n *\n * The adapter's \"query builder\" is a {@link RunQueryDraft} (see it for why), and translation is\n * TOTAL rather than best-effort: `RunQuery` has a fixed set of predicates, so a field/operator pair\n * outside it raises {@link BadRequestException} instead of being ignored. That is the difference\n * between a console that says \"I can't filter by that\" and one that quietly shows every run.\n */\n@Injectable()\nexport class RunQueryAdapter implements FilterAdapter {\n constructor(private readonly gateway: RunGateway) {}\n\n createQueryBuilder<E>(entity: Type<E>): unknown {\n this.assertDurableRun(entity);\n return new RunQueryDraft();\n }\n\n getEntityFields(entity: Type<unknown>): EntityFieldInfo[] | null {\n return entity === DurableRun ? DURABLE_RUN_FIELDS : null;\n }\n\n /** No relations: a run is a flat row here, and the one nested-looking address (`attr.<key>`) is a\n * JSON path. Returning `[]` (not null) tells the runner the answer is \"none\", not \"can't say\". */\n getEntityRelations(): [] {\n return [];\n }\n\n /**\n * Which addresses resolve, and as what. `attr` and `attr.<key>` are JSON paths — that classification\n * is what routes a flat `?attr.tier=pro` through the same translation as a structured\n * `where[attr.tier]`, instead of it being dropped as an unknown key.\n */\n resolveFieldPath(entity: Type<unknown>, path: string): 'field' | 'relation' | 'json' | null {\n if (entity !== DurableRun) return null;\n if (path === ATTRIBUTE_FIELD || path.startsWith(`${ATTRIBUTE_FIELD}.`)) return 'json';\n return DURABLE_RUN_FIELDS.some((f) => f.name === path) ? 'field' : null;\n }\n\n applyColumnFilters(qb: unknown, filters: ColumnFilter[], entity?: Type<unknown>): void {\n if (entity) this.assertDurableRun(entity);\n const draft = this.draft(qb);\n for (const clause of filters) {\n // A run query is a flat AND of named predicates — there is no nesting to put a group INSIDE.\n if (clause.AND || clause.OR) {\n throw new BadRequestException(\n 'The durable run list cannot filter with nested AND/OR groups: its predicates are a flat conjunction.',\n );\n }\n this.applyClause(draft, clause);\n }\n }\n\n private applyClause(draft: RunQueryDraft, clause: ColumnFilter): void {\n const { field, operator } = clause;\n if (field === ATTRIBUTE_FIELD || field.startsWith(`${ATTRIBUTE_FIELD}.`)) {\n this.applyAttributeClause(draft, clause);\n return;\n }\n const anyOf = operator === 'in' || operator === 'isAnyOf';\n switch (field) {\n case 'status':\n if (operator === 'equals') {\n draft.narrow({ status: String(clause.value) as RunStatus });\n } else if (anyOf) {\n draft.narrow({ statuses: operands(clause).map((v) => String(v) as RunStatus) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'workflow':\n if (operator === 'equals') {\n draft.narrow({ workflow: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ workflows: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'namespace':\n if (operator === 'equals') {\n draft.narrow({ namespace: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ namespaces: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'tag':\n // Membership, not equality with the set — see `DurableRun.tag`.\n if (operator === 'equals') {\n draft.narrow({ tag: String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ tags: operands(clause).map(String) });\n } else {\n unsupported(clause, 'equals, in');\n }\n return;\n case 'origin':\n // `null` is a real value on this axis (the unattributed bucket), so it survives translation\n // instead of being coerced to the string \"null\".\n if (operator === 'isNull') {\n draft.narrow({ origin: null });\n } else if (operator === 'equals') {\n draft.narrow({ origin: clause.value === null ? null : String(clause.value) });\n } else if (anyOf) {\n draft.narrow({ origins: operands(clause).map((v) => (v === null ? null : String(v))) });\n } else {\n // `isNotNull` is deliberately absent: \"any origin at all\" is not a predicate `RunQuery`\n // has, and answering it by listing every known origin would silently miss the ones with no\n // runs.\n unsupported(clause, 'equals, in, isNull');\n }\n return;\n default:\n unsupported(clause, `one of ${DURABLE_RUN_FIELDS.map((f) => f.name).join(', ')}`);\n }\n }\n\n /** `attr.<key>` → one {@link AttributeFilter}. The operator set is the store's, so the mapping is\n * 1:1 and anything outside it is rejected rather than approximated. */\n private applyAttributeClause(draft: RunQueryDraft, clause: ColumnFilter): void {\n const key = clause.field.slice(ATTRIBUTE_FIELD.length + 1);\n if (!key) {\n throw new BadRequestException(\n 'Filtering search attributes needs a key: `attr.<key>`, e.g. `attr.tier`.',\n );\n }\n const scalarOps: Record<string, Exclude<AttributeOp, 'in'>> = {\n equals: 'eq',\n notEquals: 'ne',\n gt: 'gt',\n gte: 'gte',\n lt: 'lt',\n lte: 'lte',\n };\n const scalar = scalarOps[clause.operator as string];\n if (scalar) {\n draft.attribute({ key, op: scalar, value: coerce(clause.value) });\n return;\n }\n if (clause.operator === 'in' || clause.operator === 'isAnyOf') {\n draft.attribute({ key, op: 'in', values: operands(clause).map(coerce) });\n return;\n }\n unsupported(clause, 'equals, notEquals, gt, gte, lt, lte, in');\n }\n\n applyOffsetPagination(qb: unknown, page: number, size: number): void {\n this.draft(qb).narrow({ limit: size, offset: (page - 1) * size });\n }\n\n /**\n * A run listing has ONE order — newest first — because that is what every store adapter's\n * `listRuns` emits and what the console's paging depends on. A request for any other sort is\n * refused rather than accepted and ignored: silently returning a differently-ordered page is how a\n * paginated list starts duplicating and skipping rows.\n */\n applySort(_qb: unknown, sorts: SortItem[]): void {\n for (const sort of sorts) {\n if (sort.field !== FIXED_ORDER.field || sort.direction !== FIXED_ORDER.direction) {\n throw new BadRequestException(\n `The durable run list is always ordered \\`${FIXED_ORDER.field} ${FIXED_ORDER.direction}\\` and cannot be sorted by \\`${sort.field} ${sort.direction}\\`.`,\n );\n }\n }\n }\n\n getResult(qb: unknown): Promise<RunListItem[]> {\n return this.gateway.listRuns(this.draft(qb).query);\n }\n\n /**\n * A page plus the size of the whole matching set. The total comes from the facet aggregate rather\n * than from a second listing — that is the entire reason durable exposes one, and it keeps the\n * count exact while the page stays bounded.\n */\n async getResultAndCount(qb: unknown): Promise<{ rows: unknown[]; total: number }> {\n const draft = this.draft(qb);\n const [rows, facets] = await Promise.all([\n this.gateway.listRuns(draft.query),\n this.gateway.runFacets(draft.facetQuery()),\n ]);\n const { status, statuses, origin, origins } = draft.query;\n const total = facets\n .filter((cell) => {\n if (status && cell.status !== status) return false;\n if (statuses && !statuses.includes(cell.status)) return false;\n if (origin !== undefined && cell.origin !== origin) return false;\n if (origins && !origins.includes(cell.origin)) return false;\n return true;\n })\n .reduce((sum, cell) => sum + cell.count, 0);\n return { rows, total };\n }\n\n /**\n * The distinct values of one field over the runs the active predicates select — the console's\n * pickers. `limit` is honoured because it has to be: tag and search-attribute cardinality grows\n * with the data, so the unbounded answer is a listing rather than an aggregate. `offset` and\n * `search` are what keep that bound from becoming a ceiling — the picker pages as it scrolls and\n * narrows as the operator types, both against the whole matching set rather than the fetched page.\n */\n async groupByCount(\n qb: unknown,\n field: GroupByCountField,\n entity: Type<unknown>,\n opts?: { bucket?: number; limit?: number; offset?: number; search?: string },\n ): Promise<Array<{ value: unknown; count: number }>> {\n this.assertDurableRun(entity);\n if (typeof field !== 'string') {\n throw new BadRequestException(\n 'The durable run list has no computed fields to group by — group by one of its own fields.',\n );\n }\n if (opts?.bucket !== undefined) {\n throw new BadRequestException(\n 'The durable run list cannot bucket a group-by-count: none of its filterable fields is numeric.',\n );\n }\n const axis = runValueAxisFor(field);\n if (!axis) {\n throw new BadRequestException(\n `The durable run list cannot enumerate values of \\`${field}\\`.`,\n );\n }\n const rows = await this.gateway.runValueFacets(axis, this.draft(qb).facetQuery(), {\n limit: opts?.limit ?? RUN_VALUE_FACET_LIMIT,\n ...(opts?.offset !== undefined && { offset: opts.offset }),\n ...(opts?.search !== undefined && { search: opts.search }),\n });\n return rows.map((row) => ({ value: row.value, count: row.count }));\n }\n\n getPrimaryKey(): string {\n return 'id';\n }\n\n private draft(qb: unknown): RunQueryDraft {\n if (!(qb instanceof RunQueryDraft)) {\n throw new Error(\n 'The durable run adapter was handed a query builder it did not create. A filter using it must declare `@Filterable({ entity: DurableRun, adapter: RUN_QUERY_ADAPTER })`.',\n );\n }\n return qb;\n }\n\n private assertDurableRun(entity: Type<unknown>): void {\n if (entity !== DurableRun) {\n throw new Error(\n `The durable run adapter only answers for DurableRun, not ${entity?.name ?? String(entity)}.`,\n );\n }\n }\n}\n\n/** Re-exported for a host that wants to narrow the query itself (see `RunQueryDraft`). */\nexport type { RunQuery };\n","import type { AttributeFilter, RunFacetQuery, RunQuery } from '@dudousxd/nestjs-durable-core';\n\n/**\n * What this adapter hands the filter runner in place of a query builder: a {@link RunQuery} under\n * construction.\n *\n * The runner never inspects it — it only passes it back to the adapter's own methods — so the shape\n * is ours to choose, and a plain predicate bag is the honest one. There is no SQL to append to here;\n * a run query is a fixed set of named predicates that the gateway resolves, so \"building\" it is\n * filling fields in, and everything the filter lib can express but `RunQuery` cannot is rejected at\n * the point of translation rather than accumulated and silently dropped.\n */\nexport class RunQueryDraft {\n readonly query: RunQuery = {};\n\n /** Narrow by one or more predicates. Later calls overwrite the same field — the runner applies one\n * clause per field, so the last one wins, exactly as a repeated `where` on a builder would. */\n narrow(patch: RunQuery): void {\n Object.assign(this.query, patch);\n }\n\n /** Add one search-attribute predicate. These ACCUMULATE (unlike {@link narrow}) because\n * `RunQuery.attributes` is an ANDed list — two predicates on different keys are both meant. */\n attribute(filter: AttributeFilter): void {\n this.query.attributes = [...(this.query.attributes ?? []), filter];\n }\n\n /** The predicates minus the page window and the axes a facet count reports on — what\n * `runFacets`/`runValueFacets` are taken over. */\n facetQuery(): RunFacetQuery {\n const { status, statuses, origin, origins, limit, offset, ...facetQuery } = this.query;\n return facetQuery;\n }\n}\n","import 'reflect-metadata';\nimport { ApplyFilterInterceptor, FilterRunner } from '@dudousxd/nestjs-filter';\nimport { type DynamicModule, Module, type Provider, type Type } from '@nestjs/common';\nimport { ModuleRef, RouterModule } from '@nestjs/core';\nimport {\n CONSOLE_AUTHORIZE_DECIDE,\n type ConsoleAuthorize,\n UseConsoleAuthorize,\n resolveConsoleAuthorize,\n} from './console-authorize.js';\nimport { DurableConsoleRegistration, DurableConsolesController } from './console-switcher.js';\nimport { controllerFor, markOpenToAuthorization } from './controller-decorators.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { DurableDevToolsRegistration } from './dev-tools.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';\nimport { RUN_QUERY_ADAPTER, RunQueryAdapter } from './run-query.adapter.js';\nimport { RunFilter } from './run.filter.js';\nimport { DASHBOARD_DEV_TOOLS } from './tokens.js';\n\n// Every controller this module mounts is open to `@nestjs/authorization`'s global guard, which\n// denies by default: access is decided by `authorize` (the console's own guard), and a `@Can()`\n// passed in `decorators` is still enforced. The `*Base` classes a host extends for `routes: false`\n// are NOT marked, so the official guard's deny-by-default applies to a self-mounted console until it\n// declares a `@Can()` (or `@Can.Anyone()`).\nmarkOpenToAuthorization(DurableApiController);\nmarkOpenToAuthorization(DurableUiController);\nmarkOpenToAuthorization(DurableConsolesController);\n\nexport interface DurableDashboardOptions {\n /**\n * Who may use the console (the SPA, its JSON API and the run stream). Not the `ctx.webhook()`\n * callback (`POST <apiBasePath>/webhooks/:token`), which third parties call with the token as the\n * secret; {@link decorators} still apply to it. One of:\n *\n * - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);\n * - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;\n * its dependencies resolve from {@link imports} or global modules);\n * - a lazy import `() => import('./console-access.js')` whose default export is such a class;\n * - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;\n * - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.\n *\n * `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in\n * {@link decorators}, so `request.user` is already set.\n *\n * Omitted: the console is open outside production and answers `403` to every request when\n * `NODE_ENV=production`, and one warning is logged at boot.\n */\n authorize?: ConsoleAuthorize;\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 * Extra `imports` merged into the dashboard's dynamic module: the DI resolution path for a guard\n * passed through {@link decorators} (`UseGuards(...)`), whose dependencies resolve from the module\n * that hosts the controller. Typically the host's own auth module, unless it is global.\n */\n imports?: DynamicModule['imports'];\n /**\n * Class decorators applied to BOTH controllers this module mounts (the SPA and its JSON API), the\n * way `@nestjs/mcp`'s own `decorators` option works. Use it to hand the console to the official NestJS tooling, e.g.\n * `[UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]` with `@nestjs/authentication`\n * and `@nestjs/authorization`.\n *\n * Each registration gets fresh subclasses of the controllers, so decorators never leak into\n * another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves\n * its dependencies from {@link imports} (or a global module).\n *\n * `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs\n * after these. Use `decorators` for what must run first, typically authentication.\n *\n * The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard\n * otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still\n * enforced.\n */\n decorators?: ClassDecorator[];\n /**\n * `false` mounts NO controllers: the SPA and its JSON API are not registered,\n * while every provider still is (the run read/action service, the run filter, the SPA's base\n * paths). Serve the console from controllers of your own instead, by extending\n * {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them\n * with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),\n * or protect them with ordinary Nest decorators:\n *\n * ```ts\n * @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)\n * class ConsoleUi extends DurableUiControllerBase {}\n * @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)\n * class ConsoleApi extends DurableApiControllerBase {}\n * // in the module that declares them:\n * imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]\n * ```\n *\n * `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and\n * the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.\n *\n * `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request\n * through outside production and answers `403` in production, and the boot warning is logged.\n * The `*Base` classes carry no\n * guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass\n * without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than\n * being silently ignored.\n *\n * Default `true`. Named `routes` rather than `path: false` because `basePath`/`apiBasePath` are\n * strings that keep their meaning (where the console lives) whether or not this module mounts it.\n */\n routes?: boolean;\n /**\n * Register the console's reads and run/schedule actions as developer tools on the agent MCP\n * server's dev endpoint (`/mcp/aviary`), gated by this console's `authorize`. Default: on when\n * the console's routes are mounted, off with `routes: false`.\n */\n devTools?: boolean;\n}\n\n/** `routes: false` mounts nothing, so `decorators` would decorate nothing. Refuse it at boot. */\nfunction assertNothingToDecorate(options: DurableDashboardOptions): void {\n if (options.decorators === undefined) return;\n throw new Error(\n [\n 'DurableDashboardModule.forRoot({ routes: false }) mounts no controllers, so `decorators`',\n 'would protect nothing. Put your protection on the controllers you mount (extend',\n 'DurableUiControllerBase / DurableApiControllerBase and decorate them with @UseGuards /',\n '@Can), and drop `decorators`.',\n ].join(' '),\n );\n}\n\n/** The app's `decorators` first, then the console guard, so `authorize` sees `request.user`. */\nfunction consoleController<T extends Type>(base: T, decorators: ClassDecorator[] | undefined): T {\n return controllerFor(base, [...(decorators ?? []), UseConsoleAuthorize()]);\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/**\n * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own\n * dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their\n * controller's host module, so this module, not the outer wrapper, must carry the host's `imports`.\n */\n@Module({})\nexport class DurableApiModule {\n static register(options: {\n imports?: DynamicModule['imports'];\n /** `false`: register the providers only, no controller (see `routes`). */\n routes?: boolean;\n /** See {@link DurableDashboardOptions.decorators}. */\n decorators?: ClassDecorator[];\n /** See {@link DurableDashboardOptions.authorize}. */\n authorize?: ConsoleAuthorize;\n /** See {@link DurableDashboardOptions.devTools}. Default: on when the routes are mounted. */\n devTools?: boolean;\n }): DynamicModule {\n const routes = options.routes !== false;\n return {\n module: DurableApiModule,\n imports: [...(options.imports ?? [])],\n controllers: routes\n ? [\n consoleController(DurableApiController, options.decorators),\n consoleController(DurableConsolesController, options.decorators),\n ]\n : [],\n providers: [\n DashboardService,\n { provide: DASHBOARD_DEV_TOOLS, useValue: options.devTools ?? routes },\n DurableDevToolsRegistration,\n // Resolved at boot: a gate name without nestjs-authz, or a policy without\n // @nestjs/authorization, fails here rather than on the first request.\n {\n provide: CONSOLE_AUTHORIZE_DECIDE,\n // Also with `routes: false`: `@UseConsoleAuthorize()` still applies the production 403.\n useFactory: (moduleRef: ModuleRef) =>\n resolveConsoleAuthorize(options.authorize, moduleRef),\n inject: [ModuleRef],\n },\n // `@dudousxd/nestjs-filter` is wired PRIVATELY here rather than through\n // `FilterModule.forRoot()`, which is a global registration. A library calling that would\n // fight the host app over one set of module options and register a second app-wide\n // interceptor — and it cannot tell at module-definition time whether the host already\n // called it. Everything below is scoped to this module, so the console composes with an app\n // that uses the filter library, and with one that has never heard of it, identically.\n //\n // `validation: 'off'`: the run filter's inputs are validated by their own translation —\n // every field/operator pair either maps onto a `RunQuery` predicate or is rejected by\n // name — so there is nothing left for class-validator to check, and requiring it would put\n // a peer dependency on every consumer of this dashboard for no gain.\n //\n // The `null` adapter is the GLOBAL one, deliberately absent: `RunFilter` names its own via\n // `@Filterable({ adapter })`, so the console runs on the durable run gateway while the host\n // app's filters keep running on whatever adapter it registered.\n {\n provide: FilterRunner,\n useFactory: (moduleRef: ModuleRef) =>\n new FilterRunner(moduleRef, { validation: 'off' }, null),\n inject: [ModuleRef],\n },\n ApplyFilterInterceptor,\n RunQueryAdapter,\n { provide: RUN_QUERY_ADAPTER, useExisting: RunQueryAdapter },\n RunFilter,\n ],\n // Self-mounted (`routes: false`): the host's own subclass of `DurableApiControllerBase` is\n // hosted in the HOST's module, so what its constructor injects must be exported. `FilterRunner`\n // is only exported then: exporting it from a mounted console would shadow a host's own\n // `FilterModule` runner in whatever module imports the dashboard.\n exports: routes\n ? [DashboardService, CONSOLE_AUTHORIZE_DECIDE]\n : [DashboardService, FilterRunner, CONSOLE_AUTHORIZE_DECIDE],\n };\n }\n}\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.\n *\n * Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open\n * outside production and denies every request in production, with one warning at boot.\n * `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see\n * {@link DurableDashboardOptions.routes}).\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 const pathProviders: Provider[] = [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n ];\n if (options.routes === false) {\n assertNothingToDecorate(options);\n const api = DurableApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n ...(options.devTools !== undefined ? { devTools: options.devTools } : {}),\n routes: false,\n });\n return {\n module: DurableDashboardModule,\n imports: [...(options.imports ?? []), api],\n controllers: [],\n providers: pathProviders,\n // What a host's subclasses of `DurableUiControllerBase` / `DurableApiControllerBase` inject:\n // the SPA's paths, the read/action service, the filter runner and the resolved `authorize`\n // (for `@UseConsoleAuthorize()`) via the API module.\n exports: [DurableApiModule, DASHBOARD_BASE_PATH, DASHBOARD_API_PATH],\n };\n }\n return {\n module: DurableDashboardModule,\n imports: [\n ...(options.imports ?? []),\n // Host imports must reach the API controller's HOST module: enhancers resolve from their\n // controller's own module, never from a parent (see DurableApiModule.register).\n // Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.\n DurableApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.decorators ? { decorators: options.decorators } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n ...(options.devTools !== undefined ? { devTools: options.devTools } : {}),\n }),\n RouterModule.register([\n { path: basePath, module: DurableDashboardModule }, // the UI controller below\n { path: apiBasePath, module: DurableApiModule },\n ]),\n ],\n controllers: [consoleController(DurableUiController, options.decorators)],\n // The console switcher lists this console only where it serves its own page.\n providers: [...pathProviders, DurableConsoleRegistration],\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 {\n Controller,\n type ExecutionContext,\n Get,\n Header,\n Inject,\n Injectable,\n Logger,\n type OnModuleDestroy,\n type OnModuleInit,\n createParamDecorator,\n} from '@nestjs/common';\nimport { CONSOLE_AUTHORIZE_DECIDE, type ConsoleDecide } from './console-authorize.js';\nimport { DASHBOARD_BASE_PATH } from './tokens.js';\n\n// ── the console registry (v1), re-declared: no Aviary console imports another ──────────────\n\n/** The process-wide slot every Aviary console registers into (created by whichever boots first). */\nexport const AVIARY_CONSOLES_SLOT: symbol = Symbol.for('@dudousxd/aviary:consoles');\n\n/** The icon a console shows in the switcher. An unknown value renders as `generic`. */\nexport type AviaryConsoleIcon =\n | 'workflow'\n | 'telescope'\n | 'media'\n | 'payments'\n | 'agent'\n | 'identity'\n | 'generic';\n\n/** What a console's `authorize` answers; only `allowed: true` lists it. */\nexport interface AviaryConsoleDecision {\n allowed: boolean;\n reason?: string;\n redirectTo?: string;\n}\n\n/** One console the switcher can list. */\nexport interface AviaryConsoleEntry {\n /** `/^[a-z][a-z0-9-]*$/`: `'durable'`, `'telescope'`, `'agent'`, `'media'`, … */\n id: string;\n /** Product title: `'Workflows'`, `'Telescope'`, `'Agents'`, `'Media'`. */\n title: string;\n icon: AviaryConsoleIcon;\n /** Same-origin absolute path of the console's page (`/durable`). */\n path: string;\n /** Sort key: durable 10, telescope 20, agent 30, media 50. */\n order: number;\n /** The console's resolved gate (what its own routes run). Never throws. */\n authorize(context: ExecutionContext): Promise<AviaryConsoleDecision>;\n}\n\n/** The value at {@link AVIARY_CONSOLES_SLOT}. Data only, so every console's copy is identical. */\nexport interface AviaryConsoleRegistry {\n readonly version: 1;\n readonly entries: AviaryConsoleEntry[];\n}\n\n/** One console as `GET <api>/consoles` returns it. */\nexport interface VisibleConsole {\n id: string;\n title: string;\n icon: AviaryConsoleIcon;\n href: string;\n}\n\n/** The answer of `GET <api>/consoles`. */\nexport interface VisibleConsoles {\n version: 1;\n current: string;\n consoles: VisibleConsole[];\n}\n\n/** The `switcher` field of the shell's config block. */\nexport interface ConsoleSwitcherConfig {\n endpoint: string;\n others: number;\n}\n\nconst ID = /^[a-z][a-z0-9-]*$/;\nconst ICONS = new Set<string>([\n 'workflow',\n 'telescope',\n 'media',\n 'payments',\n 'agent',\n 'identity',\n 'generic',\n]);\n\n/** How long another console's `authorize` may take before it is left out. */\nexport const CONSOLE_PROBE_TIMEOUT_MS: number = 300;\n\n/** Whether `path` is a same-origin absolute path (`/x`; never `//host` or a URL). */\nexport function isSameOriginPath(path: unknown): path is string {\n return typeof path === 'string' && path.startsWith('/') && !path.startsWith('//');\n}\n\n/** The registry, when one is set with a version this copy reads. */\nexport function readAviaryConsoles(): AviaryConsoleRegistry | undefined {\n const value = (globalThis as Record<symbol, unknown>)[AVIARY_CONSOLES_SLOT] as\n | Partial<AviaryConsoleRegistry>\n | undefined;\n if (value?.version !== 1 || !Array.isArray(value.entries)) return undefined;\n return value as AviaryConsoleRegistry;\n}\n\n/**\n * Adds a console to the process's switcher registry, creating it when absent. Returns a function\n * that removes the entry again, or `undefined` when it was not added (another registry version, or\n * the id is taken: logged as a warning). A bad `id` or `path` throws.\n */\nexport function registerAviaryConsole(\n entry: AviaryConsoleEntry,\n logger?: { warn(message: string): void },\n): (() => void) | undefined {\n if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);\n if (!isSameOriginPath(entry.path)) {\n throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single \"/\"`);\n }\n const holder = globalThis as Record<symbol, unknown>;\n if (holder[AVIARY_CONSOLES_SLOT] === undefined) {\n const fresh: AviaryConsoleRegistry = { version: 1, entries: [] };\n holder[AVIARY_CONSOLES_SLOT] = fresh;\n }\n const registry = readAviaryConsoles();\n if (registry === undefined) return undefined;\n if (registry.entries.some((existing) => existing.id === entry.id)) {\n logger?.warn(`console id \"${entry.id}\" is already registered`);\n return undefined;\n }\n registry.entries.push(entry);\n return () => {\n const index = registry.entries.indexOf(entry);\n if (index !== -1) registry.entries.splice(index, 1);\n };\n}\n\n/** The shell's `switcher` config: only when another console is registered (no `authorize` runs). */\nexport function consoleSwitcherConfig(\n selfId: string,\n endpoint: string,\n): ConsoleSwitcherConfig | undefined {\n const registry = readAviaryConsoles();\n if (!registry?.entries.some((entry) => entry.id === selfId)) return undefined;\n const others = registry.entries.filter((entry) => entry.id !== selfId).length;\n return others > 0 ? { endpoint, others } : undefined;\n}\n\n/** Whether `entry` allows the viewer within `ms`; a rejection or a timeout is a no. */\nasync function allows(\n entry: AviaryConsoleEntry,\n context: ExecutionContext,\n ms: number,\n): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(false), ms);\n });\n try {\n return await Promise.race([\n Promise.resolve()\n .then(() => entry.authorize(context))\n .then(\n (decision) => decision?.allowed === true,\n () => false,\n ),\n timeout,\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * The consoles other than `selfId` the viewer may open, by `order` then `id`. Every other entry's\n * `authorize` runs in parallel, each given `timeoutMs`; a timeout, a rejection or any denial hides\n * it. No reason is ever returned.\n */\nexport async function listVisibleConsoles(\n registry: AviaryConsoleRegistry | undefined,\n selfId: string,\n context: ExecutionContext,\n timeoutMs = CONSOLE_PROBE_TIMEOUT_MS,\n): Promise<VisibleConsoles> {\n const others = (registry?.entries ?? [])\n .filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path))\n .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));\n const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));\n return {\n version: 1,\n current: selfId,\n consoles: others\n .filter((_, index) => verdicts[index])\n .map((entry) => ({\n id: entry.id,\n title: entry.title,\n icon: ICONS.has(entry.icon) ? entry.icon : 'generic',\n href: entry.path,\n })),\n };\n}\n\n// ── the durable console's side ─────────────────────────────────────────────────\n\n/** The durable console's id in the registry. */\nexport const DURABLE_CONSOLE_ID: string = 'durable';\n\nconst logger = new Logger('DurableDashboardModule');\n\n/**\n * Registers the durable console into the switcher registry (the console's page path, gated by its\n * resolved `authorize`). Provided only when the console's routes are mounted; removed on shutdown.\n */\n@Injectable()\nexport class DurableConsoleRegistration implements OnModuleInit, OnModuleDestroy {\n #unregister: (() => void) | undefined;\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(CONSOLE_AUTHORIZE_DECIDE) private readonly decide: ConsoleDecide,\n ) {}\n\n onModuleInit(): void {\n this.#unregister = registerAviaryConsole(\n {\n id: DURABLE_CONSOLE_ID,\n title: 'Workflows',\n icon: 'workflow',\n order: 10,\n path: this.basePath,\n authorize: (context) => this.decide(context),\n },\n logger,\n );\n }\n\n onModuleDestroy(): void {\n this.#unregister?.();\n this.#unregister = undefined;\n }\n}\n\n/** What the route hands on to the other consoles' `authorize`: this request's `ExecutionContext`. */\ninterface CurrentContext {\n context: ExecutionContext;\n}\n\nconst CurrentExecutionContext = createParamDecorator(\n (_data: unknown, context: ExecutionContext): CurrentContext => ({ context }),\n);\n\n/**\n * `GET <api>/consoles`: the other Aviary consoles this viewer may open. Behind the console's own\n * access check, like every API route, so only someone who may open this console learns the list.\n */\n@Controller()\nexport class DurableConsolesController {\n @Get('consoles')\n @Header('Cache-Control', 'private, no-store')\n consoles(@CurrentExecutionContext() current: CurrentContext): Promise<VisibleConsoles> {\n return listVisibleConsoles(readAviaryConsoles(), DURABLE_CONSOLE_ID, current.context);\n }\n}\n","import { RunGateway } from '@dudousxd/nestjs-durable-core';\n\n/**\n * @deprecated Inject the `RunGateway` abstract class directly (it is its own DI token). This alias\n * is kept for internal back-compat only.\n *\n * The bound `RunGateway` port is owned by `@dudousxd/nestjs-durable-core` — a required peer dep of\n * both this dashboard package and `@dudousxd/nestjs-durable`. Since core's `RunGateway` is now an\n * abstract class, that single class is the shared DI token across packages: both bind and inject the\n * same imported class, so the previous `Symbol.for` value-sharing hack (which existed only because\n * the dashboard cannot import from `@dudousxd/nestjs-durable`) is no longer needed.\n */\nexport const RUN_GATEWAY = RunGateway;\n\n/** Carries whether the durable tools register into the dev-tools slot (`options.devTools`). */\nexport const DASHBOARD_DEV_TOOLS: symbol = Symbol('DASHBOARD_DEV_TOOLS');\n\n/** DI token carrying the UI mount base (e.g. `/durable`). */\nexport const DASHBOARD_BASE_PATH: symbol = 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 = Symbol('DASHBOARD_API_PATH');\n","import 'reflect-metadata';\nimport type { Type } from '@nestjs/common';\n\n/**\n * `@nestjs/authorization`'s `CAN_ANYONE_METADATA` key (what `@Can.Anyone()` writes), INLINED: the\n * constant is not exported, and this package takes no dependency on `@nestjs/authorization`. A drift\n * spec asserts that `Can.Anyone()` still writes exactly this key.\n */\nexport const CAN_ANYONE_METADATA = 'authorization:can-anyone';\n\n/**\n * Marks a controller as open to `@nestjs/authorization`'s global `AuthorizationGuard`, which\n * otherwise denies every route that declares no `@Can()`. A `@Can()` passed through `decorators` is\n * still enforced: the official guard only treats `can-anyone` as open when the route declares no\n * requirement.\n */\nexport function markOpenToAuthorization(controller: Type): void {\n Reflect.defineMetadata(CAN_ANYONE_METADATA, true, controller);\n}\n\n/**\n * The controller one registration mounts. With no `decorators`, the shared class itself (unchanged\n * behaviour). With `decorators`, a FRESH subclass made for this registration only, so decorators\n * given to one `forRoot(...)` never stick to the next registration, another app in the same\n * process, or the exported class (the approach `@nestjs/mcp`'s `createMcpController` takes).\n *\n * The subclass inherits routes, filters, interceptors and constructor injection from the base.\n * Decorators are applied in array order, like `applyDecorators`.\n */\nexport function controllerFor<T extends Type>(\n base: T,\n decorators: readonly ClassDecorator[] | undefined,\n): T {\n if (decorators === undefined || decorators.length === 0) return base;\n const derived = class extends base {};\n // Nest logs, `AuthorizationGuard` denial events and route tables read the class name.\n Object.defineProperty(derived, 'name', { value: base.name });\n markOpenToAuthorization(derived);\n let decorated: T = derived;\n for (const decorator of decorators) {\n // A class decorator may return a replacement class, as with `@Decorator() class X {}`.\n decorated = (decorator(decorated) as T | undefined) ?? decorated;\n }\n return decorated;\n}\n","import type {\n GroupHealth,\n RunFacetQuery,\n RunListItem,\n RunQuery,\n RunResult,\n RunStatus,\n RunValueAxis,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n type ExecutionContext,\n HttpException,\n Inject,\n Injectable,\n Logger,\n NotFoundException,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { parseAttrFilters } from './attr-filter.js';\nimport { CONSOLE_AUTHORIZE_DECIDE, type ConsoleDecide } from './console-authorize.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { toRunListRow } from './run-list-row.js';\nimport { DASHBOARD_DEV_TOOLS } from './tokens.js';\n\n// ── the dev-tools contract (v1), re-declared: this package never imports the agent's MCP server ──\n\n/** The process-wide slot `@dudousxd/nestjs-agent-mcp-server` serves on its dev MCP endpoint. */\nexport const DEV_TOOLS_SLOT: symbol = Symbol.for('@dudousxd/aviary:dev-tools');\n\n/** One property of a {@link DevToolInputSchema}. */\nexport interface DevToolInputProperty {\n type: 'string' | 'number' | 'integer' | 'boolean' | 'array';\n description?: string;\n enum?: readonly string[];\n /** Arrays hold strings only. */\n items?: { type: 'string' };\n minimum?: number;\n maximum?: number;\n}\n\n/** An object of scalar / enum / array-of-string properties. */\nexport interface DevToolInputSchema {\n type: 'object';\n properties: Record<string, DevToolInputProperty>;\n required?: string[];\n}\n\n/** What the slot's `authorize` answers: the console's resolved decision. */\nexport type DevToolsDecision =\n | { allowed: true; caller?: string }\n | { allowed: false; reason: 'unauthenticated' | 'forbidden'; redirectTo?: string };\n\n/** What a tool's `run` receives besides its input. */\nexport interface DevToolCall {\n context: ExecutionContext;\n caller: { id: string; via: 'token' | 'session' | 'open' };\n limits: { maxRows: number; maxBytes: number };\n}\n\n/** One developer tool, as the dev-tools slot takes it. */\nexport interface DurableDevTool {\n /** `durable_<verb>_<noun>`. */\n name: string;\n /** `read` tools are listed by default; `action` tools only when the app opts in. */\n kind: 'read' | 'action';\n description: string;\n input: DevToolInputSchema;\n /** Set on actions that cancel. */\n destructive?: boolean;\n /** Capability, not access: hides a tool this deployment cannot run. */\n available?(): boolean | Promise<boolean>;\n run(input: Record<string, unknown>, call?: DevToolCall): Promise<unknown>;\n}\n\ninterface DevToolsLib {\n lib: string;\n title: string;\n authorize(context: ExecutionContext): Promise<DevToolsDecision>;\n tools: DurableDevTool[];\n}\n\ninterface DevToolsSlot {\n readonly version: 1;\n readonly libs: DevToolsLib[];\n register(lib: DevToolsLib): () => void;\n}\n\n/** The slot, created when absent (whoever loads first creates it). */\nfunction devToolsSlot(): Partial<DevToolsSlot> {\n const holder = globalThis as Record<symbol, unknown>;\n const existing = holder[DEV_TOOLS_SLOT] as Partial<DevToolsSlot> | undefined;\n if (existing !== undefined) return existing;\n const libs: DevToolsLib[] = [];\n const slot: DevToolsSlot = {\n version: 1,\n libs,\n register(lib) {\n if (libs.some((entry) => entry.lib === lib.lib)) {\n throw new Error(`dev tools: the lib \"${lib.lib}\" is already registered`);\n }\n libs.push(lib);\n return () => {\n const index = libs.indexOf(lib);\n if (index !== -1) libs.splice(index, 1);\n };\n },\n };\n holder[DEV_TOOLS_SLOT] = slot;\n return slot;\n}\n\n// ── the durable tools ────────────────────────────────────────────────────────\n\nconst DEFAULT_LIMIT = 20;\nconst MAX_LIMIT = 100;\n/** Runs `durable_retry_run` / `durable_cancel_run` act on at most per call. */\nexport const MAX_RUN_IDS = 100;\n/** A failed run's error message is cut to this many characters in a list. */\nconst ERROR_SUMMARY_CHARS = 300;\n\nconst RUN_STATUSES: readonly RunStatus[] = [\n 'pending',\n 'running',\n 'suspended',\n 'completed',\n 'failed',\n 'cancelling',\n 'cancelled',\n 'dead',\n 'blocked',\n] as const satisfies readonly RunStatus[];\n\nconst COUNT_FIELDS = [\n 'status',\n 'workflow',\n 'origin',\n 'namespace',\n 'tag',\n 'attributeKey',\n 'attributeValue',\n] as const;\n\nconst limit: DevToolInputProperty = {\n type: 'integer',\n description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,\n minimum: 1,\n maximum: MAX_LIMIT,\n};\nconst cursor: DevToolInputProperty = {\n type: 'string',\n description: 'Pass the `next` of the previous page.',\n};\nconst runId: DevToolInputProperty = { type: 'string', description: 'Run id.' };\nconst runIds: DevToolInputProperty = {\n type: 'array',\n items: { type: 'string' },\n description: `Several run ids (1 to ${MAX_RUN_IDS}), instead of \\`id\\`.`,\n};\nconst scheduleKey: DevToolInputProperty = { type: 'string', description: 'Schedule id.' };\n\n/** The run filters every run tool shares, as the console's run list reads them. */\nconst runFilters: Record<string, DevToolInputProperty> = {\n workflow: { type: 'string', description: 'Workflow name.' },\n tag: { type: 'string', description: 'A tag the run carries.' },\n namespace: { type: 'string', description: 'Worker pool (namespace).' },\n attr: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Search-attribute predicates `key:op:value` (ops: eq, ne, gt, gte, lt, lte, in with `|`), ANDed.',\n },\n};\n\nconst str = (value: unknown) => (typeof value === 'string' && value !== '' ? value : undefined);\n\nfunction limitOf(input: Record<string, unknown>): number {\n const value = input.limit;\n return typeof value === 'number' && Number.isInteger(value) && value > 0\n ? Math.min(value, MAX_LIMIT)\n : DEFAULT_LIMIT;\n}\n\n/** The offset a `cursor` carries (`0` without one). A cursor this tool never issued is an error. */\nfunction offsetOf(input: Record<string, unknown>): number {\n const value = str(input.cursor);\n if (value === undefined) return 0;\n if (!/^\\d+$/.test(value)) {\n throw new Error(`\"cursor\" must be the \\`next\\` of a previous page, got \"${value}\"`);\n }\n return Number(value);\n}\n\nfunction required(input: Record<string, unknown>, key: string): string {\n const value = str(input[key]);\n if (value === undefined) throw new Error(`\"${key}\" is required`);\n return value;\n}\n\n/** One page out of `limit + 1` fetched rows: the extra row only proves there is a next page. */\nfunction page<T>(rows: T[], offset: number, size: number): { items: T[]; next: string | null } {\n return {\n items: rows.slice(0, size),\n next: rows.length > size ? String(offset + size) : null,\n };\n}\n\n/** The run predicates of `input` (no status, no paging): what the console's filter builds. */\nfunction runPredicates(input: Record<string, unknown>): RunFacetQuery {\n const query: RunFacetQuery = {};\n const workflow = str(input.workflow);\n if (workflow !== undefined) query.workflow = workflow;\n const tag = str(input.tag);\n if (tag !== undefined) query.tag = tag;\n const namespace = str(input.namespace);\n if (namespace !== undefined) query.namespace = namespace;\n const attr = Array.isArray(input.attr)\n ? input.attr.filter((entry): entry is string => typeof entry === 'string' && entry !== '')\n : [];\n if (attr.length > 0) {\n const attributes = parseAttrFilters(attr);\n if (attributes === undefined || attributes.length !== attr.length) {\n throw new Error(`\"attr\" entries must read \\`key:op:value\\`, got ${JSON.stringify(attr)}`);\n }\n query.attributes = attributes;\n }\n return query;\n}\n\nfunction statusOf(input: Record<string, unknown>): RunStatus | undefined {\n const status = str(input.status);\n if (status === undefined) return undefined;\n if (!RUN_STATUSES.includes(status as RunStatus)) {\n throw new Error(`\"status\" must be one of ${RUN_STATUSES.join(', ')}, got \"${status}\"`);\n }\n return status as RunStatus;\n}\n\n/** A list row: the console's run row plus a one-line summary of its error. */\nfunction listRow(run: RunListItem) {\n const row: Record<string, unknown> = { ...toRunListRow(run) };\n const message = run.error?.message;\n if (typeof message === 'string' && message !== '') {\n const line = message.split('\\n', 1)[0] ?? '';\n row.error = line.length > ERROR_SUMMARY_CHARS ? `${line.slice(0, ERROR_SUMMARY_CHARS)}…` : line;\n }\n return row;\n}\n\n/**\n * Runs a console call, turning its HTTP errors into plain errors (the endpoint answers a throw as an\n * MCP `isError` result): a 404 reads `<what> not found`, any other HTTP error keeps its message.\n * Core's `ScheduleNotFoundError` reads as not found too.\n */\nasync function call<T>(what: () => string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n if (\n error instanceof NotFoundException ||\n (error instanceof Error && error.name === 'ScheduleNotFoundError')\n ) {\n throw new Error(`${what()} not found`);\n }\n if (error instanceof HttpException) throw new Error(error.message);\n throw error;\n }\n}\n\n/** `null` from the service means the run is unknown (or hidden by tenancy): not found. */\nfunction found<T>(value: T | null | undefined, what: string): T {\n if (value === null || value === undefined) throw new Error(`${what} not found`);\n return value;\n}\n\n/** The ids an action applies to: `id` or `ids` (1 to {@link MAX_RUN_IDS}). */\nfunction idsOf(input: Record<string, unknown>): { ids: string[]; many: boolean } {\n const one = str(input.id);\n const many = Array.isArray(input.ids)\n ? input.ids.filter((id): id is string => typeof id === 'string' && id !== '')\n : undefined;\n if (one !== undefined && many !== undefined) throw new Error('pass \"id\" or \"ids\", not both');\n if (one !== undefined) return { ids: [one], many: false };\n if (many === undefined || many.length === 0) throw new Error('\"id\" or \"ids\" is required');\n if (many.length > MAX_RUN_IDS) {\n throw new Error(`\"ids\" lists ${many.length} runs; at most ${MAX_RUN_IDS} per call`);\n }\n return { ids: many, many: true };\n}\n\n/**\n * Applies `act` to each run in order. One `id`: the result, or the error. Several `ids`: stops at\n * the first that fails and returns `{ applied, failed: { id, error }, skipped }`, so what changed is\n * never in doubt.\n */\nasync function each(\n input: Record<string, unknown>,\n act: (id: string) => Promise<RunResult>,\n): Promise<unknown> {\n const { ids, many } = idsOf(input);\n if (!many) return act(ids[0] as string);\n const applied: RunResult[] = [];\n for (const [index, id] of ids.entries()) {\n try {\n applied.push(await act(id));\n } catch (error) {\n return {\n applied,\n failed: { id, error: error instanceof Error ? error.message : String(error) },\n skipped: ids.slice(index + 1),\n };\n }\n }\n return { applied };\n}\n\n/** Backlog groups nobody consumes, deepest first: the alert the console's Workers panel shows. */\nfunction starvedGroups(groups: GroupHealth[]) {\n return groups\n .filter((group) => group.depth > 0 && group.liveWorkers.length === 0)\n .sort((a, b) => b.depth - a.depth)\n .map(({ group, depth, kind }) => ({ group, depth, ...(kind ? { kind } : {}) }));\n}\n\n/**\n * The durable developer tools (lib `durable`): the console's reads and its run and schedule\n * actions, through the same {@link DashboardService} methods its API serves, so tenancy and the\n * app's redaction apply. Signals, run events and schedules are available only on the control plane.\n */\nexport function durableDevTools(service: DashboardService): DurableDevTool[] {\n const controlPlane = () => service.capabilities().controlPlane;\n const schedules = () => service.capabilities().schedules;\n return [\n {\n name: 'durable_list_runs',\n kind: 'read',\n description:\n 'Workflow runs, newest first: id, workflow, status, what a suspended run waits on, times, tags and a one-line error summary (no input or output). Filters: status, workflow, tag, namespace, origin, attr. Returns { items, next }; pass next as cursor.',\n input: {\n type: 'object',\n properties: {\n status: { type: 'string', enum: RUN_STATUSES, description: 'Run status.' },\n ...runFilters,\n origin: { type: 'string', description: 'The package the run came from.' },\n cursor,\n limit,\n },\n },\n async run(input) {\n const size = limitOf(input);\n const offset = offsetOf(input);\n const status = statusOf(input);\n const origin = str(input.origin);\n const query: RunQuery = {\n ...runPredicates(input),\n ...(status !== undefined ? { status } : {}),\n ...(origin !== undefined ? { origin } : {}),\n offset,\n limit: size + 1,\n };\n const runs = await call(\n () => 'runs',\n () => service.listRuns(query),\n );\n return page(runs.map(listRow), offset, size);\n },\n },\n {\n name: 'durable_get_run',\n kind: 'read',\n description:\n 'One run: its input, output, error, step timeline, children, fans (summarized over 50 members unless full) and lifecycle links. `field` returns that one field only (e.g. \"steps\"). An error when no run has that id.',\n input: {\n type: 'object',\n properties: {\n id: runId,\n full: { type: 'boolean', description: 'Every fan member inline (default false).' },\n field: { type: 'string', description: 'Return only this top-level field.' },\n },\n required: ['id'],\n },\n async run(input) {\n const id = required(input, 'id');\n const detail = found(\n await call(\n () => `run ${id}`,\n () =>\n service.getRunDetail(id, input.full === true ? undefined : { summarizeFansOver: 50 }),\n ),\n `run ${id}`,\n );\n const { etag: _etag, notModified: _notModified, ...body } = detail;\n const field = str(input.field);\n if (field === undefined) return body;\n if (!Object.hasOwn(body, field)) {\n throw new Error(\n `run ${id} has no field \"${field}\"; fields: ${Object.keys(body).join(', ')}`,\n );\n }\n return { id, [field]: (body as Record<string, unknown>)[field] };\n },\n },\n {\n name: 'durable_list_fan_members',\n kind: 'read',\n description:\n \"One fan's members (the steps or children of a fan-out group), in order. Filters: failed (only failed members). Returns { items, total, next }; pass next as cursor. An error when the run or the fan does not exist.\",\n input: {\n type: 'object',\n properties: {\n id: runId,\n group: { type: 'string', description: 'The fan group, as the run detail names it.' },\n failed: { type: 'boolean', description: 'Only failed members.' },\n cursor,\n limit,\n },\n required: ['id', 'group'],\n },\n async run(input) {\n const id = required(input, 'id');\n const group = required(input, 'group');\n const size = limitOf(input);\n const offset = offsetOf(input);\n const fan = found(\n await call(\n () => `fan ${group} of run ${id}`,\n () =>\n service.getFanMembers(id, group, {\n offset,\n limit: size,\n failedOnly: input.failed === true,\n }),\n ),\n `fan ${group} of run ${id}`,\n );\n const end = offset + fan.items.length;\n return {\n items: fan.items,\n total: fan.total,\n next: fan.items.length > 0 && end < fan.total ? String(end) : null,\n };\n },\n },\n {\n name: 'durable_count_runs_by',\n kind: 'read',\n description:\n 'How many runs have each value of one field (status, workflow, origin, namespace, tag, attributeKey, or attributeValue with key), most frequent first. Filters: workflow, tag, namespace, attr. Returns { items: [{ value, count }], next }; pass next as cursor. Empty when the store cannot count that field.',\n input: {\n type: 'object',\n properties: {\n field: { type: 'string', enum: COUNT_FIELDS, description: 'The field to count by.' },\n key: { type: 'string', description: 'The search attribute, for attributeValue.' },\n ...runFilters,\n cursor,\n limit,\n },\n required: ['field'],\n },\n async run(input) {\n const field = required(input, 'field');\n if (!(COUNT_FIELDS as readonly string[]).includes(field)) {\n throw new Error(`\"field\" must be one of ${COUNT_FIELDS.join(', ')}, got \"${field}\"`);\n }\n const size = limitOf(input);\n const offset = offsetOf(input);\n const predicates = runPredicates(input);\n if (field === 'status' || field === 'origin') {\n // The console's chips: (status, origin) counts in one query, summed per value here.\n const rows = await call(\n () => 'runs',\n () => service.runFacets(predicates),\n );\n const counts = new Map<string | null, number>();\n for (const row of rows) {\n const value = field === 'status' ? row.status : row.origin;\n counts.set(value, (counts.get(value) ?? 0) + row.count);\n }\n const items = [...counts]\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || String(a.value).localeCompare(String(b.value)));\n return page(items.slice(offset), offset, size);\n }\n const axis: RunValueAxis =\n field === 'attributeValue'\n ? { field, key: required(input, 'key') }\n : { field: field as Exclude<RunValueAxis['field'], 'attributeValue'> };\n const rows = await call(\n () => 'runs',\n () => service.runValueFacets(axis, predicates, { offset, limit: size + 1 }),\n );\n return page(rows, offset, size);\n },\n },\n {\n name: 'durable_get_health',\n kind: 'read',\n description:\n \"The fleet at a glance: run counts by status (pending backlog, dead, blocked: a run no compatible worker can take), queues with backlog and no live worker (starved), live worker count, and this deployment's role (control plane or tenant).\",\n input: { type: 'object', properties: {} },\n async run() {\n const [facets, groups] = await Promise.all([\n call(\n () => 'runs',\n () => service.runFacets({}),\n ),\n call(\n () => 'workers',\n () => service.workerHealth(),\n ),\n ]);\n const runs: Record<string, number> = {};\n for (const row of facets) runs[row.status] = (runs[row.status] ?? 0) + row.count;\n const workers = new Set<string>();\n for (const group of groups) {\n for (const worker of group.liveWorkers) workers.add(worker.instanceId);\n }\n const starved = starvedGroups(groups);\n return {\n topology: service.topology(),\n runs,\n backlog: runs.pending ?? 0,\n dead: runs.dead ?? 0,\n blocked: runs.blocked ?? 0,\n queues: groups.length,\n liveWorkers: workers.size,\n starved,\n healthy: starved.length === 0 && (runs.blocked ?? 0) === 0,\n };\n },\n },\n {\n name: 'durable_list_workers',\n kind: 'read',\n description:\n 'Worker groups (queues): group, kind (workflow or step), queue depth and the live workers with their heartbeats. A group with depth > 0 and no live worker is starved. Returns { items, next: null }.',\n input: { type: 'object', properties: {} },\n async run() {\n const groups = await call(\n () => 'workers',\n () => service.workerHealth(),\n );\n return { items: groups, next: null };\n },\n },\n {\n name: 'durable_list_schedules',\n kind: 'read',\n description:\n 'Persisted schedules, soonest due first: id, workflow, cadence (cron, every or rrule), paused, nextFireAt, lastFireAt, lastRunId, fires, lastError. Filters: workflow, tag, namespace, paused. Returns { items, next }; pass next as cursor.',\n input: {\n type: 'object',\n properties: {\n workflow: { type: 'string', description: 'Workflow name.' },\n tag: { type: 'string', description: 'A tag the schedule carries.' },\n namespace: { type: 'string', description: 'Worker pool (namespace).' },\n paused: { type: 'boolean', description: 'Only paused (true) or active (false).' },\n cursor,\n limit,\n },\n },\n available: schedules,\n async run(input) {\n const size = limitOf(input);\n const offset = offsetOf(input);\n const workflow = str(input.workflow);\n const tag = str(input.tag);\n const namespace = str(input.namespace);\n const rows = await call(\n () => 'schedules',\n () =>\n service.listSchedules({\n ...(workflow !== undefined ? { workflow } : {}),\n ...(tag !== undefined ? { tag } : {}),\n ...(namespace !== undefined ? { namespace } : {}),\n ...(typeof input.paused === 'boolean' ? { paused: input.paused } : {}),\n offset,\n limit: size + 1,\n }),\n );\n return page(rows, offset, size);\n },\n },\n {\n name: 'durable_get_run_event',\n kind: 'read',\n description:\n 'The latest value a run published for `key` with ctx.setEvent (a live query, no side effect). Returns { id, key, value }; value is null when the run never published that key. An error when no run has that id.',\n input: {\n type: 'object',\n properties: { id: runId, key: { type: 'string', description: 'Event key.' } },\n required: ['id', 'key'],\n },\n available: controlPlane,\n async run(input) {\n const id = required(input, 'id');\n const key = required(input, 'key');\n if (\n !(await call(\n () => `run ${id}`,\n () => service.runExists(id),\n ))\n ) {\n throw new Error(`run ${id} not found`);\n }\n const value = await call(\n () => `run ${id}`,\n () => service.getEvent(id, key),\n );\n return { id, key, value: value ?? null };\n },\n },\n {\n name: 'durable_retry_run',\n kind: 'action',\n description: `Re-runs a failed, dead or cancelled run; completed steps replay from their checkpoints. Returns { runId, status }. With ids (at most ${MAX_RUN_IDS}), runs them in order and stops at the first that fails: { applied, failed: { id, error }, skipped }. An error when no run has that id.`,\n input: { type: 'object', properties: { id: runId, ids: runIds } },\n async run(input) {\n return each(input, async (id) =>\n found(\n await call(\n () => `run ${id}`,\n () => service.retry(id),\n ),\n `run ${id}`,\n ),\n );\n },\n },\n {\n name: 'durable_cancel_run',\n kind: 'action',\n destructive: true,\n description: `Cancels a run that has not finished (with compensate: true, its completed steps' compensations run first). Returns { runId, status }. With ids (at most ${MAX_RUN_IDS}), cancels them in order and stops at the first that fails: { applied, failed: { id, error }, skipped }. An error when no run has that id.`,\n input: {\n type: 'object',\n properties: {\n id: runId,\n ids: runIds,\n compensate: { type: 'boolean', description: 'Run compensations (default false).' },\n },\n },\n async run(input) {\n const compensate = input.compensate === true;\n return each(input, async (id) =>\n found(\n await call(\n () => `run ${id}`,\n () => service.cancel(id, { compensate }),\n ),\n `run ${id}`,\n ),\n );\n },\n },\n {\n name: 'durable_continue_run',\n kind: 'action',\n description:\n 'Resumes a run paused at a ctx.breakpoint. Returns { runId, status }. An error when no run has that id, or when it is not paused at a breakpoint.',\n input: { type: 'object', properties: { id: runId }, required: ['id'] },\n async run(input) {\n const id = required(input, 'id');\n const what = () => `run ${id}`;\n const result = await call(what, () => service.continue(id));\n if (result) return result;\n if (!(await call(what, () => service.runExists(id))))\n throw new Error(`run ${id} not found`);\n throw new Error(`run ${id} is not paused at a breakpoint`);\n },\n },\n {\n name: 'durable_signal_run',\n kind: 'action',\n description:\n 'Sends a signal: delivers payload (JSON text; omitted is null) to the run waiting on token (ctx.waitForSignal or a ctx.webhook token). Returns { delivered: true, runId, status }, or { delivered: false } when no run you can see waits on the token yet: the engine then buffers the payload for the next run that waits on it.',\n input: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'The signal token the run waits on.' },\n payload: { type: 'string', description: 'The payload, as JSON text.' },\n },\n required: ['token'],\n },\n available: controlPlane,\n async run(input) {\n const token = required(input, 'token');\n let payload: unknown = null;\n if (typeof input.payload === 'string' && input.payload.trim() !== '') {\n try {\n payload = JSON.parse(input.payload);\n } catch {\n throw new Error('\"payload\" must be JSON text (quote a plain string: \"\\\\\"ok\\\\\"\")');\n }\n }\n const result = await call(\n () => `token ${token}`,\n () => service.signal(token, payload),\n );\n return result\n ? { delivered: true, runId: result.runId, status: result.status }\n : { delivered: false };\n },\n },\n {\n name: 'durable_trigger_schedule',\n kind: 'action',\n description:\n 'Starts one run of a schedule now, outside its cadence (it does not count as a fire). Returns { runId, status }. An error when no schedule has that id.',\n input: { type: 'object', properties: { key: scheduleKey }, required: ['key'] },\n available: schedules,\n async run(input) {\n const key = required(input, 'key');\n return call(\n () => `schedule ${key}`,\n () => service.triggerSchedule(key),\n );\n },\n },\n {\n name: 'durable_pause_schedule',\n kind: 'action',\n description:\n 'Pauses a schedule (paused: true, the default) or resumes it from its next window (paused: false). Returns the schedule. An error when no schedule has that id.',\n input: {\n type: 'object',\n properties: {\n key: scheduleKey,\n paused: { type: 'boolean', description: 'false resumes it (default true).' },\n },\n required: ['key'],\n },\n available: schedules,\n async run(input) {\n const key = required(input, 'key');\n return call(\n () => `schedule ${key}`,\n () => (input.paused === false ? service.resumeSchedule(key) : service.pauseSchedule(key)),\n );\n },\n },\n ];\n}\n\nconst logger = new Logger('DurableDashboardModule');\n\n/**\n * Registers the durable tools into the dev-tools slot, gated by the console's own resolved\n * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.\n */\n@Injectable()\nexport class DurableDevToolsRegistration implements OnModuleInit, OnModuleDestroy {\n #unregister: (() => void) | undefined;\n\n constructor(\n @Inject(DASHBOARD_DEV_TOOLS) private readonly enabled: boolean,\n @Inject(DashboardService) private readonly service: DashboardService,\n @Inject(CONSOLE_AUTHORIZE_DECIDE) private readonly decide: ConsoleDecide,\n ) {}\n\n onModuleInit(): void {\n if (!this.enabled) return;\n const slot = devToolsSlot();\n if (slot.version !== 1 || typeof slot.register !== 'function') return;\n try {\n this.#unregister = slot.register({\n lib: 'durable',\n title: 'Durable workflows',\n authorize: (context) => this.decide(context),\n tools: durableDevTools(this.service),\n });\n } catch (error) {\n // Another application in this process registered the durable tools: keep the console up.\n logger.warn(\n `durable dev tools not registered: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n onModuleDestroy(): void {\n this.#unregister?.();\n this.#unregister = undefined;\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';\nimport { ConsolePage } from './console-authorize.js';\nimport { DURABLE_CONSOLE_ID, consoleSwitcherConfig } from './console-switcher.js';\nimport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH } from './tokens.js';\n\nexport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH };\n\n/** The id of the page's JSON config block. */\nexport const DURABLE_DASHBOARD_CONFIG_ID = 'durable-dashboard-config';\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 controller routes are relative: the mounted {@link DurableUiController} gets its path from\n * `RouterModule` (set by `DurableDashboardModule.forRoot({ basePath })`).\n *\n * This base is what a host EXTENDS to serve the console from a controller of its own (with\n * `forRoot({ routes: false, basePath })`): `@Controller('<basePath>') class MyConsole extends\n * DurableUiControllerBase {}`, protected with the host's own decorators. It carries no guards and\n * no `@nestjs/authorization` mark, so a subclass is protected only by what the host puts on it (and,\n * under `AuthorizationModule`'s global guard, is denied until it declares a `@Can()`).\n */\n@Controller()\n@ConsolePage()\nexport class DurableUiControllerBase {\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>${this.configBlock()}`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n /**\n * The page's JSON config block (`durable-dashboard-config`, CSP-safe: data, not script). It\n * carries `switcher` only when another Aviary console is registered.\n */\n private configBlock(): string {\n const switcher = consoleSwitcherConfig(DURABLE_CONSOLE_ID, `${this.apiBasePath}/consoles`);\n const json = JSON.stringify({\n apiBase: this.apiBasePath,\n ...(switcher !== undefined ? { switcher } : {}),\n }).replaceAll('<', '\\\\u003c');\n return `<script type=\"application/json\" id=\"${DURABLE_DASHBOARD_CONFIG_ID}\">${json}</script>`;\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\n/**\n * The SPA controller `DurableDashboardModule.forRoot(...)` mounts at `basePath`. The module puts\n * the `@nestjs/authorization` open mark on THIS class (and `decorators` on a per-registration\n * subclass of it), never on {@link DurableUiControllerBase}, so a host's own subclass of the base\n * inherits none of them.\n */\n@Controller()\nexport class DurableUiController extends DurableUiControllerBase {}\n"],"mappings":"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","names":["CONSOLE_GATE","Symbol","for","PACKAGE","MODULE","CONSOLE_AUTHORIZE_MISSING_WARNING","allow","caller","undefined","allowed","forbidden","reason","unauthenticated","isProduction","process","env","NODE_ENV","trim","toLowerCase","isAuthorizerClass","value","prototype","authorize","test","Function","toString","call","isPolicyCheck","policy","ability","isLazyModule","default","normalizeConsoleResult","result","shape","redirectTo","hasUser","user","policyDecider","check","moduleRef","pkg","Error","AuthorizationService","AUTHORIZATION_MODULE_OPTIONS","defaultGetUser","get","strict","getUser","options","context","authorization","can","gateDecider","name","gate","allows","classDecider","instances","Map","instanceOf","Class","cached","pending","registered","create","catch","delete","set","resolveConsoleAuthorize","logger","Logger","warnIfMissing","warn","production","decide","fromClass","lazy","fn","error","message","String","sha256","createHash","update","digest","authorizeByToken","token","length","expected","slice","header","switchToHttp","getRequest","headers","match","exec","timingSafeEqual","anyAuthorize","forms","AnyConsoleAuthorizer","Promise","all","map","form","decision","Reflect","defineMetadata","ModuleRef","CONSOLE_AUTHORIZE_DECIDE","CONSOLE_PAGE_METADATA","ConsolePage","target","ConsoleAccessDenied","HttpException","page","CONSOLE_PUBLIC_METADATA","ConsolePublic","_target","_key","descriptor","ConsoleAuthorizeGuard","canActivate","getMetadata","getHandler","isPageNavigation","getType","getClass","request","accept","method","includes","consolePage","title","DENIED_PAGE","SIGN_IN_PAGE","ConsoleAuthorizeFilter","adapterHost","exception","host","adapter","httpAdapter","response","getResponse","redirect","forbiddenPage","setHeader","reply","getStatus","UseConsoleAuthorize","applyDecorators","UseGuards","UseFilters","import_common","DashboardService","metricsCollector","gateway","store","engine","collectMetrics","undefined","controlPlane","NotImplementedException","listSchedules","query","schedules","supported","list","pauseSchedule","id","pause","resumeSchedule","resume","triggerSchedule","trigger","metrics","cap","pending","running","dead","Promise","all","listRuns","status","limit","gauges","length","health","workerHealth","push","h","group","depth","liveWorkers","prometheus","join","runFacets","runValueFacets","axis","opts","topology","getRunDetail","runId","options","getFanMembers","retryWithInput","input","retry","cancel","republishLifecycle","subscriber","workflowLifecycle","workflow","links","counts","triggerCountsOf","triggerCounts","snapshot","startsOnSettle","triggered","triggeredBySubscriber","failed","triggerFailedBySubscriber","bulk","action","filter","runs","applied","r","matched","continue","redispatch","redispatchPending","deliverWebhook","token","body","parsed","parseWebhookToken","run","getRun","TERMINAL_RUN_STATUSES","includes","resolved","getCheckpoint","seq","delivered","signal","now","takeBufferedSignal","fresh","capabilities","runExists","summarizeFansOver","payload","getEvent","key","update","name","arg","streamRun","Observable","off","subscribe","event","next","data","import_nestjs_filter","import_common","DurableRun","workflow","status","origin","namespace","tag","attr","ATTRIBUTE_FIELD","DURABLE_RUN_FIELDS","name","columnName","type","runValueAxisFor","field","startsWith","key","slice","length","import_common","isRawResponse","value","candidate","setHeader","end","rawResponse","response","raw","setRawHeader","name","writeNotModifiedRaw","etag","statusCode","NotModifiedException","HttpException","NotModifiedFilter","catch","exception","host","switchToHttp","getResponse","setEtag","toRunListRow","run","input","_input","output","_output","error","_error","row","import_common","SCALAR_OPS","Set","IN_SEPARATOR","coerce","v","Number","isNaN","parseAttrFilters","attr","undefined","raw","Array","isArray","filters","entry","key","op","rest","split","length","operand","join","values","filter","Boolean","map","push","has","value","import_nestjs_durable_core","import_common","RunQueryDraft","query","narrow","patch","Object","assign","attribute","filter","attributes","facetQuery","status","statuses","origin","origins","limit","offset","RUN_QUERY_ADAPTER","Symbol","FIXED_ORDER","field","direction","coerce","value","raw","String","Number","isNaN","operands","clause","Array","isArray","unsupported","supported","BadRequestException","operator","RunQueryAdapter","gateway","createQueryBuilder","entity","assertDurableRun","RunQueryDraft","getEntityFields","DurableRun","DURABLE_RUN_FIELDS","getEntityRelations","resolveFieldPath","path","ATTRIBUTE_FIELD","startsWith","some","f","name","applyColumnFilters","qb","filters","draft","AND","OR","applyClause","applyAttributeClause","anyOf","narrow","status","statuses","map","v","workflow","workflows","namespace","namespaces","tag","tags","origin","origins","join","key","slice","length","scalarOps","equals","notEquals","gt","gte","lt","lte","scalar","attribute","op","values","applyOffsetPagination","page","size","limit","offset","applySort","_qb","sorts","sort","getResult","listRuns","query","getResultAndCount","rows","facets","Promise","all","runFacets","facetQuery","total","filter","cell","includes","undefined","reduce","sum","count","groupByCount","opts","bucket","axis","runValueAxisFor","runValueFacets","RUN_VALUE_FACET_LIMIT","search","row","getPrimaryKey","Error","list","value","Array","isArray","filter","v","undefined","map","String","bound","n","Number","isInteger","RunFilter","BaseFilter","applyStatus","values","length","$query","narrow","status","statuses","applyWorkflow","workflow","workflows","applyNamespace","namespace","namespaces","applyTag","tag","tags","applyOrigin","resolveOrigin","applyUnattributed","named","$input","origin","raw","unattributed","wantsUnattributed","only","origins","applyAttributes","parseAttrFilters","attribute","applyLimit","limit","applyOffset","offset","entity","DurableRun","adapter","RUN_QUERY_ADAPTER","autoFields","FAN_INLINE_MAX","DurableApiControllerBase","dashboard","runner","runs","draft","listRuns","query","map","toRunListRow","facets","runFacets","facetQuery","values","groupByCount","DurableRun","filterClass","RunFilter","metrics","workers","workerHealth","schedules","namespace","workflow","tag","paused","limit","listSchedules","Math","min","Number","pauseSchedule","id","catch","notFound","resumeSchedule","triggerSchedule","workflowLifecycle","name","links","notSupportedByServer","NotImplementedException","topology","run","full","ifNoneMatch","response","detail","getRunDetail","undefined","summarizeFansOver","NotFoundException","etag","notModified","body","NotModifiedException","setEtag","fanMembers","group","offset","failed","page","getFanMembers","failedOnly","stream","streamRun","retry","retryWithInput","input","bulk","action","compensate","filter","cancel","result","republishLifecycle","subscriber","error","message","Error","String","test","BadRequestException","code","continue","redispatch","webhook","token","deliverWebhook","event","key","getEvent","update","passthrough","DurableApiController","import_reflect_metadata","import_nestjs_filter","import_common","import_core","import_common","import_nestjs_durable_core","DASHBOARD_DEV_TOOLS","Symbol","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","AVIARY_CONSOLES_SLOT","Symbol","for","ID","ICONS","Set","CONSOLE_PROBE_TIMEOUT_MS","isSameOriginPath","path","startsWith","readAviaryConsoles","value","globalThis","version","Array","isArray","entries","undefined","registerAviaryConsole","entry","logger","test","id","Error","JSON","stringify","holder","fresh","registry","some","existing","warn","push","index","indexOf","splice","consoleSwitcherConfig","selfId","endpoint","others","filter","length","allows","context","ms","timer","timeout","Promise","resolve","setTimeout","race","then","authorize","decision","allowed","clearTimeout","listVisibleConsoles","timeoutMs","sort","a","b","order","localeCompare","verdicts","all","map","current","consoles","_","title","icon","has","href","DURABLE_CONSOLE_ID","Logger","DurableConsoleRegistration","basePath","decide","onModuleInit","onModuleDestroy","CurrentExecutionContext","createParamDecorator","_data","DurableConsolesController","import_reflect_metadata","CAN_ANYONE_METADATA","markOpenToAuthorization","controller","Reflect","defineMetadata","controllerFor","base","decorators","undefined","length","derived","Object","defineProperty","value","name","decorated","decorator","import_common","DEV_TOOLS_SLOT","Symbol","for","devToolsSlot","holder","globalThis","existing","undefined","libs","slot","version","register","lib","some","entry","Error","push","index","indexOf","splice","DEFAULT_LIMIT","MAX_LIMIT","MAX_RUN_IDS","ERROR_SUMMARY_CHARS","RUN_STATUSES","COUNT_FIELDS","limit","type","description","minimum","maximum","cursor","runId","runIds","items","scheduleKey","runFilters","workflow","tag","namespace","attr","str","value","limitOf","input","Number","isInteger","Math","min","offsetOf","test","required","key","page","rows","offset","size","slice","next","length","String","runPredicates","query","Array","isArray","filter","attributes","parseAttrFilters","JSON","stringify","statusOf","status","includes","join","listRow","run","row","toRunListRow","message","error","line","split","call","what","NotFoundException","name","HttpException","found","idsOf","one","id","many","ids","each","act","applied","entries","failed","skipped","starvedGroups","groups","group","depth","liveWorkers","sort","a","b","map","kind","durableDevTools","service","controlPlane","capabilities","schedules","properties","enum","origin","runs","listRuns","full","field","detail","getRunDetail","summarizeFansOver","etag","_etag","notModified","_notModified","body","Object","hasOwn","keys","fan","getFanMembers","failedOnly","end","total","predicates","runFacets","counts","Map","set","get","count","localeCompare","axis","runValueFacets","facets","Promise","all","workerHealth","workers","Set","worker","add","instanceId","starved","topology","backlog","pending","dead","blocked","queues","healthy","paused","available","listSchedules","runExists","getEvent","retry","destructive","compensate","cancel","result","continue","token","payload","trim","parse","signal","delivered","triggerSchedule","resumeSchedule","pauseSchedule","logger","Logger","DurableDevToolsRegistration","enabled","decide","onModuleInit","title","authorize","context","tools","warn","onModuleDestroy","import_common","DURABLE_DASHBOARD_CONFIG_ID","BUILD_BASE","spaDir","fileURLToPath","URL","CONTENT_TYPES","DurableUiControllerBase","dir","basePath","apiBasePath","index","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","configBlock","includes","replace","switcher","consoleSwitcherConfig","DURABLE_CONSOLE_ID","json","JSON","stringify","apiBase","undefined","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","DurableUiController","markOpenToAuthorization","DurableApiController","DurableUiController","DurableConsolesController","assertNothingToDecorate","options","decorators","undefined","Error","join","consoleController","base","controllerFor","UseConsoleAuthorize","normalize","path","replace","DurableApiModule","register","routes","module","imports","controllers","providers","DashboardService","provide","DASHBOARD_DEV_TOOLS","useValue","devTools","DurableDevToolsRegistration","CONSOLE_AUTHORIZE_DECIDE","useFactory","moduleRef","resolveConsoleAuthorize","authorize","inject","ModuleRef","FilterRunner","validation","ApplyFilterInterceptor","RunQueryAdapter","RUN_QUERY_ADAPTER","useExisting","RunFilter","exports","DurableDashboardModule","forRoot","basePath","apiBasePath","pathProviders","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","api","RouterModule","DurableConsoleRegistration"]}