@dudousxd/nestjs-durable-dashboard 0.43.2 → 0.45.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,8 +1,10 @@
1
1
  import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
2
- import { RunGateway, StateStore, WorkflowEngine, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, DurableTopology, GroupHealth, RunDetail, RunResult, UpdateResult, EngineEvent, RunStatus } from '@dudousxd/nestjs-durable-core';
2
+ import { RunGateway, StateStore, WorkflowEngine, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetail, RunResult, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
3
3
  export { RunDetail } from '@dudousxd/nestjs-durable-core';
4
4
  import * as rxjs from 'rxjs';
5
5
  import { Observable } from 'rxjs';
6
+ import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
7
+ import { FilterRunner } from '@dudousxd/nestjs-filter';
6
8
  import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, ExecutionContext, StreamableFile } from '@nestjs/common';
7
9
 
8
10
  /** The session user the host `login` hook returns to mint a cookie. */
@@ -137,6 +139,10 @@ declare class DashboardService {
137
139
  /** `(status, origin)` counts over the same predicates {@link listRuns} pages — what keeps the
138
140
  * console's chips exact while its list is bounded to a page. */
139
141
  runFacets(query: RunFacetQuery): Promise<RunFacetRow[]>;
142
+ /** The distinct values of one filter axis over the same predicates — what fills the console's
143
+ * tenant/tag/attribute pickers, so an operator picks a value that exists instead of typing one
144
+ * blind. `[]` when the store cannot enumerate them; the pickers then fall back to free text. */
145
+ runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
140
146
  /** This deployment's durable role (control plane vs tenant) + tenant name — for the dashboard header
141
147
  * badge. Routes through the gateway, which knows its own topology (store-backed = operator, proxy =
142
148
  * tenant). Works on both topologies (no store/engine needed). */
@@ -210,28 +216,75 @@ declare class DashboardService {
210
216
  */
211
217
  type RunListRow = Omit<RunListItem, 'input' | 'output' | 'error'>;
212
218
 
213
- /** JSON API consumed by the control-plane SPA. Mounted at `apiBasePath` (set by RouterModule). */
219
+ /**
220
+ * What this adapter hands the filter runner in place of a query builder: a {@link RunQuery} under
221
+ * construction.
222
+ *
223
+ * The runner never inspects it — it only passes it back to the adapter's own methods — so the shape
224
+ * is ours to choose, and a plain predicate bag is the honest one. There is no SQL to append to here;
225
+ * a run query is a fixed set of named predicates that the gateway resolves, so "building" it is
226
+ * filling fields in, and everything the filter lib can express but `RunQuery` cannot is rejected at
227
+ * the point of translation rather than accumulated and silently dropped.
228
+ */
229
+ declare class RunQueryDraft {
230
+ readonly query: RunQuery;
231
+ /** Narrow by one or more predicates. Later calls overwrite the same field — the runner applies one
232
+ * clause per field, so the last one wins, exactly as a repeated `where` on a builder would. */
233
+ narrow(patch: RunQuery): void;
234
+ /** Add one search-attribute predicate. These ACCUMULATE (unlike {@link narrow}) because
235
+ * `RunQuery.attributes` is an ANDed list — two predicates on different keys are both meant. */
236
+ attribute(filter: AttributeFilter): void;
237
+ /** The predicates minus the page window and the axes a facet count reports on — what
238
+ * `runFacets`/`runValueFacets` are taken over. */
239
+ facetQuery(): RunFacetQuery;
240
+ }
241
+
242
+ /**
243
+ * JSON API consumed by the control-plane SPA. Mounted at `apiBasePath` (set by RouterModule).
244
+ *
245
+ * The filter interceptor (what resolves `@ApplyFilter` into a query) is bound HERE rather than as
246
+ * the app-wide `APP_INTERCEPTOR` that `FilterModule.forRoot()` registers: a library must not add a
247
+ * global interceptor to its host, and on an app that already has one this would be the second copy,
248
+ * running every filter in the app twice.
249
+ */
214
250
  declare class DurableApiController {
215
251
  private readonly dashboard;
216
- constructor(dashboard: DashboardService);
252
+ private readonly runner;
253
+ constructor(dashboard: DashboardService, runner: FilterRunner);
217
254
  /**
218
- * The run list, filtered by whatever the operator asked for. Every predicate is OPTIONAL and an
219
- * omitted one means "don't narrow on this axis" — in particular `namespace`, whose absence keeps
220
- * the historical, deliberate behaviour that read paths are NOT namespace-scoped (core,
221
- * `WorkflowRun.namespace`): the console shows every tenant's runs until an operator chooses one.
222
- * A blank param (`?namespace=`, from a cleared filter box) is the same as an absent one; passing
223
- * `''` through would be an exact match on a tenant nobody has, i.e. a silently empty console.
255
+ * The run list, filtered by whatever the operator asked for.
256
+ *
257
+ * The predicates are parsed, validated and translated by `@dudousxd/nestjs-filter` (see
258
+ * `RunFilter` for the two spellings it accepts, and `RunQueryAdapter` for how they become a
259
+ * `RunQuery`). Every predicate is OPTIONAL and an omitted one means "don't narrow on this axis" —
260
+ * in particular `namespace`, whose absence keeps the historical, deliberate behaviour that read
261
+ * paths are NOT namespace-scoped (core, `WorkflowRun.namespace`): the console shows every tenant's
262
+ * runs until an operator chooses one.
224
263
  */
225
- runs(status?: RunStatus | RunStatus[], workflow?: string, tag?: string, attr?: string | string[], namespace?: string, origin?: string, unattributed?: string, limit?: string, offset?: string): Promise<RunListRow[]>;
264
+ runs(draft: RunQueryDraft): Promise<RunListRow[]>;
226
265
  /**
227
266
  * `(status, origin)` counts for the runs matching the same tag/tenant/attribute predicates as
228
267
  * `GET runs`. This is what lets that listing be PAGED at all: the page bounds what the console
229
268
  * renders, this bounds nothing and keeps its status and origin chips exact.
230
269
  *
231
- * `status`, `origin` and the page bounds are deliberately NOT accepted — they are the axes being
232
- * counted, so narrowing by them would report the answer back to itself.
270
+ * `status`, `origin` and the page bounds are accepted (the route takes the same filter) but
271
+ * deliberately DROPPED before counting — they are the axes being counted, so narrowing by them
272
+ * would report the answer back to itself.
273
+ */
274
+ facets(draft: RunQueryDraft): Promise<_dudousxd_nestjs_durable_core.RunFacetRow[]>;
275
+ /**
276
+ * The distinct values one filter field takes across the runs matching every OTHER active
277
+ * predicate, with counts — what the console's tenant/tag/attribute pickers list.
278
+ *
279
+ * `?groupByCount[field]=tag&groupByCount[limit]=20`, with the filters in the structured
280
+ * `filter[where][...]` form (what `filterQuery()` builds). Scoping the values to the active
281
+ * filters is the point: picking a tenant leaves the tag picker offering only that tenant's tags,
282
+ * so a picker never offers a value whose result set is empty.
283
+ *
284
+ * `limit` matters rather than being a nicety — tag and attribute cardinality grows with the data
285
+ * (a `singleton:<key>` tag is minted per key), so the unbounded answer is a listing.
233
286
  */
234
- facets(workflow?: string, tag?: string, attr?: string | string[], namespace?: string): Promise<_dudousxd_nestjs_durable_core.RunFacetRow[]>;
287
+ values(query: Record<string, unknown>): Promise<_dudousxd_nestjs_filter.GroupByCountResult>;
235
288
  /** Prometheus-text metrics (runs/steps by outcome, per-workflow counts) for a scrape. */
236
289
  metrics(): Promise<string>;
237
290
  /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. */
@@ -256,7 +309,7 @@ declare class DurableApiController {
256
309
  * The filter takes the SAME params as `GET runs` and is parsed identically, so "retry all" acts on
257
310
  * exactly the set the operator was looking at rather than a wider one.
258
311
  */
259
- bulk(action: 'retry' | 'cancel', status?: RunStatus, tag?: string, workflow?: string, attr?: string | string[], compensate?: string, namespace?: string, origin?: string, unattributed?: string): Promise<{
312
+ bulk(action: 'retry' | 'cancel', draft: RunQueryDraft, compensate?: string): Promise<{
260
313
  matched: number;
261
314
  applied: number;
262
315
  }>;
@@ -1,8 +1,10 @@
1
1
  import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
2
- import { RunGateway, StateStore, WorkflowEngine, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, DurableTopology, GroupHealth, RunDetail, RunResult, UpdateResult, EngineEvent, RunStatus } from '@dudousxd/nestjs-durable-core';
2
+ import { RunGateway, StateStore, WorkflowEngine, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetail, RunResult, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
3
3
  export { RunDetail } from '@dudousxd/nestjs-durable-core';
4
4
  import * as rxjs from 'rxjs';
5
5
  import { Observable } from 'rxjs';
6
+ import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
7
+ import { FilterRunner } from '@dudousxd/nestjs-filter';
6
8
  import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, ExecutionContext, StreamableFile } from '@nestjs/common';
7
9
 
8
10
  /** The session user the host `login` hook returns to mint a cookie. */
@@ -137,6 +139,10 @@ declare class DashboardService {
137
139
  /** `(status, origin)` counts over the same predicates {@link listRuns} pages — what keeps the
138
140
  * console's chips exact while its list is bounded to a page. */
139
141
  runFacets(query: RunFacetQuery): Promise<RunFacetRow[]>;
142
+ /** The distinct values of one filter axis over the same predicates — what fills the console's
143
+ * tenant/tag/attribute pickers, so an operator picks a value that exists instead of typing one
144
+ * blind. `[]` when the store cannot enumerate them; the pickers then fall back to free text. */
145
+ runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
140
146
  /** This deployment's durable role (control plane vs tenant) + tenant name — for the dashboard header
141
147
  * badge. Routes through the gateway, which knows its own topology (store-backed = operator, proxy =
142
148
  * tenant). Works on both topologies (no store/engine needed). */
@@ -210,28 +216,75 @@ declare class DashboardService {
210
216
  */
211
217
  type RunListRow = Omit<RunListItem, 'input' | 'output' | 'error'>;
212
218
 
213
- /** JSON API consumed by the control-plane SPA. Mounted at `apiBasePath` (set by RouterModule). */
219
+ /**
220
+ * What this adapter hands the filter runner in place of a query builder: a {@link RunQuery} under
221
+ * construction.
222
+ *
223
+ * The runner never inspects it — it only passes it back to the adapter's own methods — so the shape
224
+ * is ours to choose, and a plain predicate bag is the honest one. There is no SQL to append to here;
225
+ * a run query is a fixed set of named predicates that the gateway resolves, so "building" it is
226
+ * filling fields in, and everything the filter lib can express but `RunQuery` cannot is rejected at
227
+ * the point of translation rather than accumulated and silently dropped.
228
+ */
229
+ declare class RunQueryDraft {
230
+ readonly query: RunQuery;
231
+ /** Narrow by one or more predicates. Later calls overwrite the same field — the runner applies one
232
+ * clause per field, so the last one wins, exactly as a repeated `where` on a builder would. */
233
+ narrow(patch: RunQuery): void;
234
+ /** Add one search-attribute predicate. These ACCUMULATE (unlike {@link narrow}) because
235
+ * `RunQuery.attributes` is an ANDed list — two predicates on different keys are both meant. */
236
+ attribute(filter: AttributeFilter): void;
237
+ /** The predicates minus the page window and the axes a facet count reports on — what
238
+ * `runFacets`/`runValueFacets` are taken over. */
239
+ facetQuery(): RunFacetQuery;
240
+ }
241
+
242
+ /**
243
+ * JSON API consumed by the control-plane SPA. Mounted at `apiBasePath` (set by RouterModule).
244
+ *
245
+ * The filter interceptor (what resolves `@ApplyFilter` into a query) is bound HERE rather than as
246
+ * the app-wide `APP_INTERCEPTOR` that `FilterModule.forRoot()` registers: a library must not add a
247
+ * global interceptor to its host, and on an app that already has one this would be the second copy,
248
+ * running every filter in the app twice.
249
+ */
214
250
  declare class DurableApiController {
215
251
  private readonly dashboard;
216
- constructor(dashboard: DashboardService);
252
+ private readonly runner;
253
+ constructor(dashboard: DashboardService, runner: FilterRunner);
217
254
  /**
218
- * The run list, filtered by whatever the operator asked for. Every predicate is OPTIONAL and an
219
- * omitted one means "don't narrow on this axis" — in particular `namespace`, whose absence keeps
220
- * the historical, deliberate behaviour that read paths are NOT namespace-scoped (core,
221
- * `WorkflowRun.namespace`): the console shows every tenant's runs until an operator chooses one.
222
- * A blank param (`?namespace=`, from a cleared filter box) is the same as an absent one; passing
223
- * `''` through would be an exact match on a tenant nobody has, i.e. a silently empty console.
255
+ * The run list, filtered by whatever the operator asked for.
256
+ *
257
+ * The predicates are parsed, validated and translated by `@dudousxd/nestjs-filter` (see
258
+ * `RunFilter` for the two spellings it accepts, and `RunQueryAdapter` for how they become a
259
+ * `RunQuery`). Every predicate is OPTIONAL and an omitted one means "don't narrow on this axis" —
260
+ * in particular `namespace`, whose absence keeps the historical, deliberate behaviour that read
261
+ * paths are NOT namespace-scoped (core, `WorkflowRun.namespace`): the console shows every tenant's
262
+ * runs until an operator chooses one.
224
263
  */
225
- runs(status?: RunStatus | RunStatus[], workflow?: string, tag?: string, attr?: string | string[], namespace?: string, origin?: string, unattributed?: string, limit?: string, offset?: string): Promise<RunListRow[]>;
264
+ runs(draft: RunQueryDraft): Promise<RunListRow[]>;
226
265
  /**
227
266
  * `(status, origin)` counts for the runs matching the same tag/tenant/attribute predicates as
228
267
  * `GET runs`. This is what lets that listing be PAGED at all: the page bounds what the console
229
268
  * renders, this bounds nothing and keeps its status and origin chips exact.
230
269
  *
231
- * `status`, `origin` and the page bounds are deliberately NOT accepted — they are the axes being
232
- * counted, so narrowing by them would report the answer back to itself.
270
+ * `status`, `origin` and the page bounds are accepted (the route takes the same filter) but
271
+ * deliberately DROPPED before counting — they are the axes being counted, so narrowing by them
272
+ * would report the answer back to itself.
273
+ */
274
+ facets(draft: RunQueryDraft): Promise<_dudousxd_nestjs_durable_core.RunFacetRow[]>;
275
+ /**
276
+ * The distinct values one filter field takes across the runs matching every OTHER active
277
+ * predicate, with counts — what the console's tenant/tag/attribute pickers list.
278
+ *
279
+ * `?groupByCount[field]=tag&groupByCount[limit]=20`, with the filters in the structured
280
+ * `filter[where][...]` form (what `filterQuery()` builds). Scoping the values to the active
281
+ * filters is the point: picking a tenant leaves the tag picker offering only that tenant's tags,
282
+ * so a picker never offers a value whose result set is empty.
283
+ *
284
+ * `limit` matters rather than being a nicety — tag and attribute cardinality grows with the data
285
+ * (a `singleton:<key>` tag is minted per key), so the unbounded answer is a listing.
233
286
  */
234
- facets(workflow?: string, tag?: string, attr?: string | string[], namespace?: string): Promise<_dudousxd_nestjs_durable_core.RunFacetRow[]>;
287
+ values(query: Record<string, unknown>): Promise<_dudousxd_nestjs_filter.GroupByCountResult>;
235
288
  /** Prometheus-text metrics (runs/steps by outcome, per-workflow counts) for a scrape. */
236
289
  metrics(): Promise<string>;
237
290
  /** Per-group worker health (queue backlog + live worker heartbeats) for the Workers panel. */
@@ -256,7 +309,7 @@ declare class DurableApiController {
256
309
  * The filter takes the SAME params as `GET runs` and is parsed identically, so "retry all" acts on
257
310
  * exactly the set the operator was looking at rather than a wider one.
258
311
  */
259
- bulk(action: 'retry' | 'cancel', status?: RunStatus, tag?: string, workflow?: string, attr?: string | string[], compensate?: string, namespace?: string, origin?: string, unattributed?: string): Promise<{
312
+ bulk(action: 'retry' | 'cancel', draft: RunQueryDraft, compensate?: string): Promise<{
260
313
  matched: number;
261
314
  applied: number;
262
315
  }>;