@dudousxd/nestjs-durable-dashboard 0.56.0 → 0.57.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/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/**\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 /** 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 { 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 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\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAA,8BAAO;AACP,yBAA4C;AAC5C,oBAcO;AACP,kBAAgD;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CzC,IAAMA,eAAeC,OAAOC,IAAI,+BAAA;AAKvC,IAAMC,UAAU;AAChB,IAAMC,SAAS;AAGR,IAAMC,oCAAoC,IAAIF,OAAAA,yIAAgJC,MAAAA;AAIrM,IAAME,QAAQ,8BAAwB;EAAEC,SAAS;AAAK,IAAxC;AACd,IAAMC,YAAY,8BAAwB;EAAED,SAAS;EAAOE,QAAQ;AAAY,IAA9D;AAClB,IAAMC,kBAAkB,8BAAwB;EAAEH,SAAS;EAAOE,QAAQ;AAAkB,IAApE;AAExB,SAASE,eAAAA;AACP,UAAQC,QAAQC,IAAIC,YAAY,IAAIC,KAAI,EAAGC,YAAW,MAAO;AAC/D;AAFSL;AAQT,SAASM,kBAAkBC,OAAc;AACvC,MAAI,OAAOA,UAAU,WAAY,QAAO;AACxC,MACE,OAAQA,MAAkDC,WAAWC,cAAc,YACnF;AACA,WAAO;EACT;AACA,SAAO,cAAcC,KAAKC,SAASH,UAAUI,SAASC,KAAKN,KAAAA,CAAAA;AAC7D;AARSD;AAUT,SAASQ,cAAcP,OAAc;AACnC,SACE,OAAOA,UAAU,YACjBA,UAAU,QACV,OAAQA,MAA6BQ,WAAW,cAChD,OAAQR,MAA6BS,YAAY;AAErD;AAPSF;AAST,SAASG,aAAaV,OAAc;AAClC,SACE,OAAOA,UAAU,YACjBA,UAAU,QACVD,kBAAmBC,MAAgCW,OAAO;AAE9D;AANSD;AASF,SAASE,uBAAuBC,QAAe;AACpD,MAAIA,WAAW,KAAM,QAAOzB,MAAAA;AAC5B,MAAI,OAAOyB,WAAW,YAAYA,WAAW,KAAM,QAAOvB,UAAAA;AAC1D,QAAMwB,QAAQD;AACd,MAAI,OAAOC,MAAMC,eAAe,YAAYD,MAAMzB,YAAY,MAAM;AAClE,WAAO;MAAEA,SAAS;MAAOE,QAAQ;MAAmBwB,YAAYD,MAAMC;IAAW;EACnF;AACA,MAAID,MAAMzB,YAAY,KAAM,QAAOD,MAAAA;AACnC,SAAO0B,MAAMvB,WAAW,oBAAoBC,gBAAAA,IAAoBF,UAAAA;AAClE;AATgBsB;AAYhB,SAASI,QAAQC,MAAa;AAC5B,SAAOA,SAAS,QAAQA,SAASC,UAAaD,SAAS;AACzD;AAFSD;AAIT,eAAeG,cACbC,OACAC,WAAoB;AAEpB,MAAIC;AACJ,MAAI;AACFA,UAAM,MAAM,OAAO,uBAAA;EACrB,QAAQ;AACN,UAAM,IAAIC,MACR,GAAGrC,MAAAA,kLAAwL;EAE/L;AACA,QAAM,EAAEsC,sBAAsBC,8BAA8BC,eAAc,IAAKJ;AAC/E,MAAI;AACFD,cAAUM,IAAIH,sBAAsB;MAAEI,QAAQ;IAAM,CAAA;EACtD,QAAQ;AACN,UAAM,IAAIL,MACR,GAAGrC,MAAAA,0HAAgI;EAEvI;AAIA,MAAI2C,UAAUH;AACd,MAAI;AACF,UAAMI,UAAUT,UAAUM,IACxBF,8BACA;MAAEG,QAAQ;IAAM,CAAA;AAElB,QAAI,OAAOE,SAASD,YAAY,WAAYA,WAAUC,QAAQD;EAChE,QAAQ;EAER;AACA,SAAO,OAAOE,YAAAA;AACZ,UAAMC,gBAAgBX,UAAUM,IAAIH,sBAAsB;MAAEI,QAAQ;IAAM,CAAA;AAC1E,UAAMX,OAAO,MAAMY,QAAQE,OAAAA;AAC3B,UAAME,MAAMD,cAAcC;AAK1B,QAAI,MAAMA,IAAI3B,KAAK0B,eAAeZ,MAAMZ,QAAQY,MAAMX,SAASO,QAAQC,IAAAA,IAAQA,OAAO,IAAA,GAAO;AAC3F,aAAO7B,MAAAA;IACT;AACA,WAAO4B,QAAQC,IAAAA,IAAQ3B,UAAAA,IAAcE,gBAAAA;EACvC;AACF;AA9Ce2B;AAgDf,SAASe,YAAYC,MAAcd,WAAoB;AACrD,MAAI;AACFA,cAAUM,IAAI7C,cAAc;MAAE8C,QAAQ;IAAM,CAAA;EAC9C,QAAQ;AACN,UAAM,IAAIL,MACR,iBAAiBY,IAAAA,8HAAkI;EAEvJ;AACA,SAAO,OAAOJ,YAAAA;AACZ,UAAMK,OAAOf,UAAUM,IAAiB7C,cAAc;MAAE8C,QAAQ;IAAM,CAAA;AACtE,WAAOhB,uBAAuB,MAAMwB,KAAKC,OAAOF,MAAMJ,OAAAA,CAAAA;EACxD;AACF;AAZSG;AAcT,SAASI,aAAajB,WAAoB;AACxC,QAAMkB,YAAY,oBAAIC,IAAAA;AACtB,QAAMC,aAAa,wBAACC,UAAAA;AAClB,UAAMC,SAASJ,UAAUZ,IAAIe,KAAAA;AAC7B,QAAIC,OAAQ,QAAOA;AACnB,UAAMC,WAAW,YAAA;AACf,UAAI;AAEF,cAAMC,aAAaxB,UAAUM,IAAIe,OAAO;UAAEd,QAAQ;QAAM,CAAA;AACxD,YAAIiB,WAAY,QAAOA;MACzB,QAAQ;MAER;AACA,aAAOxB,UAAUyB,OAAOJ,KAAAA;IAC1B,GAAA;AAEAE,YAAQG,MAAM,MAAMR,UAAUS,OAAON,KAAAA,CAAAA;AACrCH,cAAUU,IAAIP,OAAOE,OAAAA;AACrB,WAAOA;EACT,GAjBmB;AAkBnB,SAAO,CAACF,UAAU,OAAOX,YACvBnB,uBAAuB,OAAO,MAAM6B,WAAWC,KAAAA,GAAQxC,UAAU6B,OAAAA,CAAAA;AACrE;AAtBSO;AAgCT,eAAsBY,wBACpBhD,WACAmB,WACAS,UAAwD,CAAC,GAAC;AAE1D,QAAMqB,SAASrB,QAAQqB,UAAU,IAAIC,qBAAOlE,MAAAA;AAC5C,MAAIgB,cAAcgB,QAAW;AAC3B,QAAIY,QAAQuB,kBAAkB,MAAOF,QAAOG,KAAKnE,iCAAAA;AACjD,UAAMoE,aAAa9D,aAAAA;AACnB,WAAO,YAAa8D,aAAajE,UAAAA,IAAcF,MAAAA;EACjD;AAEA,MAAIoE;AACJ,MAAI,OAAOtD,cAAc,UAAU;AACjCsD,aAAStB,YAAYhC,WAAWmB,SAAAA;EAClC,WAAWd,cAAcL,SAAAA,GAAY;AACnCsD,aAAS,MAAMrC,cAAcjB,WAAWmB,SAAAA;EAC1C,WAAWtB,kBAAkBG,SAAAA,GAAY;AACvCsD,aAASlB,aAAajB,SAAAA,EAAWnB,SAAAA;EACnC,WAAW,OAAOA,cAAc,YAAY;AAC1C,UAAMuD,YAAYnB,aAAajB,SAAAA;AAC/B,QAAIqC;AACJ,UAAMC,KAAKzD;AACXsD,aAAS,8BAAOzB,YAAAA;AACd,UAAI2B,KAAM,QAAOA,KAAK3B,OAAAA;AACtB,YAAMlB,SAAS,MAAM8C,GAAG5B,OAAAA;AACxB,UAAI,CAACrB,aAAaG,MAAAA,EAAS,QAAOD,uBAAuBC,MAAAA;AAEzD6C,aAAOD,UAAU5C,OAAOF,OAAO;AAC/B,aAAO+C,KAAK3B,OAAAA;IACd,GAPS;EAQX,OAAO;AACL,UAAM,IAAIR,MACR,GAAGrC,MAAAA,kHAAwH;EAE/H;AAEA,SAAO,OAAO6C,YAAAA;AACZ,QAAI;AACF,aAAO,MAAMyB,OAAOzB,OAAAA;IACtB,SAAS6B,OAAO;AACdT,aAAOG,KACL,qDAAqDM,iBAAiBrC,QAAQqC,MAAMC,UAAUC,OAAOF,KAAAA,CAAAA,EAAQ;AAE/G,aAAOtE,UAAAA;IACT;EACF;AACF;AA/CsB4D;AAqDtB,IAAMa,SAAS,wBAAC/D,cAA0BgE,+BAAW,QAAA,EAAUC,OAAOjE,KAAAA,EAAOkE,OAAM,GAApE;AAOR,SAASC,iBAAiBrC,SAAsC;AACrE,QAAMsC,QAAQtC,QAAQsC;AACtB,MAAI,OAAOA,UAAU,YAAYA,MAAMC,WAAW,GAAG;AACnD,UAAM,IAAI9C,MAAM,oEAAA;EAClB;AACA,QAAM+C,WAAWP,OAAOK,KAAAA;AACxB,SAAO,CAACrC,YAAAA;AACN,UAAMwC,SAASxC,QAAQyC,aAAY,EAAGC,WAAU,GAC5CC,SAAS1C;AACb,UAAM2C,QAAQ,OAAOJ,WAAW,WAAW,mBAAmBK,KAAKL,OAAO1E,KAAI,CAAA,IAAM;AACpF,QAAI8E,QAAQ,CAAA,SAAME,oCAAgBd,OAAOY,MAAM,CAAA,CAAE,GAAGL,QAAAA,EAAW,QAAO;AACtE,WAAO;MAAEjF,SAAS;MAAOE,QAAQ;IAAkB;EACrD;AACF;AAbgB4E;AAoBT,IAAMW,2BAA2B/F,OAAO,GAAGE,OAAAA,oBAA2B;AAGtE,IAAM8F,wBAAwB,GAAG9F,OAAAA;AACjC,IAAM+F,cAAc,6BAAsB,CAACC,WAAAA;AAChDC,UAAQC,eAAeJ,uBAAuB,MAAME,MAAAA;AACtD,GAF2B;AAQpB,IAAMG,sBAAN,cAAkCC,4BAAAA;SAAAA;;;;;EACvC,YACWC,UACAC,MACT;AACA,UAAM/F,mBAAkB8F,SAAS/F,WAAW;AAC5C,UACEC,mBACI;MACEoE,OAAO;MACP,GAAI0B,SAASvE,aAAa;QAAEA,YAAYuE,SAASvE;MAAW,IAAI,CAAC;IACnE,IACA;MAAE6C,OAAO;IAAY,GACzBpE,mBAAkB,MAAM,GAAA,GAAA,KAXjB8F,WAAAA,UAAAA,KACAC,OAAAA;EAYX;AACF;AAMO,IAAMC,0BAA0B,GAAGvG,OAAAA;AACnC,IAAMwG,gBAAgB,6BAAuB,CAACC,SAASC,MAAMC,eAAAA;AAClEV,UAAQC,eAAeK,yBAAyB,MAAMI,WAAW5F,KAAK;AACxE,GAF6B;AAKtB,IAAM6F,wBAAN,MAAMA;SAAAA;;;;EACX,YAA+DrC,QAAuB;SAAvBA,SAAAA;EAAwB;EAEvF,MAAMsC,YAAY/D,SAA6C;AAC7D,QAAImD,QAAQa,YAAYP,yBAAyBzD,QAAQiE,WAAU,CAAA,MAAQ,KAAM,QAAO;AACxF,UAAMV,WAAW,MAAM,KAAK9B,OAAOzB,OAAAA;AACnC,QAAIuD,SAASjG,QAAS,QAAO;AAC7B,UAAM,IAAI+F,oBAAoBE,UAAUW,iBAAiBlE,OAAAA,CAAAA;EAC3D;AACF;;;;;;;;;AAEA,SAASkE,iBAAiBlE,SAAyB;AACjD,MAAIA,QAAQmE,QAAO,MAAO,OAAQ,QAAO;AACzC,MAAIhB,QAAQa,YAAYhB,uBAAuBhD,QAAQoE,SAAQ,CAAA,MAAQ,KAAM,QAAO;AACpF,QAAMC,UAAUrE,QACbyC,aAAY,EACZC,WAAU;AACb,QAAM4B,SAASD,SAAS1B,SAAS2B;AACjC,SAAOD,SAASE,WAAW,SAAS,OAAOD,WAAW,YAAYA,OAAOE,SAAS,WAAA;AACpF;AARSN;AAWT,IAAMO,cAAc,wBAACC,OAAe5C,YAClC,wIAAwI4C,KAAAA,+JAAoKA,KAAAA,WAAgB5C,OAAAA,6BAD1S;AAEpB,IAAM6C,cAAcF,YAAY,iBAAiB,yCAAA;AACjD,IAAMG,eAAeH,YAAY,oBAAoB,+BAAA;AAG9C,IAAMI,yBAAN,MAAMA;SAAAA;;;;EACX,YAA6BC,aAA8B;SAA9BA,cAAAA;EAA+B;EAE5D9D,MAAM+D,WAAgCC,MAA2B;AAC/D,UAAMC,UAAU,KAAKH,YAAYI;AACjC,UAAMC,WAAWH,KAAKvC,aAAY,EAAG2C,YAAW;AAChD,UAAM,EAAE7B,UAAUC,KAAI,IAAKuB;AAC3B,QAAIvB,QAAQD,SAASvE,YAAY;AAC/BiG,cAAQI,SAASF,UAAU,KAAK5B,SAASvE,UAAU;AACnD;IACF;AACA,QAAIwE,MAAM;AACR,YAAM8B,gBAAgB/B,SAAS/F,WAAW;AAC1CyH,cAAQM,UAAUJ,UAAU,gBAAgB,0BAAA;AAC5CF,cAAQO,MACNL,UACAG,gBAAgBX,cAAcC,cAC9BU,gBAAgB,MAAM,GAAA;AAExB;IACF;AACAL,YAAQO,MAAML,UAAUJ,UAAUK,YAAW,GAAIL,UAAUU,UAAS,CAAA;EACtE;AACF;;;;;;;;AAMO,IAAMC,sBAAsB,iCACjCC,mCAAgBC,yBAAU9B,qBAAAA,OAAwB+B,0BAAWhB,sBAAAA,CAAAA,GAD5B;;;AChZnC,iCA6BO;AACP,IAAAiB,iBAAsE;AACtE,kBAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BpB,IAAMC,mBAAN,MAAMA;SAAAA;;;;;;;;EAGMC;EAEjB,YACmBC,SAC2CC,OAC/BC,QAC7B;SAHiBF,UAAAA;SAC2CC,QAAAA;SAC/BC,SAAAA;AAI7B,SAAKH,mBAAmB,KAAKE,SAAS,KAAKC,aAASC,2CAAe,KAAKD,MAAM,IAAIE;EACpF;;;;;;;EAQQC,eAA8D;AACpE,QAAI,CAAC,KAAKJ,SAAS,CAAC,KAAKC,QAAQ;AAG/B,YAAM,IAAII,uCACR,qGAAA;IAEJ;AACA,WAAO;MAAEL,OAAO,KAAKA;MAAOC,QAAQ,KAAKA;IAAO;EAClD;;;;;;EAOA,MAAMK,cAAcC,OAA0D;AAC5E,QAAI,CAAC,KAAKP,SAAS,CAAC,KAAKC,QAAQO,WAAWC,UAAW,QAAO,CAAA;AAC9D,WAAO,KAAKR,OAAOO,UAAUE,KAAKH,KAAAA;EACpC;;EAGAI,cAAcC,IAA0C;AACtD,WAAO,KAAKR,aAAY,EAAGH,OAAOO,UAAUK,MAAMD,EAAAA;EACpD;;EAGAE,eAAeF,IAA0C;AACvD,WAAO,KAAKR,aAAY,EAAGH,OAAOO,UAAUO,OAAOH,EAAAA;EACrD;;EAGAI,gBAAgBJ,IAAgC;AAC9C,WAAO,KAAKR,aAAY,EAAGH,OAAOO,UAAUS,QAAQL,EAAAA;EACtD;;;;;;;EAQA,MAAMM,UAA2B;AAC/B,UAAM,EAAElB,OAAOC,OAAM,IAAK,KAAKG,aAAY;AAC3C,UAAMe,MAAM;AACZ,UAAM,CAACC,SAASC,SAASC,IAAAA,IAAQ,MAAMC,QAAQC,IAAI;MACjDxB,MAAMyB,SAAS;QAAEC,QAAQ;QAAWC,OAAOR;MAAI,CAAA;MAC/CnB,MAAMyB,SAAS;QAAEC,QAAQ;QAAWC,OAAOR;MAAI,CAAA;MAC/CnB,MAAMyB,SAAS;QAAEC,QAAQ;QAAQC,OAAOR;MAAI,CAAA;KAC7C;AACD,UAAMS,SAAS;MACb;MACA,wBAAwBR,QAAQS,MAAM;MACtC;MACA,wBAAwBR,QAAQQ,MAAM;MACtC;MACA,qBAAqBP,KAAKO,MAAM;;AAIlC,UAAMC,SAAS,MAAM7B,OAAO8B,aAAY;AACxC,QAAID,OAAOD,SAAS,GAAG;AACrBD,aAAOI,KAAK,wCAAA;AACZ,iBAAWC,KAAKH,QAAQ;AACtBF,eAAOI,KAAK,oCAAoCC,EAAEC,KAAK,MAAMD,EAAEE,KAAK,EAAE;MACxE;AACAP,aAAOI,KAAK,yCAAA;AACZ,iBAAWC,KAAKH,QAAQ;AACtBF,eAAOI,KAAK,qCAAqCC,EAAEC,KAAK,MAAMD,EAAEG,YAAYP,MAAM,EAAE;MACtF;IACF;AACA,WAAO,GAAG,KAAK/B,kBAAkBuC,WAAAA,KAAgB,EAAA,GAAKT,OAAOU,KAAK,IAAA,CAAA;;EACpE;EAEAb,SAASlB,OAAyC;AAChD,WAAO,KAAKR,QAAQ0B,SAASlB,KAAAA;EAC/B;;;EAIAgC,UAAUhC,OAA8C;AACtD,WAAO,KAAKR,QAAQwC,UAAUhC,KAAAA;EAChC;;;;EAKAiC,eACEC,MACAlC,OACAmC,MAC6B;AAC7B,WAAO,KAAK3C,QAAQyC,eAAeC,MAAMlC,OAAOmC,IAAAA;EAClD;;;;EAKAC,WAA4B;AAC1B,WAAO,KAAK5C,QAAQ4C,SAAQ;EAC9B;;;;;EAMA,MAAMZ,eAAuC;AAC3C,WAAO,KAAKhC,QAAQgC,aAAY;EAClC;EAEAa,aAAaC,OAAeC,SAAuD;AACjF,WAAO,KAAK/C,QAAQ6C,aAAaC,OAAOC,OAAAA;EAC1C;;EAGA,MAAMC,cAAcF,OAAeX,OAAe3B,OAA8C;AAC9F,WAAQ,MAAM,KAAKR,QAAQgD,gBAAgBF,OAAOX,OAAO3B,KAAAA,KAAW;EACtE;;EAGAyC,eAAeH,OAAeI,OAAmD;AAC/E,WAAO,KAAKlD,QAAQiD,eAAeH,OAAOI,KAAAA;EAC5C;;EAGAC,MAAML,OAA0C;AAC9C,WAAO,KAAK9C,QAAQmD,MAAML,KAAAA;EAC5B;EAEAM,OAAON,OAAeH,MAA4D;AAChF,WAAO,KAAK3C,QAAQoD,OAAON,OAAOH,IAAAA;EACpC;;;;;EAMA,MAAMU,mBACJP,OACAQ,YAC+C;AAC/C,WAAO,KAAKtD,QAAQqD,qBAAqB;MACvCP;MACA,GAAIQ,eAAelD,SAAY;QAAEkD;MAAW,IAAI,CAAC;IACnD,CAAA;EACF;;;;;;EAOA,MAAMC,kBAAkBC,UAA8D;AACpF,UAAMC,QAAQ,MAAM,KAAKzD,QAAQuD,oBAAoBC,QAAAA;AACrD,QAAI,CAACC,MAAO,QAAOA;AACnB,UAAMC,SAAS,KAAKC,gBAAgBF,KAAAA;AACpC,WAAOC,SAAS;MAAE,GAAGD;MAAOG,eAAeF;IAAO,IAAID;EACxD;;;;;;EAOQE,gBACNF,OACmE;AACnE,UAAMI,WAAW,KAAK9D,kBAAkB8D,SAAAA;AACxC,QAAI,CAACA,YAAYJ,MAAMK,eAAehC,WAAW,EAAG,QAAO1B;AAC3D,UAAM2D,YAAYF,SAASG,sBAAsBP,MAAMD,QAAQ,KAAK,CAAC;AACrE,UAAMS,SAASJ,SAASK,0BAA0BT,MAAMD,QAAQ,KAAK,CAAC;AACtE,UAAME,SAAgE,CAAC;AACvE,eAAW,EAAEF,SAAQ,KAAMC,MAAMK,gBAAgB;AAC/CJ,aAAOF,QAAAA,IAAY;QAAEO,WAAWA,UAAUP,QAAAA,KAAa;QAAGS,QAAQA,OAAOT,QAAAA,KAAa;MAAE;IAC1F;AACA,WAAOE;EACT;;;;;;;;;;EAWA,MAAMS,KACJC,QACAC,QACA1B,MAC+C;AAC/C,UAAM2B,OAAO,MAAM,KAAKtE,QAAQ0B,SAAS;MAAE,GAAG2C;MAAQzC,OAAO;IAAI,CAAA;AACjE,QAAI2C,UAAU;AACd,eAAWC,KAAKF,MAAM;AACpB,UAAI;AACF,YAAIF,WAAW,QAAS,OAAM,KAAKpE,QAAQmD,MAAMqB,EAAE3D,EAAE;YAChD,OAAM,KAAKb,QAAQoD,OAAOoB,EAAE3D,IAAI8B,IAAAA;AACrC4B,mBAAW;MACb,QAAQ;MAER;IACF;AACA,WAAO;MAAEE,SAASH,KAAKxC;MAAQyC;IAAQ;EACzC;;EAGAG,SAAS5B,OAA0C;AACjD,WAAO,KAAK9C,QAAQ0E,SAAS5B,KAAAA;EAC/B;;EAGA6B,WAAW7B,OAAe;AACxB,WAAO,KAAK9C,QAAQ4E,kBAAkB9B,KAAAA;EACxC;;;;;;;;;;EAWA,MAAM+B,eAAeC,OAAeC,MAA0C;AAC5E,UAAM,EAAE9E,OAAOC,OAAM,IAAK,KAAKG,aAAY;AAC3C,UAAM2E,aAASC,8CAAkBH,KAAAA;AACjC,QAAI,CAACE,OAAQ,QAAO;AACpB,UAAME,MAAM,MAAMjF,MAAMkF,OAAOH,OAAOlC,KAAK;AAC3C,QAAI,CAACoC,OAAOE,iDAAsBC,SAASH,IAAIvD,MAAM,EAAG,QAAO;AAC/D,UAAM2D,WAAW,MAAMrF,MAAMsF,cAAcP,OAAOlC,OAAOkC,OAAOQ,GAAG;AACnE,QAAIF,UAAU3D,WAAW,YAAa,QAAO;AAC7C,UAAM8D,YAAY,MAAMvF,OAAOwF,OAAOZ,OAAOC,IAAAA;AAC7C,QAAIU,UAAW,QAAOA;AAItB,UAAME,MAAM,MAAM1F,MAAMsF,cAAcP,OAAOlC,OAAOkC,OAAOQ,GAAG;AAC9D,QAAIG,KAAKhE,WAAW,eAAgB,MAAM1B,MAAM2F,mBAAmBd,KAAAA,EAAS,QAAO;AACnF,UAAMe,QAAQ,MAAM5F,MAAMkF,OAAOH,OAAOlC,KAAK;AAC7C,WAAO;MAAEA,OAAOoC,IAAIrE;MAAIc,QAAQkE,OAAOlE,UAAUuD,IAAIvD;IAAO;EAC9D;;EAGA,MAAMmE,SAAShD,OAAeiD,KAA+B;AAC3D,WAAO,KAAK1F,aAAY,EAAGH,OAAO4F,SAAShD,OAAOiD,GAAAA;EACpD;;EAGA,MAAMC,OAAOlD,OAAemD,MAAcC,KAAqC;AAC7E,WAAO,KAAK7F,aAAY,EAAGH,OAAO8F,OAAOlD,OAAOmD,MAAMC,GAAAA;EACxD;;;;;;;EAQAC,UAAUrD,OAAkD;AAC1D,WAAO,IAAIsD,uBAAkC,CAAC9C,eAAAA;AAC5C,YAAM+C,MAAM,KAAKrG,QAAQsG,UAAUxD,OAAO,CAACyD,UAAUjD,WAAWkD,KAAK;QAAEC,MAAMF;MAAM,CAAA,CAAA;AACnF,aAAO,MAAMF,IAAAA;IACf,CAAA;EACF;AACF;;;;;;;;;;;;;;;ACxVA,IAAAK,wBAAkE;AAClE,IAAAC,iBAgBO;;;ACHA,IAAMC,aAAN,MAAMA;EAXb,OAWaA;;;EACXC;EACAC;;EAEAC;;EAEAC;;;;;;EAMAC;;EAEAC;AACF;AAGO,IAAMC,kBAAkB;AAOxB,IAAMC,qBAAwC;EACnD;IAAEC,MAAM;IAAYC,YAAY;IAAYC,MAAM;EAAS;EAC3D;IAAEF,MAAM;IAAUC,YAAY;IAAUC,MAAM;EAAS;EACvD;IAAEF,MAAM;IAAUC,YAAY;IAAUC,MAAM;EAAS;EACvD;IAAEF,MAAM;IAAaC,YAAY;IAAaC,MAAM;EAAS;EAC7D;IAAEF,MAAM;IAAOC,YAAY;IAAQC,MAAM;EAAS;EAClD;IAAEF,MAAMF;IAAiBG,YAAY;IAAqBC,MAAM;EAAO;;AAUlE,SAASC,gBAAgBC,OAAa;AAC3C,MAAIA,UAAUN,gBAAiB,QAAO;IAAEM,OAAO;EAAe;AAC9D,MAAIA,MAAMC,WAAW,GAAGP,eAAAA,GAAkB,GAAG;AAC3C,UAAMQ,MAAMF,MAAMG,MAAMT,gBAAgBU,SAAS,CAAA;AACjD,WAAOF,MAAM;MAAEF,OAAO;MAAkBE;IAAI,IAAI;EAClD;AACA,MACEF,UAAU,cACVA,UAAU,YACVA,UAAU,YACVA,UAAU,eACVA,UAAU,OACV;AACA,WAAO;MAAEA;IAAM;EACjB;AACA,SAAO;AACT;AAhBgBD;;;ACvDhB,IAAAM,iBAA+E;;;;;;;;;;;;;;;AAQ/E,SAASC,cAAcC,OAAc;AACnC,MAAI,OAAOA,UAAU,YAAYA,UAAU,KAAM,QAAO;AACxD,QAAMC,YAAYD;AAClB,SAAO,OAAOC,UAAUC,cAAc,cAAc,OAAOD,UAAUE,QAAQ;AAC/E;AAJSJ;AAUT,SAASK,YAAYC,UAAiB;AACpC,MAAIN,cAAcM,QAAAA,EAAW,QAAOA;AACpC,QAAMC,MAAOD,UAAuCC;AACpD,SAAOP,cAAcO,GAAAA,IAAOA,MAAM;AACpC;AAJSF;AAOT,SAASG,aAAaF,UAAmBG,MAAcR,OAAa;AAClEI,cAAYC,QAAAA,GAAWH,UAAUM,MAAMR,KAAAA;AACzC;AAFSO;AAQT,SAASE,oBAAoBJ,UAAmBK,MAAY;AAC1D,QAAMJ,MAAMF,YAAYC,QAAAA;AACxB,MAAI,CAACC,IAAK;AACVA,MAAIK,aAAa;AACjBL,MAAIJ,UAAU,QAAQQ,IAAAA;AACtBJ,MAAIJ,UAAU,iBAAiB,mBAAA;AAC/BI,MAAIH,IAAG;AACT;AAPSM;AAcF,IAAMG,uBAAN,cAAmCC,6BAAAA;SAAAA;;;;EACxC,YAAqBH,MAAc;AACjC,UAAM,gBAAgB,GAAA,GAAA,KADHA,OAAAA;EAErB;AACF;AAIO,IAAMI,oBAAN,MAAMA;SAAAA;;;EACXC,MAAMC,WAAiCC,MAA2B;AAChER,wBAAoBQ,KAAKC,aAAY,EAAGC,YAAW,GAAIH,UAAUN,IAAI;EACvE;AACF;;;;AAMO,SAASU,QAAQf,UAAmBK,MAAY;AACrDH,eAAaF,UAAU,QAAQK,IAAAA;AAC/BH,eAAaF,UAAU,iBAAiB,mBAAA;AAC1C;AAHgBe;;;AChDT,SAASC,aAAaC,KAAgB;AAC3C,QAAM,EAAEC,OAAOC,QAAQC,QAAQC,SAASC,OAAOC,QAAQ,GAAGC,IAAAA,IAAQP;AAClE,SAAOO;AACT;AAHgBR;;;AChBhB,2BAAkD;AAClD,IAAAS,iBAA2B;;;ACA3B,IAAMC,aAAa,oBAAIC,IAAiB;EAAC;EAAM;EAAM;EAAM;EAAO;EAAM;CAAM;AAI9E,IAAMC,eAAe;AAGrB,SAASC,OAAOC,GAAS;AACvB,MAAIA,MAAM,OAAQ,QAAO;AACzB,MAAIA,MAAM,QAAS,QAAO;AAC1B,MAAIA,MAAM,MAAM,CAACC,OAAOC,MAAMD,OAAOD,CAAAA,CAAAA,EAAK,QAAOC,OAAOD,CAAAA;AACxD,SAAOA;AACT;AALSD;AAgBF,SAASI,iBAAiBC,MAAwB;AACvD,MAAI,CAACA,KAAM,QAAOC;AAClB,QAAMC,MAAMC,MAAMC,QAAQJ,IAAAA,IAAQA,OAAO;IAACA;;AAC1C,QAAMK,UAA6B,CAAA;AACnC,aAAWC,SAASJ,KAAK;AACvB,UAAM,CAACK,KAAKC,IAAI,GAAGC,IAAAA,IAAQH,MAAMI,MAAM,GAAA;AACvC,QAAI,CAACH,OAAOE,KAAKE,WAAW,EAAG;AAC/B,UAAMC,UAAUH,KAAKI,KAAK,GAAA;AAC1B,QAAIL,OAAO,MAAM;AACf,YAAMM,SAASF,QAAQF,MAAMhB,YAAAA,EAAcqB,OAAOC,OAAAA,EAASC,IAAItB,MAAAA;AAC/D,UAAImB,OAAOH,OAAQN,SAAQa,KAAK;QAAEX;QAAKC,IAAI;QAAMM;MAAO,CAAA;AACxD;IACF;AACA,QAAI,CAACtB,WAAW2B,IAAIX,EAAAA,EAAoB;AACxCH,YAAQa,KAAK;MAAEX;MAAKC;MAAsCY,OAAOzB,OAAOiB,OAAAA;IAAS,CAAA;EACnF;AACA,SAAOP,QAAQM,SAASN,UAAUJ;AACpC;AAjBgBF;;;ACzBhB,IAAAsB,8BAQO;AAQP,IAAAC,iBAA2D;;;ACJpD,IAAMC,iBAAN,MAAMA;EAVb,OAUaA;;;EACFC,QAAkB,CAAC;;;EAI5BC,OAAOC,OAAuB;AAC5BC,WAAOC,OAAO,KAAKJ,OAAOE,KAAAA;EAC5B;;;EAIAG,UAAUC,QAA+B;AACvC,SAAKN,MAAMO,aAAa;SAAK,KAAKP,MAAMO,cAAc,CAAA;MAAKD;;EAC7D;;;EAIAE,aAA4B;AAC1B,UAAM,EAAEC,QAAQC,UAAUC,QAAQC,SAASC,OAAOC,QAAQ,GAAGN,WAAAA,IAAe,KAAKR;AACjF,WAAOQ;EACT;AACF;;;;;;;;;;;;;;;;;;;;;;;ADXO,IAAMO,oBAAoBC,OAAO,2BAAA;AAGxC,IAAMC,cAAwB;EAAEC,OAAO;EAAaC,WAAW;AAAO;AAOtE,SAASC,QAAOC,OAAc;AAC5B,MAAI,OAAOA,UAAU,YAAY,OAAOA,UAAU,UAAW,QAAOA;AACpE,QAAMC,MAAMC,OAAOF,KAAAA;AACnB,MAAIC,QAAQ,OAAQ,QAAO;AAC3B,MAAIA,QAAQ,QAAS,QAAO;AAC5B,MAAIA,QAAQ,MAAM,CAACE,OAAOC,MAAMD,OAAOF,GAAAA,CAAAA,EAAO,QAAOE,OAAOF,GAAAA;AAC5D,SAAOA;AACT;AAPSF,OAAAA,SAAAA;AAUT,SAASM,SAASC,QAAoB;AACpC,SAAOC,MAAMC,QAAQF,OAAON,KAAK,IAAIM,OAAON,QAAQ;IAACM,OAAON;;AAC9D;AAFSK;AAMT,SAASI,YAAYH,QAAsBI,WAAiB;AAC1D,QAAM,IAAIC,mCACR,wCAAwCL,OAAOT,KAAK,aAAaS,OAAOM,QAAQ,uBAAuBF,SAAAA,GAAY;AAEvH;AAJSD;AAoBF,IAAMI,kBAAN,MAAMA;SAAAA;;;;EACX,YAA6BC,SAAqB;SAArBA,UAAAA;EAAsB;EAEnDC,mBAAsBC,QAA0B;AAC9C,SAAKC,iBAAiBD,MAAAA;AACtB,WAAO,IAAIE,eAAAA;EACb;EAEAC,gBAAgBH,QAAiD;AAC/D,WAAOA,WAAWI,aAAaC,qBAAqB;EACtD;;;EAIAC,qBAAyB;AACvB,WAAO,CAAA;EACT;;;;;;EAOAC,iBAAiBP,QAAuBQ,MAAoD;AAC1F,QAAIR,WAAWI,WAAY,QAAO;AAClC,QAAII,SAASC,mBAAmBD,KAAKE,WAAW,GAAGD,eAAAA,GAAkB,EAAG,QAAO;AAC/E,WAAOJ,mBAAmBM,KAAK,CAACC,MAAMA,EAAEC,SAASL,IAAAA,IAAQ,UAAU;EACrE;EAEAM,mBAAmBC,IAAaC,SAAyBhB,QAA8B;AACrF,QAAIA,OAAQ,MAAKC,iBAAiBD,MAAAA;AAClC,UAAMiB,QAAQ,KAAKA,MAAMF,EAAAA;AACzB,eAAWzB,UAAU0B,SAAS;AAE5B,UAAI1B,OAAO4B,OAAO5B,OAAO6B,IAAI;AAC3B,cAAM,IAAIxB,mCACR,sGAAA;MAEJ;AACA,WAAKyB,YAAYH,OAAO3B,MAAAA;IAC1B;EACF;EAEQ8B,YAAYH,OAAsB3B,QAA4B;AACpE,UAAM,EAAET,OAAOe,SAAQ,IAAKN;AAC5B,QAAIT,UAAU4B,mBAAmB5B,MAAM6B,WAAW,GAAGD,eAAAA,GAAkB,GAAG;AACxE,WAAKY,qBAAqBJ,OAAO3B,MAAAA;AACjC;IACF;AACA,UAAMgC,QAAQ1B,aAAa,QAAQA,aAAa;AAChD,YAAQf,OAAAA;MACN,KAAK;AACH,YAAIe,aAAa,UAAU;AACzBqB,gBAAMM,OAAO;YAAEC,QAAQtC,OAAOI,OAAON,KAAK;UAAe,CAAA;QAC3D,WAAWsC,OAAO;AAChBL,gBAAMM,OAAO;YAAEE,UAAUpC,SAASC,MAAAA,EAAQoC,IAAI,CAACC,MAAMzC,OAAOyC,CAAAA,CAAAA;UAAiB,CAAA;QAC/E,OAAO;AACLlC,sBAAYH,QAAQ,YAAA;QACtB;AACA;MACF,KAAK;AACH,YAAIM,aAAa,UAAU;AACzBqB,gBAAMM,OAAO;YAAEK,UAAU1C,OAAOI,OAAON,KAAK;UAAE,CAAA;QAChD,WAAWsC,OAAO;AAChBL,gBAAMM,OAAO;YAAEM,WAAWxC,SAASC,MAAAA,EAAQoC,IAAIxC,MAAAA;UAAQ,CAAA;QACzD,OAAO;AACLO,sBAAYH,QAAQ,YAAA;QACtB;AACA;MACF,KAAK;AACH,YAAIM,aAAa,UAAU;AACzBqB,gBAAMM,OAAO;YAAEO,WAAW5C,OAAOI,OAAON,KAAK;UAAE,CAAA;QACjD,WAAWsC,OAAO;AAChBL,gBAAMM,OAAO;YAAEQ,YAAY1C,SAASC,MAAAA,EAAQoC,IAAIxC,MAAAA;UAAQ,CAAA;QAC1D,OAAO;AACLO,sBAAYH,QAAQ,YAAA;QACtB;AACA;MACF,KAAK;AAEH,YAAIM,aAAa,UAAU;AACzBqB,gBAAMM,OAAO;YAAES,KAAK9C,OAAOI,OAAON,KAAK;UAAE,CAAA;QAC3C,WAAWsC,OAAO;AAChBL,gBAAMM,OAAO;YAAEU,MAAM5C,SAASC,MAAAA,EAAQoC,IAAIxC,MAAAA;UAAQ,CAAA;QACpD,OAAO;AACLO,sBAAYH,QAAQ,YAAA;QACtB;AACA;MACF,KAAK;AAGH,YAAIM,aAAa,UAAU;AACzBqB,gBAAMM,OAAO;YAAEW,QAAQ;UAAK,CAAA;QAC9B,WAAWtC,aAAa,UAAU;AAChCqB,gBAAMM,OAAO;YAAEW,QAAQ5C,OAAON,UAAU,OAAO,OAAOE,OAAOI,OAAON,KAAK;UAAE,CAAA;QAC7E,WAAWsC,OAAO;AAChBL,gBAAMM,OAAO;YAAEY,SAAS9C,SAASC,MAAAA,EAAQoC,IAAI,CAACC,MAAOA,MAAM,OAAO,OAAOzC,OAAOyC,CAAAA,CAAAA;UAAK,CAAA;QACvF,OAAO;AAILlC,sBAAYH,QAAQ,oBAAA;QACtB;AACA;MACF;AACEG,oBAAYH,QAAQ,UAAUe,mBAAmBqB,IAAI,CAACd,MAAMA,EAAEC,IAAI,EAAEuB,KAAK,IAAA,CAAA,EAAO;IACpF;EACF;;;EAIQf,qBAAqBJ,OAAsB3B,QAA4B;AAC7E,UAAM+C,MAAM/C,OAAOT,MAAMyD,MAAM7B,gBAAgB8B,SAAS,CAAA;AACxD,QAAI,CAACF,KAAK;AACR,YAAM,IAAI1C,mCACR,0EAAA;IAEJ;AACA,UAAM6C,YAAwD;MAC5DC,QAAQ;MACRC,WAAW;MACXC,IAAI;MACJC,KAAK;MACLC,IAAI;MACJC,KAAK;IACP;AACA,UAAMC,SAASP,UAAUlD,OAAOM,QAAQ;AACxC,QAAImD,QAAQ;AACV9B,YAAM+B,UAAU;QAAEX;QAAKY,IAAIF;QAAQ/D,OAAOD,QAAOO,OAAON,KAAK;MAAE,CAAA;AAC/D;IACF;AACA,QAAIM,OAAOM,aAAa,QAAQN,OAAOM,aAAa,WAAW;AAC7DqB,YAAM+B,UAAU;QAAEX;QAAKY,IAAI;QAAMC,QAAQ7D,SAASC,MAAAA,EAAQoC,IAAI3C,OAAAA;MAAQ,CAAA;AACtE;IACF;AACAU,gBAAYH,QAAQ,yCAAA;EACtB;EAEA6D,sBAAsBpC,IAAaqC,MAAcC,MAAoB;AACnE,SAAKpC,MAAMF,EAAAA,EAAIQ,OAAO;MAAE+B,OAAOD;MAAME,SAASH,OAAO,KAAKC;IAAK,CAAA;EACjE;;;;;;;EAQAG,UAAUC,KAAcC,OAAyB;AAC/C,eAAWC,QAAQD,OAAO;AACxB,UAAIC,KAAK9E,UAAUD,YAAYC,SAAS8E,KAAK7E,cAAcF,YAAYE,WAAW;AAChF,cAAM,IAAIa,mCACR,4CAA4Cf,YAAYC,KAAK,IAAID,YAAYE,SAAS,gCAAgC6E,KAAK9E,KAAK,IAAI8E,KAAK7E,SAAS,KAAK;MAE3J;IACF;EACF;EAEA8E,UAAU7C,IAAqC;AAC7C,WAAO,KAAKjB,QAAQ+D,SAAS,KAAK5C,MAAMF,EAAAA,EAAI+C,KAAK;EACnD;;;;;;EAOA,MAAMC,kBAAkBhD,IAA0D;AAChF,UAAME,QAAQ,KAAKA,MAAMF,EAAAA;AACzB,UAAM,CAACiD,MAAMC,MAAAA,IAAU,MAAMC,QAAQC,IAAI;MACvC,KAAKrE,QAAQ+D,SAAS5C,MAAM6C,KAAK;MACjC,KAAKhE,QAAQsE,UAAUnD,MAAMoD,WAAU,CAAA;KACxC;AACD,UAAM,EAAE7C,QAAQC,UAAUS,QAAQC,QAAO,IAAKlB,MAAM6C;AACpD,UAAMQ,QAAQL,OACXM,OAAO,CAACC,SAAAA;AACP,UAAIhD,UAAUgD,KAAKhD,WAAWA,OAAQ,QAAO;AAC7C,UAAIC,YAAY,CAACA,SAASgD,SAASD,KAAKhD,MAAM,EAAG,QAAO;AACxD,UAAIU,WAAWwC,UAAaF,KAAKtC,WAAWA,OAAQ,QAAO;AAC3D,UAAIC,WAAW,CAACA,QAAQsC,SAASD,KAAKtC,MAAM,EAAG,QAAO;AACtD,aAAO;IACT,CAAA,EACCyC,OAAO,CAACC,KAAKJ,SAASI,MAAMJ,KAAKK,OAAO,CAAA;AAC3C,WAAO;MAAEb;MAAMM;IAAM;EACvB;;;;;;;;EASA,MAAMQ,aACJ/D,IACAlC,OACAmB,QACA+E,MACmD;AACnD,SAAK9E,iBAAiBD,MAAAA;AACtB,QAAI,OAAOnB,UAAU,UAAU;AAC7B,YAAM,IAAIc,mCACR,gGAAA;IAEJ;AACA,QAAIoF,MAAMC,WAAWN,QAAW;AAC9B,YAAM,IAAI/E,mCACR,gGAAA;IAEJ;AACA,UAAMsF,OAAOC,gBAAgBrG,KAAAA;AAC7B,QAAI,CAACoG,MAAM;AACT,YAAM,IAAItF,mCACR,qDAAqDd,KAAAA,KAAU;IAEnE;AACA,UAAMmF,OAAO,MAAM,KAAKlE,QAAQqF,eAAeF,MAAM,KAAKhE,MAAMF,EAAAA,EAAIsD,WAAU,GAAI;MAChFf,OAAOyB,MAAMzB,SAAS8B;MACtB,GAAIL,MAAMxB,WAAWmB,UAAa;QAAEnB,QAAQwB,KAAKxB;MAAO;MACxD,GAAIwB,MAAMM,WAAWX,UAAa;QAAEW,QAAQN,KAAKM;MAAO;IAC1D,CAAA;AACA,WAAOrB,KAAKtC,IAAI,CAAC4D,SAAS;MAAEtG,OAAOsG,IAAItG;MAAO6F,OAAOS,IAAIT;IAAM,EAAA;EACjE;EAEAU,gBAAwB;AACtB,WAAO;EACT;EAEQtE,MAAMF,IAA4B;AACxC,QAAI,EAAEA,cAAcb,iBAAgB;AAClC,YAAM,IAAIsF,MACR,yKAAA;IAEJ;AACA,WAAOzE;EACT;EAEQd,iBAAiBD,QAA6B;AACpD,QAAIA,WAAWI,YAAY;AACzB,YAAM,IAAIoF,MACR,4DAA4DxF,QAAQa,QAAQ3B,OAAOc,MAAAA,CAAAA,GAAU;IAEjG;EACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AF3SA,SAASyF,KAAKC,OAAc;AAC1B,UAAQC,MAAMC,QAAQF,KAAAA,IAASA,QAAQ;IAACA;KACrCG,OAAO,CAACC,MAAMA,MAAMC,UAAaD,MAAM,QAAQA,MAAM,EAAA,EACrDE,IAAIC,MAAAA;AACT;AAJSR;AAQT,SAASS,MAAMR,OAAc;AAC3B,QAAMS,IAAIC,OAAOV,KAAAA;AACjB,SAAOA,UAAUK,UAAaL,UAAU,MAAMU,OAAOC,UAAUF,CAAAA,KAAMA,KAAK,IAAIA,IAAIJ;AACpF;AAHSG;AAqBF,IAAMI,YAAN,cAAwBC,gCAAAA;SAAAA;;;;;EAI7BC,YAAYd,OAAsB;AAChC,UAAMe,SAAShB,KAAKC,KAAAA;AACpB,QAAIe,OAAOC,WAAW,EAAG;AACzB,SAAKC,OAAOC,OAAOH,OAAOC,WAAW,IAAI;MAAEG,QAAQJ,OAAO,CAAA;IAAG,IAAI;MAAEK,UAAUL;IAAO,CAAA;EACtF;EAGAM,cAAcrB,OAAsB;AAClC,UAAMe,SAAShB,KAAKC,KAAAA;AACpB,QAAIe,OAAOC,WAAW,EAAG;AACzB,SAAKC,OAAOC,OAAOH,OAAOC,WAAW,IAAI;MAAEM,UAAUP,OAAO,CAAA;IAAG,IAAI;MAAEQ,WAAWR;IAAO,CAAA;EACzF;;;EAKAS,eAAexB,OAAsB;AACnC,UAAMe,SAAShB,KAAKC,KAAAA;AACpB,QAAIe,OAAOC,WAAW,EAAG;AACzB,SAAKC,OAAOC,OAAOH,OAAOC,WAAW,IAAI;MAAES,WAAWV,OAAO,CAAA;IAAG,IAAI;MAAEW,YAAYX;IAAO,CAAA;EAC3F;EAGAY,SAAS3B,OAAsB;AAC7B,UAAMe,SAAShB,KAAKC,KAAAA;AACpB,QAAIe,OAAOC,WAAW,EAAG;AACzB,SAAKC,OAAOC,OAAOH,OAAOC,WAAW,IAAI;MAAEY,KAAKb,OAAO,CAAA;IAAG,IAAI;MAAEc,MAAMd;IAAO,CAAA;EAC/E;;;;;;;;;;;;;;;;;EAmBAe,cAAoB;AAClB,SAAKC,cAAa;EACpB;EAGAC,oBAA0B;AACxB,SAAKD,cAAa;EACpB;EAEQA,gBAAsB;AAC5B,UAAME,QAAQlC,KAAK,KAAKmC,OAAOC,MAAM;AACrC,UAAMC,MAAM,KAAKF,OAAOG;AACxB,UAAMC,oBAAoBF,QAAQ,UAAUA,QAAQ,OAAOA,QAAQ;AACnE,UAAMrB,SAA+B;SAAIkB;SAAWK,oBAAoB;QAAC;UAAQ,CAAA;;AACjF,QAAIvB,OAAOC,WAAW,EAAG;AACzB,QAAID,OAAOC,WAAW,GAAG;AACvB,YAAMuB,OAAOxB,OAAO,CAAA;AACpB,WAAKE,OAAOC,OAAO;QAAEiB,QAAQI;MAAK,CAAA;AAClC;IACF;AACA,SAAKtB,OAAOC,OAAO;MAAEsB,SAASzB;IAAO,CAAA;EACvC;;;EAKA0B,gBAAgBzC,OAAsB;AACpC,eAAWG,UAAUuC,iBAAiB1C,KAAAA,KAA+B,CAAA,GAAI;AACvE,WAAKiB,OAAO0B,UAAUxC,MAAAA;IACxB;EACF;EAGAyC,WAAW5C,OAAsB;AAC/B,UAAM6C,QAAQrC,MAAMR,KAAAA;AACpB,QAAI6C,UAAUxC,OAAW,MAAKY,OAAOC,OAAO;MAAE2B;IAAM,CAAA;EACtD;EAGAC,YAAY9C,OAAsB;AAChC,UAAM+C,SAASvC,MAAMR,KAAAA;AACrB,QAAI+C,WAAW1C,OAAW,MAAKY,OAAOC,OAAO;MAAE6B;IAAO,CAAA;EACxD;AACF;;;;;;;2CAzF+B;;;;;;;;2CAOE;;;;;;;;2CASC;;;;;;;;2CAON;;;;;;2CAuBX;;;;;;2CAKM;;;;;;;;2CAqBY;;;;;;;;2CAOL;;;;;;;;2CAMC;;;;;IA1FjBC,QAAQC;IAAYC,SAASC;IAAmBC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AJnB1E,IAAMC,iBAAiB;AAwBhB,IAAMC,2BAAN,MAAMA;SAAAA;;;;;EACX,YACmBC,WACAC,QACjB;SAFiBD,YAAAA;SACAC,SAAAA;EAChB;;;;;;;;;;;EAYH,MACMC,KAA6BC,OAA6C;AAC9E,UAAMD,OAAO,MAAM,KAAKF,UAAUI,SAASD,MAAME,KAAK;AAEtD,WAAOH,KAAKI,IAAIC,YAAAA;EAClB;;;;;;;;;;EAYAC,OAA+BL,OAAsB;AACnD,WAAO,KAAKH,UAAUS,UAAUN,MAAMO,WAAU,CAAA;EAClD;;;;;;;;;;;;;;;EAiBAC,OAAgBN,OAAgC;AAC9C,WAAO,KAAKJ,OAAOW,aAAaC,YAAYR,OAAO;MAAES,aAAaC;IAAU,CAAA;EAC9E;;EAKAC,UAAU;AACR,WAAO,KAAKhB,UAAUgB,QAAO;EAC/B;;EAIAC,UAAU;AACR,WAAO,KAAKjB,UAAUkB,aAAY;EACpC;;;;;EAOAC,UACsBC,WACDC,UACLC,KACGC,QACDC,OAChB;AACA,WAAO,KAAKxB,UAAUyB,cAAc;MAClC,GAAIL,YAAY;QAAEA;MAAU,IAAI,CAAC;MACjC,GAAIC,WAAW;QAAEA;MAAS,IAAI,CAAC;MAC/B,GAAIC,MAAM;QAAEA;MAAI,IAAI,CAAC;MACrB,GAAIC,WAAW,UAAUA,WAAW,UAAU;QAAEA,QAAQA,WAAW;MAAO,IAAI,CAAC;MAC/EC,OAAOE,KAAKC,IAAIC,OAAOJ,KAAAA,KAAU,KAAK,GAAA;IACxC,CAAA;EACF;EAGAK,cAA2BC,IAAY;AACrC,WAAO,KAAK9B,UAAU6B,cAAcC,EAAAA,EAAIC,MAAMC,QAAAA;EAChD;EAGAC,eAA4BH,IAAY;AACtC,WAAO,KAAK9B,UAAUiC,eAAeH,EAAAA,EAAIC,MAAMC,QAAAA;EACjD;;EAIAE,gBAA6BJ,IAAY;AACvC,WAAO,KAAK9B,UAAUkC,gBAAgBJ,EAAAA,EAAIC,MAAMC,QAAAA;EAClD;;;;;EAMA,MACMG,kBAAiCC,MAAc;AACnD,UAAMC,QAAQ,MAAM,KAAKrC,UAAUmC,kBAAkBC,IAAAA,EAAML,MAAMO,oBAAAA;AACjE,QAAI,CAACD,MACH,OAAM,IAAIE,uCAAwB,iDAAA;AACpC,WAAOF;EACT;;EAIAG,WAAW;AACT,WAAO,KAAKxC,UAAUwC,SAAQ;EAChC;;;;;;;;;EAUA,MACMC,IACSX,IACEY,MACWC,aACEC,UAC5B;AACA,UAAMC,SAAS,MAAM,KAAK7C,UAAU8C,aAClChB,IACAY,SAAS,SACLK,SACA;MAAEC,mBAAmBlD;MAAgB,GAAI6C,eAAe;QAAEA;MAAY;IAAG,CAAA;AAE/E,QAAI,CAACE,OAAQ,OAAM,IAAII,iCAAkB,OAAOnB,EAAAA,YAAc;AAC9D,UAAM,EAAEoB,MAAMC,aAAa,GAAGC,KAAAA,IAASP;AACvC,QAAIK,MAAM;AACR,UAAIC,YAAa,OAAM,IAAIE,qBAAqBH,IAAAA;AAChDI,cAAQV,UAAUM,IAAAA;IACpB;AACA,WAAOE;EACT;;EAGA,MACMG,WACSzB,IACG0B,OACCC,QACDjC,OACCkC,QACSf,aACEC,UAC5B;AACA,UAAMe,OAAO,MAAM,KAAK3D,UAAU4D,cAAc9B,IAAI0B,OAAO;MACzDC,QAAQA,WAAWV,SAAYA,SAAYnB,OAAO6B,MAAAA;MAClDjC,OAAOA,UAAUuB,SAAYA,SAAYnB,OAAOJ,KAAAA;MAChDqC,YAAYH,WAAW;MACvB,GAAIf,eAAe;QAAEA;MAAY;IACnC,CAAA;AACA,QAAI,CAACgB,KAAM,OAAM,IAAIV,iCAAkB,OAAOO,KAAAA,WAAgB1B,EAAAA,YAAc;AAC5E,UAAM,EAAEoB,MAAMC,aAAa,GAAGC,KAAAA,IAASO;AACvC,QAAIT,MAAM;AACR,UAAIC,YAAa,OAAM,IAAIE,qBAAqBH,IAAAA;AAChDI,cAAQV,UAAUM,IAAAA;IACpB;AACA,WAAOE;EACT;;;EAKAU,OAAoBhC,IAAY;AAC9B,WAAO,KAAK9B,UAAU+D,UAAUjC,EAAAA;EAClC;EAGAkC,MAAmBlC,IAAY;AAC7B,WAAO,KAAK9B,UAAUgE,MAAMlC,EAAAA;EAC9B;;EAIAmC,eAA4BnC,IAAoBsB,MAA0B;AACxE,WAAO,KAAKpD,UAAUiE,eAAenC,IAAIsB,MAAMc,KAAAA;EACjD;;;;;;EAQAC,KACmBC,QACOjE,OACHkE,YACrB;AAIA,UAAM,EAAE7C,OAAOiC,QAAQ,GAAGa,OAAAA,IAAWnE,MAAME;AAC3C,WAAO,KAAKL,UAAUmE,KAAKC,WAAW,WAAW,WAAW,SAASE,QAAQ;MAC3ED,YAAYA,eAAe;IAC7B,CAAA;EACF;EAEA,MACME,OAAoBzC,IAAiCuC,YAAqB;AAC9E,UAAMG,SAAS,MAAM,KAAKxE,UAAUuE,OAAOzC,IAAI;MAAEuC,YAAYA,eAAe;IAAO,CAAA;AACnF,QAAI,CAACG,OAAQ,OAAM,IAAIvB,iCAAkB,OAAOnB,EAAAA,YAAc;AAC9D,WAAO0C;EACT;;;;;EAMA,MACMC,mBAAgC3C,IAAoBsB,MAAgC;AACxF,QAAIoB;AACJ,QAAI;AACFA,eAAS,MAAM,KAAKxE,UAAUyE,mBAAmB3C,IAAIsB,MAAMsB,cAAc3B,MAAAA;IAC3E,SAAS4B,OAAO;AACd,YAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,UAAUE,OAAOH,KAAAA;AAChE,UAAI,qBAAqBI,KAAKH,OAAAA,EAAU,OAAM,IAAI3B,iCAAkB2B,OAAAA;AACpE,UAAI,0BAA0BG,KAAKH,OAAAA,EAAU,OAAM,IAAII,mCAAoBJ,OAAAA;AAC3E,UAAKD,OAAqCM,SAAS,eAAe;AAChE,cAAM,IAAID,mCAAoBJ,OAAAA;MAChC;AACA,aAAOtC,qBAAqBqC,KAAAA;IAC9B;AACA,QAAI,CAACH,QAAQ;AACX,YAAM,IAAIjC,uCAAwB,uDAAA;IACpC;AACA,WAAOiC;EACT;EAEA,MACMU,SAAsBpD,IAAY;AACtC,UAAM0C,SAAS,MAAM,KAAKxE,UAAUkF,SAASpD,EAAAA;AAC7C,QAAI,CAAC0C,OAAQ,OAAM,IAAIvB,iCAAkB,OAAOnB,EAAAA,gCAAkC;AAClF,WAAO0C;EACT;;EAGA,MACMW,WAAwBrD,IAAY;AACxC,UAAM0C,SAAS,MAAM,KAAKxE,UAAUmF,WAAWrD,EAAAA;AAC/C,QAAI,CAAC0C,OAAQ,OAAM,IAAIvB,iCAAkB,OAAOnB,EAAAA,YAAc;AAC9D,WAAO0C;EACT;;;;;;;EAQA,MAEMY,QAAwBC,OAAuBjC,MAAe;AAClE,UAAMoB,SAAS,MAAM,KAAKxE,UAAUsF,eAAeD,OAAOjC,IAAAA;AAC1D,QAAI,CAACoB,OAAQ,OAAM,IAAIvB,iCAAkB,sCAAsCoC,KAAAA,EAAO;AACtF,WAAOb;EACT;;EAIAe,MAAmBzD,IAA0B0D,KAAa;AACxD,WAAO,KAAKxF,UAAUyF,SAAS3D,IAAI0D,GAAAA;EACrC;;EAIAE,OAAoB5D,IAA2BM,MAAsBgB,MAAe;AAClF,WAAO,KAAKpD,UAAU0F,OAAO5D,IAAIM,MAAMgB,IAAAA;EACzC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAxJWuC,aAAa;;;;;;;;;;;;;;;;;;;;IA0BbA,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiIxB,SAAS3D,SAAS2C,OAAc;AAC9B,MAAIA,iBAAiBE,SAASF,MAAMvC,SAAS,yBAAyB;AACpE,UAAM,IAAIa,iCAAkB0B,MAAMC,OAAO;EAC3C;AACA,QAAMD;AACR;AALS3C;AAWT,SAASM,qBAAqBqC,OAAc;AAC1C,MAAKA,OAAqCM,SAAS,uBAAuB;AACxE,UAAM,IAAI1C,uCAAwBoC,iBAAiBE,QAAQF,MAAMC,UAAUE,OAAOH,KAAAA,CAAAA;EACpF;AACA,QAAMA;AACR;AALSrC;AAcF,IAAMsD,uBAAN,cAAmC7F,yBAAAA;SAAAA;;;AAA0B;;;;;;AQ3WpE,IAAA8F,2BAAO;AACP,IAAAC,wBAAqD;AACrD,IAAAC,iBAAqE;AACrE,IAAAC,eAAwC;;;ACHxC,IAAAC,2BAAO;AAQA,IAAMC,sBAAsB;AAQ5B,SAASC,wBAAwBC,YAAgB;AACtDC,UAAQC,eAAeJ,qBAAqB,MAAME,UAAAA;AACpD;AAFgBD;AAaT,SAASI,cACdC,MACAC,YAAiD;AAEjD,MAAIA,eAAeC,UAAaD,WAAWE,WAAW,EAAG,QAAOH;AAChE,QAAMI,UAAU,cAAcJ,KAAAA;IAlChC,OAkCgCA;;;EAAM;AAEpCK,SAAOC,eAAeF,SAAS,QAAQ;IAAEG,OAAOP,KAAKQ;EAAK,CAAA;AAC1Db,0BAAwBS,OAAAA;AACxB,MAAIK,YAAeL;AACnB,aAAWM,aAAaT,YAAY;AAElCQ,gBAAaC,UAAUD,SAAAA,KAAgCA;EACzD;AACA,SAAOA;AACT;AAfgBV;;;AC7BhB,qBAAyC;AACzC,uBAAsD;AACtD,sBAA8B;AAC9B,IAAAY,iBAQO;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAMC,sBAAsBC,OAAO,qBAAA;AAEnC,IAAMC,qBAAqBD,OAAO,oBAAA;AAGzC,IAAME,aAAa;AAGnB,SAASC,SAAAA;AACP,aAAOC,+BAAc,IAAIC,IAAI,UAAU,eAAe,CAAA;AACxD;AAFSF;AAIT,IAAMG,gBAAwC;EAC5C,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,UAAU;EACV,QAAQ;AACV;AAeO,IAAMC,0BAAN,MAAMA;SAAAA;;;;;EACMC,MAAML,OAAAA;EAEvB,YACgDM,UACDC,aAC7C;SAF8CD,WAAAA;SACDC,cAAAA;EAC5C;;;EAOHC,QAAgB;AACd,UAAMC,gBAAYC,uBAAK,KAAKL,KAAK,YAAA;AACjC,QAAI,KAACM,2BAAWF,SAAAA,GAAY;AAC1B,YAAM,IAAIG,iCAAkB,gDAAA;IAC9B;AAGA,UAAMC,WAAOC,6BAAaL,WAAW,MAAA,EAAQM,WAC3C,KAAKhB,UAAAA,KACL,KAAK,KAAKO,QAAQ,GAAG;AAGvB,UAAMU,SAAS,oCAAoC,KAAKV,QAAQ,6BAA6B,KAAKC,WAAW;AAC7G,WAAOM,KAAKI,SAAS,SAAA,IAAaJ,KAAKK,QAAQ,WAAW,GAAGF,MAAAA,SAAe,IAAIA,SAASH;EAC3F;EAIAM,MAAqBC,MAA8B;AACjD,UAAMC,WAAOC,2BAASF,IAAAA;AACtB,QAAIC,SAASD,KAAM,OAAM,IAAIR,iCAAAA;AAC7B,UAAMW,WAAOC,0BAAQ,KAAKnB,KAAK,QAAA;AAC/B,UAAMoB,gBAAYD,0BAAQD,MAAMF,IAAAA;AAChC,QAAI,CAACI,UAAUC,WAAWH,OAAOI,oBAAAA,KAAQ,KAAChB,2BAAWc,SAAAA,GAAY;AAC/D,YAAM,IAAIb,iCAAAA;IACZ;AACA,UAAMgB,OAAOzB,kBAAc0B,0BAAQR,IAAAA,CAAAA,KAAU;AAC7C,WAAO,IAAIS,kCAAehB,6BAAaW,SAAAA,GAAY;MAAEG;IAAK,CAAA;EAC5D;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASO,IAAMG,sBAAN,cAAkC3B,wBAAAA;SAAAA;;;AAAyB;;;;;;;;;;;;;;;;;;;;AF3ElE4B,wBAAwBC,oBAAAA;AACxBD,wBAAwBE,mBAAAA;AA2FxB,SAASC,wBAAwBC,SAAgC;AAC/D,MAAIA,QAAQC,eAAeC,OAAW;AACtC,QAAM,IAAIC,MACR;IACE;IACA;IACA;IACA;IACAC,KAAK,GAAA,CAAA;AAEX;AAVSL;AAaT,SAASM,kBAAkCC,MAASL,YAAwC;AAC1F,SAAOM,cAAcD,MAAM;OAAKL,cAAc,CAAA;IAAKO,oBAAAA;GAAsB;AAC3E;AAFSH;AAKT,SAASI,UAAUC,MAAY;AAC7B,SAAO,IAAIA,KAAKC,QAAQ,cAAc,EAAA,CAAA;AACxC;AAFSF;AAUF,IAAMG,mBAAN,MAAMA,kBAAAA;SAAAA;;;EACX,OAAOC,SAASb,SAQE;AAChB,UAAMc,SAASd,QAAQc,WAAW;AAClC,WAAO;MACLC,QAAQH;MACRI,SAAS;WAAKhB,QAAQgB,WAAW,CAAA;;MACjCC,aAAaH,SAAS;QAACT,kBAAkBR,sBAAsBG,QAAQC,UAAU;UAAK,CAAA;MACtFiB,WAAW;QACTC;;;QAGA;UACEC,SAASC;;UAETC,YAAY,wBAACC,cACXC,wBAAwBxB,QAAQyB,WAAWF,SAAAA,GADjC;UAEZG,QAAQ;YAACC;;QACX;;;;;;;;;;;;;;;;QAgBA;UACEP,SAASQ;UACTN,YAAY,wBAACC,cACX,IAAIK,mCAAaL,WAAW;YAAEM,YAAY;UAAM,GAAG,IAAA,GADzC;UAEZH,QAAQ;YAACC;;QACX;QACAG;QACAC;QACA;UAAEX,SAASY;UAAmBC,aAAaF;QAAgB;QAC3DG;;;;;;MAMFC,SAASrB,SACL;QAACK;QAAkBE;UACnB;QAACF;QAAkBS;QAAcP;;IACvC;EACF;AACF;;;;AAaO,IAAMe,yBAAN,MAAMA,wBAAAA;SAAAA;;;EACX,OAAOC,QAAQrC,UAAmC,CAAC,GAAkB;AACnE,UAAMsC,WAAW7B,UAAUT,QAAQsC,YAAY,UAAA;AAC/C,UAAMC,cAAc9B,UAAUT,QAAQuC,eAAe,GAAGD,QAAAA,MAAc;AACtE,UAAME,gBAA4B;MAChC;QAAEpB,SAASqB;QAAqBC,UAAUJ;MAAS;MACnD;QAAElB,SAASuB;QAAoBD,UAAUH;MAAY;;AAEvD,QAAIvC,QAAQc,WAAW,OAAO;AAC5Bf,8BAAwBC,OAAAA;AACxB,YAAM4C,MAAMhC,iBAAiBC,SAAS;QACpC,GAAIb,QAAQgB,UAAU;UAAEA,SAAShB,QAAQgB;QAAQ,IAAI,CAAC;QACtD,GAAIhB,QAAQyB,cAAcvB,SAAY;UAAEuB,WAAWzB,QAAQyB;QAAU,IAAI,CAAC;QAC1EX,QAAQ;MACV,CAAA;AACA,aAAO;QACLC,QAAQqB;QACRpB,SAAS;aAAKhB,QAAQgB,WAAW,CAAA;UAAK4B;;QACtC3B,aAAa,CAAA;QACbC,WAAWsB;;;;QAIXL,SAAS;UAACvB;UAAkB6B;UAAqBE;;MACnD;IACF;AACA,WAAO;MACL5B,QAAQqB;MACRpB,SAAS;WACHhB,QAAQgB,WAAW,CAAA;;;;QAIvBJ,iBAAiBC,SAAS;UACxB,GAAIb,QAAQgB,UAAU;YAAEA,SAAShB,QAAQgB;UAAQ,IAAI,CAAC;UACtD,GAAIhB,QAAQC,aAAa;YAAEA,YAAYD,QAAQC;UAAW,IAAI,CAAC;UAC/D,GAAID,QAAQyB,cAAcvB,SAAY;YAAEuB,WAAWzB,QAAQyB;UAAU,IAAI,CAAC;QAC5E,CAAA;QACAoB,0BAAahC,SAAS;UACpB;YAAEH,MAAM4B;YAAUvB,QAAQqB;UAAuB;UACjD;YAAE1B,MAAM6B;YAAaxB,QAAQH;UAAiB;SAC/C;;MAEHK,aAAa;QAACZ,kBAAkBP,qBAAqBE,QAAQC,UAAU;;MACvEiB,WAAWsB;;MAEXL,SAAS;QAACvB;;IACZ;EACF;AACF;;;;","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","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","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"]}