@mintplayer/ng-spark 22.22.0 → 22.23.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.
|
@@ -922,7 +922,9 @@ class SparkQueryGridComponent {
|
|
|
922
922
|
}
|
|
923
923
|
const sourceName = extractSourceName(query.source);
|
|
924
924
|
const singular = singularize(sourceName);
|
|
925
|
-
|
|
925
|
+
// `name + 's'` because the "-es" rule over-strips (Vehicles -> "Vehicl", Roles -> "Rol"); the
|
|
926
|
+
// page resolver in spark-query-list has always matched this way.
|
|
927
|
+
return entityTypes.find(t => t.name === sourceName || t.name === singular || t.name + 's' === sourceName || t.clrType?.endsWith(singular)) ?? null;
|
|
926
928
|
}
|
|
927
929
|
makeFetch(query, parentId, parentType) {
|
|
928
930
|
return (req) => this.sparkService.executeQuery(query.id, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-grid.mjs","sources":["../../grid/src/spark-grid-columns.ts","../../grid/src/spark-grid-renderers.ts","../../grid/src/spark-query-slots.ts","../../grid/src/spark-grid-cell.component.ts","../../grid/src/spark-grid-cell.component.html","../../grid/src/spark-query-grid.component.ts","../../grid/src/spark-query-grid.component.html","../../grid/src/spark-query-card.component.ts","../../grid/src/spark-query-card.component.html","../../grid/mintplayer-ng-spark-grid.ts"],"sourcesContent":["import { DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';\nimport { SortColumn } from '@mintplayer/pagination';\nimport {\n EntityAttributeDefinition,\n EntityType,\n ShowedOn,\n SparkQuery,\n hasShowedOnFlag,\n} from '@mintplayer/ng-spark/models';\n\n/** Page sizes offered by every Spark grid. */\nexport const SPARK_GRID_PAGE_SIZES = [10, 25, 50];\n\n/**\n * The attributes a grid shows, in display order.\n *\n * Shared so the two grids cannot disagree about what \"visible\" means — they each had their own\n * copy of this expression, which is the kind of thing that stays identical right up until it\n * doesn't.\n */\nexport function visibleGridAttributes(entityType: EntityType | null): EntityAttributeDefinition[] {\n return entityType?.attributes\n .filter(a => a.isVisible && hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) ?? [];\n}\n\n/**\n * Initial datatable settings for a query, seeded with the query's declared sort.\n *\n * The datatable owns paging and sorting from here on and calls the fetch callback per page; this\n * only decides where it starts.\n */\nexport function initialGridSettings(query: SparkQuery | null): DatatableSettings {\n // Nullable because one caller resolves its query from a route param and may not have one yet;\n // an unsorted grid is the right fallback, not a crash.\n const sortColumns: SortColumn[] = (query?.sortColumns || []).map(sc => ({\n property: sc.property,\n direction: sc.direction === 'desc' ? 'descending' as const : 'ascending' as const,\n }));\n\n return new DatatableSettings({\n perPage: { values: SPARK_GRID_PAGE_SIZES, selected: SPARK_GRID_PAGE_SIZES[0] },\n page: { values: [1], selected: 1 },\n sortColumns,\n });\n}\n\n/** Whether a query renders as a virtual-scrolling grid rather than a paged one. */\nexport function isVirtualScrollingQuery(query: SparkQuery | null): boolean {\n return query?.renderMode === 'VirtualScrolling';\n}\n","import { inject, Injectable, Type } from '@angular/core';\nimport {\n EntityAttributeDefinition,\n LookupReference,\n QueryColumn,\n QueryResultItem,\n SparkCellColumn,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { SPARK_ATTRIBUTE_RENDERERS, cellValue, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport { SparkService } from '@mintplayer/ng-spark/services';\n\n/**\n * Renderer lookup and lookup-reference loading for a Spark grid.\n *\n * This was extracted when `spark-query-list` and `spark-sub-query` were two components holding\n * byte-identical copies of it — around 120 lines between them. That duplication was not a\n * tidiness complaint: it produced drift. The two copies disagreed about `[indeterminate]`, about\n * resetting permission state, about whether a fetch failure surfaces or is swallowed, and about\n * virtual-scroll sizing — four user-visible bugs, each fixed on one side and not the other.\n *\n * There is now one grid, {@link SparkQueryGridComponent}, so the drift cannot recur. This stays\n * separate because it is stateless service-shaped logic rather than view state, and because it is\n * what a custom grid would need in order to render Spark cells at all.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkGridRenderers {\n private readonly registry = inject(SPARK_ATTRIBUTE_RENDERERS);\n private readonly sparkService = inject(SparkService);\n\n /** The registered column component for a column, or null to fall back to the default cell. */\n columnComponentFor(column: SparkCellColumn): Type<any> | null {\n if (!column.renderer) return null;\n return this.registry.find(r => r.name === column.renderer)?.columnComponent ?? null;\n }\n\n /** The registered filter-label function for a column, or null to use the server-computed text. */\n filterLabelFor(column: SparkCellColumn): ((value: unknown) => string) | null {\n if (!column.renderer) return null;\n return this.registry.find(r => r.name === column.renderer)?.filterLabel ?? null;\n }\n\n /**\n * Inputs for a query-grid cell renderer, filtered to what the component actually declares —\n * `NgComponentOutlet` throws on an input the target does not have, which is what lets every\n * member of the renderer contract be optional.\n */\n columnInputsFor(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any> {\n return this.cellInputsFor(component, cellValue(valueFor(item, column.name)), column, item);\n }\n\n /**\n * The same contract for a row that is not a query result — an AsDetail sub-table row, which is\n * a plain dictionary of embedded values with no id and no column metadata of its own, so its\n * value cannot be read the way {@link columnInputsFor} reads one.\n *\n * Only the extraction differs; the renderer contract is identical, and stating it once is what\n * stops the two from drifting the way the cell markup already had.\n */\n cellInputsFor(component: Type<any>, value: unknown, column: SparkCellColumn, item: unknown): Record<string, any> {\n return withDeclaredInputs(component, {\n value,\n column,\n options: column.rendererOptions,\n item,\n });\n }\n\n /**\n * Loads every lookup reference the visible columns need, in one pass.\n *\n * Returns an empty map rather than throwing when there are none, so callers never branch on it.\n */\n async loadLookupOptions(columns: readonly (SparkCellColumn | EntityAttributeDefinition)[]): Promise<Record<string, LookupReference>> {\n const lookupColumns = columns.filter(c => c.lookupReferenceType);\n if (lookupColumns.length === 0) return {};\n\n const names = [...new Set(lookupColumns.map(c => c.lookupReferenceType!))];\n const entries = await Promise.all(\n names.map(async name => [name, await this.sparkService.getLookupReference(name)] as const),\n );\n return entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, LookupReference>);\n }\n}\n","import { Directive, inject, input, TemplateRef } from '@angular/core';\nimport { CustomActionDefinition, PersistentObject, SparkQuery } from '@mintplayer/ng-spark/models';\n\n/**\n * Header slots for `<spark-query-card>`.\n *\n * Each directive marks a template that REPLACES one region of the card header. Supplying one\n * leaves the other two alone — that is the whole point of having three rather than the single\n * whole-header template they replace, where changing the icon meant re-implementing the caption\n * and the action bar as well.\n *\n * An omitted slot is not an empty slot: the card renders its default. This is what keeps the\n * auto-rendered sub-query working. A sub-query is rendered from `EntityTypeDefinition.Queries`\n * with no host to project into, so a mechanism that only ever REPLACED chrome would leave that\n * call site with nothing — which is why an earlier design had the query declare its own chrome\n * server-side. Overriding a default needs no host; replacing one does.\n *\n * The two ends of that are visible in the defaults themselves. Neither `SparkQuery` nor\n * `EntityType` carries an icon, so the server has nothing to say and the slot is the only\n * source. Actions are the mirror image: the server declares them per type, so the default is\n * the real answer and the slot is a rare override.\n *\n * Naming follows ng-bootstrap's `*bsDatatableColumn` convention — prefix, component, slot —\n * with `spark`, since these are ng-spark directives and `bs` is another package's prefix. These\n * are the first attribute directives in ng-spark; every existing selector is an element.\n *\n * ## Targeting one query\n *\n * Each slot takes an optional query alias or id as its value. A detail page renders one card per\n * entry in `EntityTypeDefinition.Queries`, so a host that supplies a bare slot would decorate\n * every one of them identically — rarely what is wanted. A targeted slot applies to that query;\n * an untargeted one is the fallback for the rest.\n *\n * ```html\n * <span *sparkQueryIcon=\"'cars'\"><spark-icon name=\"car-front\" /></span>\n * <span *sparkQueryIcon><spark-icon name=\"table\" /></span>\n * ```\n *\n * Matching is case-insensitive, against the query's alias and then its id. This is the\n * `bsPriorityNavItem` shape: a value input aliased to the selector, collected with\n * `contentChildren`.\n */\n\n/** Resolves a slot list to the one that applies to a query: targeted first, then untargeted. */\nexport function slotFor<T extends { forQuery: () => string }>(\n slots: readonly T[],\n query: SparkQuery | null,\n): T | null {\n if (!slots.length) return null;\n\n const matches = (target: string) =>\n !!target && (\n target.localeCompare(query?.alias ?? '', undefined, { sensitivity: 'accent' }) === 0 ||\n target.localeCompare(query?.id ?? '', undefined, { sensitivity: 'accent' }) === 0);\n\n // A targeted slot wins over the catch-all even when it is declared later: \"this one query\"\n // is the more specific statement, and declaration order in a template is not a priority.\n return slots.find(s => matches(s.forQuery())) ?? slots.find(s => !s.forQuery()) ?? null;\n}\n\n/**\n * The icon at the header's leading edge. No default: nothing in the model describes one.\n *\n * ```html\n * <spark-query-card [queryId]=\"'cars'\">\n * <spark-icon *sparkQueryIcon name=\"car-front\" />\n * </spark-query-card>\n * ```\n */\n@Directive({ selector: '[sparkQueryIcon]' })\nexport class SparkQueryIconDirective {\n readonly templateRef = inject<TemplateRef<SparkQuerySlotContext>>(TemplateRef);\n\n /** Query alias or id this slot applies to. Empty targets every query on the page. */\n readonly forQuery = input('', { alias: 'sparkQueryIcon' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryIconDirective,\n ctx: unknown,\n ): ctx is SparkQuerySlotContext {\n return true;\n }\n}\n\n/**\n * The header caption. Defaults to the query's translated description, falling back to its name.\n *\n * The context carries that resolved default as `$implicit`, so a host can decorate it — add a\n * count, a badge — without re-resolving the translation itself.\n */\n@Directive({ selector: '[sparkQueryCaption]' })\nexport class SparkQueryCaptionDirective {\n readonly templateRef = inject<TemplateRef<SparkQueryCaptionContext>>(TemplateRef);\n\n readonly forQuery = input('', { alias: 'sparkQueryCaption' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryCaptionDirective,\n ctx: unknown,\n ): ctx is SparkQueryCaptionContext {\n return true;\n }\n}\n\n/**\n * The buttons at the header's trailing edge. Defaults to the server-declared custom actions.\n *\n * The context carries those actions and the current selection, so a host that wants its own\n * buttons ALONGSIDE the server's can render both rather than choosing. Without that, overriding\n * this slot to add one button would silently drop every action the type declares — and the\n * server's actions are the ones carrying `selectionRule` and the permission filter.\n *\n * ```html\n * <ng-container *sparkQueryActions=\"let actions; selection as rows\">\n * <button (click)=\"export(rows)\">Export</button>\n * </ng-container>\n * ```\n */\n@Directive({ selector: '[sparkQueryActions]' })\nexport class SparkQueryActionsDirective {\n readonly templateRef = inject<TemplateRef<SparkQueryActionsContext>>(TemplateRef);\n\n readonly forQuery = input('', { alias: 'sparkQueryActions' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryActionsDirective,\n ctx: unknown,\n ): ctx is SparkQueryActionsContext {\n return true;\n }\n}\n\n/** Context for the icon slot: the query whose card is being rendered. */\nexport class SparkQuerySlotContext {\n $implicit: SparkQuery | null = null;\n}\n\nexport class SparkQueryCaptionContext {\n /** The caption the card would have rendered: description, or the query name. */\n $implicit = '';\n query: SparkQuery | null = null;\n}\n\nexport class SparkQueryActionsContext {\n /** The custom actions the card would have rendered, already filtered for this query. */\n $implicit: CustomActionDefinition[] = [];\n /** Rows currently ticked. Empty unless an action is selection-gated. */\n selection: PersistentObject[] = [];\n query: SparkQuery | null = null;\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { CommonModule, NgComponentOutlet } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { EntityAttributeDefinition, SparkCellColumn } from '@mintplayer/ng-spark/models';\nimport { ReferenceChip } from '@mintplayer/ng-spark/pipes';\nimport { SparkGridRenderers } from './spark-grid-renderers';\n\n/**\n * One cell of a Spark table, wherever that table is.\n *\n * ## Why this exists\n *\n * The cell markup was written out three times: twice in the query grids (since merged into\n * {@link SparkQueryGridComponent}) and once more in the AsDetail table on the PO detail page. The\n * third copy was never counted, and it had already drifted — a `boolean` column rendered the text\n * `\"true\"` there while the grid rendered a checkbox, a `color` rendered its hex string rather than\n * a swatch, and a custom renderer was dispatched by a second, hand-copied lookup.\n *\n * ## What it does and does not own\n *\n * It owns **presentation**: which control a `dataType` becomes, and dispatching a declared custom\n * renderer. It does not own **value resolution**, and deliberately so — the two callers read from\n * genuinely different row models. A query row is a `PersistentObject` with an attribute list and\n * an id; an AsDetail row is a plain dictionary of embedded values with neither. Trying to unify\n * that would put a second row-access path inside this component and buy nothing.\n *\n * So the caller resolves the value with whichever pipe fits its row model, and passes the result.\n *\n * ## The two links are not the same link, and only one is here\n *\n * `link` is for a cell whose own value points at another entity — an AsDetail reference column.\n * The query grid's first-column link is a different rule (it points at the row's own detail page,\n * gated on `Read`), and it stays in the grid, wrapping this component. Passing both would nest\n * anchors, which is invalid HTML; the grid therefore leaves `link` unset.\n */\n@Component({\n selector: 'spark-grid-cell',\n imports: [CommonModule, NgComponentOutlet, RouterModule],\n templateUrl: './spark-grid-cell.component.html',\n styleUrl: './spark-grid-cell.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkGridCellComponent {\n private readonly renderers = inject(SparkGridRenderers);\n\n /** The column being rendered. Its `dataType` and `renderer` decide everything below. */\n column = input.required<SparkCellColumn>();\n\n /**\n * What the caller's value pipe produced: the display text for most columns, and the raw value\n * for `boolean` and `color`, which need the value rather than a rendering of it.\n */\n display = input<unknown>(null);\n\n /**\n * The value handed to a declared custom renderer.\n *\n * Separate from {@link display} because they are not the same thing: a renderer receives the\n * underlying value — for AsDetail, the nested object itself — while `display` has already been\n * flattened to something printable.\n */\n rendererValue = input<unknown>(null);\n\n /** The row, passed through to a custom renderer as `item`. */\n item = input<unknown>(null);\n\n /**\n * Pre-resolved chips for a reference-array column, or empty when the column is not one.\n *\n * Resolved by the caller because the label source differs: a query row carries a per-id\n * breadcrumb map, while an AsDetail row's `__sparkBreadcrumbs` is keyed by column name only and\n * so cannot label the members of an array. Rather than render a row of raw ids, an AsDetail\n * caller passes nothing and the cell falls through to text.\n */\n chips = input<ReferenceChip[]>([]);\n\n /** Route for a cell whose value references another entity. See the class comment. */\n link = input<unknown[] | null>(null);\n\n protected readonly rendererComponent = computed(() => this.renderers.columnComponentFor(this.column()));\n\n protected readonly rendererInputs = computed(() => {\n const component = this.rendererComponent();\n return component\n ? this.renderers.cellInputsFor(component, this.rendererValue(), this.column(), this.item())\n : {};\n });\n\n /** Null and undefined are distinct from `false` here — see the template. */\n protected readonly isUnsetBoolean = computed(() => this.display() == null);\n\n /**\n * {@link display} when it really is a `Date`, and `null` otherwise.\n *\n * Angular's `DatePipe` does not degrade on a non-date: it **throws**\n * `NG02100: InvalidPipeArgument`, which would take down the whole cell. Both cell pipes\n * deliberately fall back to the raw text for an unparseable date rather than hiding the stored\n * value, so that text reaches this component and must not be piped. A template cannot express\n * `instanceof`, hence the computed.\n */\n protected readonly dateValue = computed(() => {\n const value = this.display();\n return value instanceof Date && !Number.isNaN(value.getTime()) ? value : null;\n });\n}\n","@if (rendererComponent(); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: rendererInputs()\"></ng-container>\n} @else if (column().dataType === 'boolean') {\n <!-- [indeterminate] matters: without it a null boolean renders as an unchecked box, which reads\n as an explicit `false`. One of the three copies of this cell bound it and the others had\n drifted, so the same column said different things on a detail page and in a grid. -->\n <input type=\"checkbox\"\n [checked]=\"display() === true\"\n [indeterminate]=\"isUnsetBoolean()\"\n disabled\n onclick=\"return false;\">\n} @else if (column().dataType === 'date' || column().dataType === 'datetime') {\n <!--\n Formatted in ONE place for both grids. Before this, a date column printed whatever its pipe\n produced, which was the raw ISO string — `2026-09-07T13:25:57.3119825Z` in a table cell. Both\n cell pipes now hand over a real Date, and `date:` accepts a string too, so an unparseable value\n that fell back to text still renders as that text rather than blank.\n -->\n @if (dateValue(); as when) {\n {{ when | date:(column().dataType === 'date' ? 'mediumDate' : 'short') }}\n } @else {\n {{ display() }}\n }\n} @else if (column().dataType === 'color') {\n @if (display(); as colorVal) {\n <!-- Shares `.spark-color-swatch` with the detail page, which is where the sizing already\n lived. It used to be inline here because the grid rendered inside <mp-datatable>'s\n shadow root, where neither this component's scoped rules nor Bootstrap's arrived;\n ng-bootstrap 22.18.0 moved the datatable to the light DOM, so the class reaches it. -->\n <span class=\"spark-color-swatch d-inline-block align-middle border rounded\"\n [style.background-color]=\"colorVal\"></span>\n }\n} @else if (column().dataType === 'image') {\n @if (display(); as src) {\n <!-- `alt` is empty on purpose — the cell is decorative next to the row's own labelled\n columns, and a filename read aloud is worse than nothing. -->\n <img [src]=\"src\" alt=\"\" loading=\"lazy\" class=\"spark-grid-image align-middle rounded\">\n }\n} @else if (column().dataType === 'url') {\n @if (display(); as href) {\n <!-- rel is not optional with target=_blank: without noopener the opened page gets a handle on\n this one through window.opener, and these hrefs are data. -->\n <a [href]=\"href\" target=\"_blank\" rel=\"noopener noreferrer\">{{ href }}</a>\n }\n} @else if (chips().length) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of chips(); track chip.id) {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n </span>\n} @else if (link(); as route) {\n <a [routerLink]=\"route\">{{ display() }}</a>\n} @else {\n {{ display() }}\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, model, output, signal, untracked, Type } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { RouterModule } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsDatatableFilterPanelDirective, BsRowTemplateDirective, DatatableSettings, type BsDatatableFetch, type DatatableDistincts, type FilterChangeDetail } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SparkQueryRefreshService } from '@mintplayer/ng-spark/client-operations';\nimport { cellValue } from '@mintplayer/ng-spark/renderers';\nimport { QueryCellValuePipe, QueryReferenceChipsPipe, ResolveTranslationPipe, TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkLanguageService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkColumnFilterPanelComponent } from '@mintplayer/ng-spark/column-filter';\nimport {\n CustomActionDefinition,\n EntityAttributeDefinition,\n EntityType,\n LookupReference,\n QueryColumn,\n QueryColumnFilter,\n QueryResultItem,\n SparkQuery,\n filterQueryActions,\n parseSelectionRule,\n selectionModeFor,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { SPARK_GRID_PAGE_SIZES, initialGridSettings, isVirtualScrollingQuery } from './spark-grid-columns';\nimport { SparkGridRenderers } from './spark-grid-renderers';\nimport { SparkGridCellComponent } from './spark-grid-cell.component';\n\n/**\n * The one Spark grid: a `<bs-datatable>` rendering a query or a sub-query.\n *\n * This replaces two components that were the same grid written twice — and, counting\n * `spark-query-list`'s streaming and paged branches, the `<bs-datatable>` element itself written\n * three times. The duplication had already produced four user-visible bugs, each fixed on one\n * side and not the other: `[indeterminate]` on null booleans, permission state surviving a failed\n * reload, a swallowed fetch failure, and virtual-scroll sizing. `SparkGridRenderers` was\n * extracted to stop that and could only take the stateless helpers with it; this takes the rest.\n *\n * ## What it deliberately does NOT own: streaming\n *\n * `spark-grid-renderers.ts` records why — a websocket dependency graph must not reach the bundle\n * of every PO detail page, none of which stream. So rows can come from **either** side:\n *\n * - unbound `data` — the grid fetches for itself, paging and sorting server-side;\n * - bound `data` — the grid renders what it is given and never fetches.\n *\n * `spark-query-list` keeps the socket and feeds its snapshot in. This is the line `bs-datatable`\n * itself draws: `[data]` and `[fetch]` are mutually exclusive, and binding both makes `[fetch]`\n * win silently.\n *\n * ## Reading its state\n *\n * `query`, `entityType`, `customActions`, `selection`, `canRead`, `canCreate` and `resultCount`\n * are readable so a host can render chrome around the grid. Read them through a template\n * reference variable — `<spark-query-grid #grid>` … `grid.query()` — not `viewChild`: hosts wrap\n * grids in `@if`, where a `viewChild` is intermittently undefined.\n */\n@Component({\n selector: 'spark-query-grid',\n imports: [CommonModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsDatatableFilterPanelDirective, BsRowTemplateDirective, BsSpinnerComponent, SparkGridCellComponent, ResolveTranslationPipe, QueryCellValuePipe, QueryReferenceChipsPipe, TranslateKeyPipe, SparkAttributeDescriptionComponent, SparkColumnFilterPanelComponent],\n templateUrl: './spark-query-grid.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkQueryGridComponent {\n private readonly sparkService = inject(SparkService);\n private readonly gridRenderers = inject(SparkGridRenderers);\n private readonly queryRefresh = inject(SparkQueryRefreshService);\n readonly lang = inject(SparkLanguageService);\n\n /** Query alias or id. */\n queryId = input.required<string>();\n\n /**\n * The parent persistent object this query is scoped to, when it has one.\n *\n * Optional, because not every query is a detail of something: a page can host a grid that\n * stands on its own — \"my accounts\", a dashboard list — and the server already treats an absent\n * parent as \"no parent\" rather than as an error. Requiring these made that shape impossible to\n * express: the component simply never loaded, with no request, no error and no log.\n *\n * Pass both or neither. One without the other is ignored, matching `SparkService.executeQuery`,\n * which omits either param when it is falsy, and the execute endpoint, which resolves a parent\n * only when both are present.\n */\n parentId = input<string>('');\n parentType = input<string>('');\n\n /**\n * Rows supplied from outside. When bound, the grid renders these and runs no fetch.\n *\n * This is how streaming stays out of this component (see the class comment). `null` — the\n * default — means \"fetch for yourself\"; an empty array means \"you have been given no rows\",\n * which is a different statement and renders an empty grid rather than triggering a load.\n */\n data = input<QueryResultItem[] | null>(null);\n\n /**\n * Server-side search term, passed through to the query.\n *\n * An input rather than a box, so the host owns the control and the grid owns the request. A\n * host with client-side rows filters them itself before binding `data`.\n */\n search = input<string>('');\n\n /**\n * Column metadata for externally supplied rows. Required whenever `data` is bound, because a\n * projection cannot describe itself — there is no per-row metadata left to infer it from.\n */\n columns = input<QueryColumn[] | null>(null);\n\n /**\n * Change this to re-run the query. Any value works; only its identity matters.\n *\n * A declarative token rather than only a `reload()` method, because calling a method means\n * holding a component handle, and hosts wrap this grid in `@if`, where a `viewChild` is\n * intermittently undefined.\n *\n * This drives the CHEAP refresh (see {@link reload}). It deliberately does not feed the main\n * effect: re-running `loadData` would re-resolve the query, the entity types, the permissions\n * and the lookups, and reset the user's page and sort on every button press.\n */\n reloadToken = input<unknown>(null);\n\n /**\n * Paging and sorting. Two-way, so a host driving `data` can sort those rows by whatever the\n * user actually clicked — the datatable writes the new sort back through here.\n */\n settings = model(new DatatableSettings({\n perPage: { values: SPARK_GRID_PAGE_SIZES, selected: SPARK_GRID_PAGE_SIZES[0] },\n page: { values: [1], selected: 1 },\n sortColumns: [],\n }));\n\n /** Rows the user has ticked. Two-way so chrome outside the grid can read and clear them. */\n selection = model<QueryResultItem[]>([]);\n\n /**\n * Replaces where the first column's link points, per row.\n *\n * Return `null` to suppress the link for that row — useful when a grid mixes rows that have a\n * detail page with rows that do not.\n *\n * ⚠️ **It changes the destination, not the permission.** `canRead()` still gates whether any\n * link is rendered at all, and this function is never consulted when that gate is closed. It\n * cannot be used to reach a row the rights model withheld; it exists for the case where the\n * row's natural destination is not `/po/{type}/{id}` — a composed row that maps to a page of\n * its own, or a grid embedded in a route that owns its own child paths.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n /** Emitted whenever a load or a page fetch fails, for a host in bespoke chrome. */\n error = output<HttpErrorResponse>();\n rowClicked = output<QueryResultItem>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /**\n * Where the first column's link points for this row: the host's `rowRoute` when it supplied one,\n * otherwise the row's own detail page.\n *\n * A method rather than a computed because the answer is per row, and the template already\n * iterates rows — a computed would have to be a map keyed by id, rebuilt on every page.\n */\n protected rowRouteFor(row: QueryResultItem): unknown[] | null {\n const custom = this.rowRoute();\n if (custom) return custom(row);\n\n const type = this.entityType();\n\n // ⚠️ A composed type has no per-row detail page by construction: its rows are computed, and\n // `{Name}Actions.OnLoadAsync` serves ONE page which is free to ignore the id it was given. So\n // /po/{type}/{rowId} does not 404 — it silently renders that same page, which is worse, because\n // nothing looks wrong. This branch is live in DemoApp today: Read/StartPage implies\n // Query/StartPage, so the composed grid renders with canRead() true and every row linked.\n //\n // `clrType` is absent exactly for those types and is already on the wire; the grid simply never\n // consulted it. A host whose composed rows DO have a page says so with `rowRoute`.\n if (!type?.clrType) return null;\n\n return ['/po', type.alias || type.id, row.id];\n }\n\n query = signal<SparkQuery | null>(null);\n entityType = signal<EntityType | null>(null);\n allEntityTypes = signal<EntityType[]>([]);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n loading = signal(true);\n canRead = signal(false);\n canCreate = signal(false);\n resultCount = signal<number | null>(null);\n customActions = signal<CustomActionDefinition[]>([]);\n\n /** Names the server withheld for this RESULT; see QueryResult.disabledActions. */\n private readonly disabledActions = signal<string[]>([]);\n\n /**\n * The actions actually offered for this result.\n *\n * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE and is never told what an\n * execution returned, so an action that applies to only some results cannot be filtered there.\n * A custom query withholds what does not apply via `CustomQueryArgs.DisableActions(...)`, and\n * the answer arrives on the result.\n *\n * An affordance, not a permission -- the action handler still refuses on its own terms.\n */\n visibleCustomActions = computed(() => {\n const withheld = this.disabledActions();\n if (!withheld.length) return this.customActions();\n\n const lowered = new Set(withheld.map(name => name.toLowerCase()));\n return this.customActions().filter(action => !lowered.has(action.name.toLowerCase()));\n });\n fetchFn = signal<BsDatatableFetch<QueryResultItem> | null>(null);\n\n /**\n * Why the component renders its own failure instead of only reporting one.\n *\n * `SparkService` is a bare `firstValueFrom` passthrough with no interceptor, so every failure\n * surfaces here and nowhere else. A host embedding this grid cannot surface what it never sees,\n * and the default has to be visible with no host cooperation — hence a rendered alert, not just\n * an output.\n */\n errorMessage = signal<string | null>(null);\n\n /** 'none' unless an action is selection-gated, so unaffected grids gain no checkbox column. */\n selectionMode = computed(() => selectionModeFor(this.customActions()));\n\n isVirtualScrolling = computed(() => isVirtualScrollingQuery(this.query()));\n\n /**\n * Columns as sent with the most recent fetched page.\n *\n * A host binding `data` (streaming) supplies its own via the `columns` input instead — the\n * snapshot carries them once, exactly as a paged result does.\n */\n private readonly fetchedColumns = signal<QueryColumn[]>([]);\n\n /** Every column on the wire, drawn or not — what renderers and lookups resolve against. */\n allColumns = computed(() => this.columns() ?? this.fetchedColumns());\n\n /**\n * The columns the grid draws. `isVisible: false` ships a value without a column, so a renderer\n * can read a sibling the grid does not show; undefined means visible, so a server that predates\n * the field still draws everything.\n */\n visibleColumns = computed(() => this.allColumns().filter(c => c.isVisible !== false));\n\n /**\n * True when rows do not come from this component's own fetch.\n *\n * Either because the host bound `data`, or because the query streams — a streaming query's rows\n * arrive over a socket and `/execute` is not a way to get them. The grid recognises the second\n * case itself rather than waiting to be told, because it resolves the query before the host can\n * see it: left to `data` alone, every streaming grid would fire one pointless fetch on mount,\n * before the host had anything to bind.\n */\n hasExternalData = computed(() =>\n this.data() !== null || this.query()?.isStreamingQuery === true);\n\n /** Whether an action's selection rule is satisfied right now. The server checks it again. */\n isActionEnabled(action: CustomActionDefinition): boolean {\n return parseSelectionRule(action.selectionRule)(this.selection().map(r => r.id).length);\n }\n\n constructor() {\n effect(() => {\n const qId = this.queryId();\n const pId = this.parentId();\n const pType = this.parentType();\n // Only the query id is required. Requiring a parent here is what made a standalone grid\n // silently render nothing.\n if (qId) {\n this.loadData(qId, pId, pType);\n } else {\n // No query id at all. `loading` starts true, so without this the component would spin\n // forever instead of saying anything.\n this.loading.set(false);\n }\n });\n\n // Separate effect, so the token drives the cheap refresh and never the full metadata reload.\n // `first` skips the initial run: the effect above has already fetched, and reacting to the\n // token's starting value would double-fetch on mount.\n let first = true;\n effect(() => {\n // Both the host's token and the server's refreshQuery drive the same cheap refresh.\n this.reloadToken();\n this.queryRefresh.tokenFor(this.queryId());\n if (first) { first = false; return; }\n untracked(() => this.reload());\n });\n\n // A new search term must refetch even when page, perPage and sort are unchanged — the\n // datatable dedupes reloads by exactly that triple, and a new fetch identity is what resets\n // its dedupe key. Skipping the first run keeps mount to a single request.\n let firstSearch = true;\n effect(() => {\n this.search();\n if (firstSearch) { firstSearch = false; return; }\n untracked(() => this.onSearchChanged());\n });\n }\n\n /**\n * Re-run the query, keeping the current page, sort and scroll position.\n *\n * Data-level on purpose: it re-seeds the fetch closure and nothing else. For a definition\n * change — new columns, a renamed query — the inputs themselves must change; that is the\n * expensive path. Inert when rows come from the host: there is nothing here to re-run.\n */\n reload(): void {\n if (this.hasExternalData()) return;\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q, this.parentId(), this.parentType()));\n }\n\n private onSearchChanged(): void {\n if (this.hasExternalData()) return;\n const s = this.settings();\n // Back to page 1: the old page number means nothing against a different result set.\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n this.reload();\n }\n\n /**\n * Replaces the fetched columns only when they actually differ.\n *\n * Columns ship **once per result**, so every page fetch returns an identical set. Assigning it\n * unconditionally wrote a new array of new objects into the signal each time, re-rendering every\n * header and re-evaluating every `*bsDatatableColumn` input for no change in content.\n *\n * Compared by serialized value rather than by reference: the objects are freshly deserialized from\n * JSON on every response, so reference equality is always false and would defeat the check. A\n * dozen small objects per fetch is nothing beside the request that produced them.\n *\n * ⚠️ This was written believing it fixed a panel-teardown bug (#431 F7). Measured in a browser: it\n * does not. The columns were already identical on every fetch, so the guard hits every time and the\n * teardown had another cause entirely. Kept because avoiding a pointless re-render of every header\n * on every page is worth having on its own — but it is an optimization, not a fix, and the F7\n * question remains open.\n */\n private setFetchedColumns(columns: QueryColumn[]): void {\n const current = this.fetchedColumns();\n if (current.length === columns.length && JSON.stringify(current) === JSON.stringify(columns)) return;\n\n this.fetchedColumns.set(columns);\n }\n\n /**\n * The per-column filters currently applied (#431).\n *\n * Read inside `makeFetch`'s closure, like `search`, so a change refetches by producing a new fetch\n * identity rather than by being captured.\n */\n private readonly filters = signal<QueryColumnFilter[]>([]);\n\n /**\n * The value source behind every column's panel.\n *\n * ⚠️ **Referentially stable, and that is the whole point.** It reads `filters()` when the datatable\n * *calls* it, not when it is built — so it always sees the current filters while never changing\n * identity.\n *\n * This was a `computed` first, on the reasoning that it had to close over the current filters\n * because `DistinctsRequest` carries only `{ column, search, signal }`. That was wrong, and the way\n * it was wrong is worth keeping: a new identity reassigns `[distincts]` on the element, and the\n * component aborts the in-flight request and bumps a generation counter — so a response already on\n * its way is discarded as stale and the list renders empty over a perfectly good answer.\n *\n * Reading the signal at call time removes the race entirely and is simpler besides.\n *\n * The asked-for column's own filter is excluded: a panel must offer the values you could still\n * pick, not only the ones you already picked.\n */\n /**\n * The last complete distinct list seen per column, for translating a `< none >` selection.\n *\n * ⚠️ Only stored when the server reported the list was NOT truncated (`hasMore === false`). The\n * translation below is the complement of what is listed, so a partial list would exclude the wrong\n * set — and silently, which is the failure mode this whole feature exists to remove.\n */\n private readonly completeDistincts = new Map<string, unknown[]>();\n\n protected readonly distinctsFn: DatatableDistincts = async request => {\n const queryId = this.query()?.id;\n if (!queryId) return null;\n\n // Read at call time — never captured.\n const others = this.filters().filter(f => f.name !== request.column);\n\n // The server's shape IS the datatable's shape, so there is nothing to map.\n const result = await this.sparkService.getDistinctValues(queryId, request.column, {\n search: request.search,\n columns: others,\n parentId: this.parentId(),\n parentType: this.parentType(),\n });\n\n // A searched list is a subset by construction, so it is never a basis for a complement.\n if (result && !result.hasMore && !request.search)\n this.completeDistincts.set(request.column, result.matching.map(v => v.value));\n else\n this.completeDistincts.delete(request.column);\n\n return result;\n };\n\n /**\n * Translates one filter change into the wire shape and refetches.\n *\n * The event always arrives as the `'values'` shape because the panel only ever calls\n * `ctx.apply(...)`; comparison mode is never declared. `inverse` is a flag on the event and two\n * arrays on the wire — the translation is this method's whole job.\n */\n protected onFilterChange(detail: FilterChangeDetail): void {\n if (detail.mode !== 'values') return;\n\n const values = detail.selected.map(v => v.value);\n const next = this.filters().filter(f => f.name !== detail.column);\n\n if (values.length > 0) {\n // ⚠️ `< none >` cannot be asked for directly. The server compares against the index term, and\n // RavenDB gives a field the document never wrote NO term at all — so `Col == null` matches a\n // field written as JSON null but NOT one that is absent, while `Col != null` puts the absent row\n // with the rows that HAVE a value. Both rows render blank, the panel offers one entry for the\n // pair, and no predicate selects that pair. Measured in AbsentVersusNullFieldTests.\n //\n // The complement is expressible, though: \"none of the real values\" catches absent and null\n // alike. So a selection containing the null entry is sent as an exclusion of everything NOT\n // selected, which is exactly the same set and needs no server change.\n const complete = this.completeDistincts.get(detail.column);\n const selectsNone = values.some(v => v === null || v === undefined);\n\n if (selectsNone && !detail.inverse && complete) {\n const unselected = complete.filter(v =>\n v !== null && v !== undefined && !values.some(s => s === v));\n\n // Everything is selected, so the filter narrows nothing — drop it rather than sending an\n // empty exclusion, which would read as \"exclude nothing\" only by accident.\n if (unselected.length > 0)\n next.push({ name: detail.column, excludes: unselected });\n } else {\n next.push(detail.inverse\n ? { name: detail.column, excludes: values }\n : { name: detail.column, includes: values });\n }\n }\n\n this.filters.set(next);\n this.onFilterChanged();\n }\n\n /**\n * Back to page 1 and a fresh fetch identity, exactly as a new search term does.\n *\n * Both halves matter: the old page number means nothing against a different result set, and the\n * datatable dedupes reloads by `(page, perPage, sort)` — none of which a filter changes — so\n * without a new fetch identity the request is silently never made.\n */\n private onFilterChanged(): void {\n if (this.hasExternalData()) return;\n const s = this.settings();\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n this.reload();\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n if (action.confirmationMessageKey) {\n const message = this.lang.t(action.confirmationMessageKey) || 'Are you sure?';\n if (!confirm(message)) return;\n }\n try {\n // The sub-query's container travels with the action (#327). Without it an action invoked\n // from a company's Cars tab could not tell it was on a company's page at all — the grid knew\n // (it filters the rows by that parent) and simply dropped the fact on the way out.\n //\n // Sent as parentId/parentType, NOT as the `parent` argument: that one means an object of this\n // action's OWN type and is loaded under it server-side, so handing it a Company id for a Car\n // action refuses — correctly, and confusingly.\n const pId = this.parentId();\n const pType = this.parentType();\n await this.sparkService.executeCustomAction(\n this.entityType()!.id,\n action.name,\n undefined,\n this.selection().map(r => r.id),\n pId && pType ? { id: pId, type: pType } : undefined,\n // The query too: the server re-runs it narrowed to these ids, so the action receives the\n // rows this grid rendered rather than a re-derivation from documents.\n this.query()?.id);\n this.customActionExecuted.emit({ action });\n if (action.refreshOnCompleted) this.reload();\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || this.lang.t('common.actionFailed') || 'Action failed');\n }\n }\n\n private reportError(err: HttpErrorResponse): void {\n this.errorMessage.set(this.describe(err));\n this.error.emit(err);\n }\n\n /**\n * A 404 is deliberately generic.\n *\n * `Endpoints/Queries/Get.cs` answers 404 with byte-identical bodies for \"no such query\" and\n * \"you may not see it\", so existence is not disclosed (security audit M-3). The component\n * therefore genuinely cannot tell them apart, and both \"Not found\" and \"Access denied\" would be\n * a guess — one of them leaking, the other misleading.\n */\n private describe(err: HttpErrorResponse): string {\n if (err?.status === 404) {\n return this.lang.t('spark.query.unavailable') || 'This list is not available.';\n }\n return err?.error?.error || err?.message\n || this.lang.t('common.unexpectedError') || 'An unexpected error occurred';\n }\n\n private async loadData(queryId: string, parentId: string, parentType: string): Promise<void> {\n this.loading.set(true);\n this.errorMessage.set(null);\n this.fetchFn.set(null);\n // Reset everything derived from the previous query, not just the fetch. Leaving\n // `entityType`/`canRead` behind let a failed reload build a row link out of the PREVIOUS type\n // and the previous permission.\n this.query.set(null);\n this.entityType.set(null);\n this.canRead.set(false);\n this.canCreate.set(false);\n this.customActions.set([]);\n this.resultCount.set(null);\n // Ids from the previous query are meaningless against the next one, and would be POSTed as\n // though they belonged to it.\n this.selection.set([]);\n // A filter belongs to the query it was applied to. Surviving a query switch would POST it\n // against the next query, where the column may not exist or may mean something else entirely.\n this.filters.set([]);\n try {\n const [resolvedQuery, entityTypes] = await Promise.all([\n this.sparkService.getQuery(queryId),\n this.sparkService.getEntityTypes(),\n ]);\n\n this.query.set(resolvedQuery);\n this.allEntityTypes.set(entityTypes);\n\n const et = this.resolveEntityType(resolvedQuery, entityTypes);\n this.entityType.set(et);\n if (et) {\n const [permissions, actions] = await Promise.all([\n this.sparkService.getPermissions(et.id),\n this.sparkService.getCustomActions(et.id),\n ]);\n this.canRead.set(permissions.canRead);\n this.canCreate.set(permissions.canCreate);\n // 'query', not 'list'. The server model has always documented \"detail\" | \"query\" |\n // \"both\"; a filter testing for a value nothing emits renders the action NOWHERE.\n this.customActions.set(filterQueryActions(actions));\n }\n\n this.settings.set(initialGridSettings(resolvedQuery));\n // The datatable drives paging/sorting via [(settings)] and calls fetchFn per page. Virtual\n // scrolling is just the [virtualScroll] template flag.\n // `resolvedQuery`, not `hasExternalData()`: the query signal is set above, but reading the\n // computed here would depend on signal-read ordering inside an async method. Ask the value\n // directly.\n if (this.data() === null && !resolvedQuery?.isStreamingQuery) {\n this.fetchFn.set(this.makeFetch(resolvedQuery, parentId, parentType));\n }\n\n this.loadLookupReferenceOptions();\n } catch (e) {\n this.fetchFn.set(null);\n this.reportError(e as HttpErrorResponse);\n } finally {\n this.loading.set(false);\n }\n }\n\n /**\n * The query's entity type.\n *\n * The source-name fallback is not optional garnish: a `Database.*` query need not declare\n * `entityType`, and the sub-query grid — which matched on the declared name alone — rendered\n * those as an empty card with no columns, no rows and no error, while the identical query\n * rendered correctly as a page. Both grids are this one now, so there is one answer.\n */\n private resolveEntityType(query: SparkQuery | null, entityTypes: EntityType[]): EntityType | null {\n if (!query) return null;\n\n if (query.entityType) {\n return entityTypes.find(t =>\n t.name === query.entityType || t.alias === query.entityType?.toLowerCase()) ?? null;\n }\n\n const sourceName = extractSourceName(query.source);\n const singular = singularize(sourceName);\n return entityTypes.find(t =>\n t.name === sourceName || t.name === singular || t.clrType?.endsWith(singular)) ?? null;\n }\n\n private makeFetch(query: SparkQuery, parentId: string, parentType: string): BsDatatableFetch<QueryResultItem> {\n return (req) => this.sparkService.executeQuery(query.id, {\n sortColumns: req.sortColumns,\n skip: (req.page - 1) * req.perPage,\n take: req.perPage,\n search: this.search() || undefined,\n // Read inside the closure, exactly as `search` is: what forces a refetch is the fetch\n // identity `reload()` creates, not the value captured when this closure was built.\n columns: this.filters(),\n parentId, parentType,\n }).then(r => {\n this.errorMessage.set(null);\n this.resultCount.set(r.totalItems);\n // Columns come from the result now, not from the entity type: the server decides the query\n // surface (ShowedOn.Query) and is the same place the sort-column allow-list is checked.\n // ?? [] because a malformed or older response must render an empty grid, not throw inside a\n // computed — where the stack points at the column filter and not at the response that lacked them.\n this.setFetchedColumns(r.columns ?? []);\n // Per-result, so it is re-read on every page rather than latched from the first.\n this.disabledActions.set(r.disabledActions ?? []);\n return {\n data: r.items,\n totalRecords: r.totalItems,\n totalPages: Math.ceil(r.totalItems / req.perPage) || 1,\n perPage: req.perPage,\n page: req.page,\n };\n }).catch((e: HttpErrorResponse) => {\n // Report before returning the empty page, or a failed fetch is indistinguishable from a\n // query that legitimately has no rows.\n this.reportError(e);\n this.resultCount.set(0);\n return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };\n });\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n // Over ALL columns, not just the drawn ones: an undrawn column's value can still be rendered\n // by a renderer reading it as a sibling, and it would resolve to a raw key without its options.\n this.lookupReferenceOptions.set(await this.gridRenderers.loadLookupOptions(this.allColumns()));\n }\n\n /**\n * The underlying value a custom renderer receives, which is not the printable one.\n *\n * A projection is flat, so this is the cell's own value rather than a display string. It is\n * deliberately not the attribute-shaped `rendererValue`: a query row has no nested object to\n * fall back to, and reaching for one would hand every renderer `undefined`.\n */\n rendererValueFor(item: QueryResultItem, column: QueryColumn): unknown {\n return cellValue(valueFor(item, column.name));\n }\n\n getColumnRendererComponent(column: QueryColumn): Type<any> | null {\n return this.gridRenderers.columnComponentFor(column);\n }\n\n /** The renderer's optional filter-label override for this column, or null for the server text. */\n filterLabelFor(column: QueryColumn): ((value: unknown) => string) | null {\n return this.gridRenderers.filterLabelFor(column);\n }\n\n getColumnRendererInputs(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any> {\n return this.gridRenderers.columnInputsFor(component, item, column);\n }\n}\n\nfunction extractSourceName(source: string): string {\n const dotIndex = source.indexOf('.');\n return dotIndex >= 0 ? source.substring(dotIndex + 1) : source;\n}\n\n/**\n * Enough English to map a query source onto a type name — `Cars` to `Car`, `People` to `Person`.\n *\n * Deliberately a small table plus three suffix rules rather than a library: it runs against type\n * names the application itself chose, and a wrong guess costs nothing, since the caller falls\n * through to matching the plural and the CLR type name.\n */\nfunction singularize(plural: string): string {\n const irregulars: Record<string, string> = {\n 'People': 'Person',\n 'Children': 'Child',\n 'Men': 'Man',\n 'Women': 'Woman',\n };\n if (irregulars[plural]) return irregulars[plural];\n if (plural.endsWith('ies')) return plural.slice(0, -3) + 'y';\n if (plural.endsWith('es')) return plural.slice(0, -2);\n if (plural.endsWith('s')) return plural.slice(0, -1);\n return plural;\n}\n","<!--\n Three explicit states, in this order, and the order is load-bearing.\n\n This template used to open with `@if (query(); as q)` wrapping EVERYTHING, and `query()` is only\n set after loadData's awaits resolve. Three separate bugs fell out of that one line: the spinner\n was unreachable on a first load (it rendered inside the gate, while `loading` starts true and\n `query` starts null), a first-load failure rendered ZERO DOM — no card, no message, nothing to\n see or report — and a failed RELOAD left the previous query's rows on screen.\n\n So: `loading` is checked first and outside any dependency on `query`, and the final `@else`\n always renders something. Do not fold these back into one gate, and do not move the spinner\n inside the `query()` branch.\n-->\n@if (loading()) {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n} @else if (query()) {\n <!-- A page-fetch failure while the grid is already resolved. Without this the catch returns an\n empty page and a failed query is indistinguishable from one that legitimately has no rows. -->\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n\n <!--\n One datatable, two row sources. `[data]` and `[fetch]` are mutually exclusive in\n ng-bootstrap — binding both makes `[fetch]` win silently — so exactly one is ever non-null\n here: `fetchFn` is left null whenever `data` is bound. See the class comment for why rows can\n arrive from outside at all (streaming must not put a websocket in every detail page's bundle).\n -->\n <bs-datatable\n [selectionMode]=\"selectionMode()\"\n [(selection)]=\"selection\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"data()\"\n [fetch]=\"fetchFn()\"\n [distincts]=\"distinctsFn\"\n (filterChange)=\"onFilterChange($event)\"\n [(settings)]=\"settings\">\n @for (col of visibleColumns(); track col.name) {\n <!--\n `canSort`/`canFilter` arrive already resolved by the server (query override → attribute →\n true), so this only spells the polarity. `!== false` is needed for BOTH, for opposite\n reasons: absent means capable on our wire, while the datatable's own `filterable` defaults\n to false, unlike `sortable`.\n\n ⚠️ `filterActive` and `filterSummary` are deliberately NOT bound. Any *bsDatatableColumn\n input change recomputes the column set, which retires the live filter view — while the web\n component's mount-once guard leaves the panel's nodes in place and never re-renders them.\n Binding them to state our own panel mutates would freeze the panel the instant a value is\n ticked, until it is closed and reopened. The cost is that a filtered column carries no\n visual marker yet, which is worth far less than a panel that dies on first use. See #431 F7.\n -->\n <div *bsDatatableColumn=\"col.name; sortable: col.canSort !== false; filterable: col.canFilter !== false\">\n {{ (col.label | resolveTranslation) || col.name }}\n <spark-attribute-description [description]=\"col.description\" />\n\n @if (col.canFilter !== false) {\n <ng-container *bsDatatableFilterPanel=\"let values; let ctx = ctx\">\n <spark-column-filter-panel\n [column]=\"col\"\n [values]=\"values()\"\n [ctx]=\"ctx\"\n [filterLabel]=\"filterLabelFor(col)\" />\n </ng-container>\n }\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @let row = $any(item);\n @for (col of visibleColumns(); track col.name; let first = $first) {\n <td>\n @if (row) {\n <!-- The first column links to the row's detail page. Two things about this are\n routinely misread:\n\n 1. `cellContent` is projected INSIDE the anchor, so a custom `renderer` does NOT\n suppress this link — a renderer that emits its own <a> produces nested anchors\n (invalid HTML) rather than replacing this one.\n 2. `canRead()` is the whole gate, and it is the rights model: `Query` without\n `Read` lists the rows and withholds the link. Withhold `Read` for a query\n whose rows no detail page can load — a Custom.* query that fabricates rows in\n memory, say.\n 3. `rowRoute` replaces the DESTINATION only, never the gate: returning a route\n from it cannot make a row clickable that `canRead()` withheld, and returning\n null from it suppresses the link for that one row. -->\n @if (first && canRead() && rowRouteFor(row); as route) {\n <a [routerLink]=\"route\"\n (click)=\"rowClicked.emit(row)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, col: col }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, col: col }\"></ng-container>\n }\n } @else {\n \n }\n </td>\n }\n </ng-container>\n </bs-datatable>\n} @else {\n <!-- The query could not be resolved. Rendering something keeps the host's layout intact and,\n crucially, makes the failure visible: this branch used to render nothing at all. -->\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">\n {{ errorMessage() || ('spark.query.unavailable' | t) }}\n </bs-alert>\n}\n\n<ng-template #cellContent let-item let-col=\"col\">\n <!--\n Presentation lives in <spark-grid-cell>, shared with the AsDetail table on the PO detail page.\n Value resolution stays here: a query row is a PersistentObject with an attribute list, and the\n AsDetail table's rows are plain dictionaries, so only the rendering is common.\n\n `link` is deliberately not passed. The first-column link is the grid's own rule and wraps this\n template from outside; setting both would nest anchors.\n -->\n <spark-grid-cell\n [column]=\"col\"\n [display]=\"(col | queryCellValue:item:lookupReferenceOptions())\"\n [rendererValue]=\"rendererValueFor(item, col)\"\n [item]=\"item\"\n [chips]=\"(col | queryReferenceChips:item)\" />\n</ng-template>\n","import { ChangeDetectionStrategy, Component, computed, contentChildren, input, output, viewChild, TemplateRef } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { CustomActionDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\nimport { inject } from '@angular/core';\nimport { SparkQueryGridComponent } from './spark-query-grid.component';\nimport {\n SparkQueryActionsDirective,\n SparkQueryCaptionDirective,\n SparkQueryIconDirective,\n slotFor,\n} from './spark-query-slots';\n\n/**\n * A `<bs-card>` around a {@link SparkQueryGridComponent}: icon, caption, actions, grid.\n *\n * ## Chrome is overridden, not replaced\n *\n * Every header region has a default, and a host replaces only the ones it supplies. That is what\n * lets this component serve the auto-rendered case: a sub-query is rendered once per entry in\n * `EntityTypeDefinition.Queries` with nobody projecting into it, and it must look right with no\n * host cooperation at all. A slot mechanism that only ever *replaced* chrome would leave that\n * call site blank — which is why an earlier design had the query carry its own chrome from the\n * server. Overriding a default needs no host; replacing one does.\n *\n * ## Two ways in, one set of templates\n *\n * Hand-written markup uses the structural directives and is found with `contentChildren`.\n * The auto-rendered path cannot: `spark-po-detail` is created by the router, so in a default app\n * there is no tag to project into. It therefore forwards `TemplateRef`s as inputs instead — a\n * directive cannot cross a component boundary but its template can, and that forwarding is\n * already the idiom in `spark-po-detail` (`extraActionsTemplate`, `extraContentTemplate`).\n *\n * Content wins over a forwarded input: the closer declaration is the more specific one.\n */\n@Component({\n selector: 'spark-query-card',\n imports: [CommonModule, BsCardComponent, BsCardHeaderComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, SparkQueryGridComponent, ResolveTranslationPipe],\n templateUrl: './spark-query-card.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkQueryCardComponent {\n readonly lang = inject(SparkLanguageService);\n\n queryId = input.required<string>();\n parentId = input<string>('');\n parentType = input<string>('');\n reloadToken = input<unknown>(null);\n data = input<QueryResultItem[] | null>(null);\n search = input<string>('');\n\n /**\n * Slots forwarded from a host that cannot project content — see the class comment. A card\n * reached through hand-written markup should use the structural directives instead.\n */\n iconTemplate = input<TemplateRef<any> | null>(null);\n captionTemplate = input<TemplateRef<any> | null>(null);\n actionsTemplate = input<TemplateRef<any> | null>(null);\n\n error = output<HttpErrorResponse>();\n rowClicked = output<QueryResultItem>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n /**\n * Forwarded to the grid. Without this the escape hatch was unreachable from a card — i.e. from\n * every auto-rendered sub-query and program-unit query page, which is most grids.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n colors = Color;\n\n private readonly iconSlots = contentChildren(SparkQueryIconDirective);\n private readonly captionSlots = contentChildren(SparkQueryCaptionDirective);\n private readonly actionSlots = contentChildren(SparkQueryActionsDirective);\n\n /**\n * The grid, read only to surface its state — the query, the actions, the selection — to this\n * component's own header.\n *\n * Optional rather than `required` on purpose. The header renders ABOVE the grid in this\n * template, so on the first change-detection pass the view query has not resolved yet and\n * `viewChild.required()` would throw where a plain one returns `undefined`. Everything below\n * therefore reads it defensively; the signal updates once the view initialises and the header\n * re-renders with the real query.\n *\n * A host reads grid state through a template reference variable instead, because a host may put\n * the grid behind an `@if`, where a view query genuinely is intermittently undefined.\n */\n protected readonly grid = viewChild(SparkQueryGridComponent);\n\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n\n protected readonly iconTpl = computed(() =>\n slotFor(this.iconSlots(), this.query())?.templateRef ?? this.iconTemplate());\n\n protected readonly captionTpl = computed(() =>\n slotFor(this.captionSlots(), this.query())?.templateRef ?? this.captionTemplate());\n\n protected readonly actionsTpl = computed(() =>\n slotFor(this.actionSlots(), this.query())?.templateRef ?? this.actionsTemplate());\n\n /** The caption the card renders unless a slot replaces it. */\n protected readonly caption = computed(() => {\n const q = this.query();\n return (q?.description ? this.lang.resolve(q.description) : '') || q?.name || '';\n });\n\n protected readonly customActions = computed(() => this.grid()?.visibleCustomActions() ?? []);\n protected readonly selection = computed(() => this.grid()?.selection() ?? []);\n}\n","<!--\n The card is always rendered. A host that wants no card uses <spark-query-grid> directly — which\n is what the old `showCard=\"false\"` meant, expressed as a component choice rather than a flag.\n-->\n<bs-card class=\"d-block my-3\">\n <bs-card-header>\n <div class=\"d-flex align-items-center gap-2\">\n @if (iconTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: query() }\"></ng-container>\n }\n\n @if (actionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: customActions(), selection: selection(), query: query() }\"></ng-container>\n } @else if (customActions().length) {\n <!-- The default: the actions the server declares for this type. The nav renders only when\n there are any, so a query without actions is pixel-identical to a plain caption.\n\n Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"!grid()?.isActionEnabled(action)\" (click)=\"grid()?.onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n </bs-priority-nav>\n }\n\n <!--\n Caption last, actions first. The auto margin is conditional rather than fixed, and that is\n the point: with actions present `ms-auto` pushes the caption to the trailing edge, and with\n none it falls back to `me-auto` so a plain card looks exactly as it always did. A fixed\n `ms-auto` would have right-aligned the caption of every action-less query card in every app\n — a change nobody asked for, to fix a layout only some cards have.\n -->\n <span [class.ms-auto]=\"customActions().length\" [class.me-auto]=\"!customActions().length\">\n @if (captionTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: caption(), query: query() }\"></ng-container>\n } @else {\n {{ caption() }}\n }\n </span>\n </div>\n </bs-card-header>\n\n <div class=\"p-3\">\n <spark-query-grid\n [queryId]=\"queryId()\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [reloadToken]=\"reloadToken()\"\n [data]=\"data()\"\n [search]=\"search()\"\n [rowRoute]=\"rowRoute()\"\n (error)=\"error.emit($event)\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n </div>\n</bs-card>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-grid.mjs","sources":["../../grid/src/spark-grid-columns.ts","../../grid/src/spark-grid-renderers.ts","../../grid/src/spark-query-slots.ts","../../grid/src/spark-grid-cell.component.ts","../../grid/src/spark-grid-cell.component.html","../../grid/src/spark-query-grid.component.ts","../../grid/src/spark-query-grid.component.html","../../grid/src/spark-query-card.component.ts","../../grid/src/spark-query-card.component.html","../../grid/mintplayer-ng-spark-grid.ts"],"sourcesContent":["import { DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';\nimport { SortColumn } from '@mintplayer/pagination';\nimport {\n EntityAttributeDefinition,\n EntityType,\n ShowedOn,\n SparkQuery,\n hasShowedOnFlag,\n} from '@mintplayer/ng-spark/models';\n\n/** Page sizes offered by every Spark grid. */\nexport const SPARK_GRID_PAGE_SIZES = [10, 25, 50];\n\n/**\n * The attributes a grid shows, in display order.\n *\n * Shared so the two grids cannot disagree about what \"visible\" means — they each had their own\n * copy of this expression, which is the kind of thing that stays identical right up until it\n * doesn't.\n */\nexport function visibleGridAttributes(entityType: EntityType | null): EntityAttributeDefinition[] {\n return entityType?.attributes\n .filter(a => a.isVisible && hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) ?? [];\n}\n\n/**\n * Initial datatable settings for a query, seeded with the query's declared sort.\n *\n * The datatable owns paging and sorting from here on and calls the fetch callback per page; this\n * only decides where it starts.\n */\nexport function initialGridSettings(query: SparkQuery | null): DatatableSettings {\n // Nullable because one caller resolves its query from a route param and may not have one yet;\n // an unsorted grid is the right fallback, not a crash.\n const sortColumns: SortColumn[] = (query?.sortColumns || []).map(sc => ({\n property: sc.property,\n direction: sc.direction === 'desc' ? 'descending' as const : 'ascending' as const,\n }));\n\n return new DatatableSettings({\n perPage: { values: SPARK_GRID_PAGE_SIZES, selected: SPARK_GRID_PAGE_SIZES[0] },\n page: { values: [1], selected: 1 },\n sortColumns,\n });\n}\n\n/** Whether a query renders as a virtual-scrolling grid rather than a paged one. */\nexport function isVirtualScrollingQuery(query: SparkQuery | null): boolean {\n return query?.renderMode === 'VirtualScrolling';\n}\n","import { inject, Injectable, Type } from '@angular/core';\nimport {\n EntityAttributeDefinition,\n LookupReference,\n QueryColumn,\n QueryResultItem,\n SparkCellColumn,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { SPARK_ATTRIBUTE_RENDERERS, cellValue, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport { SparkService } from '@mintplayer/ng-spark/services';\n\n/**\n * Renderer lookup and lookup-reference loading for a Spark grid.\n *\n * This was extracted when `spark-query-list` and `spark-sub-query` were two components holding\n * byte-identical copies of it — around 120 lines between them. That duplication was not a\n * tidiness complaint: it produced drift. The two copies disagreed about `[indeterminate]`, about\n * resetting permission state, about whether a fetch failure surfaces or is swallowed, and about\n * virtual-scroll sizing — four user-visible bugs, each fixed on one side and not the other.\n *\n * There is now one grid, {@link SparkQueryGridComponent}, so the drift cannot recur. This stays\n * separate because it is stateless service-shaped logic rather than view state, and because it is\n * what a custom grid would need in order to render Spark cells at all.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkGridRenderers {\n private readonly registry = inject(SPARK_ATTRIBUTE_RENDERERS);\n private readonly sparkService = inject(SparkService);\n\n /** The registered column component for a column, or null to fall back to the default cell. */\n columnComponentFor(column: SparkCellColumn): Type<any> | null {\n if (!column.renderer) return null;\n return this.registry.find(r => r.name === column.renderer)?.columnComponent ?? null;\n }\n\n /** The registered filter-label function for a column, or null to use the server-computed text. */\n filterLabelFor(column: SparkCellColumn): ((value: unknown) => string) | null {\n if (!column.renderer) return null;\n return this.registry.find(r => r.name === column.renderer)?.filterLabel ?? null;\n }\n\n /**\n * Inputs for a query-grid cell renderer, filtered to what the component actually declares —\n * `NgComponentOutlet` throws on an input the target does not have, which is what lets every\n * member of the renderer contract be optional.\n */\n columnInputsFor(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any> {\n return this.cellInputsFor(component, cellValue(valueFor(item, column.name)), column, item);\n }\n\n /**\n * The same contract for a row that is not a query result — an AsDetail sub-table row, which is\n * a plain dictionary of embedded values with no id and no column metadata of its own, so its\n * value cannot be read the way {@link columnInputsFor} reads one.\n *\n * Only the extraction differs; the renderer contract is identical, and stating it once is what\n * stops the two from drifting the way the cell markup already had.\n */\n cellInputsFor(component: Type<any>, value: unknown, column: SparkCellColumn, item: unknown): Record<string, any> {\n return withDeclaredInputs(component, {\n value,\n column,\n options: column.rendererOptions,\n item,\n });\n }\n\n /**\n * Loads every lookup reference the visible columns need, in one pass.\n *\n * Returns an empty map rather than throwing when there are none, so callers never branch on it.\n */\n async loadLookupOptions(columns: readonly (SparkCellColumn | EntityAttributeDefinition)[]): Promise<Record<string, LookupReference>> {\n const lookupColumns = columns.filter(c => c.lookupReferenceType);\n if (lookupColumns.length === 0) return {};\n\n const names = [...new Set(lookupColumns.map(c => c.lookupReferenceType!))];\n const entries = await Promise.all(\n names.map(async name => [name, await this.sparkService.getLookupReference(name)] as const),\n );\n return entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, LookupReference>);\n }\n}\n","import { Directive, inject, input, TemplateRef } from '@angular/core';\nimport { CustomActionDefinition, PersistentObject, SparkQuery } from '@mintplayer/ng-spark/models';\n\n/**\n * Header slots for `<spark-query-card>`.\n *\n * Each directive marks a template that REPLACES one region of the card header. Supplying one\n * leaves the other two alone — that is the whole point of having three rather than the single\n * whole-header template they replace, where changing the icon meant re-implementing the caption\n * and the action bar as well.\n *\n * An omitted slot is not an empty slot: the card renders its default. This is what keeps the\n * auto-rendered sub-query working. A sub-query is rendered from `EntityTypeDefinition.Queries`\n * with no host to project into, so a mechanism that only ever REPLACED chrome would leave that\n * call site with nothing — which is why an earlier design had the query declare its own chrome\n * server-side. Overriding a default needs no host; replacing one does.\n *\n * The two ends of that are visible in the defaults themselves. Neither `SparkQuery` nor\n * `EntityType` carries an icon, so the server has nothing to say and the slot is the only\n * source. Actions are the mirror image: the server declares them per type, so the default is\n * the real answer and the slot is a rare override.\n *\n * Naming follows ng-bootstrap's `*bsDatatableColumn` convention — prefix, component, slot —\n * with `spark`, since these are ng-spark directives and `bs` is another package's prefix. These\n * are the first attribute directives in ng-spark; every existing selector is an element.\n *\n * ## Targeting one query\n *\n * Each slot takes an optional query alias or id as its value. A detail page renders one card per\n * entry in `EntityTypeDefinition.Queries`, so a host that supplies a bare slot would decorate\n * every one of them identically — rarely what is wanted. A targeted slot applies to that query;\n * an untargeted one is the fallback for the rest.\n *\n * ```html\n * <span *sparkQueryIcon=\"'cars'\"><spark-icon name=\"car-front\" /></span>\n * <span *sparkQueryIcon><spark-icon name=\"table\" /></span>\n * ```\n *\n * Matching is case-insensitive, against the query's alias and then its id. This is the\n * `bsPriorityNavItem` shape: a value input aliased to the selector, collected with\n * `contentChildren`.\n */\n\n/** Resolves a slot list to the one that applies to a query: targeted first, then untargeted. */\nexport function slotFor<T extends { forQuery: () => string }>(\n slots: readonly T[],\n query: SparkQuery | null,\n): T | null {\n if (!slots.length) return null;\n\n const matches = (target: string) =>\n !!target && (\n target.localeCompare(query?.alias ?? '', undefined, { sensitivity: 'accent' }) === 0 ||\n target.localeCompare(query?.id ?? '', undefined, { sensitivity: 'accent' }) === 0);\n\n // A targeted slot wins over the catch-all even when it is declared later: \"this one query\"\n // is the more specific statement, and declaration order in a template is not a priority.\n return slots.find(s => matches(s.forQuery())) ?? slots.find(s => !s.forQuery()) ?? null;\n}\n\n/**\n * The icon at the header's leading edge. No default: nothing in the model describes one.\n *\n * ```html\n * <spark-query-card [queryId]=\"'cars'\">\n * <spark-icon *sparkQueryIcon name=\"car-front\" />\n * </spark-query-card>\n * ```\n */\n@Directive({ selector: '[sparkQueryIcon]' })\nexport class SparkQueryIconDirective {\n readonly templateRef = inject<TemplateRef<SparkQuerySlotContext>>(TemplateRef);\n\n /** Query alias or id this slot applies to. Empty targets every query on the page. */\n readonly forQuery = input('', { alias: 'sparkQueryIcon' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryIconDirective,\n ctx: unknown,\n ): ctx is SparkQuerySlotContext {\n return true;\n }\n}\n\n/**\n * The header caption. Defaults to the query's translated description, falling back to its name.\n *\n * The context carries that resolved default as `$implicit`, so a host can decorate it — add a\n * count, a badge — without re-resolving the translation itself.\n */\n@Directive({ selector: '[sparkQueryCaption]' })\nexport class SparkQueryCaptionDirective {\n readonly templateRef = inject<TemplateRef<SparkQueryCaptionContext>>(TemplateRef);\n\n readonly forQuery = input('', { alias: 'sparkQueryCaption' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryCaptionDirective,\n ctx: unknown,\n ): ctx is SparkQueryCaptionContext {\n return true;\n }\n}\n\n/**\n * The buttons at the header's trailing edge. Defaults to the server-declared custom actions.\n *\n * The context carries those actions and the current selection, so a host that wants its own\n * buttons ALONGSIDE the server's can render both rather than choosing. Without that, overriding\n * this slot to add one button would silently drop every action the type declares — and the\n * server's actions are the ones carrying `selectionRule` and the permission filter.\n *\n * ```html\n * <ng-container *sparkQueryActions=\"let actions; selection as rows\">\n * <button (click)=\"export(rows)\">Export</button>\n * </ng-container>\n * ```\n */\n@Directive({ selector: '[sparkQueryActions]' })\nexport class SparkQueryActionsDirective {\n readonly templateRef = inject<TemplateRef<SparkQueryActionsContext>>(TemplateRef);\n\n readonly forQuery = input('', { alias: 'sparkQueryActions' });\n\n static ngTemplateContextGuard(\n _dir: SparkQueryActionsDirective,\n ctx: unknown,\n ): ctx is SparkQueryActionsContext {\n return true;\n }\n}\n\n/** Context for the icon slot: the query whose card is being rendered. */\nexport class SparkQuerySlotContext {\n $implicit: SparkQuery | null = null;\n}\n\nexport class SparkQueryCaptionContext {\n /** The caption the card would have rendered: description, or the query name. */\n $implicit = '';\n query: SparkQuery | null = null;\n}\n\nexport class SparkQueryActionsContext {\n /** The custom actions the card would have rendered, already filtered for this query. */\n $implicit: CustomActionDefinition[] = [];\n /** Rows currently ticked. Empty unless an action is selection-gated. */\n selection: PersistentObject[] = [];\n query: SparkQuery | null = null;\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { CommonModule, NgComponentOutlet } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { EntityAttributeDefinition, SparkCellColumn } from '@mintplayer/ng-spark/models';\nimport { ReferenceChip } from '@mintplayer/ng-spark/pipes';\nimport { SparkGridRenderers } from './spark-grid-renderers';\n\n/**\n * One cell of a Spark table, wherever that table is.\n *\n * ## Why this exists\n *\n * The cell markup was written out three times: twice in the query grids (since merged into\n * {@link SparkQueryGridComponent}) and once more in the AsDetail table on the PO detail page. The\n * third copy was never counted, and it had already drifted — a `boolean` column rendered the text\n * `\"true\"` there while the grid rendered a checkbox, a `color` rendered its hex string rather than\n * a swatch, and a custom renderer was dispatched by a second, hand-copied lookup.\n *\n * ## What it does and does not own\n *\n * It owns **presentation**: which control a `dataType` becomes, and dispatching a declared custom\n * renderer. It does not own **value resolution**, and deliberately so — the two callers read from\n * genuinely different row models. A query row is a `PersistentObject` with an attribute list and\n * an id; an AsDetail row is a plain dictionary of embedded values with neither. Trying to unify\n * that would put a second row-access path inside this component and buy nothing.\n *\n * So the caller resolves the value with whichever pipe fits its row model, and passes the result.\n *\n * ## The two links are not the same link, and only one is here\n *\n * `link` is for a cell whose own value points at another entity — an AsDetail reference column.\n * The query grid's first-column link is a different rule (it points at the row's own detail page,\n * gated on `Read`), and it stays in the grid, wrapping this component. Passing both would nest\n * anchors, which is invalid HTML; the grid therefore leaves `link` unset.\n */\n@Component({\n selector: 'spark-grid-cell',\n imports: [CommonModule, NgComponentOutlet, RouterModule],\n templateUrl: './spark-grid-cell.component.html',\n styleUrl: './spark-grid-cell.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkGridCellComponent {\n private readonly renderers = inject(SparkGridRenderers);\n\n /** The column being rendered. Its `dataType` and `renderer` decide everything below. */\n column = input.required<SparkCellColumn>();\n\n /**\n * What the caller's value pipe produced: the display text for most columns, and the raw value\n * for `boolean` and `color`, which need the value rather than a rendering of it.\n */\n display = input<unknown>(null);\n\n /**\n * The value handed to a declared custom renderer.\n *\n * Separate from {@link display} because they are not the same thing: a renderer receives the\n * underlying value — for AsDetail, the nested object itself — while `display` has already been\n * flattened to something printable.\n */\n rendererValue = input<unknown>(null);\n\n /** The row, passed through to a custom renderer as `item`. */\n item = input<unknown>(null);\n\n /**\n * Pre-resolved chips for a reference-array column, or empty when the column is not one.\n *\n * Resolved by the caller because the label source differs: a query row carries a per-id\n * breadcrumb map, while an AsDetail row's `__sparkBreadcrumbs` is keyed by column name only and\n * so cannot label the members of an array. Rather than render a row of raw ids, an AsDetail\n * caller passes nothing and the cell falls through to text.\n */\n chips = input<ReferenceChip[]>([]);\n\n /** Route for a cell whose value references another entity. See the class comment. */\n link = input<unknown[] | null>(null);\n\n protected readonly rendererComponent = computed(() => this.renderers.columnComponentFor(this.column()));\n\n protected readonly rendererInputs = computed(() => {\n const component = this.rendererComponent();\n return component\n ? this.renderers.cellInputsFor(component, this.rendererValue(), this.column(), this.item())\n : {};\n });\n\n /** Null and undefined are distinct from `false` here — see the template. */\n protected readonly isUnsetBoolean = computed(() => this.display() == null);\n\n /**\n * {@link display} when it really is a `Date`, and `null` otherwise.\n *\n * Angular's `DatePipe` does not degrade on a non-date: it **throws**\n * `NG02100: InvalidPipeArgument`, which would take down the whole cell. Both cell pipes\n * deliberately fall back to the raw text for an unparseable date rather than hiding the stored\n * value, so that text reaches this component and must not be piped. A template cannot express\n * `instanceof`, hence the computed.\n */\n protected readonly dateValue = computed(() => {\n const value = this.display();\n return value instanceof Date && !Number.isNaN(value.getTime()) ? value : null;\n });\n}\n","@if (rendererComponent(); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: rendererInputs()\"></ng-container>\n} @else if (column().dataType === 'boolean') {\n <!-- [indeterminate] matters: without it a null boolean renders as an unchecked box, which reads\n as an explicit `false`. One of the three copies of this cell bound it and the others had\n drifted, so the same column said different things on a detail page and in a grid. -->\n <input type=\"checkbox\"\n [checked]=\"display() === true\"\n [indeterminate]=\"isUnsetBoolean()\"\n disabled\n onclick=\"return false;\">\n} @else if (column().dataType === 'date' || column().dataType === 'datetime') {\n <!--\n Formatted in ONE place for both grids. Before this, a date column printed whatever its pipe\n produced, which was the raw ISO string — `2026-09-07T13:25:57.3119825Z` in a table cell. Both\n cell pipes now hand over a real Date, and `date:` accepts a string too, so an unparseable value\n that fell back to text still renders as that text rather than blank.\n -->\n @if (dateValue(); as when) {\n {{ when | date:(column().dataType === 'date' ? 'mediumDate' : 'short') }}\n } @else {\n {{ display() }}\n }\n} @else if (column().dataType === 'color') {\n @if (display(); as colorVal) {\n <!-- Shares `.spark-color-swatch` with the detail page, which is where the sizing already\n lived. It used to be inline here because the grid rendered inside <mp-datatable>'s\n shadow root, where neither this component's scoped rules nor Bootstrap's arrived;\n ng-bootstrap 22.18.0 moved the datatable to the light DOM, so the class reaches it. -->\n <span class=\"spark-color-swatch d-inline-block align-middle border rounded\"\n [style.background-color]=\"colorVal\"></span>\n }\n} @else if (column().dataType === 'image') {\n @if (display(); as src) {\n <!-- `alt` is empty on purpose — the cell is decorative next to the row's own labelled\n columns, and a filename read aloud is worse than nothing. -->\n <img [src]=\"src\" alt=\"\" loading=\"lazy\" class=\"spark-grid-image align-middle rounded\">\n }\n} @else if (column().dataType === 'url') {\n @if (display(); as href) {\n <!-- rel is not optional with target=_blank: without noopener the opened page gets a handle on\n this one through window.opener, and these hrefs are data. -->\n <a [href]=\"href\" target=\"_blank\" rel=\"noopener noreferrer\">{{ href }}</a>\n }\n} @else if (chips().length) {\n <span class=\"d-inline-flex flex-wrap gap-1\">\n @for (chip of chips(); track chip.id) {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n </span>\n} @else if (link(); as route) {\n <a [routerLink]=\"route\">{{ display() }}</a>\n} @else {\n {{ display() }}\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, inject, input, model, output, signal, untracked, Type } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { RouterModule } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsDatatableFilterPanelDirective, BsRowTemplateDirective, DatatableSettings, type BsDatatableFetch, type DatatableDistincts, type FilterChangeDetail } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SparkQueryRefreshService } from '@mintplayer/ng-spark/client-operations';\nimport { cellValue } from '@mintplayer/ng-spark/renderers';\nimport { QueryCellValuePipe, QueryReferenceChipsPipe, ResolveTranslationPipe, TranslateKeyPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkLanguageService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkColumnFilterPanelComponent } from '@mintplayer/ng-spark/column-filter';\nimport {\n CustomActionDefinition,\n EntityAttributeDefinition,\n EntityType,\n LookupReference,\n QueryColumn,\n QueryColumnFilter,\n QueryResultItem,\n SparkQuery,\n filterQueryActions,\n parseSelectionRule,\n selectionModeFor,\n valueFor,\n} from '@mintplayer/ng-spark/models';\nimport { SPARK_GRID_PAGE_SIZES, initialGridSettings, isVirtualScrollingQuery } from './spark-grid-columns';\nimport { SparkGridRenderers } from './spark-grid-renderers';\nimport { SparkGridCellComponent } from './spark-grid-cell.component';\n\n/**\n * The one Spark grid: a `<bs-datatable>` rendering a query or a sub-query.\n *\n * This replaces two components that were the same grid written twice — and, counting\n * `spark-query-list`'s streaming and paged branches, the `<bs-datatable>` element itself written\n * three times. The duplication had already produced four user-visible bugs, each fixed on one\n * side and not the other: `[indeterminate]` on null booleans, permission state surviving a failed\n * reload, a swallowed fetch failure, and virtual-scroll sizing. `SparkGridRenderers` was\n * extracted to stop that and could only take the stateless helpers with it; this takes the rest.\n *\n * ## What it deliberately does NOT own: streaming\n *\n * `spark-grid-renderers.ts` records why — a websocket dependency graph must not reach the bundle\n * of every PO detail page, none of which stream. So rows can come from **either** side:\n *\n * - unbound `data` — the grid fetches for itself, paging and sorting server-side;\n * - bound `data` — the grid renders what it is given and never fetches.\n *\n * `spark-query-list` keeps the socket and feeds its snapshot in. This is the line `bs-datatable`\n * itself draws: `[data]` and `[fetch]` are mutually exclusive, and binding both makes `[fetch]`\n * win silently.\n *\n * ## Reading its state\n *\n * `query`, `entityType`, `customActions`, `selection`, `canRead`, `canCreate` and `resultCount`\n * are readable so a host can render chrome around the grid. Read them through a template\n * reference variable — `<spark-query-grid #grid>` … `grid.query()` — not `viewChild`: hosts wrap\n * grids in `@if`, where a `viewChild` is intermittently undefined.\n */\n@Component({\n selector: 'spark-query-grid',\n imports: [CommonModule, RouterModule, BsAlertComponent, BsDatatableComponent, BsDatatableColumnDirective, BsDatatableFilterPanelDirective, BsRowTemplateDirective, BsSpinnerComponent, SparkGridCellComponent, ResolveTranslationPipe, QueryCellValuePipe, QueryReferenceChipsPipe, TranslateKeyPipe, SparkAttributeDescriptionComponent, SparkColumnFilterPanelComponent],\n templateUrl: './spark-query-grid.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkQueryGridComponent {\n private readonly sparkService = inject(SparkService);\n private readonly gridRenderers = inject(SparkGridRenderers);\n private readonly queryRefresh = inject(SparkQueryRefreshService);\n readonly lang = inject(SparkLanguageService);\n\n /** Query alias or id. */\n queryId = input.required<string>();\n\n /**\n * The parent persistent object this query is scoped to, when it has one.\n *\n * Optional, because not every query is a detail of something: a page can host a grid that\n * stands on its own — \"my accounts\", a dashboard list — and the server already treats an absent\n * parent as \"no parent\" rather than as an error. Requiring these made that shape impossible to\n * express: the component simply never loaded, with no request, no error and no log.\n *\n * Pass both or neither. One without the other is ignored, matching `SparkService.executeQuery`,\n * which omits either param when it is falsy, and the execute endpoint, which resolves a parent\n * only when both are present.\n */\n parentId = input<string>('');\n parentType = input<string>('');\n\n /**\n * Rows supplied from outside. When bound, the grid renders these and runs no fetch.\n *\n * This is how streaming stays out of this component (see the class comment). `null` — the\n * default — means \"fetch for yourself\"; an empty array means \"you have been given no rows\",\n * which is a different statement and renders an empty grid rather than triggering a load.\n */\n data = input<QueryResultItem[] | null>(null);\n\n /**\n * Server-side search term, passed through to the query.\n *\n * An input rather than a box, so the host owns the control and the grid owns the request. A\n * host with client-side rows filters them itself before binding `data`.\n */\n search = input<string>('');\n\n /**\n * Column metadata for externally supplied rows. Required whenever `data` is bound, because a\n * projection cannot describe itself — there is no per-row metadata left to infer it from.\n */\n columns = input<QueryColumn[] | null>(null);\n\n /**\n * Change this to re-run the query. Any value works; only its identity matters.\n *\n * A declarative token rather than only a `reload()` method, because calling a method means\n * holding a component handle, and hosts wrap this grid in `@if`, where a `viewChild` is\n * intermittently undefined.\n *\n * This drives the CHEAP refresh (see {@link reload}). It deliberately does not feed the main\n * effect: re-running `loadData` would re-resolve the query, the entity types, the permissions\n * and the lookups, and reset the user's page and sort on every button press.\n */\n reloadToken = input<unknown>(null);\n\n /**\n * Paging and sorting. Two-way, so a host driving `data` can sort those rows by whatever the\n * user actually clicked — the datatable writes the new sort back through here.\n */\n settings = model(new DatatableSettings({\n perPage: { values: SPARK_GRID_PAGE_SIZES, selected: SPARK_GRID_PAGE_SIZES[0] },\n page: { values: [1], selected: 1 },\n sortColumns: [],\n }));\n\n /** Rows the user has ticked. Two-way so chrome outside the grid can read and clear them. */\n selection = model<QueryResultItem[]>([]);\n\n /**\n * Replaces where the first column's link points, per row.\n *\n * Return `null` to suppress the link for that row — useful when a grid mixes rows that have a\n * detail page with rows that do not.\n *\n * ⚠️ **It changes the destination, not the permission.** `canRead()` still gates whether any\n * link is rendered at all, and this function is never consulted when that gate is closed. It\n * cannot be used to reach a row the rights model withheld; it exists for the case where the\n * row's natural destination is not `/po/{type}/{id}` — a composed row that maps to a page of\n * its own, or a grid embedded in a route that owns its own child paths.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n /** Emitted whenever a load or a page fetch fails, for a host in bespoke chrome. */\n error = output<HttpErrorResponse>();\n rowClicked = output<QueryResultItem>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n colors = Color;\n\n /**\n * Where the first column's link points for this row: the host's `rowRoute` when it supplied one,\n * otherwise the row's own detail page.\n *\n * A method rather than a computed because the answer is per row, and the template already\n * iterates rows — a computed would have to be a map keyed by id, rebuilt on every page.\n */\n protected rowRouteFor(row: QueryResultItem): unknown[] | null {\n const custom = this.rowRoute();\n if (custom) return custom(row);\n\n const type = this.entityType();\n\n // ⚠️ A composed type has no per-row detail page by construction: its rows are computed, and\n // `{Name}Actions.OnLoadAsync` serves ONE page which is free to ignore the id it was given. So\n // /po/{type}/{rowId} does not 404 — it silently renders that same page, which is worse, because\n // nothing looks wrong. This branch is live in DemoApp today: Read/StartPage implies\n // Query/StartPage, so the composed grid renders with canRead() true and every row linked.\n //\n // `clrType` is absent exactly for those types and is already on the wire; the grid simply never\n // consulted it. A host whose composed rows DO have a page says so with `rowRoute`.\n if (!type?.clrType) return null;\n\n return ['/po', type.alias || type.id, row.id];\n }\n\n query = signal<SparkQuery | null>(null);\n entityType = signal<EntityType | null>(null);\n allEntityTypes = signal<EntityType[]>([]);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n loading = signal(true);\n canRead = signal(false);\n canCreate = signal(false);\n resultCount = signal<number | null>(null);\n customActions = signal<CustomActionDefinition[]>([]);\n\n /** Names the server withheld for this RESULT; see QueryResult.disabledActions. */\n private readonly disabledActions = signal<string[]>([]);\n\n /**\n * The actions actually offered for this result.\n *\n * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE and is never told what an\n * execution returned, so an action that applies to only some results cannot be filtered there.\n * A custom query withholds what does not apply via `CustomQueryArgs.DisableActions(...)`, and\n * the answer arrives on the result.\n *\n * An affordance, not a permission -- the action handler still refuses on its own terms.\n */\n visibleCustomActions = computed(() => {\n const withheld = this.disabledActions();\n if (!withheld.length) return this.customActions();\n\n const lowered = new Set(withheld.map(name => name.toLowerCase()));\n return this.customActions().filter(action => !lowered.has(action.name.toLowerCase()));\n });\n fetchFn = signal<BsDatatableFetch<QueryResultItem> | null>(null);\n\n /**\n * Why the component renders its own failure instead of only reporting one.\n *\n * `SparkService` is a bare `firstValueFrom` passthrough with no interceptor, so every failure\n * surfaces here and nowhere else. A host embedding this grid cannot surface what it never sees,\n * and the default has to be visible with no host cooperation — hence a rendered alert, not just\n * an output.\n */\n errorMessage = signal<string | null>(null);\n\n /** 'none' unless an action is selection-gated, so unaffected grids gain no checkbox column. */\n selectionMode = computed(() => selectionModeFor(this.customActions()));\n\n isVirtualScrolling = computed(() => isVirtualScrollingQuery(this.query()));\n\n /**\n * Columns as sent with the most recent fetched page.\n *\n * A host binding `data` (streaming) supplies its own via the `columns` input instead — the\n * snapshot carries them once, exactly as a paged result does.\n */\n private readonly fetchedColumns = signal<QueryColumn[]>([]);\n\n /** Every column on the wire, drawn or not — what renderers and lookups resolve against. */\n allColumns = computed(() => this.columns() ?? this.fetchedColumns());\n\n /**\n * The columns the grid draws. `isVisible: false` ships a value without a column, so a renderer\n * can read a sibling the grid does not show; undefined means visible, so a server that predates\n * the field still draws everything.\n */\n visibleColumns = computed(() => this.allColumns().filter(c => c.isVisible !== false));\n\n /**\n * True when rows do not come from this component's own fetch.\n *\n * Either because the host bound `data`, or because the query streams — a streaming query's rows\n * arrive over a socket and `/execute` is not a way to get them. The grid recognises the second\n * case itself rather than waiting to be told, because it resolves the query before the host can\n * see it: left to `data` alone, every streaming grid would fire one pointless fetch on mount,\n * before the host had anything to bind.\n */\n hasExternalData = computed(() =>\n this.data() !== null || this.query()?.isStreamingQuery === true);\n\n /** Whether an action's selection rule is satisfied right now. The server checks it again. */\n isActionEnabled(action: CustomActionDefinition): boolean {\n return parseSelectionRule(action.selectionRule)(this.selection().map(r => r.id).length);\n }\n\n constructor() {\n effect(() => {\n const qId = this.queryId();\n const pId = this.parentId();\n const pType = this.parentType();\n // Only the query id is required. Requiring a parent here is what made a standalone grid\n // silently render nothing.\n if (qId) {\n this.loadData(qId, pId, pType);\n } else {\n // No query id at all. `loading` starts true, so without this the component would spin\n // forever instead of saying anything.\n this.loading.set(false);\n }\n });\n\n // Separate effect, so the token drives the cheap refresh and never the full metadata reload.\n // `first` skips the initial run: the effect above has already fetched, and reacting to the\n // token's starting value would double-fetch on mount.\n let first = true;\n effect(() => {\n // Both the host's token and the server's refreshQuery drive the same cheap refresh.\n this.reloadToken();\n this.queryRefresh.tokenFor(this.queryId());\n if (first) { first = false; return; }\n untracked(() => this.reload());\n });\n\n // A new search term must refetch even when page, perPage and sort are unchanged — the\n // datatable dedupes reloads by exactly that triple, and a new fetch identity is what resets\n // its dedupe key. Skipping the first run keeps mount to a single request.\n let firstSearch = true;\n effect(() => {\n this.search();\n if (firstSearch) { firstSearch = false; return; }\n untracked(() => this.onSearchChanged());\n });\n }\n\n /**\n * Re-run the query, keeping the current page, sort and scroll position.\n *\n * Data-level on purpose: it re-seeds the fetch closure and nothing else. For a definition\n * change — new columns, a renamed query — the inputs themselves must change; that is the\n * expensive path. Inert when rows come from the host: there is nothing here to re-run.\n */\n reload(): void {\n if (this.hasExternalData()) return;\n const q = this.query();\n if (q) this.fetchFn.set(this.makeFetch(q, this.parentId(), this.parentType()));\n }\n\n private onSearchChanged(): void {\n if (this.hasExternalData()) return;\n const s = this.settings();\n // Back to page 1: the old page number means nothing against a different result set.\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n this.reload();\n }\n\n /**\n * Replaces the fetched columns only when they actually differ.\n *\n * Columns ship **once per result**, so every page fetch returns an identical set. Assigning it\n * unconditionally wrote a new array of new objects into the signal each time, re-rendering every\n * header and re-evaluating every `*bsDatatableColumn` input for no change in content.\n *\n * Compared by serialized value rather than by reference: the objects are freshly deserialized from\n * JSON on every response, so reference equality is always false and would defeat the check. A\n * dozen small objects per fetch is nothing beside the request that produced them.\n *\n * ⚠️ This was written believing it fixed a panel-teardown bug (#431 F7). Measured in a browser: it\n * does not. The columns were already identical on every fetch, so the guard hits every time and the\n * teardown had another cause entirely. Kept because avoiding a pointless re-render of every header\n * on every page is worth having on its own — but it is an optimization, not a fix, and the F7\n * question remains open.\n */\n private setFetchedColumns(columns: QueryColumn[]): void {\n const current = this.fetchedColumns();\n if (current.length === columns.length && JSON.stringify(current) === JSON.stringify(columns)) return;\n\n this.fetchedColumns.set(columns);\n }\n\n /**\n * The per-column filters currently applied (#431).\n *\n * Read inside `makeFetch`'s closure, like `search`, so a change refetches by producing a new fetch\n * identity rather than by being captured.\n */\n private readonly filters = signal<QueryColumnFilter[]>([]);\n\n /**\n * The value source behind every column's panel.\n *\n * ⚠️ **Referentially stable, and that is the whole point.** It reads `filters()` when the datatable\n * *calls* it, not when it is built — so it always sees the current filters while never changing\n * identity.\n *\n * This was a `computed` first, on the reasoning that it had to close over the current filters\n * because `DistinctsRequest` carries only `{ column, search, signal }`. That was wrong, and the way\n * it was wrong is worth keeping: a new identity reassigns `[distincts]` on the element, and the\n * component aborts the in-flight request and bumps a generation counter — so a response already on\n * its way is discarded as stale and the list renders empty over a perfectly good answer.\n *\n * Reading the signal at call time removes the race entirely and is simpler besides.\n *\n * The asked-for column's own filter is excluded: a panel must offer the values you could still\n * pick, not only the ones you already picked.\n */\n /**\n * The last complete distinct list seen per column, for translating a `< none >` selection.\n *\n * ⚠️ Only stored when the server reported the list was NOT truncated (`hasMore === false`). The\n * translation below is the complement of what is listed, so a partial list would exclude the wrong\n * set — and silently, which is the failure mode this whole feature exists to remove.\n */\n private readonly completeDistincts = new Map<string, unknown[]>();\n\n protected readonly distinctsFn: DatatableDistincts = async request => {\n const queryId = this.query()?.id;\n if (!queryId) return null;\n\n // Read at call time — never captured.\n const others = this.filters().filter(f => f.name !== request.column);\n\n // The server's shape IS the datatable's shape, so there is nothing to map.\n const result = await this.sparkService.getDistinctValues(queryId, request.column, {\n search: request.search,\n columns: others,\n parentId: this.parentId(),\n parentType: this.parentType(),\n });\n\n // A searched list is a subset by construction, so it is never a basis for a complement.\n if (result && !result.hasMore && !request.search)\n this.completeDistincts.set(request.column, result.matching.map(v => v.value));\n else\n this.completeDistincts.delete(request.column);\n\n return result;\n };\n\n /**\n * Translates one filter change into the wire shape and refetches.\n *\n * The event always arrives as the `'values'` shape because the panel only ever calls\n * `ctx.apply(...)`; comparison mode is never declared. `inverse` is a flag on the event and two\n * arrays on the wire — the translation is this method's whole job.\n */\n protected onFilterChange(detail: FilterChangeDetail): void {\n if (detail.mode !== 'values') return;\n\n const values = detail.selected.map(v => v.value);\n const next = this.filters().filter(f => f.name !== detail.column);\n\n if (values.length > 0) {\n // ⚠️ `< none >` cannot be asked for directly. The server compares against the index term, and\n // RavenDB gives a field the document never wrote NO term at all — so `Col == null` matches a\n // field written as JSON null but NOT one that is absent, while `Col != null` puts the absent row\n // with the rows that HAVE a value. Both rows render blank, the panel offers one entry for the\n // pair, and no predicate selects that pair. Measured in AbsentVersusNullFieldTests.\n //\n // The complement is expressible, though: \"none of the real values\" catches absent and null\n // alike. So a selection containing the null entry is sent as an exclusion of everything NOT\n // selected, which is exactly the same set and needs no server change.\n const complete = this.completeDistincts.get(detail.column);\n const selectsNone = values.some(v => v === null || v === undefined);\n\n if (selectsNone && !detail.inverse && complete) {\n const unselected = complete.filter(v =>\n v !== null && v !== undefined && !values.some(s => s === v));\n\n // Everything is selected, so the filter narrows nothing — drop it rather than sending an\n // empty exclusion, which would read as \"exclude nothing\" only by accident.\n if (unselected.length > 0)\n next.push({ name: detail.column, excludes: unselected });\n } else {\n next.push(detail.inverse\n ? { name: detail.column, excludes: values }\n : { name: detail.column, includes: values });\n }\n }\n\n this.filters.set(next);\n this.onFilterChanged();\n }\n\n /**\n * Back to page 1 and a fresh fetch identity, exactly as a new search term does.\n *\n * Both halves matter: the old page number means nothing against a different result set, and the\n * datatable dedupes reloads by `(page, perPage, sort)` — none of which a filter changes — so\n * without a new fetch identity the request is silently never made.\n */\n private onFilterChanged(): void {\n if (this.hasExternalData()) return;\n const s = this.settings();\n this.settings.set(new DatatableSettings({\n perPage: { values: s.perPage.values, selected: s.perPage.selected },\n page: { values: [1], selected: 1 },\n sortColumns: s.sortColumns,\n }));\n this.reload();\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n if (action.confirmationMessageKey) {\n const message = this.lang.t(action.confirmationMessageKey) || 'Are you sure?';\n if (!confirm(message)) return;\n }\n try {\n // The sub-query's container travels with the action (#327). Without it an action invoked\n // from a company's Cars tab could not tell it was on a company's page at all — the grid knew\n // (it filters the rows by that parent) and simply dropped the fact on the way out.\n //\n // Sent as parentId/parentType, NOT as the `parent` argument: that one means an object of this\n // action's OWN type and is loaded under it server-side, so handing it a Company id for a Car\n // action refuses — correctly, and confusingly.\n const pId = this.parentId();\n const pType = this.parentType();\n await this.sparkService.executeCustomAction(\n this.entityType()!.id,\n action.name,\n undefined,\n this.selection().map(r => r.id),\n pId && pType ? { id: pId, type: pType } : undefined,\n // The query too: the server re-runs it narrowed to these ids, so the action receives the\n // rows this grid rendered rather than a re-derivation from documents.\n this.query()?.id);\n this.customActionExecuted.emit({ action });\n if (action.refreshOnCompleted) this.reload();\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || this.lang.t('common.actionFailed') || 'Action failed');\n }\n }\n\n private reportError(err: HttpErrorResponse): void {\n this.errorMessage.set(this.describe(err));\n this.error.emit(err);\n }\n\n /**\n * A 404 is deliberately generic.\n *\n * `Endpoints/Queries/Get.cs` answers 404 with byte-identical bodies for \"no such query\" and\n * \"you may not see it\", so existence is not disclosed (security audit M-3). The component\n * therefore genuinely cannot tell them apart, and both \"Not found\" and \"Access denied\" would be\n * a guess — one of them leaking, the other misleading.\n */\n private describe(err: HttpErrorResponse): string {\n if (err?.status === 404) {\n return this.lang.t('spark.query.unavailable') || 'This list is not available.';\n }\n return err?.error?.error || err?.message\n || this.lang.t('common.unexpectedError') || 'An unexpected error occurred';\n }\n\n private async loadData(queryId: string, parentId: string, parentType: string): Promise<void> {\n this.loading.set(true);\n this.errorMessage.set(null);\n this.fetchFn.set(null);\n // Reset everything derived from the previous query, not just the fetch. Leaving\n // `entityType`/`canRead` behind let a failed reload build a row link out of the PREVIOUS type\n // and the previous permission.\n this.query.set(null);\n this.entityType.set(null);\n this.canRead.set(false);\n this.canCreate.set(false);\n this.customActions.set([]);\n this.resultCount.set(null);\n // Ids from the previous query are meaningless against the next one, and would be POSTed as\n // though they belonged to it.\n this.selection.set([]);\n // A filter belongs to the query it was applied to. Surviving a query switch would POST it\n // against the next query, where the column may not exist or may mean something else entirely.\n this.filters.set([]);\n try {\n const [resolvedQuery, entityTypes] = await Promise.all([\n this.sparkService.getQuery(queryId),\n this.sparkService.getEntityTypes(),\n ]);\n\n this.query.set(resolvedQuery);\n this.allEntityTypes.set(entityTypes);\n\n const et = this.resolveEntityType(resolvedQuery, entityTypes);\n this.entityType.set(et);\n if (et) {\n const [permissions, actions] = await Promise.all([\n this.sparkService.getPermissions(et.id),\n this.sparkService.getCustomActions(et.id),\n ]);\n this.canRead.set(permissions.canRead);\n this.canCreate.set(permissions.canCreate);\n // 'query', not 'list'. The server model has always documented \"detail\" | \"query\" |\n // \"both\"; a filter testing for a value nothing emits renders the action NOWHERE.\n this.customActions.set(filterQueryActions(actions));\n }\n\n this.settings.set(initialGridSettings(resolvedQuery));\n // The datatable drives paging/sorting via [(settings)] and calls fetchFn per page. Virtual\n // scrolling is just the [virtualScroll] template flag.\n // `resolvedQuery`, not `hasExternalData()`: the query signal is set above, but reading the\n // computed here would depend on signal-read ordering inside an async method. Ask the value\n // directly.\n if (this.data() === null && !resolvedQuery?.isStreamingQuery) {\n this.fetchFn.set(this.makeFetch(resolvedQuery, parentId, parentType));\n }\n\n this.loadLookupReferenceOptions();\n } catch (e) {\n this.fetchFn.set(null);\n this.reportError(e as HttpErrorResponse);\n } finally {\n this.loading.set(false);\n }\n }\n\n /**\n * The query's entity type.\n *\n * The source-name fallback is not optional garnish: a `Database.*` query need not declare\n * `entityType`, and the sub-query grid — which matched on the declared name alone — rendered\n * those as an empty card with no columns, no rows and no error, while the identical query\n * rendered correctly as a page. Both grids are this one now, so there is one answer.\n */\n private resolveEntityType(query: SparkQuery | null, entityTypes: EntityType[]): EntityType | null {\n if (!query) return null;\n\n if (query.entityType) {\n return entityTypes.find(t =>\n t.name === query.entityType || t.alias === query.entityType?.toLowerCase()) ?? null;\n }\n\n const sourceName = extractSourceName(query.source);\n const singular = singularize(sourceName);\n // `name + 's'` because the \"-es\" rule over-strips (Vehicles -> \"Vehicl\", Roles -> \"Rol\"); the\n // page resolver in spark-query-list has always matched this way.\n return entityTypes.find(t =>\n t.name === sourceName || t.name === singular || t.name + 's' === sourceName || t.clrType?.endsWith(singular)) ?? null;\n }\n\n private makeFetch(query: SparkQuery, parentId: string, parentType: string): BsDatatableFetch<QueryResultItem> {\n return (req) => this.sparkService.executeQuery(query.id, {\n sortColumns: req.sortColumns,\n skip: (req.page - 1) * req.perPage,\n take: req.perPage,\n search: this.search() || undefined,\n // Read inside the closure, exactly as `search` is: what forces a refetch is the fetch\n // identity `reload()` creates, not the value captured when this closure was built.\n columns: this.filters(),\n parentId, parentType,\n }).then(r => {\n this.errorMessage.set(null);\n this.resultCount.set(r.totalItems);\n // Columns come from the result now, not from the entity type: the server decides the query\n // surface (ShowedOn.Query) and is the same place the sort-column allow-list is checked.\n // ?? [] because a malformed or older response must render an empty grid, not throw inside a\n // computed — where the stack points at the column filter and not at the response that lacked them.\n this.setFetchedColumns(r.columns ?? []);\n // Per-result, so it is re-read on every page rather than latched from the first.\n this.disabledActions.set(r.disabledActions ?? []);\n return {\n data: r.items,\n totalRecords: r.totalItems,\n totalPages: Math.ceil(r.totalItems / req.perPage) || 1,\n perPage: req.perPage,\n page: req.page,\n };\n }).catch((e: HttpErrorResponse) => {\n // Report before returning the empty page, or a failed fetch is indistinguishable from a\n // query that legitimately has no rows.\n this.reportError(e);\n this.resultCount.set(0);\n return { data: [], totalRecords: 0, totalPages: 1, perPage: req.perPage, page: req.page };\n });\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n // Over ALL columns, not just the drawn ones: an undrawn column's value can still be rendered\n // by a renderer reading it as a sibling, and it would resolve to a raw key without its options.\n this.lookupReferenceOptions.set(await this.gridRenderers.loadLookupOptions(this.allColumns()));\n }\n\n /**\n * The underlying value a custom renderer receives, which is not the printable one.\n *\n * A projection is flat, so this is the cell's own value rather than a display string. It is\n * deliberately not the attribute-shaped `rendererValue`: a query row has no nested object to\n * fall back to, and reaching for one would hand every renderer `undefined`.\n */\n rendererValueFor(item: QueryResultItem, column: QueryColumn): unknown {\n return cellValue(valueFor(item, column.name));\n }\n\n getColumnRendererComponent(column: QueryColumn): Type<any> | null {\n return this.gridRenderers.columnComponentFor(column);\n }\n\n /** The renderer's optional filter-label override for this column, or null for the server text. */\n filterLabelFor(column: QueryColumn): ((value: unknown) => string) | null {\n return this.gridRenderers.filterLabelFor(column);\n }\n\n getColumnRendererInputs(component: Type<any>, item: QueryResultItem, column: QueryColumn): Record<string, any> {\n return this.gridRenderers.columnInputsFor(component, item, column);\n }\n}\n\nfunction extractSourceName(source: string): string {\n const dotIndex = source.indexOf('.');\n return dotIndex >= 0 ? source.substring(dotIndex + 1) : source;\n}\n\n/**\n * Enough English to map a query source onto a type name — `Cars` to `Car`, `People` to `Person`.\n *\n * Deliberately a small table plus three suffix rules rather than a library: it runs against type\n * names the application itself chose, and a wrong guess costs nothing, since the caller falls\n * through to matching the plural and the CLR type name.\n */\nfunction singularize(plural: string): string {\n const irregulars: Record<string, string> = {\n 'People': 'Person',\n 'Children': 'Child',\n 'Men': 'Man',\n 'Women': 'Woman',\n };\n if (irregulars[plural]) return irregulars[plural];\n if (plural.endsWith('ies')) return plural.slice(0, -3) + 'y';\n if (plural.endsWith('es')) return plural.slice(0, -2);\n if (plural.endsWith('s')) return plural.slice(0, -1);\n return plural;\n}\n","<!--\n Three explicit states, in this order, and the order is load-bearing.\n\n This template used to open with `@if (query(); as q)` wrapping EVERYTHING, and `query()` is only\n set after loadData's awaits resolve. Three separate bugs fell out of that one line: the spinner\n was unreachable on a first load (it rendered inside the gate, while `loading` starts true and\n `query` starts null), a first-load failure rendered ZERO DOM — no card, no message, nothing to\n see or report — and a failed RELOAD left the previous query's rows on screen.\n\n So: `loading` is checked first and outside any dependency on `query`, and the final `@else`\n always renders something. Do not fold these back into one gate, and do not move the spinner\n inside the `query()` branch.\n-->\n@if (loading()) {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n} @else if (query()) {\n <!-- A page-fetch failure while the grid is already resolved. Without this the catch returns an\n empty page and a failed query is indistinguishable from one that legitimately has no rows. -->\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n }\n\n <!--\n One datatable, two row sources. `[data]` and `[fetch]` are mutually exclusive in\n ng-bootstrap — binding both makes `[fetch]` win silently — so exactly one is ever non-null\n here: `fetchFn` is left null whenever `data` is bound. See the class comment for why rows can\n arrive from outside at all (streaming must not put a websocket in every detail page's bundle).\n -->\n <bs-datatable\n [selectionMode]=\"selectionMode()\"\n [(selection)]=\"selection\"\n [virtualScroll]=\"isVirtualScrolling()\"\n [itemSize]=\"40\"\n [isResponsive]=\"isVirtualScrolling()\"\n [data]=\"data()\"\n [fetch]=\"fetchFn()\"\n [distincts]=\"distinctsFn\"\n (filterChange)=\"onFilterChange($event)\"\n [(settings)]=\"settings\">\n @for (col of visibleColumns(); track col.name) {\n <!--\n `canSort`/`canFilter` arrive already resolved by the server (query override → attribute →\n true), so this only spells the polarity. `!== false` is needed for BOTH, for opposite\n reasons: absent means capable on our wire, while the datatable's own `filterable` defaults\n to false, unlike `sortable`.\n\n ⚠️ `filterActive` and `filterSummary` are deliberately NOT bound. Any *bsDatatableColumn\n input change recomputes the column set, which retires the live filter view — while the web\n component's mount-once guard leaves the panel's nodes in place and never re-renders them.\n Binding them to state our own panel mutates would freeze the panel the instant a value is\n ticked, until it is closed and reopened. The cost is that a filtered column carries no\n visual marker yet, which is worth far less than a panel that dies on first use. See #431 F7.\n -->\n <div *bsDatatableColumn=\"col.name; sortable: col.canSort !== false; filterable: col.canFilter !== false\">\n {{ (col.label | resolveTranslation) || col.name }}\n <spark-attribute-description [description]=\"col.description\" />\n\n @if (col.canFilter !== false) {\n <ng-container *bsDatatableFilterPanel=\"let values; let ctx = ctx\">\n <spark-column-filter-panel\n [column]=\"col\"\n [values]=\"values()\"\n [ctx]=\"ctx\"\n [filterLabel]=\"filterLabelFor(col)\" />\n </ng-container>\n }\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @let row = $any(item);\n @for (col of visibleColumns(); track col.name; let first = $first) {\n <td>\n @if (row) {\n <!-- The first column links to the row's detail page. Two things about this are\n routinely misread:\n\n 1. `cellContent` is projected INSIDE the anchor, so a custom `renderer` does NOT\n suppress this link — a renderer that emits its own <a> produces nested anchors\n (invalid HTML) rather than replacing this one.\n 2. `canRead()` is the whole gate, and it is the rights model: `Query` without\n `Read` lists the rows and withholds the link. Withhold `Read` for a query\n whose rows no detail page can load — a Custom.* query that fabricates rows in\n memory, say.\n 3. `rowRoute` replaces the DESTINATION only, never the gate: returning a route\n from it cannot make a row clickable that `canRead()` withheld, and returning\n null from it suppresses the link for that one row. -->\n @if (first && canRead() && rowRouteFor(row); as route) {\n <a [routerLink]=\"route\"\n (click)=\"rowClicked.emit(row)\">\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, col: col }\"></ng-container>\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"cellContent; context: { $implicit: row, col: col }\"></ng-container>\n }\n } @else {\n \n }\n </td>\n }\n </ng-container>\n </bs-datatable>\n} @else {\n <!-- The query could not be resolved. Rendering something keeps the host's layout intact and,\n crucially, makes the failure visible: this branch used to render nothing at all. -->\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">\n {{ errorMessage() || ('spark.query.unavailable' | t) }}\n </bs-alert>\n}\n\n<ng-template #cellContent let-item let-col=\"col\">\n <!--\n Presentation lives in <spark-grid-cell>, shared with the AsDetail table on the PO detail page.\n Value resolution stays here: a query row is a PersistentObject with an attribute list, and the\n AsDetail table's rows are plain dictionaries, so only the rendering is common.\n\n `link` is deliberately not passed. The first-column link is the grid's own rule and wraps this\n template from outside; setting both would nest anchors.\n -->\n <spark-grid-cell\n [column]=\"col\"\n [display]=\"(col | queryCellValue:item:lookupReferenceOptions())\"\n [rendererValue]=\"rendererValueFor(item, col)\"\n [item]=\"item\"\n [chips]=\"(col | queryReferenceChips:item)\" />\n</ng-template>\n","import { ChangeDetectionStrategy, Component, computed, contentChildren, input, output, viewChild, TemplateRef } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { CustomActionDefinition, PersistentObject, QueryResultItem } from '@mintplayer/ng-spark/models';\nimport { inject } from '@angular/core';\nimport { SparkQueryGridComponent } from './spark-query-grid.component';\nimport {\n SparkQueryActionsDirective,\n SparkQueryCaptionDirective,\n SparkQueryIconDirective,\n slotFor,\n} from './spark-query-slots';\n\n/**\n * A `<bs-card>` around a {@link SparkQueryGridComponent}: icon, caption, actions, grid.\n *\n * ## Chrome is overridden, not replaced\n *\n * Every header region has a default, and a host replaces only the ones it supplies. That is what\n * lets this component serve the auto-rendered case: a sub-query is rendered once per entry in\n * `EntityTypeDefinition.Queries` with nobody projecting into it, and it must look right with no\n * host cooperation at all. A slot mechanism that only ever *replaced* chrome would leave that\n * call site blank — which is why an earlier design had the query carry its own chrome from the\n * server. Overriding a default needs no host; replacing one does.\n *\n * ## Two ways in, one set of templates\n *\n * Hand-written markup uses the structural directives and is found with `contentChildren`.\n * The auto-rendered path cannot: `spark-po-detail` is created by the router, so in a default app\n * there is no tag to project into. It therefore forwards `TemplateRef`s as inputs instead — a\n * directive cannot cross a component boundary but its template can, and that forwarding is\n * already the idiom in `spark-po-detail` (`extraActionsTemplate`, `extraContentTemplate`).\n *\n * Content wins over a forwarded input: the closer declaration is the more specific one.\n */\n@Component({\n selector: 'spark-query-card',\n imports: [CommonModule, BsCardComponent, BsCardHeaderComponent, BsPriorityNavComponent, BsPriorityNavItemDirective, SparkQueryGridComponent, ResolveTranslationPipe],\n templateUrl: './spark-query-card.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SparkQueryCardComponent {\n readonly lang = inject(SparkLanguageService);\n\n queryId = input.required<string>();\n parentId = input<string>('');\n parentType = input<string>('');\n reloadToken = input<unknown>(null);\n data = input<QueryResultItem[] | null>(null);\n search = input<string>('');\n\n /**\n * Slots forwarded from a host that cannot project content — see the class comment. A card\n * reached through hand-written markup should use the structural directives instead.\n */\n iconTemplate = input<TemplateRef<any> | null>(null);\n captionTemplate = input<TemplateRef<any> | null>(null);\n actionsTemplate = input<TemplateRef<any> | null>(null);\n\n error = output<HttpErrorResponse>();\n rowClicked = output<QueryResultItem>();\n customActionExecuted = output<{ action: CustomActionDefinition }>();\n\n /**\n * Forwarded to the grid. Without this the escape hatch was unreachable from a card — i.e. from\n * every auto-rendered sub-query and program-unit query page, which is most grids.\n */\n rowRoute = input<((row: QueryResultItem) => unknown[] | null) | null>(null);\n\n colors = Color;\n\n private readonly iconSlots = contentChildren(SparkQueryIconDirective);\n private readonly captionSlots = contentChildren(SparkQueryCaptionDirective);\n private readonly actionSlots = contentChildren(SparkQueryActionsDirective);\n\n /**\n * The grid, read only to surface its state — the query, the actions, the selection — to this\n * component's own header.\n *\n * Optional rather than `required` on purpose. The header renders ABOVE the grid in this\n * template, so on the first change-detection pass the view query has not resolved yet and\n * `viewChild.required()` would throw where a plain one returns `undefined`. Everything below\n * therefore reads it defensively; the signal updates once the view initialises and the header\n * re-renders with the real query.\n *\n * A host reads grid state through a template reference variable instead, because a host may put\n * the grid behind an `@if`, where a view query genuinely is intermittently undefined.\n */\n protected readonly grid = viewChild(SparkQueryGridComponent);\n\n protected readonly query = computed(() => this.grid()?.query() ?? null);\n\n protected readonly iconTpl = computed(() =>\n slotFor(this.iconSlots(), this.query())?.templateRef ?? this.iconTemplate());\n\n protected readonly captionTpl = computed(() =>\n slotFor(this.captionSlots(), this.query())?.templateRef ?? this.captionTemplate());\n\n protected readonly actionsTpl = computed(() =>\n slotFor(this.actionSlots(), this.query())?.templateRef ?? this.actionsTemplate());\n\n /** The caption the card renders unless a slot replaces it. */\n protected readonly caption = computed(() => {\n const q = this.query();\n return (q?.description ? this.lang.resolve(q.description) : '') || q?.name || '';\n });\n\n protected readonly customActions = computed(() => this.grid()?.visibleCustomActions() ?? []);\n protected readonly selection = computed(() => this.grid()?.selection() ?? []);\n}\n","<!--\n The card is always rendered. A host that wants no card uses <spark-query-grid> directly — which\n is what the old `showCard=\"false\"` meant, expressed as a component choice rather than a flag.\n-->\n<bs-card class=\"d-block my-3\">\n <bs-card-header>\n <div class=\"d-flex align-items-center gap-2\">\n @if (iconTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: query() }\"></ng-container>\n }\n\n @if (actionsTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: customActions(), selection: selection(), query: query() }\"></ng-container>\n } @else if (customActions().length) {\n <!-- The default: the actions the server declares for this type. The nav renders only when\n there are any, so a query without actions is pixel-identical to a plain caption.\n\n Disabled, not hidden: hiding makes the affordance undiscoverable and reflows the\n priority nav on every selection change. The server enforces the same rule. -->\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n @for (action of customActions(); track action.name) {\n <button *bsPriorityNavItem=\"10 + action.offset\" class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"!grid()?.isActionEnabled(action)\" (click)=\"grid()?.onCustomAction(action)\">\n {{ action.displayName | resolveTranslation }}\n </button>\n }\n </bs-priority-nav>\n }\n\n <!--\n Caption last, actions first. The auto margin is conditional rather than fixed, and that is\n the point: with actions present `ms-auto` pushes the caption to the trailing edge, and with\n none it falls back to `me-auto` so a plain card looks exactly as it always did. A fixed\n `ms-auto` would have right-aligned the caption of every action-less query card in every app\n — a change nobody asked for, to fix a layout only some cards have.\n -->\n <span [class.ms-auto]=\"customActions().length\" [class.me-auto]=\"!customActions().length\">\n @if (captionTpl(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: { $implicit: caption(), query: query() }\"></ng-container>\n } @else {\n {{ caption() }}\n }\n </span>\n </div>\n </bs-card-header>\n\n <div class=\"p-3\">\n <spark-query-grid\n [queryId]=\"queryId()\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [reloadToken]=\"reloadToken()\"\n [data]=\"data()\"\n [search]=\"search()\"\n [rowRoute]=\"rowRoute()\"\n (error)=\"error.emit($event)\"\n (rowClicked)=\"rowClicked.emit($event)\"\n (customActionExecuted)=\"customActionExecuted.emit($event)\" />\n </div>\n</bs-card>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -809,9 +809,17 @@ function evaluateRule(name, label, rule, value, text) {
|
|
|
809
809
|
return null;
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
|
+
/**
|
|
813
|
+
* Mirrors the server's `TryGetIntValue`: an integer number, or a string `int.TryParse` accepts.
|
|
814
|
+
* Anything else means "no rule". `Number(value)` is NOT that — it turns null and '' into 0 (so every
|
|
815
|
+
* non-empty value failed "must be at most 0 characters") and truncated 2.5, which the server ignores.
|
|
816
|
+
*/
|
|
812
817
|
function toInt(value) {
|
|
813
|
-
|
|
814
|
-
|
|
818
|
+
if (typeof value === 'number')
|
|
819
|
+
return Number.isInteger(value) ? value : null;
|
|
820
|
+
if (typeof value === 'string' && /^\s*[+-]?\d+\s*$/.test(value))
|
|
821
|
+
return parseInt(value, 10);
|
|
822
|
+
return null;
|
|
815
823
|
}
|
|
816
824
|
function toNumber(value) {
|
|
817
825
|
const n = Number(value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-models.mjs","sources":["../../models/src/translated-string.ts","../../models/src/showed-on.ts","../../models/src/entity-type.ts","../../models/src/datetime-local.ts","../../models/src/as-detail-conversions.ts","../../models/src/query-result.ts","../../models/src/lookup-reference.ts","../../models/src/query-actions.ts","../../models/src/selection-rule.ts","../../models/src/selection-mode.ts","../../models/src/refresh-overlay.ts","../../models/src/rule-evaluation.ts","../../models/mintplayer-ng-spark-models.ts"],"sourcesContent":["import { signal, type WritableSignal } from '@angular/core';\n\nexport type TranslatedString = Record<string, string>;\n\n/** Global reactive language state — shared across library boundaries via globalThis */\nexport const currentLanguage: WritableSignal<string> =\n ((globalThis as any).__sparkCurrentLanguage ??= signal('en'));\n\nexport function resolveTranslation(ts: TranslatedString | undefined, lang?: string): string {\n if (!ts) return '';\n const language = lang ?? currentLanguage();\n return ts[language] ?? ts['en'] ?? Object.values(ts)[0] ?? '';\n}\n","/**\n * Flags enum controlling on which pages an attribute should be displayed.\n * Values can be combined: ShowedOn.Query | ShowedOn.PersistentObject\n */\nexport enum ShowedOn {\n Query = 1,\n PersistentObject = 2,\n}\n\n/**\n * Helper function to check if a ShowedOn value includes a specific flag.\n */\nexport function hasShowedOnFlag(value: ShowedOn | string | undefined, flag: ShowedOn): boolean {\n if (value === undefined) return true; // Default: show on all pages\n\n // Handle string values from JSON (e.g., \"Query, PersistentObject\")\n if (typeof value === 'string') {\n const parts = value.split(',').map(s => s.trim());\n const flagName = ShowedOn[flag];\n return parts.includes(flagName);\n }\n\n // Handle numeric flag values\n return (value & flag) === flag;\n}\n","import { ShowedOn } from './showed-on';\nimport { TranslatedString } from './translated-string';\nimport { ValidationRule } from './validation-rule';\n\n/**\n * Controls how a Reference attribute is picked in the PO-edit form.\n * Serialized as a string by the server (mirrors the .NET EReferenceDisplayType).\n */\nexport enum EReferenceDisplayType {\n /** Renders as a `<bs-select>` listing every referenced item. */\n Dropdown = 'Dropdown',\n /** Renders a readonly textbox + \"…\" button that opens a searchable modal grid picker. */\n Modal = 'Modal',\n}\n\nexport interface EntityAttributeDefinition {\n id: string;\n name: string;\n label?: TranslatedString;\n /** Help text rendered as an [i] tooltip beside the label. Absent when the model declares none. */\n description?: TranslatedString;\n dataType: string;\n isRequired: boolean;\n isVisible: boolean;\n isReadOnly: boolean;\n order: number;\n query?: string;\n /** For reference attributes, specifies the target entity type's CLR type name */\n referenceType?: string;\n /** For AsDetail attributes, specifies the nested entity type's CLR type name */\n asDetailType?: string;\n /** When true, the attribute represents an array/collection of AsDetail objects */\n isArray?: boolean;\n /** For array AsDetail attributes: \"modal\" (default) or \"inline\" */\n editMode?: 'inline' | 'modal';\n /**\n * For Reference attributes: 'Modal' renders the \"…\" + modal query-grid picker;\n * 'Dropdown'/absent (default) renders a `<bs-select>`. Hand-set in the model JSON.\n */\n referenceDisplayType?: EReferenceDisplayType;\n /** For array AsDetail attributes: when true, rows can be drag-reordered (order = array position) */\n isSortable?: boolean;\n /**\n * When true, changing this attribute's value posts the in-progress object to\n * `/spark/po/{objectTypeId}/refresh` and applies the reshaped result as an overlay.\n * Schema-only by design — it never travels on a PersistentObjectAttribute, so a client\n * cannot claim a trigger the model did not declare.\n */\n triggersRefresh?: boolean;\n /** For LookupReference attributes, specifies the lookup reference type name */\n lookupReferenceType?: string;\n /**\n * Controls on which pages the attribute should be displayed.\n * Query = shown in list views, PersistentObject = shown in detail/edit views.\n * Can be a numeric flag value or a string like \"Query, PersistentObject\".\n */\n showedOn?: ShowedOn | string;\n rules: ValidationRule[];\n /** References an AttributeGroup.id to assign this attribute to a group */\n group?: string;\n /** Number of grid columns this attribute spans within a tab's column layout */\n columnSpan?: number;\n /** Renderer component name for custom display in detail/list views */\n renderer?: string;\n /** Options passed to the renderer component */\n rendererOptions?: Record<string, any>;\n}\n\nexport interface AttributeTab {\n id: string;\n name: string;\n label?: TranslatedString;\n order: number;\n /** Number of columns for the grid layout within this tab */\n columnCount?: number;\n}\n\nexport interface AttributeGroup {\n id: string;\n name: string;\n label?: TranslatedString;\n /** References an AttributeTab.id to assign this group to a tab */\n tab?: string;\n order: number;\n}\n\nexport interface EntityType {\n id: string;\n name: string;\n description?: TranslatedString;\n /**\n * The backing CLR type, or absent for a JSON-only virtual type (#325).\n *\n * Optional on purpose: the server sends null for a virtual type, and this was declared\n * non-nullable, so any unguarded `.endsWith(...)` on it threw a TypeError naming an innocent\n * query. Types are data from the wire, not a promise about it.\n */\n clrType?: string;\n\n /**\n * Whether this caller may OPEN one of these, as opposed to list them.\n *\n * The catalogue is gated on Query, so its presence here means \"you may list this type\" and says\n * nothing about opening one. A reference must not render as a link when this is false: it used\n * to, and the click refused on arrival.\n */\n canRead?: boolean;\n alias?: string;\n /**\n * Breadcrumb template: literal text plus `{AttributeName}` placeholders. A scalar placeholder\n * renders its value; a reference placeholder renders the referenced entity's breadcrumb.\n * The server resolves this — clients only read the resulting strings. Example: \"{Street}, {City}\".\n */\n breadcrumb?: string;\n /**\n * When false, the breadcrumb needs the collection document (a placeholder field is not on the\n * projection). null/absent means renderable from the projection. Informational on the client.\n */\n breadcrumbProjectionSatisfiable?: boolean;\n tabs?: AttributeTab[];\n groups?: AttributeGroup[];\n attributes: EntityAttributeDefinition[];\n /** Query aliases or IDs to display as related query tables on the detail page. */\n queries?: string[];\n /**\n * The definitions of this type's AsDetail row types, sent alongside it so a detail table can draw\n * its columns.\n *\n * Needed because a row type usually has no rights of its own — a row is edited through its parent\n * — and the entity-type catalogue is gated on `Query`. Resolving a row type from the catalogue\n * therefore failed silently, and the table rendered with **no columns at all**.\n *\n * Per-caller and gated on the *parent's* right, which is the same gate that already ships each\n * row's attribute schema inside `attr.objects`. Pruned server-side: no `queryType`, `indexName`,\n * `queries` or `alias`.\n */\n detailTypes?: EntityType[];\n /**\n * Whether adding or removing a row of this type asks the server first.\n *\n * Absent or false — the default — keeps the purely client-side behaviour: New pushes a blank row,\n * Delete splices it out, and the parent's save is the first the server hears of either. On, the\n * grid calls `newObject` before showing a row and `deleteRow` before removing one, so\n * `OnNewAsync` can default it and the delete hook can refuse.\n *\n * ⚠️ Read it off the **row type**, never the parent: the type that owns the hooks owns the\n * decision. For a row type resolved out of `detailTypes` the flag survives the server-side\n * pruning, so both sources agree.\n *\n * ⚠️ It governs the round trip and nothing else. It is not a permission, and it is outside the\n * model hash, so nothing that gates a write may be conditional on it — the save path enforces the\n * row type's New/Edit/Delete rights on every embedded collection whether this is on or off.\n */\n serverSideRowLifecycle?: boolean;\n}\n","/**\n * Conversions between the wire format for a timestamp and what an `<input type=\"datetime-local\">`\n * or `<input type=\"date\">` will accept.\n *\n * A `DateTimeOffset` in Spark means an **instant**. The wire carries a full ISO-8601 string with an\n * offset (`2026-12-31T23:59:00-08:00`); an HTML date/time control speaks only bare local wall clock\n * (`2026-12-31T23:59`). Neither end can consume the other's format, so both directions need\n * converting, and the browser is deliberately the side that does it: its timezone rules are kept\n * fresh by the operating system, while a container's are frozen at image build time.\n *\n * Assigning a wire value straight to a `datetime-local` input does not throw and does not warn -- the\n * control simply renders **blank**, and saving the untouched form then writes that blank back.\n */\n\nfunction pad(n: number): string {\n return String(n).padStart(2, '0');\n}\n\n/** Formats a UTC offset in minutes as `+HH:mm` / `-HH:mm`. */\nfunction formatOffset(offsetMinutes: number): string {\n const sign = offsetMinutes >= 0 ? '+' : '-';\n const abs = Math.abs(offsetMinutes);\n return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;\n}\n\n/**\n * Parses a wire timestamp. Returns `null` for anything unusable, matching the contract of\n * `parsedDate` / `queryCellValue` -- a bad value renders as empty rather than as `Invalid Date`.\n */\nexport function parseWireDate(value: unknown): Date | null {\n if (value === null || value === undefined || value === '') return null;\n if (value instanceof Date) return isNaN(value.getTime()) ? null : value;\n if (typeof value !== 'string' && typeof value !== 'number') return null;\n\n const parsed = new Date(value);\n return isNaN(parsed.getTime()) ? null : parsed;\n}\n\n/**\n * Wire value -> the `yyyy-MM-ddTHH:mm` a `datetime-local` input needs, in the **viewer's** zone.\n *\n * The conversion is date-sensitive, not a fixed shift: a value is rendered using the zone's offset\n * *on that value's own date*, so a timestamp in July and one in January convert differently in a\n * zone that observes DST.\n */\nexport function toDateTimeLocalInput(value: unknown): string {\n const d = parseWireDate(value);\n if (!d) return '';\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n}\n\n/** Wire value -> the `yyyy-MM-dd` a `date` input needs, in the viewer's zone. */\nexport function toDateInput(value: unknown): string {\n const d = parseWireDate(value);\n if (!d) return '';\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/**\n * `yyyy-MM-ddTHH:mm` from the control -> a complete ISO-8601 string carrying the viewer's offset\n * **for the entered date**.\n *\n * Building the `Date` from its parts rather than parsing the string is what makes this correct: it\n * lets the browser apply its own timezone rules for that date, including DST. Using the *current*\n * offset instead would be wrong for any value outside the present season -- entering a January date\n * during summer would record an offset an hour off, and the stored instant would be an hour wrong.\n *\n * Across a DST discontinuity the browser resolves the wall clock itself, taking the offset in force\n * before a fall-back and after a spring-forward. That is specified ECMAScript behaviour, and the\n * server's conversion helper deliberately reproduces the same choice so the two cannot drift.\n */\nexport function fromDateTimeLocalInput(local: unknown): string | null {\n if (typeof local !== 'string' || local === '') return null;\n\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?/.exec(local);\n if (!match) return null;\n\n const [, y, mo, d, h, mi, s] = match;\n const date = new Date(+y, +mo - 1, +d, +h, +mi, s ? +s : 0);\n if (isNaN(date.getTime())) return null;\n\n // getTimezoneOffset() is minutes to ADD to local to reach UTC, so it is the negation of the offset\n // an ISO string carries.\n return `${y}-${mo}-${d}T${h}:${mi}:${pad(s ? +s : 0)}${formatOffset(-date.getTimezoneOffset())}`;\n}\n\n/** `yyyy-MM-dd` from a `date` control -> an ISO string at local midnight with the viewer's offset. */\nexport function fromDateInput(local: unknown): string | null {\n if (typeof local !== 'string' || local === '') return null;\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(local);\n if (!match) return null;\n return fromDateTimeLocalInput(`${local}T00:00`);\n}\n\n/**\n * Compares two wire timestamps **by instant**, which is what a `DateTimeOffset` means in Spark.\n *\n * Round-tripping a value through a date control legitimately rewrites its text -- a document holding\n * `2026-12-31T23:59:00-08:00` comes back as `2027-01-01T08:59:00+01:00` from a viewer in Brussels --\n * so a string comparison would mark every untouched date as edited and send a needless write on every\n * save. Both of those name the same moment.\n *\n * Note this is the *opposite* of the trap on the server side, where `DateTimeOffset.Equals` compares\n * the instant and silently hides a destroyed offset. Here comparing by instant is the correct thing:\n * we are asking \"did the user change this value\", not \"is the offset intact\".\n */\nexport function wireDatesEqual(a: unknown, b: unknown): boolean {\n const da = parseWireDate(a);\n const db = parseWireDate(b);\n if (da === null || db === null) return da === db;\n return da.getTime() === db.getTime();\n}\n\n/** True for the attribute data types that are edited through a native date/time control. */\nexport function isDateDataType(dataType: string | null | undefined): dataType is 'date' | 'datetime' {\n return dataType === 'date' || dataType === 'datetime';\n}\n\n/** Wire -> control, picking the right shape for the data type. */\nexport function toDateInputValue(dataType: string | null | undefined, value: unknown): string {\n return dataType === 'date' ? toDateInput(value) : toDateTimeLocalInput(value);\n}\n\n/** Control -> wire, picking the right shape for the data type. */\nexport function fromDateInputValue(dataType: string | null | undefined, value: unknown): string | null {\n return dataType === 'date' ? fromDateInput(value) : fromDateTimeLocalInput(value);\n}\n","import { fromDateInputValue, isDateDataType, toDateInputValue, wireDatesEqual } from './datetime-local';\nimport { EntityAttributeDefinition } from './entity-type';\nimport { EntityType } from './entity-type';\nimport { PersistentObject } from './persistent-object';\nimport { PersistentObjectAttribute } from './persistent-object-attribute';\n\n/**\n * Resolves an `EntityType` by its CLR type name (e.g. `\"HR.Entities.Address\"`).\n * Callers typically close over a list they already hold. `getEntityTypes()` is NOT cached --\n * it issues a request per call -- so resolve against a list you fetched once rather than\n * calling it inside the resolver.\n */\nexport type EntityTypeResolver = (clrTypeName: string) => EntityType | undefined;\n\n/**\n * Flattens a nested `PersistentObject` into the plain `Record<string, any>` shape the\n * form state uses throughout ng-spark. Primitive / reference attributes contribute their\n * `value`; nested AsDetail attributes recurse — single becomes an inner dict, array\n * becomes an array of inner dicts. Returns `{}` for `null` / `undefined` input.\n *\n * This is the ONE place that reads the server's new AsDetail wire shape and collapses it\n * back to the flat dict the form components already handle.\n */\nexport function nestedPoToDict(po: PersistentObject | null | undefined): Record<string, any> {\n if (!po) return {};\n const dict: Record<string, any> = {};\n for (const attr of po.attributes ?? []) {\n dict[attr.name] = attributeValueForForm(attr);\n }\n // The object's own server-resolved breadcrumb, kept under a reserved key so the form can label\n // it without re-deriving a template it may be structurally unable to resolve. Safe to carry\n // through save: `dictToNestedPo` walks the entity type's attributes, never the dict's keys, so\n // a reserved key is never sent to the server. Attached only when it resolved, which keeps the\n // common case byte-for-byte identical to the plain flat dict.\n if (typeof po.breadcrumb === 'string' && po.breadcrumb !== '') {\n dict[AS_DETAIL_SELF_BREADCRUMB_KEY] = po.breadcrumb;\n }\n // The row's own key, same reserved-key mechanism and for a sharper reason: without it the key\n // never returns, and a save cannot tell an edited row from a deleted one plus a new one. See\n // AS_DETAIL_ROW_KEY.\n if (typeof po.id === 'string' && po.id !== '') {\n dict[AS_DETAIL_ROW_KEY] = po.id;\n }\n // The wire values of any date cells, kept so the save side can tell an untouched cell from an\n // edited one. Without it, flattening to a wall clock and converting back rewrites the stored\n // offset to the viewer's on every save, even for rows nobody touched.\n const originalDates = originalDatesOf(po);\n if (originalDates) dict[AS_DETAIL_ORIGINAL_DATES_KEY] = originalDates;\n return dict;\n}\n\nfunction originalDatesOf(po: PersistentObject): Record<string, any> | undefined {\n let originals: Record<string, any> | undefined;\n for (const attr of po.attributes ?? []) {\n if (isDateDataType(attr.dataType)) (originals ??= {})[attr.name] = attr.value;\n }\n return originals;\n}\n\nfunction attributeValueForForm(attr: PersistentObjectAttribute): any {\n if (attr.dataType === 'AsDetail') {\n if (attr.isArray) return (attr.objects ?? []).map(po => nestedPoToDict(po));\n return attr.object ? nestedPoToDict(attr.object) : null;\n }\n // An embedded row's date cells are edited through the same native controls as a root object's,\n // so they need the same wire -> wall-clock conversion. This is the FORM path only;\n // `nestedPoToDisplayRow` deliberately keeps the wire value, because the display path formats\n // through `parsedDate` rather than feeding an input.\n if (isDateDataType(attr.dataType)) return toDateInputValue(attr.dataType, attr.value);\n return attr.value;\n}\n\n/**\n * Reserved key under which a flattened nested object keeps the breadcrumb the SERVER resolved for\n * that object itself (as opposed to {@link AS_DETAIL_BREADCRUMBS_KEY}, which keys the breadcrumbs\n * of its reference attributes by attribute name).\n *\n * This exists because a breadcrumb template can name a property the model does not carry. HR's\n * `Address` declares `[Breadcrumb, IgnoreProperty] string Crumb`, and `Address.json` renders it as\n * `\"{Crumb}\"` — the server resolves that by reflecting over the CLR property, which no client can\n * do, because `[IgnoreProperty]` is exactly the instruction to keep it out of the model. Flattening\n * used to discard the resolved string, leaving the form to substitute `{Crumb}` against a dict that\n * can never contain it.\n */\nexport const AS_DETAIL_SELF_BREADCRUMB_KEY = '__sparkBreadcrumb';\n\n/**\n * Reserved key under which a flattened nested object keeps its own row key — the value of whatever\n * property the server registered as that type's `[ValueKey]`.\n *\n * A value object's key is not a model attribute (nothing declares `Id` in its model file), so it\n * travels as the nested `PersistentObject`'s `id` and has to be carried across the flat-dict form\n * state by hand. Before this existed, flattening dropped it and `dictToNestedPo` rebuilt `id` from\n * `dict['Id']`, which is never present — so **every** embedded row reached the server with an empty\n * id, and the fresh instance the mapper builds kept the guid its own field initializer had just\n * minted.\n *\n * That is not cosmetic. Stored keys and incoming keys were then both plausible and never equal, so\n * a save could not match rows: an edit was indistinguishable from a delete plus a create. Every\n * per-row rule — preserving read-only fields, and the `New`/`Edit`/`Delete` rights of the row type —\n * rests on this key surviving the round trip.\n *\n * Safe to carry, for the same reason the breadcrumb is: `dictToNestedPo` walks the entity type's\n * attributes and never the dict's keys, so a reserved key is never sent back as an attribute.\n */\nexport const AS_DETAIL_ROW_KEY = '__sparkRowKey';\n\n/**\n * The breadcrumb the server resolved for a flattened object, or null when it resolved to nothing.\n *\n * `EntityMapper` never emits an empty breadcrumb: when the template renders blank it substitutes\n * the CLR type name, so an unset `Address` arrives as the literal string `\"Address\"`\n * (EntityMapper.cs:209-211). That is a placeholder, not data, and rendering it would be worse than\n * rendering nothing — it reads as a real value. `typeName` lets a caller filter it back out.\n */\nexport function selfBreadcrumb(row: Record<string, any> | null | undefined, typeName?: string): string | null {\n return resolvedBreadcrumb(row?.[AS_DETAIL_SELF_BREADCRUMB_KEY], typeName);\n}\n\n/**\n * The same filter as {@link selfBreadcrumb}, for a caller holding the server's breadcrumb string\n * directly rather than a flattened row dict — `attr.object.breadcrumb` on a detail page, or a query\n * cell's `breadcrumb`.\n *\n * Split out so there is exactly one answer to \"is this the type-name placeholder?\". The first\n * implementation lived inside `selfBreadcrumb` and so covered only the two pipes that flatten a row\n * first; the two that read the string straight off the wire kept printing `BuildFeedback` and\n * `GateSettings` at users (#384). A second copy of the comparison is how that happens again.\n */\nexport function resolvedBreadcrumb(value: unknown, typeName?: string): string | null {\n if (typeof value !== 'string' || value.trim() === '') return null;\n\n // Callers hold the type name in two shapes — `EntityType.name` is the short model name, while an\n // attribute's `asDetailType` is the full CLR name — and the server's placeholder is always the\n // short one. Compare on the last dotted segment so either shape filters it.\n if (typeName) {\n const shortName = typeName.slice(typeName.lastIndexOf('.') + 1);\n if (value === shortName) return null;\n }\n return value;\n}\n\n/**\n * Reserved key under which {@link nestedPoToDisplayRow} stashes the server-resolved breadcrumb of\n * each reference attribute (keyed by attribute name). Lets an AsDetail reference cell render the\n * label the server already resolved by id — page-independent — instead of guessing from a single\n * reference-query options page. Prefixed to avoid colliding with a real attribute name.\n */\nexport const AS_DETAIL_BREADCRUMBS_KEY = '__sparkBreadcrumbs';\n\n/**\n * Reserved key under which a flattened row keeps the **wire** values of its date cells, keyed by\n * attribute name.\n *\n * A date cell is edited through a native control that speaks only a bare local wall clock, so\n * flattening converts the stored instant into the viewer's zone and saving converts it back. That\n * round trip preserves the instant but necessarily rewrites the **offset** to the viewer's. For a\n * cell the user actually edited that is correct — the offset is not business data. For one nobody\n * touched it is pure loss: merely opening a row and saving would relabel a Seattle timestamp as a\n * Brussels one.\n *\n * Keeping the original lets {@link dictToNestedPo} send back exactly what it was given whenever the\n * instant is unchanged. Safe to carry for the same reason as the other reserved keys: the rebuild\n * walks the entity type's attributes, never the dict's keys.\n */\nexport const AS_DETAIL_ORIGINAL_DATES_KEY = '__sparkOriginalDates';\n\n/**\n * Whether a flattened-row key is one this module reserved rather than a model attribute.\n *\n * `nestedPoToDict` and `nestedPoToDisplayRow` stash the row key and the resolved breadcrumbs\n * alongside the real values, which is safe on the way back — `dictToNestedPo` walks the entity\n * type's attributes and never the dict's keys. It is *not* safe for anything that enumerates the\n * dict to build display text: a caller joining `Object.values(dict)` prints the row's guid and its\n * own breadcrumb as though they were fields.\n */\nexport function isReservedAsDetailKey(key: string): boolean {\n return key === AS_DETAIL_ROW_KEY\n || key === AS_DETAIL_SELF_BREADCRUMB_KEY\n || key === AS_DETAIL_BREADCRUMBS_KEY\n || key === AS_DETAIL_ORIGINAL_DATES_KEY;\n}\n\n/**\n * Like {@link nestedPoToDict}, but for the read-only detail display path. In addition to each\n * attribute's value it preserves the server-resolved per-reference `breadcrumb` under\n * {@link AS_DETAIL_BREADCRUMBS_KEY}, so an AsDetail reference cell can render the label by id\n * regardless of whether the referenced document fits on the reference query's first options page.\n * The form/edit path keeps using {@link nestedPoToDict}, which never carries breadcrumbs.\n */\nexport function nestedPoToDisplayRow(po: PersistentObject | null | undefined): Record<string, any> {\n if (!po) return {};\n const dict: Record<string, any> = {};\n let breadcrumbs: Record<string, string> | undefined;\n for (const attr of po.attributes ?? []) {\n dict[attr.name] = displayValueForAttribute(attr);\n if (attr.dataType === 'Reference' && !attr.isArray && typeof attr.breadcrumb === 'string' && attr.breadcrumb !== '') {\n (breadcrumbs ??= {})[attr.name] = attr.breadcrumb;\n }\n }\n // Only attach the side channel when something resolved — keeps reference-free rows (the common\n // case) byte-for-byte identical to the plain flat dict.\n if (breadcrumbs) dict[AS_DETAIL_BREADCRUMBS_KEY] = breadcrumbs;\n // Same self-breadcrumb the form path carries: a nested-AsDetail COLUMN in a detail table has an\n // inner dict as its value, which used to stringify to \"[object Object]\".\n if (typeof po.breadcrumb === 'string' && po.breadcrumb !== '') {\n dict[AS_DETAIL_SELF_BREADCRUMB_KEY] = po.breadcrumb;\n }\n // And the row key, for the same reason as the form path — a display row can be handed back for\n // saving, and a row that loses its key on that trip is indistinguishable from a new one.\n if (typeof po.id === 'string' && po.id !== '') {\n dict[AS_DETAIL_ROW_KEY] = po.id;\n }\n return dict;\n}\n\nfunction displayValueForAttribute(attr: PersistentObjectAttribute): any {\n if (attr.dataType === 'AsDetail') {\n if (attr.isArray) return (attr.objects ?? []).map(po => nestedPoToDisplayRow(po));\n return attr.object ? nestedPoToDisplayRow(attr.object) : null;\n }\n return attr.value;\n}\n\n/**\n * Builds a nested `PersistentObject` from a flat dict against the schema in\n * <paramref name=\"entityType\"/>. Used when the form is about to save — AsDetail attributes\n * are no longer sent as flat dicts in `attribute.value`; the server now requires\n * `attribute.object` / `attribute.objects` with fully scaffolded nested POs.\n *\n * `resolve` walks through AsDetail types registered elsewhere (usually the full\n * `getEntityTypes()` list, keyed by CLR type name). Nested AsDetail inside AsDetail is\n * handled recursively.\n */\nexport function dictToNestedPo(\n dict: Record<string, any> | null | undefined,\n entityType: EntityType,\n resolve: EntityTypeResolver,\n): PersistentObject {\n const originalDates = (dict?.[AS_DETAIL_ORIGINAL_DATES_KEY] ?? {}) as Record<string, any>;\n const attributes: PersistentObjectAttribute[] = (entityType.attributes ?? [])\n .map(attrDef => buildAttribute(attrDef, dict?.[attrDef.name], resolve, originalDates[attrDef.name]));\n\n return {\n // The reserved key first: it is the only one that is always present for a stored row, because\n // a value object's key is not a model attribute. `Id`/`id` remain as fallbacks for a root\n // object, whose id genuinely is part of the dict.\n id: (dict?.[AS_DETAIL_ROW_KEY] as string) ?? (dict?.['Id'] as string) ?? (dict?.['id'] as string) ?? '',\n name: entityType.name,\n objectTypeId: entityType.id,\n attributes,\n };\n}\n\nfunction buildAttribute(\n attrDef: EntityAttributeDefinition,\n raw: any,\n resolve: EntityTypeResolver,\n originalDate?: any,\n): PersistentObjectAttribute {\n const attr: PersistentObjectAttribute = {\n id: attrDef.id,\n name: attrDef.name,\n label: attrDef.label,\n dataType: attrDef.dataType,\n isArray: attrDef.isArray,\n isRequired: attrDef.isRequired,\n isVisible: attrDef.isVisible,\n isReadOnly: attrDef.isReadOnly,\n order: attrDef.order,\n rules: attrDef.rules ?? [],\n isValueChanged: true,\n };\n\n if (attrDef.dataType === 'AsDetail') {\n // Server expects attr.value null for AsDetail; the nested PO carries the data.\n attr.value = null;\n attr.asDetailType = attrDef.asDetailType;\n\n const nestedType = attrDef.asDetailType ? resolve(attrDef.asDetailType) : undefined;\n if (!nestedType) {\n attr.object = null;\n attr.objects = attrDef.isArray ? [] : null;\n return attr;\n }\n\n if (attrDef.isArray) {\n const items: any[] = Array.isArray(raw) ? raw : [];\n attr.objects = items.map(item => dictToNestedPo((item as Record<string, any>) ?? {}, nestedType, resolve));\n } else {\n attr.object = raw ? dictToNestedPo(raw as Record<string, any>, nestedType, resolve) : null;\n }\n return attr;\n }\n\n if (isDateDataType(attrDef.dataType)) {\n // The mirror of the conversion in `attributeValueForForm`: back from the control's bare wall\n // clock to a complete ISO-8601 instant carrying the viewer's offset for that date. When the\n // instant is unchanged, send back exactly what was loaded instead, so an untouched row keeps\n // its stored offset. See AS_DETAIL_ORIGINAL_DATES_KEY.\n const converted = fromDateInputValue(attrDef.dataType, raw);\n attr.value = wireDatesEqual(converted, originalDate) ? originalDate : converted;\n return attr;\n }\n\n attr.value = raw;\n return attr;\n}\n","import { TranslatedString } from './translated-string';\nimport { PersistentObject } from './persistent-object';\nimport { AS_DETAIL_BREADCRUMBS_KEY } from './as-detail-conversions';\n\n/**\n * What a query returns: the column metadata once, then one lightweight row per result.\n *\n * Rows used to be full `PersistentObject`s, so every row repeated the complete attribute\n * metadata the client already held from `/spark/types` and never read off the row. More\n * importantly it conflated two things that are not the same: a **row is a projection**, a\n * **persistent object is a document**. Nothing here is entitled to act on a row — every\n * mutating path re-loads by id and re-applies security server-side.\n */\nexport interface QueryResult {\n columns: QueryColumn[];\n items: QueryResultItem[];\n /** Rows matching the query after filtering, before paging. */\n totalItems: number;\n skip: number;\n take: number;\n typeHints?: Record<string, string>;\n /**\n * Custom actions withheld for THIS result, by name. Absent or empty means every action the\n * caller has the right to is offered.\n *\n * The mirror of `PersistentObject.disabledActions`, set server-side via\n * `CustomQueryArgs.DisableActions(...)`. The action catalogue is per type and is never told what\n * an execution returned, so this is where a per-result answer travels.\n *\n * An affordance, not a permission -- the action handler still refuses on its own terms.\n */\n disabledActions?: string[];\n}\n\n/**\n * The subset of column metadata a cell needs in order to render.\n *\n * Both {@link QueryColumn} (a query result's column) and `EntityAttributeDefinition` (a detail\n * page's attribute) satisfy this, which is what lets one cell component serve the query grid and\n * the AsDetail sub-table without either side knowing about the other's shape.\n */\nexport interface SparkCellColumn {\n name: string;\n label?: TranslatedString;\n /** Help text rendered as an [i] tooltip in the column header. Absent when the model declares none. */\n description?: TranslatedString;\n dataType: string;\n isArray?: boolean;\n renderer?: string;\n rendererOptions?: Record<string, any>;\n referenceType?: string;\n lookupReferenceType?: string;\n asDetailType?: string;\n}\n\n/** One column of a query result, sent once rather than repeated on every row. */\nexport interface QueryColumn extends SparkCellColumn {\n order: number;\n /**\n * Whether the grid offers a sort affordance for this column. Resolved server-side from the query's\n * override then the attribute, so it arrives already decided.\n *\n * This replaced an `isSortable` that carried the server's AsDetail *drag-reorder* flag — a\n * different concept that was `false` for every scalar column while the grid hard-coded every\n * header sortable anyway. Neither side read it. The attribute-level `isSortable` on `EntityType`\n * is the drag-reorder flag and stays.\n *\n * Absent means sortable, matching `isVisible`: a server predating the field still draws arrows.\n */\n canSort?: boolean;\n /** Whether the grid draws a filter cell for this column. Absent means filterable. */\n canFilter?: boolean;\n /**\n * Whether this column's distinct values may be listed. When `false` and `canFilter` is true the\n * panel offers a free-text filter instead of a value list — listing a column's values is a much\n * stronger disclosure than filtering by one already known.\n */\n canListDistincts?: boolean;\n /**\n * Whether the grid draws this column. `false` means the row carries the value but no column is\n * rendered — for a renderer that needs a sibling value (a lock glyph beside a name) without\n * spending a column on it. `showedOn` decides what ships; this decides what is drawn.\n */\n isVisible?: boolean;\n /** The query backing a Reference column, by name — the client's option source. */\n query?: string;\n}\n\n/**\n * One row: an id, a display string, and a value per column.\n *\n * Deliberately too weak to act on — no attribute metadata, no `can` block, no etag, because none\n * of those can be trusted from a projection.\n */\nexport interface QueryResultItem {\n /** Never null and unique within a result; the server refuses anything else. */\n id: string;\n /** What to show when the row is named rather than tabulated (a reference picker's value). */\n breadcrumb?: string;\n values: QueryResultItemValue[];\n /** Presentation hints for the whole row. Keys arrive lower-cased. */\n typeHints?: Record<string, string>;\n}\n\n/** One cell. */\nexport interface QueryResultItemValue {\n /** The {@link QueryColumn.name} this value belongs to. */\n key: string;\n /** Typed JSON, not a string — the server converts on the way out. */\n value?: any;\n /** For a reference cell: the target document's id, so a link needs no second lookup. */\n objectId?: string;\n /** For a reference cell: the target's display string. */\n breadcrumb?: string;\n /** Resolved display labels per referenced id, for a multi-reference cell. */\n breadcrumbs?: Record<string, string | null>;\n typeHints?: Record<string, string>;\n}\n\n/**\n * Every shape a renderer's `item` can arrive in.\n *\n * Three, and they are genuinely different objects rather than variations of one: a query grid hands\n * a {@link QueryResultItem}, an AsDetail sub-table hands the flat record `nestedPoToDisplayRow`\n * built, and a detail or form host hands the `PersistentObject` itself. A renderer reused across a\n * grid and an AsDetail table therefore sees two of them, which is why {@link valueFor} reads all\n * three rather than making every app write the branch.\n */\nexport type SparkRow = QueryResultItem | PersistentObject | Record<string, unknown>;\n\n/**\n * The value a row carries for a column, whatever shape the row is in — or `undefined` when it\n * carries none.\n *\n * ```ts\n * const isPrivate = valueFor(this.item(), 'IsPrivate')?.value === true;\n * ```\n *\n * Always returns the **cell** rather than the bare value, because a reference cell is worth as much\n * for its `objectId` as for its text, and a helper that discarded it would have to be replaced the\n * first time a renderer wanted to link somewhere. `?.value` is the cost, once per call site.\n *\n * ⚠️ A value only reaches a grid row if its attribute is on the **query surface**. To read a sibling\n * the grid does not draw, mark it `\"showedOn\": \"Query\", \"isVisible\": false` — shipped, not drawn. An\n * attribute marked `\"showedOn\": \"PersistentObject\"` is absent from a row by design, and this returns\n * `undefined` for it; that is the first thing to check when a sibling read comes back empty.\n */\nexport function valueFor(item: SparkRow | null | undefined, columnName: string): QueryResultItemValue | undefined {\n if (!item) return undefined;\n\n if (isQueryRow(item)) {\n return item.values.find(v => v.key === columnName);\n }\n\n if (isPersistentObject(item)) {\n const attribute = item.attributes.find(a => a.name === columnName);\n if (!attribute) return undefined;\n return {\n key: attribute.name,\n value: attribute.value,\n // A single reference's value IS the target's id, which is what the row shape carries\n // separately. Deriving it here keeps a renderer from having to know that.\n objectId: attribute.dataType === 'Reference' && !attribute.isArray && attribute.value != null\n ? String(attribute.value)\n : undefined,\n breadcrumb: attribute.breadcrumb,\n breadcrumbs: attribute.breadcrumbs,\n };\n }\n\n const record = item as Record<string, unknown>;\n if (!(columnName in record)) return undefined;\n\n const value = record[columnName];\n // The flat row's side channel is populated only for single references, so its presence for this\n // key is what identifies one — the record itself carries no dataType to ask.\n const breadcrumb = (record[AS_DETAIL_BREADCRUMBS_KEY] as Record<string, string> | undefined)?.[columnName];\n\n return {\n key: columnName,\n value,\n objectId: breadcrumb !== undefined && value != null ? String(value) : undefined,\n breadcrumb,\n };\n}\n\n/**\n * Whether this row is a query result item.\n *\n * Discriminated on the *elements*, not just on `Array.isArray(values)`: a flat AsDetail record may\n * legitimately have a column named `values` holding an array, and mistaking one for the other would\n * silently read the wrong thing.\n */\nexport function isQueryRow(row: SparkRow | null | undefined): row is QueryResultItem {\n const values = (row as QueryResultItem | undefined)?.values;\n return Array.isArray(values) && (values.length === 0 || typeof values[0]?.key === 'string');\n}\n\n/** Whether this row is a full persistent object. Discriminated on elements, as above. */\nexport function isPersistentObject(row: SparkRow | null | undefined): row is PersistentObject {\n const attributes = (row as PersistentObject | undefined)?.attributes;\n return Array.isArray(attributes) && (attributes.length === 0 || typeof attributes[0]?.name === 'string');\n}\n\n/**\n * One column's value filter (#431): the values a caller picked, or the values they excluded.\n *\n * **Values, never labels.** Each entry is the raw stored value — the same `value` the distinct\n * endpoint returned, never the `label` beside it. A reference column's values are document ids while\n * its labels are resolved breadcrumb text, and two rows can legitimately render the same text, so\n * matching on labels would be both wrong and ambiguous.\n *\n * `includes` and `excludes` are the two halves of the inverse toggle rather than a flag: the panel\n * moves values between them. `null` is a real, selectable value meaning \"no value\"; an empty or\n * absent array is the absent filter.\n */\nexport interface QueryColumnFilter {\n name: string;\n includes?: unknown[];\n excludes?: unknown[];\n}\n\n/**\n * One selectable entry in a column's filter panel.\n *\n * Structurally identical to the datatable's own `DistinctValue`, deliberately — it is what lets the\n * HTTP response be handed straight to `[distincts]` with no mapper in between.\n */\nexport interface DistinctValue {\n /** The raw stored value. `null` is a real entry meaning \"no value\", not an absent one. */\n value: unknown;\n /** What the user reads. Never matched on. */\n label: string;\n}\n\n/**\n * A column's distinct values.\n *\n * `remaining` is always empty from this server: the datatable snapshots the list when a selection is\n * first made and derives both buckets itself by diffing. The server answers \"what matches now\".\n *\n * `hasMore` reports that the cap truncated the list, and the panel re-queries only when it is set or\n * the search term widens — so a dishonest `false` strands a user typing past the cap.\n */\nexport interface DistinctValuesResult {\n matching: DistinctValue[];\n remaining: DistinctValue[];\n hasMore: boolean;\n}\n","import { TranslatedString } from './translated-string';\n\nexport enum ELookupDisplayType {\n Dropdown = 0,\n Modal = 1\n}\n\nexport interface LookupReferenceListItem {\n name: string;\n isTransient: boolean;\n valueCount: number;\n displayType: ELookupDisplayType;\n}\n\nexport interface LookupReference {\n name: string;\n isTransient: boolean;\n displayType: ELookupDisplayType;\n values: LookupReferenceValue[];\n}\n\nexport interface LookupReferenceValue {\n key: string;\n values: TranslatedString;\n isActive: boolean;\n extra?: Record<string, unknown>;\n}\n","import { CustomActionDefinition } from './custom-action';\n\n/**\n * The custom actions a query should offer, from the entity type's full set.\n *\n * `showedOn` must include the query side. The accepted values are `\"detail\"`, `\"query\"`\n * and `\"both\"` — as the server model and the custom-actions guide have always\n * documented. Both grids previously tested for `\"list\"`, a value nothing emits, so an\n * action authored per the documentation rendered nowhere at all.\n *\n * ⚠️ This narrows what is DISPLAYED. It is NOT an authorization boundary: the grant\n * is, and it is enforced independently in `ExecuteCustomAction` regardless of which\n * query the caller clicked from — a caller can always POST directly.\n */\nexport function filterQueryActions(\n actions: CustomActionDefinition[],\n): CustomActionDefinition[] {\n return actions.filter(a => a.showedOn === 'query' || a.showedOn === 'both');\n}\n","/**\n * Parses a custom action's `selectionRule` — a cardinality expression over the number\n * of selected rows — into a predicate.\n *\n * A port of the server's `SelectionRuleParser`, and the two MUST agree: they are tested\n * against one shared fixture (`selection-rule.fixture.json`) for exactly this reason.\n * Vidyano, where this grammar comes from, has the same algorithm in C# and JavaScript and\n * the two have already drifted — one throws on a non-numeric operand where the other\n * silently permits everything.\n *\n * Grammar: `X` is the count placeholder, whitespace is insignificant, terms split on `X`\n * are AND-combined (`1<X<5` is a range), operators are `<= >= < > != =` matched in that\n * order so `>=` is never read as `>`, and a number-first term is mirrored (`0<X` is `>0`).\n *\n * Client-side this only drives whether a button is disabled. The server enforces the same\n * rule independently — and neither is an authorization boundary: the action's grant is.\n */\nexport function parseSelectionRule(rule?: string | null): (count: number) => boolean {\n if (!rule || !rule.trim()) return () => true;\n try {\n return compile(rule);\n } catch {\n // Unlike the server, which refuses to start on a malformed rule, the client cannot\n // usefully fail: the rule arrived over the wire from a server that already validated\n // it. Disabling the button is the safe direction — it never permits an action the\n // server would refuse.\n return () => false;\n }\n}\n\nconst OPERATORS = ['<=', '>=', '<', '>', '!=', '='] as const;\n\nfunction compile(rule: string): (count: number) => boolean {\n const normalized = rule.replace(/ /g, '').toUpperCase();\n const terms = normalized.split('X').filter(t => t.length > 0);\n if (terms.length === 0) throw new Error(`Selection rule '${rule}' has no condition.`);\n\n const numberFirst = !normalized.startsWith('X') && normalized.includes('X');\n const predicates = terms.map((term, i) => compileTerm(term, rule, numberFirst && i === 0));\n\n return count => predicates.every(p => p(count));\n}\n\nfunction compileTerm(term: string, rule: string, mirrored: boolean): (count: number) => boolean {\n const op = OPERATORS.find(o => (mirrored ? term.endsWith(o) : term.startsWith(o)));\n if (!op) throw new Error(`Selection rule '${rule}' has no recognised operator in '${term}'.`);\n\n const numberPart = mirrored ? term.slice(0, term.length - op.length) : term.slice(op.length);\n // Number(' ') is 0 and Number('1.5') is 1.5 — neither is a valid operand here.\n if (!/^-?\\d+$/.test(numberPart)) {\n throw new Error(`Selection rule '${rule}' has a non-numeric operand in '${term}'.`);\n }\n const value = Number(numberPart);\n const effective = mirrored ? mirror(op) : op;\n\n switch (effective) {\n case '<=': return count => count <= value;\n case '>=': return count => count >= value;\n case '<': return count => count < value;\n case '>': return count => count > value;\n case '!=': return count => count !== value;\n case '=': return count => count === value;\n default: throw new Error(`Selection rule '${rule}' has an unsupported operator '${effective}'.`);\n }\n}\n\nfunction mirror(op: string): string {\n switch (op) {\n case '<': return '>';\n case '>': return '<';\n case '<=': return '>=';\n case '>=': return '<=';\n default: return op;\n }\n}\n","import { CustomActionDefinition } from './custom-action';\nimport { parseSelectionRule } from './selection-rule';\n\nexport type SparkSelectionMode = 'none' | 'single' | 'multiple';\n\n/**\n * The selection mode a grid needs in order to satisfy the actions offered on it.\n *\n * Derived rather than configured, so a grid gains a checkbox column exactly when an\n * action needs one and is otherwise pixel-identical to a grid with no selection at all.\n * Vidyano's query grid does the same thing — it renders the checkbox column only if some\n * action is selection-gated.\n *\n * `'single'` when every gated action is satisfied by one row and refused by two; anything\n * else that cares about the count gets `'multiple'`.\n */\nexport function selectionModeFor(actions: CustomActionDefinition[]): SparkSelectionMode {\n // An action with no rule is not selection-gated: it acts on the query, not on rows.\n const gated = actions.filter(a => !!a.selectionRule?.trim());\n if (gated.length === 0) return 'none';\n\n const everyRuleWantsExactlyOne = gated.every(a => {\n const rule = parseSelectionRule(a.selectionRule);\n return rule(1) && !rule(2);\n });\n\n return everyRuleWantsExactlyOne ? 'single' : 'multiple';\n}\n","import { EntityAttributeDefinition } from './entity-type';\nimport { PersistentObject } from './persistent-object';\nimport { ValidationRule } from './validation-rule';\n\n/**\n * One selectable value, as replaced by a refresh hook. Mirrors the server's\n * `PersistentObjectAttributeOption`.\n */\nexport interface RefreshedOption {\n key: string;\n label?: Record<string, string>;\n}\n\n/**\n * What a refresh changed about one attribute's *presentation*.\n *\n * Kept separate from `EntityType` on purpose. The form's option loading hangs off a single effect\n * keyed on `entityType` identity, and `SparkService` caches nothing — so applying a refresh by\n * setting a new `EntityType` re-issues every reference query, every lookup fetch, a full\n * `getEntityTypes()` and a `getPermissions()` per array-AsDetail attribute, on every refresh.\n * Mutating the existing object instead is inert, because the rendering computed would not re-run.\n * An overlay is the only shape that is both reactive and free.\n */\nexport interface AttributeOverlay {\n isRequired?: boolean;\n isReadOnly?: boolean;\n isVisible?: boolean;\n rules?: ValidationRule[];\n query?: string;\n /** `undefined` means the hook did not touch the options; an empty array means there are none. */\n options?: RefreshedOption[];\n}\n\nexport type RefreshOverlay = Record<string, AttributeOverlay>;\n\n/** Applies an overlay to one attribute definition, returning a new object when anything changed. */\nexport function applyOverlay(\n attr: EntityAttributeDefinition,\n overlay: AttributeOverlay | undefined,\n): EntityAttributeDefinition {\n if (!overlay) return attr;\n\n return {\n ...attr,\n isRequired: overlay.isRequired ?? attr.isRequired,\n isReadOnly: overlay.isReadOnly ?? attr.isReadOnly,\n isVisible: overlay.isVisible ?? attr.isVisible,\n rules: overlay.rules ?? attr.rules,\n query: overlay.query ?? attr.query,\n };\n}\n\n/**\n * Reads a refresh response into an overlay.\n *\n * Everything here is presentation the server owns outright, so it is taken verbatim — there is no\n * merging to do on this half, only on values.\n */\nexport function overlayFromResponse(response: PersistentObject): RefreshOverlay {\n const overlay: RefreshOverlay = {};\n\n for (const attr of response.attributes ?? []) {\n overlay[attr.name] = {\n isRequired: attr.isRequired,\n isReadOnly: attr.isReadOnly,\n isVisible: attr.isVisible,\n rules: attr.rules ?? [],\n query: attr.query,\n options: (attr as { options?: RefreshedOption[] | null }).options ?? undefined,\n };\n }\n\n return overlay;\n}\n\n/**\n * Merges a refresh response's values into the live form.\n *\n * The rule, and the reason for it: a refresh is not instant, and the user keeps typing during it —\n * the form is deliberately never frozen. So for each attribute we ask whether the *server* changed\n * it, by comparing the response against the values that were **sent**, not against what is on\n * screen now.\n *\n * - server value equals what we sent → the hook did not touch it, so whatever is in the form now\n * wins, including anything typed while the request was in flight;\n * - server value differs → the hook deliberately changed it, and it wins over a concurrent edit.\n *\n * Comparing against the displayed value instead is the classic \"refresh eats my typing\" bug;\n * refusing to overwrite anything the user touched is the equally wrong opposite, where a dependent\n * field the hook computed never appears.\n *\n * @param sent values as they were POSTed, captured before the request left\n * @param current values as they are now, which may have moved on\n * @param response the reshaped object\n */\nexport function mergeRefreshValues(\n sent: Record<string, any>,\n current: Record<string, any>,\n response: PersistentObject,\n): Record<string, any> {\n const merged = { ...current };\n\n for (const attr of response.attributes ?? []) {\n const serverValue = attr.value ?? null;\n const sentValue = sent[attr.name] ?? null;\n\n if (!valuesEqual(serverValue, sentValue)) {\n merged[attr.name] = attr.value;\n }\n }\n\n return merged;\n}\n\n/**\n * Structural for arrays, `===` otherwise.\n *\n * Multi-reference attributes hold `string[]`, and a fresh array of the same ids is a different\n * object — so reference equality would report every one of them as \"changed by the server\" on\n * every refresh, and clobber in-flight edits to them.\n */\nfunction valuesEqual(a: any, b: any): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => valuesEqual(item, b[i]));\n }\n // Values arrive off the wire as JSON scalars; anything else compares by JSON shape rather than\n // by identity, which is what a caller means by \"did this change\".\n if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n return false;\n}\n","import { ValidationRule } from './validation-rule';\nimport { TranslatedString } from './translated-string';\n\n/** One rule failure, in the shape the form already renders per field. */\nexport interface RuleFailure {\n attributeName: string;\n ruleType: string;\n message: string;\n}\n\nexport interface EvaluableAttribute {\n name: string;\n label?: TranslatedString;\n isRequired?: boolean;\n rules?: ValidationRule[];\n}\n\nconst EMAIL = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/i;\nconst URL = /^https?:\\/\\/[^\\s]+$/i;\n\nfunction isEmpty(value: any): boolean {\n if (value === null || value === undefined) return true;\n if (typeof value === 'string') return value.trim() === '';\n if (Array.isArray(value)) return value.length === 0;\n return false;\n}\n\n/**\n * Evaluates an attribute's rules against a value, mirroring the server's `ValidationService`.\n *\n * ⚠️ **Parity with the server is the point, and disagreement is worse than silence.** A client that\n * rejects something the server would accept blocks legitimate work with no recourse; one that\n * accepts something the server rejects merely defers the error to the round-trip, which is where it\n * used to live anyway. So the rule set here is deliberately limited to the types the server\n * implements, and an unrecognised rule type is ignored rather than guessed at.\n *\n * This exists because a refresh hook that imposes a rule needs it to bite before Save — previously\n * `rules` was carried on the wire and never evaluated in the browser at all.\n */\nexport function evaluateRules(attr: EvaluableAttribute, value: any): RuleFailure[] {\n const failures: RuleFailure[] = [];\n const label = attr.label?.['en'] ?? attr.name;\n\n if (attr.isRequired && isEmpty(value)) {\n return [{ attributeName: attr.name, ruleType: 'required', message: `${label} is required` }];\n }\n\n // A rule other than \"required\" says nothing about an absent value — that is what required is for.\n if (isEmpty(value)) return failures;\n\n const text = value?.toString() ?? '';\n\n for (const rule of attr.rules ?? []) {\n const failure = evaluateRule(attr.name, label, rule, value, text);\n if (failure) failures.push(failure);\n }\n\n return failures;\n}\n\nfunction evaluateRule(\n name: string,\n label: string,\n rule: ValidationRule,\n value: any,\n text: string,\n): RuleFailure | null {\n const message = rule.message?.['en'];\n\n switch (rule.type?.toLowerCase()) {\n case 'maxlength': {\n const max = toInt(rule.value);\n if (max === null || text.length <= max) return null;\n return { attributeName: name, ruleType: 'maxLength', message: message ?? `${label} must be at most ${max} characters` };\n }\n case 'minlength': {\n const min = toInt(rule.value);\n if (min === null || text.length >= min) return null;\n return { attributeName: name, ruleType: 'minLength', message: message ?? `${label} must be at least ${min} characters` };\n }\n case 'range': {\n const numeric = toNumber(value);\n if (numeric === null) return null;\n if (rule.min !== undefined && rule.min !== null && numeric < rule.min) {\n return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at least ${rule.min}` };\n }\n if (rule.max !== undefined && rule.max !== null && numeric > rule.max) {\n return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at most ${rule.max}` };\n }\n return null;\n }\n case 'regex': {\n const pattern = rule.value?.toString();\n if (!pattern) return null;\n let re: RegExp;\n try {\n re = new RegExp(pattern);\n } catch {\n // An unparseable pattern is a server-side authoring bug. Staying silent leaves the server to\n // report it, which is strictly better than blocking the user over a rule nobody can evaluate.\n return null;\n }\n return re.test(text) ? null : { attributeName: name, ruleType: 'regex', message: message ?? `${label} is not in the expected format` };\n }\n case 'email':\n return EMAIL.test(text) ? null : { attributeName: name, ruleType: 'email', message: message ?? `${label} must be a valid email address` };\n case 'url':\n return URL.test(text) ? null : { attributeName: name, ruleType: 'url', message: message ?? `${label} must be a valid URL` };\n default:\n return null;\n }\n}\n\nfunction toInt(value: any): number | null {\n const n = Number(value);\n return Number.isFinite(n) ? Math.trunc(n) : null;\n}\n\nfunction toNumber(value: any): number | null {\n const n = Number(value);\n return Number.isFinite(n) ? n : null;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-models.mjs","sources":["../../models/src/translated-string.ts","../../models/src/showed-on.ts","../../models/src/entity-type.ts","../../models/src/datetime-local.ts","../../models/src/as-detail-conversions.ts","../../models/src/query-result.ts","../../models/src/lookup-reference.ts","../../models/src/query-actions.ts","../../models/src/selection-rule.ts","../../models/src/selection-mode.ts","../../models/src/refresh-overlay.ts","../../models/src/rule-evaluation.ts","../../models/mintplayer-ng-spark-models.ts"],"sourcesContent":["import { signal, type WritableSignal } from '@angular/core';\n\nexport type TranslatedString = Record<string, string>;\n\n/** Global reactive language state — shared across library boundaries via globalThis */\nexport const currentLanguage: WritableSignal<string> =\n ((globalThis as any).__sparkCurrentLanguage ??= signal('en'));\n\nexport function resolveTranslation(ts: TranslatedString | undefined, lang?: string): string {\n if (!ts) return '';\n const language = lang ?? currentLanguage();\n return ts[language] ?? ts['en'] ?? Object.values(ts)[0] ?? '';\n}\n","/**\n * Flags enum controlling on which pages an attribute should be displayed.\n * Values can be combined: ShowedOn.Query | ShowedOn.PersistentObject\n */\nexport enum ShowedOn {\n Query = 1,\n PersistentObject = 2,\n}\n\n/**\n * Helper function to check if a ShowedOn value includes a specific flag.\n */\nexport function hasShowedOnFlag(value: ShowedOn | string | undefined, flag: ShowedOn): boolean {\n if (value === undefined) return true; // Default: show on all pages\n\n // Handle string values from JSON (e.g., \"Query, PersistentObject\")\n if (typeof value === 'string') {\n const parts = value.split(',').map(s => s.trim());\n const flagName = ShowedOn[flag];\n return parts.includes(flagName);\n }\n\n // Handle numeric flag values\n return (value & flag) === flag;\n}\n","import { ShowedOn } from './showed-on';\nimport { TranslatedString } from './translated-string';\nimport { ValidationRule } from './validation-rule';\n\n/**\n * Controls how a Reference attribute is picked in the PO-edit form.\n * Serialized as a string by the server (mirrors the .NET EReferenceDisplayType).\n */\nexport enum EReferenceDisplayType {\n /** Renders as a `<bs-select>` listing every referenced item. */\n Dropdown = 'Dropdown',\n /** Renders a readonly textbox + \"…\" button that opens a searchable modal grid picker. */\n Modal = 'Modal',\n}\n\nexport interface EntityAttributeDefinition {\n id: string;\n name: string;\n label?: TranslatedString;\n /** Help text rendered as an [i] tooltip beside the label. Absent when the model declares none. */\n description?: TranslatedString;\n dataType: string;\n isRequired: boolean;\n isVisible: boolean;\n isReadOnly: boolean;\n order: number;\n query?: string;\n /** For reference attributes, specifies the target entity type's CLR type name */\n referenceType?: string;\n /** For AsDetail attributes, specifies the nested entity type's CLR type name */\n asDetailType?: string;\n /** When true, the attribute represents an array/collection of AsDetail objects */\n isArray?: boolean;\n /** For array AsDetail attributes: \"modal\" (default) or \"inline\" */\n editMode?: 'inline' | 'modal';\n /**\n * For Reference attributes: 'Modal' renders the \"…\" + modal query-grid picker;\n * 'Dropdown'/absent (default) renders a `<bs-select>`. Hand-set in the model JSON.\n */\n referenceDisplayType?: EReferenceDisplayType;\n /** For array AsDetail attributes: when true, rows can be drag-reordered (order = array position) */\n isSortable?: boolean;\n /**\n * When true, changing this attribute's value posts the in-progress object to\n * `/spark/po/{objectTypeId}/refresh` and applies the reshaped result as an overlay.\n * Schema-only by design — it never travels on a PersistentObjectAttribute, so a client\n * cannot claim a trigger the model did not declare.\n */\n triggersRefresh?: boolean;\n /** For LookupReference attributes, specifies the lookup reference type name */\n lookupReferenceType?: string;\n /**\n * Controls on which pages the attribute should be displayed.\n * Query = shown in list views, PersistentObject = shown in detail/edit views.\n * Can be a numeric flag value or a string like \"Query, PersistentObject\".\n */\n showedOn?: ShowedOn | string;\n rules: ValidationRule[];\n /** References an AttributeGroup.id to assign this attribute to a group */\n group?: string;\n /** Number of grid columns this attribute spans within a tab's column layout */\n columnSpan?: number;\n /** Renderer component name for custom display in detail/list views */\n renderer?: string;\n /** Options passed to the renderer component */\n rendererOptions?: Record<string, any>;\n}\n\nexport interface AttributeTab {\n id: string;\n name: string;\n label?: TranslatedString;\n order: number;\n /** Number of columns for the grid layout within this tab */\n columnCount?: number;\n}\n\nexport interface AttributeGroup {\n id: string;\n name: string;\n label?: TranslatedString;\n /** References an AttributeTab.id to assign this group to a tab */\n tab?: string;\n order: number;\n}\n\nexport interface EntityType {\n id: string;\n name: string;\n description?: TranslatedString;\n /**\n * The backing CLR type, or absent for a JSON-only virtual type (#325).\n *\n * Optional on purpose: the server sends null for a virtual type, and this was declared\n * non-nullable, so any unguarded `.endsWith(...)` on it threw a TypeError naming an innocent\n * query. Types are data from the wire, not a promise about it.\n */\n clrType?: string;\n\n /**\n * Whether this caller may OPEN one of these, as opposed to list them.\n *\n * The catalogue is gated on Query, so its presence here means \"you may list this type\" and says\n * nothing about opening one. A reference must not render as a link when this is false: it used\n * to, and the click refused on arrival.\n */\n canRead?: boolean;\n alias?: string;\n /**\n * Breadcrumb template: literal text plus `{AttributeName}` placeholders. A scalar placeholder\n * renders its value; a reference placeholder renders the referenced entity's breadcrumb.\n * The server resolves this — clients only read the resulting strings. Example: \"{Street}, {City}\".\n */\n breadcrumb?: string;\n /**\n * When false, the breadcrumb needs the collection document (a placeholder field is not on the\n * projection). null/absent means renderable from the projection. Informational on the client.\n */\n breadcrumbProjectionSatisfiable?: boolean;\n tabs?: AttributeTab[];\n groups?: AttributeGroup[];\n attributes: EntityAttributeDefinition[];\n /** Query aliases or IDs to display as related query tables on the detail page. */\n queries?: string[];\n /**\n * The definitions of this type's AsDetail row types, sent alongside it so a detail table can draw\n * its columns.\n *\n * Needed because a row type usually has no rights of its own — a row is edited through its parent\n * — and the entity-type catalogue is gated on `Query`. Resolving a row type from the catalogue\n * therefore failed silently, and the table rendered with **no columns at all**.\n *\n * Per-caller and gated on the *parent's* right, which is the same gate that already ships each\n * row's attribute schema inside `attr.objects`. Pruned server-side: no `queryType`, `indexName`,\n * `queries` or `alias`.\n */\n detailTypes?: EntityType[];\n /**\n * Whether adding or removing a row of this type asks the server first.\n *\n * Absent or false — the default — keeps the purely client-side behaviour: New pushes a blank row,\n * Delete splices it out, and the parent's save is the first the server hears of either. On, the\n * grid calls `newObject` before showing a row and `deleteRow` before removing one, so\n * `OnNewAsync` can default it and the delete hook can refuse.\n *\n * ⚠️ Read it off the **row type**, never the parent: the type that owns the hooks owns the\n * decision. For a row type resolved out of `detailTypes` the flag survives the server-side\n * pruning, so both sources agree.\n *\n * ⚠️ It governs the round trip and nothing else. It is not a permission, and it is outside the\n * model hash, so nothing that gates a write may be conditional on it — the save path enforces the\n * row type's New/Edit/Delete rights on every embedded collection whether this is on or off.\n */\n serverSideRowLifecycle?: boolean;\n}\n","/**\n * Conversions between the wire format for a timestamp and what an `<input type=\"datetime-local\">`\n * or `<input type=\"date\">` will accept.\n *\n * A `DateTimeOffset` in Spark means an **instant**. The wire carries a full ISO-8601 string with an\n * offset (`2026-12-31T23:59:00-08:00`); an HTML date/time control speaks only bare local wall clock\n * (`2026-12-31T23:59`). Neither end can consume the other's format, so both directions need\n * converting, and the browser is deliberately the side that does it: its timezone rules are kept\n * fresh by the operating system, while a container's are frozen at image build time.\n *\n * Assigning a wire value straight to a `datetime-local` input does not throw and does not warn -- the\n * control simply renders **blank**, and saving the untouched form then writes that blank back.\n */\n\nfunction pad(n: number): string {\n return String(n).padStart(2, '0');\n}\n\n/** Formats a UTC offset in minutes as `+HH:mm` / `-HH:mm`. */\nfunction formatOffset(offsetMinutes: number): string {\n const sign = offsetMinutes >= 0 ? '+' : '-';\n const abs = Math.abs(offsetMinutes);\n return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;\n}\n\n/**\n * Parses a wire timestamp. Returns `null` for anything unusable, matching the contract of\n * `parsedDate` / `queryCellValue` -- a bad value renders as empty rather than as `Invalid Date`.\n */\nexport function parseWireDate(value: unknown): Date | null {\n if (value === null || value === undefined || value === '') return null;\n if (value instanceof Date) return isNaN(value.getTime()) ? null : value;\n if (typeof value !== 'string' && typeof value !== 'number') return null;\n\n const parsed = new Date(value);\n return isNaN(parsed.getTime()) ? null : parsed;\n}\n\n/**\n * Wire value -> the `yyyy-MM-ddTHH:mm` a `datetime-local` input needs, in the **viewer's** zone.\n *\n * The conversion is date-sensitive, not a fixed shift: a value is rendered using the zone's offset\n * *on that value's own date*, so a timestamp in July and one in January convert differently in a\n * zone that observes DST.\n */\nexport function toDateTimeLocalInput(value: unknown): string {\n const d = parseWireDate(value);\n if (!d) return '';\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n}\n\n/** Wire value -> the `yyyy-MM-dd` a `date` input needs, in the viewer's zone. */\nexport function toDateInput(value: unknown): string {\n const d = parseWireDate(value);\n if (!d) return '';\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/**\n * `yyyy-MM-ddTHH:mm` from the control -> a complete ISO-8601 string carrying the viewer's offset\n * **for the entered date**.\n *\n * Building the `Date` from its parts rather than parsing the string is what makes this correct: it\n * lets the browser apply its own timezone rules for that date, including DST. Using the *current*\n * offset instead would be wrong for any value outside the present season -- entering a January date\n * during summer would record an offset an hour off, and the stored instant would be an hour wrong.\n *\n * Across a DST discontinuity the browser resolves the wall clock itself, taking the offset in force\n * before a fall-back and after a spring-forward. That is specified ECMAScript behaviour, and the\n * server's conversion helper deliberately reproduces the same choice so the two cannot drift.\n */\nexport function fromDateTimeLocalInput(local: unknown): string | null {\n if (typeof local !== 'string' || local === '') return null;\n\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?/.exec(local);\n if (!match) return null;\n\n const [, y, mo, d, h, mi, s] = match;\n const date = new Date(+y, +mo - 1, +d, +h, +mi, s ? +s : 0);\n if (isNaN(date.getTime())) return null;\n\n // getTimezoneOffset() is minutes to ADD to local to reach UTC, so it is the negation of the offset\n // an ISO string carries.\n return `${y}-${mo}-${d}T${h}:${mi}:${pad(s ? +s : 0)}${formatOffset(-date.getTimezoneOffset())}`;\n}\n\n/** `yyyy-MM-dd` from a `date` control -> an ISO string at local midnight with the viewer's offset. */\nexport function fromDateInput(local: unknown): string | null {\n if (typeof local !== 'string' || local === '') return null;\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(local);\n if (!match) return null;\n return fromDateTimeLocalInput(`${local}T00:00`);\n}\n\n/**\n * Compares two wire timestamps **by instant**, which is what a `DateTimeOffset` means in Spark.\n *\n * Round-tripping a value through a date control legitimately rewrites its text -- a document holding\n * `2026-12-31T23:59:00-08:00` comes back as `2027-01-01T08:59:00+01:00` from a viewer in Brussels --\n * so a string comparison would mark every untouched date as edited and send a needless write on every\n * save. Both of those name the same moment.\n *\n * Note this is the *opposite* of the trap on the server side, where `DateTimeOffset.Equals` compares\n * the instant and silently hides a destroyed offset. Here comparing by instant is the correct thing:\n * we are asking \"did the user change this value\", not \"is the offset intact\".\n */\nexport function wireDatesEqual(a: unknown, b: unknown): boolean {\n const da = parseWireDate(a);\n const db = parseWireDate(b);\n if (da === null || db === null) return da === db;\n return da.getTime() === db.getTime();\n}\n\n/** True for the attribute data types that are edited through a native date/time control. */\nexport function isDateDataType(dataType: string | null | undefined): dataType is 'date' | 'datetime' {\n return dataType === 'date' || dataType === 'datetime';\n}\n\n/** Wire -> control, picking the right shape for the data type. */\nexport function toDateInputValue(dataType: string | null | undefined, value: unknown): string {\n return dataType === 'date' ? toDateInput(value) : toDateTimeLocalInput(value);\n}\n\n/** Control -> wire, picking the right shape for the data type. */\nexport function fromDateInputValue(dataType: string | null | undefined, value: unknown): string | null {\n return dataType === 'date' ? fromDateInput(value) : fromDateTimeLocalInput(value);\n}\n","import { fromDateInputValue, isDateDataType, toDateInputValue, wireDatesEqual } from './datetime-local';\nimport { EntityAttributeDefinition } from './entity-type';\nimport { EntityType } from './entity-type';\nimport { PersistentObject } from './persistent-object';\nimport { PersistentObjectAttribute } from './persistent-object-attribute';\n\n/**\n * Resolves an `EntityType` by its CLR type name (e.g. `\"HR.Entities.Address\"`).\n * Callers typically close over a list they already hold. `getEntityTypes()` is NOT cached --\n * it issues a request per call -- so resolve against a list you fetched once rather than\n * calling it inside the resolver.\n */\nexport type EntityTypeResolver = (clrTypeName: string) => EntityType | undefined;\n\n/**\n * Flattens a nested `PersistentObject` into the plain `Record<string, any>` shape the\n * form state uses throughout ng-spark. Primitive / reference attributes contribute their\n * `value`; nested AsDetail attributes recurse — single becomes an inner dict, array\n * becomes an array of inner dicts. Returns `{}` for `null` / `undefined` input.\n *\n * This is the ONE place that reads the server's new AsDetail wire shape and collapses it\n * back to the flat dict the form components already handle.\n */\nexport function nestedPoToDict(po: PersistentObject | null | undefined): Record<string, any> {\n if (!po) return {};\n const dict: Record<string, any> = {};\n for (const attr of po.attributes ?? []) {\n dict[attr.name] = attributeValueForForm(attr);\n }\n // The object's own server-resolved breadcrumb, kept under a reserved key so the form can label\n // it without re-deriving a template it may be structurally unable to resolve. Safe to carry\n // through save: `dictToNestedPo` walks the entity type's attributes, never the dict's keys, so\n // a reserved key is never sent to the server. Attached only when it resolved, which keeps the\n // common case byte-for-byte identical to the plain flat dict.\n if (typeof po.breadcrumb === 'string' && po.breadcrumb !== '') {\n dict[AS_DETAIL_SELF_BREADCRUMB_KEY] = po.breadcrumb;\n }\n // The row's own key, same reserved-key mechanism and for a sharper reason: without it the key\n // never returns, and a save cannot tell an edited row from a deleted one plus a new one. See\n // AS_DETAIL_ROW_KEY.\n if (typeof po.id === 'string' && po.id !== '') {\n dict[AS_DETAIL_ROW_KEY] = po.id;\n }\n // The wire values of any date cells, kept so the save side can tell an untouched cell from an\n // edited one. Without it, flattening to a wall clock and converting back rewrites the stored\n // offset to the viewer's on every save, even for rows nobody touched.\n const originalDates = originalDatesOf(po);\n if (originalDates) dict[AS_DETAIL_ORIGINAL_DATES_KEY] = originalDates;\n return dict;\n}\n\nfunction originalDatesOf(po: PersistentObject): Record<string, any> | undefined {\n let originals: Record<string, any> | undefined;\n for (const attr of po.attributes ?? []) {\n if (isDateDataType(attr.dataType)) (originals ??= {})[attr.name] = attr.value;\n }\n return originals;\n}\n\nfunction attributeValueForForm(attr: PersistentObjectAttribute): any {\n if (attr.dataType === 'AsDetail') {\n if (attr.isArray) return (attr.objects ?? []).map(po => nestedPoToDict(po));\n return attr.object ? nestedPoToDict(attr.object) : null;\n }\n // An embedded row's date cells are edited through the same native controls as a root object's,\n // so they need the same wire -> wall-clock conversion. This is the FORM path only;\n // `nestedPoToDisplayRow` deliberately keeps the wire value, because the display path formats\n // through `parsedDate` rather than feeding an input.\n if (isDateDataType(attr.dataType)) return toDateInputValue(attr.dataType, attr.value);\n return attr.value;\n}\n\n/**\n * Reserved key under which a flattened nested object keeps the breadcrumb the SERVER resolved for\n * that object itself (as opposed to {@link AS_DETAIL_BREADCRUMBS_KEY}, which keys the breadcrumbs\n * of its reference attributes by attribute name).\n *\n * This exists because a breadcrumb template can name a property the model does not carry. HR's\n * `Address` declares `[Breadcrumb, IgnoreProperty] string Crumb`, and `Address.json` renders it as\n * `\"{Crumb}\"` — the server resolves that by reflecting over the CLR property, which no client can\n * do, because `[IgnoreProperty]` is exactly the instruction to keep it out of the model. Flattening\n * used to discard the resolved string, leaving the form to substitute `{Crumb}` against a dict that\n * can never contain it.\n */\nexport const AS_DETAIL_SELF_BREADCRUMB_KEY = '__sparkBreadcrumb';\n\n/**\n * Reserved key under which a flattened nested object keeps its own row key — the value of whatever\n * property the server registered as that type's `[ValueKey]`.\n *\n * A value object's key is not a model attribute (nothing declares `Id` in its model file), so it\n * travels as the nested `PersistentObject`'s `id` and has to be carried across the flat-dict form\n * state by hand. Before this existed, flattening dropped it and `dictToNestedPo` rebuilt `id` from\n * `dict['Id']`, which is never present — so **every** embedded row reached the server with an empty\n * id, and the fresh instance the mapper builds kept the guid its own field initializer had just\n * minted.\n *\n * That is not cosmetic. Stored keys and incoming keys were then both plausible and never equal, so\n * a save could not match rows: an edit was indistinguishable from a delete plus a create. Every\n * per-row rule — preserving read-only fields, and the `New`/`Edit`/`Delete` rights of the row type —\n * rests on this key surviving the round trip.\n *\n * Safe to carry, for the same reason the breadcrumb is: `dictToNestedPo` walks the entity type's\n * attributes and never the dict's keys, so a reserved key is never sent back as an attribute.\n */\nexport const AS_DETAIL_ROW_KEY = '__sparkRowKey';\n\n/**\n * The breadcrumb the server resolved for a flattened object, or null when it resolved to nothing.\n *\n * `EntityMapper` never emits an empty breadcrumb: when the template renders blank it substitutes\n * the CLR type name, so an unset `Address` arrives as the literal string `\"Address\"`\n * (EntityMapper.cs:209-211). That is a placeholder, not data, and rendering it would be worse than\n * rendering nothing — it reads as a real value. `typeName` lets a caller filter it back out.\n */\nexport function selfBreadcrumb(row: Record<string, any> | null | undefined, typeName?: string): string | null {\n return resolvedBreadcrumb(row?.[AS_DETAIL_SELF_BREADCRUMB_KEY], typeName);\n}\n\n/**\n * The same filter as {@link selfBreadcrumb}, for a caller holding the server's breadcrumb string\n * directly rather than a flattened row dict — `attr.object.breadcrumb` on a detail page, or a query\n * cell's `breadcrumb`.\n *\n * Split out so there is exactly one answer to \"is this the type-name placeholder?\". The first\n * implementation lived inside `selfBreadcrumb` and so covered only the two pipes that flatten a row\n * first; the two that read the string straight off the wire kept printing `BuildFeedback` and\n * `GateSettings` at users (#384). A second copy of the comparison is how that happens again.\n */\nexport function resolvedBreadcrumb(value: unknown, typeName?: string): string | null {\n if (typeof value !== 'string' || value.trim() === '') return null;\n\n // Callers hold the type name in two shapes — `EntityType.name` is the short model name, while an\n // attribute's `asDetailType` is the full CLR name — and the server's placeholder is always the\n // short one. Compare on the last dotted segment so either shape filters it.\n if (typeName) {\n const shortName = typeName.slice(typeName.lastIndexOf('.') + 1);\n if (value === shortName) return null;\n }\n return value;\n}\n\n/**\n * Reserved key under which {@link nestedPoToDisplayRow} stashes the server-resolved breadcrumb of\n * each reference attribute (keyed by attribute name). Lets an AsDetail reference cell render the\n * label the server already resolved by id — page-independent — instead of guessing from a single\n * reference-query options page. Prefixed to avoid colliding with a real attribute name.\n */\nexport const AS_DETAIL_BREADCRUMBS_KEY = '__sparkBreadcrumbs';\n\n/**\n * Reserved key under which a flattened row keeps the **wire** values of its date cells, keyed by\n * attribute name.\n *\n * A date cell is edited through a native control that speaks only a bare local wall clock, so\n * flattening converts the stored instant into the viewer's zone and saving converts it back. That\n * round trip preserves the instant but necessarily rewrites the **offset** to the viewer's. For a\n * cell the user actually edited that is correct — the offset is not business data. For one nobody\n * touched it is pure loss: merely opening a row and saving would relabel a Seattle timestamp as a\n * Brussels one.\n *\n * Keeping the original lets {@link dictToNestedPo} send back exactly what it was given whenever the\n * instant is unchanged. Safe to carry for the same reason as the other reserved keys: the rebuild\n * walks the entity type's attributes, never the dict's keys.\n */\nexport const AS_DETAIL_ORIGINAL_DATES_KEY = '__sparkOriginalDates';\n\n/**\n * Whether a flattened-row key is one this module reserved rather than a model attribute.\n *\n * `nestedPoToDict` and `nestedPoToDisplayRow` stash the row key and the resolved breadcrumbs\n * alongside the real values, which is safe on the way back — `dictToNestedPo` walks the entity\n * type's attributes and never the dict's keys. It is *not* safe for anything that enumerates the\n * dict to build display text: a caller joining `Object.values(dict)` prints the row's guid and its\n * own breadcrumb as though they were fields.\n */\nexport function isReservedAsDetailKey(key: string): boolean {\n return key === AS_DETAIL_ROW_KEY\n || key === AS_DETAIL_SELF_BREADCRUMB_KEY\n || key === AS_DETAIL_BREADCRUMBS_KEY\n || key === AS_DETAIL_ORIGINAL_DATES_KEY;\n}\n\n/**\n * Like {@link nestedPoToDict}, but for the read-only detail display path. In addition to each\n * attribute's value it preserves the server-resolved per-reference `breadcrumb` under\n * {@link AS_DETAIL_BREADCRUMBS_KEY}, so an AsDetail reference cell can render the label by id\n * regardless of whether the referenced document fits on the reference query's first options page.\n * The form/edit path keeps using {@link nestedPoToDict}, which never carries breadcrumbs.\n */\nexport function nestedPoToDisplayRow(po: PersistentObject | null | undefined): Record<string, any> {\n if (!po) return {};\n const dict: Record<string, any> = {};\n let breadcrumbs: Record<string, string> | undefined;\n for (const attr of po.attributes ?? []) {\n dict[attr.name] = displayValueForAttribute(attr);\n if (attr.dataType === 'Reference' && !attr.isArray && typeof attr.breadcrumb === 'string' && attr.breadcrumb !== '') {\n (breadcrumbs ??= {})[attr.name] = attr.breadcrumb;\n }\n }\n // Only attach the side channel when something resolved — keeps reference-free rows (the common\n // case) byte-for-byte identical to the plain flat dict.\n if (breadcrumbs) dict[AS_DETAIL_BREADCRUMBS_KEY] = breadcrumbs;\n // Same self-breadcrumb the form path carries: a nested-AsDetail COLUMN in a detail table has an\n // inner dict as its value, which used to stringify to \"[object Object]\".\n if (typeof po.breadcrumb === 'string' && po.breadcrumb !== '') {\n dict[AS_DETAIL_SELF_BREADCRUMB_KEY] = po.breadcrumb;\n }\n // And the row key, for the same reason as the form path — a display row can be handed back for\n // saving, and a row that loses its key on that trip is indistinguishable from a new one.\n if (typeof po.id === 'string' && po.id !== '') {\n dict[AS_DETAIL_ROW_KEY] = po.id;\n }\n return dict;\n}\n\nfunction displayValueForAttribute(attr: PersistentObjectAttribute): any {\n if (attr.dataType === 'AsDetail') {\n if (attr.isArray) return (attr.objects ?? []).map(po => nestedPoToDisplayRow(po));\n return attr.object ? nestedPoToDisplayRow(attr.object) : null;\n }\n return attr.value;\n}\n\n/**\n * Builds a nested `PersistentObject` from a flat dict against the schema in\n * <paramref name=\"entityType\"/>. Used when the form is about to save — AsDetail attributes\n * are no longer sent as flat dicts in `attribute.value`; the server now requires\n * `attribute.object` / `attribute.objects` with fully scaffolded nested POs.\n *\n * `resolve` walks through AsDetail types registered elsewhere (usually the full\n * `getEntityTypes()` list, keyed by CLR type name). Nested AsDetail inside AsDetail is\n * handled recursively.\n */\nexport function dictToNestedPo(\n dict: Record<string, any> | null | undefined,\n entityType: EntityType,\n resolve: EntityTypeResolver,\n): PersistentObject {\n const originalDates = (dict?.[AS_DETAIL_ORIGINAL_DATES_KEY] ?? {}) as Record<string, any>;\n const attributes: PersistentObjectAttribute[] = (entityType.attributes ?? [])\n .map(attrDef => buildAttribute(attrDef, dict?.[attrDef.name], resolve, originalDates[attrDef.name]));\n\n return {\n // The reserved key first: it is the only one that is always present for a stored row, because\n // a value object's key is not a model attribute. `Id`/`id` remain as fallbacks for a root\n // object, whose id genuinely is part of the dict.\n id: (dict?.[AS_DETAIL_ROW_KEY] as string) ?? (dict?.['Id'] as string) ?? (dict?.['id'] as string) ?? '',\n name: entityType.name,\n objectTypeId: entityType.id,\n attributes,\n };\n}\n\nfunction buildAttribute(\n attrDef: EntityAttributeDefinition,\n raw: any,\n resolve: EntityTypeResolver,\n originalDate?: any,\n): PersistentObjectAttribute {\n const attr: PersistentObjectAttribute = {\n id: attrDef.id,\n name: attrDef.name,\n label: attrDef.label,\n dataType: attrDef.dataType,\n isArray: attrDef.isArray,\n isRequired: attrDef.isRequired,\n isVisible: attrDef.isVisible,\n isReadOnly: attrDef.isReadOnly,\n order: attrDef.order,\n rules: attrDef.rules ?? [],\n isValueChanged: true,\n };\n\n if (attrDef.dataType === 'AsDetail') {\n // Server expects attr.value null for AsDetail; the nested PO carries the data.\n attr.value = null;\n attr.asDetailType = attrDef.asDetailType;\n\n const nestedType = attrDef.asDetailType ? resolve(attrDef.asDetailType) : undefined;\n if (!nestedType) {\n attr.object = null;\n attr.objects = attrDef.isArray ? [] : null;\n return attr;\n }\n\n if (attrDef.isArray) {\n const items: any[] = Array.isArray(raw) ? raw : [];\n attr.objects = items.map(item => dictToNestedPo((item as Record<string, any>) ?? {}, nestedType, resolve));\n } else {\n attr.object = raw ? dictToNestedPo(raw as Record<string, any>, nestedType, resolve) : null;\n }\n return attr;\n }\n\n if (isDateDataType(attrDef.dataType)) {\n // The mirror of the conversion in `attributeValueForForm`: back from the control's bare wall\n // clock to a complete ISO-8601 instant carrying the viewer's offset for that date. When the\n // instant is unchanged, send back exactly what was loaded instead, so an untouched row keeps\n // its stored offset. See AS_DETAIL_ORIGINAL_DATES_KEY.\n const converted = fromDateInputValue(attrDef.dataType, raw);\n attr.value = wireDatesEqual(converted, originalDate) ? originalDate : converted;\n return attr;\n }\n\n attr.value = raw;\n return attr;\n}\n","import { TranslatedString } from './translated-string';\nimport { PersistentObject } from './persistent-object';\nimport { AS_DETAIL_BREADCRUMBS_KEY } from './as-detail-conversions';\n\n/**\n * What a query returns: the column metadata once, then one lightweight row per result.\n *\n * Rows used to be full `PersistentObject`s, so every row repeated the complete attribute\n * metadata the client already held from `/spark/types` and never read off the row. More\n * importantly it conflated two things that are not the same: a **row is a projection**, a\n * **persistent object is a document**. Nothing here is entitled to act on a row — every\n * mutating path re-loads by id and re-applies security server-side.\n */\nexport interface QueryResult {\n columns: QueryColumn[];\n items: QueryResultItem[];\n /** Rows matching the query after filtering, before paging. */\n totalItems: number;\n skip: number;\n take: number;\n typeHints?: Record<string, string>;\n /**\n * Custom actions withheld for THIS result, by name. Absent or empty means every action the\n * caller has the right to is offered.\n *\n * The mirror of `PersistentObject.disabledActions`, set server-side via\n * `CustomQueryArgs.DisableActions(...)`. The action catalogue is per type and is never told what\n * an execution returned, so this is where a per-result answer travels.\n *\n * An affordance, not a permission -- the action handler still refuses on its own terms.\n */\n disabledActions?: string[];\n}\n\n/**\n * The subset of column metadata a cell needs in order to render.\n *\n * Both {@link QueryColumn} (a query result's column) and `EntityAttributeDefinition` (a detail\n * page's attribute) satisfy this, which is what lets one cell component serve the query grid and\n * the AsDetail sub-table without either side knowing about the other's shape.\n */\nexport interface SparkCellColumn {\n name: string;\n label?: TranslatedString;\n /** Help text rendered as an [i] tooltip in the column header. Absent when the model declares none. */\n description?: TranslatedString;\n dataType: string;\n isArray?: boolean;\n renderer?: string;\n rendererOptions?: Record<string, any>;\n referenceType?: string;\n lookupReferenceType?: string;\n asDetailType?: string;\n}\n\n/** One column of a query result, sent once rather than repeated on every row. */\nexport interface QueryColumn extends SparkCellColumn {\n order: number;\n /**\n * Whether the grid offers a sort affordance for this column. Resolved server-side from the query's\n * override then the attribute, so it arrives already decided.\n *\n * This replaced an `isSortable` that carried the server's AsDetail *drag-reorder* flag — a\n * different concept that was `false` for every scalar column while the grid hard-coded every\n * header sortable anyway. Neither side read it. The attribute-level `isSortable` on `EntityType`\n * is the drag-reorder flag and stays.\n *\n * Absent means sortable, matching `isVisible`: a server predating the field still draws arrows.\n */\n canSort?: boolean;\n /** Whether the grid draws a filter cell for this column. Absent means filterable. */\n canFilter?: boolean;\n /**\n * Whether this column's distinct values may be listed. When `false` and `canFilter` is true the\n * panel offers a free-text filter instead of a value list — listing a column's values is a much\n * stronger disclosure than filtering by one already known.\n */\n canListDistincts?: boolean;\n /**\n * Whether the grid draws this column. `false` means the row carries the value but no column is\n * rendered — for a renderer that needs a sibling value (a lock glyph beside a name) without\n * spending a column on it. `showedOn` decides what ships; this decides what is drawn.\n */\n isVisible?: boolean;\n /** The query backing a Reference column, by name — the client's option source. */\n query?: string;\n}\n\n/**\n * One row: an id, a display string, and a value per column.\n *\n * Deliberately too weak to act on — no attribute metadata, no `can` block, no etag, because none\n * of those can be trusted from a projection.\n */\nexport interface QueryResultItem {\n /** Never null and unique within a result; the server refuses anything else. */\n id: string;\n /** What to show when the row is named rather than tabulated (a reference picker's value). */\n breadcrumb?: string;\n values: QueryResultItemValue[];\n /** Presentation hints for the whole row. Keys arrive lower-cased. */\n typeHints?: Record<string, string>;\n}\n\n/** One cell. */\nexport interface QueryResultItemValue {\n /** The {@link QueryColumn.name} this value belongs to. */\n key: string;\n /** Typed JSON, not a string — the server converts on the way out. */\n value?: any;\n /** For a reference cell: the target document's id, so a link needs no second lookup. */\n objectId?: string;\n /** For a reference cell: the target's display string. */\n breadcrumb?: string;\n /** Resolved display labels per referenced id, for a multi-reference cell. */\n breadcrumbs?: Record<string, string | null>;\n typeHints?: Record<string, string>;\n}\n\n/**\n * Every shape a renderer's `item` can arrive in.\n *\n * Three, and they are genuinely different objects rather than variations of one: a query grid hands\n * a {@link QueryResultItem}, an AsDetail sub-table hands the flat record `nestedPoToDisplayRow`\n * built, and a detail or form host hands the `PersistentObject` itself. A renderer reused across a\n * grid and an AsDetail table therefore sees two of them, which is why {@link valueFor} reads all\n * three rather than making every app write the branch.\n */\nexport type SparkRow = QueryResultItem | PersistentObject | Record<string, unknown>;\n\n/**\n * The value a row carries for a column, whatever shape the row is in — or `undefined` when it\n * carries none.\n *\n * ```ts\n * const isPrivate = valueFor(this.item(), 'IsPrivate')?.value === true;\n * ```\n *\n * Always returns the **cell** rather than the bare value, because a reference cell is worth as much\n * for its `objectId` as for its text, and a helper that discarded it would have to be replaced the\n * first time a renderer wanted to link somewhere. `?.value` is the cost, once per call site.\n *\n * ⚠️ A value only reaches a grid row if its attribute is on the **query surface**. To read a sibling\n * the grid does not draw, mark it `\"showedOn\": \"Query\", \"isVisible\": false` — shipped, not drawn. An\n * attribute marked `\"showedOn\": \"PersistentObject\"` is absent from a row by design, and this returns\n * `undefined` for it; that is the first thing to check when a sibling read comes back empty.\n */\nexport function valueFor(item: SparkRow | null | undefined, columnName: string): QueryResultItemValue | undefined {\n if (!item) return undefined;\n\n if (isQueryRow(item)) {\n return item.values.find(v => v.key === columnName);\n }\n\n if (isPersistentObject(item)) {\n const attribute = item.attributes.find(a => a.name === columnName);\n if (!attribute) return undefined;\n return {\n key: attribute.name,\n value: attribute.value,\n // A single reference's value IS the target's id, which is what the row shape carries\n // separately. Deriving it here keeps a renderer from having to know that.\n objectId: attribute.dataType === 'Reference' && !attribute.isArray && attribute.value != null\n ? String(attribute.value)\n : undefined,\n breadcrumb: attribute.breadcrumb,\n breadcrumbs: attribute.breadcrumbs,\n };\n }\n\n const record = item as Record<string, unknown>;\n if (!(columnName in record)) return undefined;\n\n const value = record[columnName];\n // The flat row's side channel is populated only for single references, so its presence for this\n // key is what identifies one — the record itself carries no dataType to ask.\n const breadcrumb = (record[AS_DETAIL_BREADCRUMBS_KEY] as Record<string, string> | undefined)?.[columnName];\n\n return {\n key: columnName,\n value,\n objectId: breadcrumb !== undefined && value != null ? String(value) : undefined,\n breadcrumb,\n };\n}\n\n/**\n * Whether this row is a query result item.\n *\n * Discriminated on the *elements*, not just on `Array.isArray(values)`: a flat AsDetail record may\n * legitimately have a column named `values` holding an array, and mistaking one for the other would\n * silently read the wrong thing.\n */\nexport function isQueryRow(row: SparkRow | null | undefined): row is QueryResultItem {\n const values = (row as QueryResultItem | undefined)?.values;\n return Array.isArray(values) && (values.length === 0 || typeof values[0]?.key === 'string');\n}\n\n/** Whether this row is a full persistent object. Discriminated on elements, as above. */\nexport function isPersistentObject(row: SparkRow | null | undefined): row is PersistentObject {\n const attributes = (row as PersistentObject | undefined)?.attributes;\n return Array.isArray(attributes) && (attributes.length === 0 || typeof attributes[0]?.name === 'string');\n}\n\n/**\n * One column's value filter (#431): the values a caller picked, or the values they excluded.\n *\n * **Values, never labels.** Each entry is the raw stored value — the same `value` the distinct\n * endpoint returned, never the `label` beside it. A reference column's values are document ids while\n * its labels are resolved breadcrumb text, and two rows can legitimately render the same text, so\n * matching on labels would be both wrong and ambiguous.\n *\n * `includes` and `excludes` are the two halves of the inverse toggle rather than a flag: the panel\n * moves values between them. `null` is a real, selectable value meaning \"no value\"; an empty or\n * absent array is the absent filter.\n */\nexport interface QueryColumnFilter {\n name: string;\n includes?: unknown[];\n excludes?: unknown[];\n}\n\n/**\n * One selectable entry in a column's filter panel.\n *\n * Structurally identical to the datatable's own `DistinctValue`, deliberately — it is what lets the\n * HTTP response be handed straight to `[distincts]` with no mapper in between.\n */\nexport interface DistinctValue {\n /** The raw stored value. `null` is a real entry meaning \"no value\", not an absent one. */\n value: unknown;\n /** What the user reads. Never matched on. */\n label: string;\n}\n\n/**\n * A column's distinct values.\n *\n * `remaining` is always empty from this server: the datatable snapshots the list when a selection is\n * first made and derives both buckets itself by diffing. The server answers \"what matches now\".\n *\n * `hasMore` reports that the cap truncated the list, and the panel re-queries only when it is set or\n * the search term widens — so a dishonest `false` strands a user typing past the cap.\n */\nexport interface DistinctValuesResult {\n matching: DistinctValue[];\n remaining: DistinctValue[];\n hasMore: boolean;\n}\n","import { TranslatedString } from './translated-string';\n\nexport enum ELookupDisplayType {\n Dropdown = 0,\n Modal = 1\n}\n\nexport interface LookupReferenceListItem {\n name: string;\n isTransient: boolean;\n valueCount: number;\n displayType: ELookupDisplayType;\n}\n\nexport interface LookupReference {\n name: string;\n isTransient: boolean;\n displayType: ELookupDisplayType;\n values: LookupReferenceValue[];\n}\n\nexport interface LookupReferenceValue {\n key: string;\n values: TranslatedString;\n isActive: boolean;\n extra?: Record<string, unknown>;\n}\n","import { CustomActionDefinition } from './custom-action';\n\n/**\n * The custom actions a query should offer, from the entity type's full set.\n *\n * `showedOn` must include the query side. The accepted values are `\"detail\"`, `\"query\"`\n * and `\"both\"` — as the server model and the custom-actions guide have always\n * documented. Both grids previously tested for `\"list\"`, a value nothing emits, so an\n * action authored per the documentation rendered nowhere at all.\n *\n * ⚠️ This narrows what is DISPLAYED. It is NOT an authorization boundary: the grant\n * is, and it is enforced independently in `ExecuteCustomAction` regardless of which\n * query the caller clicked from — a caller can always POST directly.\n */\nexport function filterQueryActions(\n actions: CustomActionDefinition[],\n): CustomActionDefinition[] {\n return actions.filter(a => a.showedOn === 'query' || a.showedOn === 'both');\n}\n","/**\n * Parses a custom action's `selectionRule` — a cardinality expression over the number\n * of selected rows — into a predicate.\n *\n * A port of the server's `SelectionRuleParser`, and the two MUST agree: they are tested\n * against one shared fixture (`selection-rule.fixture.json`) for exactly this reason.\n * Vidyano, where this grammar comes from, has the same algorithm in C# and JavaScript and\n * the two have already drifted — one throws on a non-numeric operand where the other\n * silently permits everything.\n *\n * Grammar: `X` is the count placeholder, whitespace is insignificant, terms split on `X`\n * are AND-combined (`1<X<5` is a range), operators are `<= >= < > != =` matched in that\n * order so `>=` is never read as `>`, and a number-first term is mirrored (`0<X` is `>0`).\n *\n * Client-side this only drives whether a button is disabled. The server enforces the same\n * rule independently — and neither is an authorization boundary: the action's grant is.\n */\nexport function parseSelectionRule(rule?: string | null): (count: number) => boolean {\n if (!rule || !rule.trim()) return () => true;\n try {\n return compile(rule);\n } catch {\n // Unlike the server, which refuses to start on a malformed rule, the client cannot\n // usefully fail: the rule arrived over the wire from a server that already validated\n // it. Disabling the button is the safe direction — it never permits an action the\n // server would refuse.\n return () => false;\n }\n}\n\nconst OPERATORS = ['<=', '>=', '<', '>', '!=', '='] as const;\n\nfunction compile(rule: string): (count: number) => boolean {\n const normalized = rule.replace(/ /g, '').toUpperCase();\n const terms = normalized.split('X').filter(t => t.length > 0);\n if (terms.length === 0) throw new Error(`Selection rule '${rule}' has no condition.`);\n\n const numberFirst = !normalized.startsWith('X') && normalized.includes('X');\n const predicates = terms.map((term, i) => compileTerm(term, rule, numberFirst && i === 0));\n\n return count => predicates.every(p => p(count));\n}\n\nfunction compileTerm(term: string, rule: string, mirrored: boolean): (count: number) => boolean {\n const op = OPERATORS.find(o => (mirrored ? term.endsWith(o) : term.startsWith(o)));\n if (!op) throw new Error(`Selection rule '${rule}' has no recognised operator in '${term}'.`);\n\n const numberPart = mirrored ? term.slice(0, term.length - op.length) : term.slice(op.length);\n // Number(' ') is 0 and Number('1.5') is 1.5 — neither is a valid operand here.\n if (!/^-?\\d+$/.test(numberPart)) {\n throw new Error(`Selection rule '${rule}' has a non-numeric operand in '${term}'.`);\n }\n const value = Number(numberPart);\n const effective = mirrored ? mirror(op) : op;\n\n switch (effective) {\n case '<=': return count => count <= value;\n case '>=': return count => count >= value;\n case '<': return count => count < value;\n case '>': return count => count > value;\n case '!=': return count => count !== value;\n case '=': return count => count === value;\n default: throw new Error(`Selection rule '${rule}' has an unsupported operator '${effective}'.`);\n }\n}\n\nfunction mirror(op: string): string {\n switch (op) {\n case '<': return '>';\n case '>': return '<';\n case '<=': return '>=';\n case '>=': return '<=';\n default: return op;\n }\n}\n","import { CustomActionDefinition } from './custom-action';\nimport { parseSelectionRule } from './selection-rule';\n\nexport type SparkSelectionMode = 'none' | 'single' | 'multiple';\n\n/**\n * The selection mode a grid needs in order to satisfy the actions offered on it.\n *\n * Derived rather than configured, so a grid gains a checkbox column exactly when an\n * action needs one and is otherwise pixel-identical to a grid with no selection at all.\n * Vidyano's query grid does the same thing — it renders the checkbox column only if some\n * action is selection-gated.\n *\n * `'single'` when every gated action is satisfied by one row and refused by two; anything\n * else that cares about the count gets `'multiple'`.\n */\nexport function selectionModeFor(actions: CustomActionDefinition[]): SparkSelectionMode {\n // An action with no rule is not selection-gated: it acts on the query, not on rows.\n const gated = actions.filter(a => !!a.selectionRule?.trim());\n if (gated.length === 0) return 'none';\n\n const everyRuleWantsExactlyOne = gated.every(a => {\n const rule = parseSelectionRule(a.selectionRule);\n return rule(1) && !rule(2);\n });\n\n return everyRuleWantsExactlyOne ? 'single' : 'multiple';\n}\n","import { EntityAttributeDefinition } from './entity-type';\nimport { PersistentObject } from './persistent-object';\nimport { ValidationRule } from './validation-rule';\n\n/**\n * One selectable value, as replaced by a refresh hook. Mirrors the server's\n * `PersistentObjectAttributeOption`.\n */\nexport interface RefreshedOption {\n key: string;\n label?: Record<string, string>;\n}\n\n/**\n * What a refresh changed about one attribute's *presentation*.\n *\n * Kept separate from `EntityType` on purpose. The form's option loading hangs off a single effect\n * keyed on `entityType` identity, and `SparkService` caches nothing — so applying a refresh by\n * setting a new `EntityType` re-issues every reference query, every lookup fetch, a full\n * `getEntityTypes()` and a `getPermissions()` per array-AsDetail attribute, on every refresh.\n * Mutating the existing object instead is inert, because the rendering computed would not re-run.\n * An overlay is the only shape that is both reactive and free.\n */\nexport interface AttributeOverlay {\n isRequired?: boolean;\n isReadOnly?: boolean;\n isVisible?: boolean;\n rules?: ValidationRule[];\n query?: string;\n /** `undefined` means the hook did not touch the options; an empty array means there are none. */\n options?: RefreshedOption[];\n}\n\nexport type RefreshOverlay = Record<string, AttributeOverlay>;\n\n/** Applies an overlay to one attribute definition, returning a new object when anything changed. */\nexport function applyOverlay(\n attr: EntityAttributeDefinition,\n overlay: AttributeOverlay | undefined,\n): EntityAttributeDefinition {\n if (!overlay) return attr;\n\n return {\n ...attr,\n isRequired: overlay.isRequired ?? attr.isRequired,\n isReadOnly: overlay.isReadOnly ?? attr.isReadOnly,\n isVisible: overlay.isVisible ?? attr.isVisible,\n rules: overlay.rules ?? attr.rules,\n query: overlay.query ?? attr.query,\n };\n}\n\n/**\n * Reads a refresh response into an overlay.\n *\n * Everything here is presentation the server owns outright, so it is taken verbatim — there is no\n * merging to do on this half, only on values.\n */\nexport function overlayFromResponse(response: PersistentObject): RefreshOverlay {\n const overlay: RefreshOverlay = {};\n\n for (const attr of response.attributes ?? []) {\n overlay[attr.name] = {\n isRequired: attr.isRequired,\n isReadOnly: attr.isReadOnly,\n isVisible: attr.isVisible,\n rules: attr.rules ?? [],\n query: attr.query,\n options: (attr as { options?: RefreshedOption[] | null }).options ?? undefined,\n };\n }\n\n return overlay;\n}\n\n/**\n * Merges a refresh response's values into the live form.\n *\n * The rule, and the reason for it: a refresh is not instant, and the user keeps typing during it —\n * the form is deliberately never frozen. So for each attribute we ask whether the *server* changed\n * it, by comparing the response against the values that were **sent**, not against what is on\n * screen now.\n *\n * - server value equals what we sent → the hook did not touch it, so whatever is in the form now\n * wins, including anything typed while the request was in flight;\n * - server value differs → the hook deliberately changed it, and it wins over a concurrent edit.\n *\n * Comparing against the displayed value instead is the classic \"refresh eats my typing\" bug;\n * refusing to overwrite anything the user touched is the equally wrong opposite, where a dependent\n * field the hook computed never appears.\n *\n * @param sent values as they were POSTed, captured before the request left\n * @param current values as they are now, which may have moved on\n * @param response the reshaped object\n */\nexport function mergeRefreshValues(\n sent: Record<string, any>,\n current: Record<string, any>,\n response: PersistentObject,\n): Record<string, any> {\n const merged = { ...current };\n\n for (const attr of response.attributes ?? []) {\n const serverValue = attr.value ?? null;\n const sentValue = sent[attr.name] ?? null;\n\n if (!valuesEqual(serverValue, sentValue)) {\n merged[attr.name] = attr.value;\n }\n }\n\n return merged;\n}\n\n/**\n * Structural for arrays, `===` otherwise.\n *\n * Multi-reference attributes hold `string[]`, and a fresh array of the same ids is a different\n * object — so reference equality would report every one of them as \"changed by the server\" on\n * every refresh, and clobber in-flight edits to them.\n */\nfunction valuesEqual(a: any, b: any): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => valuesEqual(item, b[i]));\n }\n // Values arrive off the wire as JSON scalars; anything else compares by JSON shape rather than\n // by identity, which is what a caller means by \"did this change\".\n if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n return false;\n}\n","import { ValidationRule } from './validation-rule';\nimport { TranslatedString } from './translated-string';\n\n/** One rule failure, in the shape the form already renders per field. */\nexport interface RuleFailure {\n attributeName: string;\n ruleType: string;\n message: string;\n}\n\nexport interface EvaluableAttribute {\n name: string;\n label?: TranslatedString;\n isRequired?: boolean;\n rules?: ValidationRule[];\n}\n\nconst EMAIL = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/i;\nconst URL = /^https?:\\/\\/[^\\s]+$/i;\n\nfunction isEmpty(value: any): boolean {\n if (value === null || value === undefined) return true;\n if (typeof value === 'string') return value.trim() === '';\n if (Array.isArray(value)) return value.length === 0;\n return false;\n}\n\n/**\n * Evaluates an attribute's rules against a value, mirroring the server's `ValidationService`.\n *\n * ⚠️ **Parity with the server is the point, and disagreement is worse than silence.** A client that\n * rejects something the server would accept blocks legitimate work with no recourse; one that\n * accepts something the server rejects merely defers the error to the round-trip, which is where it\n * used to live anyway. So the rule set here is deliberately limited to the types the server\n * implements, and an unrecognised rule type is ignored rather than guessed at.\n *\n * This exists because a refresh hook that imposes a rule needs it to bite before Save — previously\n * `rules` was carried on the wire and never evaluated in the browser at all.\n */\nexport function evaluateRules(attr: EvaluableAttribute, value: any): RuleFailure[] {\n const failures: RuleFailure[] = [];\n const label = attr.label?.['en'] ?? attr.name;\n\n if (attr.isRequired && isEmpty(value)) {\n return [{ attributeName: attr.name, ruleType: 'required', message: `${label} is required` }];\n }\n\n // A rule other than \"required\" says nothing about an absent value — that is what required is for.\n if (isEmpty(value)) return failures;\n\n const text = value?.toString() ?? '';\n\n for (const rule of attr.rules ?? []) {\n const failure = evaluateRule(attr.name, label, rule, value, text);\n if (failure) failures.push(failure);\n }\n\n return failures;\n}\n\nfunction evaluateRule(\n name: string,\n label: string,\n rule: ValidationRule,\n value: any,\n text: string,\n): RuleFailure | null {\n const message = rule.message?.['en'];\n\n switch (rule.type?.toLowerCase()) {\n case 'maxlength': {\n const max = toInt(rule.value);\n if (max === null || text.length <= max) return null;\n return { attributeName: name, ruleType: 'maxLength', message: message ?? `${label} must be at most ${max} characters` };\n }\n case 'minlength': {\n const min = toInt(rule.value);\n if (min === null || text.length >= min) return null;\n return { attributeName: name, ruleType: 'minLength', message: message ?? `${label} must be at least ${min} characters` };\n }\n case 'range': {\n const numeric = toNumber(value);\n if (numeric === null) return null;\n if (rule.min !== undefined && rule.min !== null && numeric < rule.min) {\n return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at least ${rule.min}` };\n }\n if (rule.max !== undefined && rule.max !== null && numeric > rule.max) {\n return { attributeName: name, ruleType: 'range', message: message ?? `${label} must be at most ${rule.max}` };\n }\n return null;\n }\n case 'regex': {\n const pattern = rule.value?.toString();\n if (!pattern) return null;\n let re: RegExp;\n try {\n re = new RegExp(pattern);\n } catch {\n // An unparseable pattern is a server-side authoring bug. Staying silent leaves the server to\n // report it, which is strictly better than blocking the user over a rule nobody can evaluate.\n return null;\n }\n return re.test(text) ? null : { attributeName: name, ruleType: 'regex', message: message ?? `${label} is not in the expected format` };\n }\n case 'email':\n return EMAIL.test(text) ? null : { attributeName: name, ruleType: 'email', message: message ?? `${label} must be a valid email address` };\n case 'url':\n return URL.test(text) ? null : { attributeName: name, ruleType: 'url', message: message ?? `${label} must be a valid URL` };\n default:\n return null;\n }\n}\n\n/**\n * Mirrors the server's `TryGetIntValue`: an integer number, or a string `int.TryParse` accepts.\n * Anything else means \"no rule\". `Number(value)` is NOT that — it turns null and '' into 0 (so every\n * non-empty value failed \"must be at most 0 characters\") and truncated 2.5, which the server ignores.\n */\nfunction toInt(value: any): number | null {\n if (typeof value === 'number') return Number.isInteger(value) ? value : null;\n if (typeof value === 'string' && /^\\s*[+-]?\\d+\\s*$/.test(value)) return parseInt(value, 10);\n return null;\n}\n\nfunction toNumber(value: any): number | null {\n const n = Number(value);\n return Number.isFinite(n) ? n : null;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|