@mintplayer/ng-spark 22.11.0 → 22.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-services.mjs","sources":["../../services/src/retry-action.service.ts","../../services/src/spark-language.service.ts","../../services/src/spark-streaming.service.ts","../../services/src/spark.service.ts","../../services/src/spark-built-in-icons.ts","../../services/src/spark-icon-registry.ts","../../services/src/spark-query-actions.service.ts","../../services/mintplayer-ng-spark-services.ts"],"sourcesContent":["import { Injectable, signal } from '@angular/core';\nimport { RetryActionPayload, RetryActionResult } from '@mintplayer/ng-spark/models';\n\n@Injectable({ providedIn: 'root' })\nexport class RetryActionService {\n private resolveRetry: ((result: RetryActionResult) => void) | null = null;\n\n payload = signal<RetryActionPayload | null>(null);\n\n show(payload: RetryActionPayload): Promise<RetryActionResult> {\n this.payload.set(payload);\n return new Promise(resolve => { this.resolveRetry = resolve; });\n }\n\n respond(result: RetryActionResult): void {\n this.payload.set(null);\n this.resolveRetry?.(result);\n this.resolveRetry = null;\n }\n}\n","import { Injectable, inject, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { TranslatedString, currentLanguage } from '@mintplayer/ng-spark/models';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\ninterface CultureConfiguration {\n languages: Record<string, TranslatedString>;\n defaultLanguage: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class SparkLanguageService {\n private readonly http = inject(HttpClient);\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly currentLang = signal('en');\n private readonly translationsMap = signal<Record<string, TranslatedString>>({});\n\n readonly language = this.currentLang.asReadonly();\n readonly languages = signal<Record<string, TranslatedString>>({});\n\n constructor() {\n this.loadCulture();\n this.loadTranslations();\n }\n\n private async loadCulture(): Promise<void> {\n const config = await firstValueFrom(this.http.get<CultureConfiguration>(`${this.baseUrl}/culture`));\n this.languages.set(config.languages);\n const saved = localStorage.getItem('spark-lang');\n const lang = saved ?? config.defaultLanguage;\n this.currentLang.set(lang);\n currentLanguage.set(lang);\n }\n\n private async loadTranslations(): Promise<void> {\n const t = await firstValueFrom(this.http.get<Record<string, TranslatedString>>(`${this.baseUrl}/translations`));\n this.translationsMap.set(t);\n }\n\n setLanguage(lang: string) {\n this.currentLang.set(lang);\n currentLanguage.set(lang);\n localStorage.setItem('spark-lang', lang);\n }\n\n resolve(ts: TranslatedString | undefined): string {\n if (!ts) return '';\n const lang = this.currentLang();\n return ts[lang] ?? ts['en'] ?? Object.values(ts)[0] ?? '';\n }\n\n t(key: string): string {\n const ts = this.translationsMap()[key];\n return this.resolve(ts) || key;\n }\n}\n","import { inject, Injectable, NgZone } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { StreamingMessage } from '@mintplayer/ng-spark/models';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\n@Injectable({ providedIn: 'root' })\nexport class SparkStreamingService {\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly ngZone = inject(NgZone);\n\n connectToStreamingQuery(queryId: string): Observable<StreamingMessage> {\n return new Observable<StreamingMessage>(subscriber => {\n let ws: WebSocket | null = null;\n let retryCount = 0;\n let retryTimeout: ReturnType<typeof setTimeout> | null = null;\n let closed = false;\n const maxRetries = 10;\n const maxDelay = 30000;\n\n const connect = () => {\n if (closed) return;\n\n const url = this.buildWebSocketUrl(queryId);\n ws = new WebSocket(url);\n\n ws.onopen = () => {\n retryCount = 0; // Reset on successful connect\n };\n\n ws.onmessage = (event) => {\n try {\n const message: StreamingMessage = JSON.parse(event.data);\n this.ngZone.run(() => subscriber.next(message));\n } catch {\n // Ignore malformed messages\n }\n };\n\n ws.onerror = () => {\n // Error will trigger onclose\n };\n\n ws.onclose = (event) => {\n if (closed) return;\n\n // Don't reconnect on normal closure\n if (event.code === 1000) {\n this.ngZone.run(() => subscriber.complete());\n return;\n }\n\n // Reconnect with exponential backoff\n if (retryCount < maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, retryCount), maxDelay);\n retryCount++;\n retryTimeout = setTimeout(() => connect(), delay);\n } else {\n this.ngZone.run(() => subscriber.error(new Error('WebSocket connection failed after maximum retries')));\n }\n };\n };\n\n connect();\n\n // Teardown: close WebSocket on unsubscribe\n return () => {\n closed = true;\n if (retryTimeout) {\n clearTimeout(retryTimeout);\n }\n if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {\n ws.close(1000, 'Client unsubscribed');\n }\n };\n });\n }\n\n private buildWebSocketUrl(queryId: string): string {\n const encodedId = encodeURIComponent(queryId);\n const path = `${this.baseUrl}/queries/${encodedId}/stream`;\n\n // If baseUrl is absolute (starts with http/https), replace protocol\n if (this.baseUrl.startsWith('http://')) {\n return path.replace(/^http:\\/\\//, 'ws://');\n }\n if (this.baseUrl.startsWith('https://')) {\n return path.replace(/^https:\\/\\//, 'wss://');\n }\n\n // Relative URL — construct from window.location\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n return `${protocol}//${window.location.host}${path}`;\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { CustomActionDefinition, EntityPermissions, EntityType, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, ProgramUnitsConfiguration, QueryResult, SparkQuery, RetryActionPayload, RetryActionResult } from '@mintplayer/ng-spark/models';\nimport { ClientOperationEnvelope, RetryOperation, SparkClientOperationDispatcher } from '@mintplayer/ng-spark/client-operations';\nimport { SortColumn } from '@mintplayer/pagination';\nimport { RetryActionService } from './retry-action.service';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\n@Injectable({ providedIn: 'root' })\nexport class SparkService {\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly http = inject(HttpClient);\n private readonly retryActionService = inject(RetryActionService);\n private readonly dispatcher = inject(SparkClientOperationDispatcher);\n\n // Entity Types\n async getEntityTypes(): Promise<EntityType[]> {\n return firstValueFrom(this.http.get<EntityType[]>(`${this.baseUrl}/types`));\n }\n\n async getEntityType(id: string): Promise<EntityType> {\n return firstValueFrom(this.http.get<EntityType>(`${this.baseUrl}/types/${encodeURIComponent(id)}`));\n }\n\n async getEntityTypeByClrType(clrType: string): Promise<EntityType | undefined> {\n const types = await this.getEntityTypes();\n return types.find(t => t.clrType === clrType);\n }\n\n // Permissions\n async getPermissions(entityTypeId: string): Promise<EntityPermissions> {\n return firstValueFrom(this.http.get<EntityPermissions>(`${this.baseUrl}/permissions/${encodeURIComponent(entityTypeId)}`));\n }\n\n // Queries\n async getQueries(): Promise<SparkQuery[]> {\n return firstValueFrom(this.http.get<SparkQuery[]>(`${this.baseUrl}/queries`));\n }\n\n async getQuery(id: string): Promise<SparkQuery> {\n return firstValueFrom(this.http.get<SparkQuery>(`${this.baseUrl}/queries/${encodeURIComponent(id)}`));\n }\n\n async getQueryByName(name: string): Promise<SparkQuery | undefined> {\n const queries = await this.getQueries();\n return queries.find(q => q.name === name);\n }\n\n async executeQuery(queryId: string, options?: {\n sortColumns?: SortColumn[];\n parentId?: string;\n parentType?: string;\n skip?: number;\n take?: number;\n search?: string;\n }): Promise<QueryResult> {\n let params = new HttpParams();\n if (options?.sortColumns?.length) {\n params = params.set('sortColumns',\n options.sortColumns.map(c => `${c.property}:${c.direction === 'descending' ? 'desc' : 'asc'}`).join(',')\n );\n }\n if (options?.parentId) params = params.set('parentId', options.parentId);\n if (options?.parentType) params = params.set('parentType', options.parentType);\n if (options?.skip != null) params = params.set('skip', options.skip);\n if (options?.take != null) params = params.set('take', options.take);\n if (options?.search) params = params.set('search', options.search);\n return firstValueFrom(this.http.get<QueryResult>(\n `${this.baseUrl}/queries/${encodeURIComponent(queryId)}/execute`,\n { params }\n ));\n }\n\n async executeQueryByName(queryName: string, options?: {\n parentId?: string;\n parentType?: string;\n }): Promise<QueryResult> {\n const query = await this.getQueryByName(queryName);\n return query ? this.executeQuery(query.id, { parentId: options?.parentId, parentType: options?.parentType }) : { columns: [], items: [], totalItems: 0, skip: 0, take: 50 };\n }\n\n // Program Units\n async getProgramUnits(): Promise<ProgramUnitsConfiguration> {\n return firstValueFrom(this.http.get<ProgramUnitsConfiguration>(`${this.baseUrl}/program-units`));\n }\n\n // Persistent Objects\n async list(type: string): Promise<PersistentObject[]> {\n return firstValueFrom(this.http.get<PersistentObject[]>(`${this.baseUrl}/po/${encodeURIComponent(type)}`));\n }\n\n async get(type: string, id: string): Promise<PersistentObject> {\n return firstValueFrom(this.http.get<PersistentObject>(`${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`));\n }\n\n async create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject> {\n return this.postWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}`,\n { persistentObject: data }\n );\n }\n\n async update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject> {\n return this.putWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,\n { persistentObject: data }\n );\n }\n\n /**\n * Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.\n *\n * Writes nothing, but goes through the envelope like every other mutating call: a refresh may\n * legitimately emit notifications, and may open the retry-action prompt.\n *\n * `triggeredBy` is the attribute's name. For a trigger inside an AsDetail row it is the same\n * path form the inline validation errors use — `Jobs[2].ProfessionId`.\n */\n async refresh(type: string, data: Partial<PersistentObject>, triggeredBy: string): Promise<PersistentObject> {\n return this.postWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/refresh`,\n { persistentObject: data, triggeredBy }\n );\n }\n\n async delete(type: string, id: string): Promise<void> {\n return this.deleteWithEnvelope<void>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,\n {}\n );\n }\n\n // Custom Actions\n async getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]> {\n return firstValueFrom(this.http.get<CustomActionDefinition[]>(`${this.baseUrl}/actions/${encodeURIComponent(objectTypeId)}`));\n }\n\n /**\n * @param parent The object of THIS type the action is operating on — the detail-page invocation.\n * @param selectedItemIds Row ids from a query. Ids, never row objects: a row is a projection.\n * @param queryParent When invoked from a sub-query, the object whose detail page it was rendered\n * on — a DIFFERENT type (the cars listed on a company's page are Cars, the page is a Company).\n * Sent as id + type, exactly as the query endpoint names it, and resolved server-side under its\n * own type with its own Read gate.\n * @param queryId The query the selection came from, so the server can re-run it narrowed to those\n * ids and hand the action the rows the grid actually had -- index-computed columns included.\n */\n async executeCustomAction(\n objectTypeId: string,\n actionName: string,\n parent?: PersistentObject,\n selectedItemIds?: string[],\n queryParent?: { id: string; type: string },\n queryId?: string,\n ): Promise<void> {\n const body: {\n parent?: PersistentObject; selectedItemIds?: string[];\n parentId?: string; parentType?: string; queryId?: string;\n retryResults?: RetryActionResult[];\n } = { parent, selectedItemIds, parentId: queryParent?.id, parentType: queryParent?.type, queryId };\n return this.postWithEnvelope<void>(\n `${this.baseUrl}/actions/${encodeURIComponent(objectTypeId)}/${encodeURIComponent(actionName)}`,\n body as any\n );\n }\n\n // LookupReferences\n async getLookupReferences(): Promise<LookupReferenceListItem[]> {\n return firstValueFrom(this.http.get<LookupReferenceListItem[]>(`${this.baseUrl}/lookupref`));\n }\n\n async getLookupReference(name: string): Promise<LookupReference> {\n return firstValueFrom(this.http.get<LookupReference>(`${this.baseUrl}/lookupref/${encodeURIComponent(name)}`));\n }\n\n async addLookupReferenceValue(name: string, value: LookupReferenceValue): Promise<LookupReferenceValue> {\n return firstValueFrom(this.http.post<LookupReferenceValue>(`${this.baseUrl}/lookupref/${encodeURIComponent(name)}`, value));\n }\n\n async updateLookupReferenceValue(name: string, key: string, value: LookupReferenceValue): Promise<LookupReferenceValue> {\n return firstValueFrom(this.http.put<LookupReferenceValue>(\n `${this.baseUrl}/lookupref/${encodeURIComponent(name)}/${encodeURIComponent(key)}`,\n value\n ));\n }\n\n async deleteLookupReferenceValue(name: string, key: string): Promise<void> {\n return firstValueFrom(this.http.delete<void>(\n `${this.baseUrl}/lookupref/${encodeURIComponent(name)}/${encodeURIComponent(key)}`\n ));\n }\n\n // Envelope-aware HTTP helpers.\n // All mutation endpoints (Create / Update / Delete / Execute custom action) emit the\n // ClientOperationEnvelope { result, operations } shape. These helpers unwrap the envelope,\n // dispatch any non-retry operations, and translate 449 retry-operations into the existing\n // RetryActionService modal flow.\n\n private postWithEnvelope<T>(url: string, body: { persistentObject?: any; triggeredBy?: string; retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => firstValueFrom(this.http.post<ClientOperationEnvelope<T>>(url, body)),\n body,\n () => this.postWithEnvelope<T>(url, body),\n );\n }\n\n private putWithEnvelope<T>(url: string, body: { persistentObject?: any; triggeredBy?: string; retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => firstValueFrom(this.http.put<ClientOperationEnvelope<T>>(url, body)),\n body,\n () => this.putWithEnvelope<T>(url, body),\n );\n }\n\n private deleteWithEnvelope<T>(url: string, body: { retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => {\n const hasRetry = body.retryResults && body.retryResults.length > 0;\n return firstValueFrom(\n hasRetry\n ? this.http.delete<ClientOperationEnvelope<T>>(url, { body })\n : this.http.delete<ClientOperationEnvelope<T>>(url)\n );\n },\n body,\n () => this.deleteWithEnvelope<T>(url, body),\n );\n }\n\n private async sendWithEnvelope<T>(\n send: () => Promise<ClientOperationEnvelope<T>>,\n body: { retryResults?: RetryActionResult[] },\n retryFn: () => Promise<T>,\n ): Promise<T> {\n try {\n const envelope = await send();\n if (envelope?.operations?.length) {\n this.dispatcher.dispatch(envelope.operations);\n }\n return envelope?.result as T;\n } catch (error) {\n return this.handleEnvelopeRetryError<T>(error as HttpErrorResponse, retryFn, body);\n }\n }\n\n private async handleEnvelopeRetryError<T>(\n error: HttpErrorResponse,\n retryFn: () => Promise<T>,\n body: { retryResults?: RetryActionResult[] }\n ): Promise<T> {\n if (error.status !== 449) throw error;\n const envelope = error.error as ClientOperationEnvelope<T> | undefined;\n if (!envelope?.operations?.length) throw error;\n\n // Dispatch any non-retry operations accumulated before the retry throw\n // so notify/refresh/etc. fire BEFORE the retry modal opens.\n const nonRetry = envelope.operations.filter(o => o.type !== 'retry');\n if (nonRetry.length) this.dispatcher.dispatch(nonRetry);\n\n const retryOp = envelope.operations.find(o => o.type === 'retry') as RetryOperation | undefined;\n if (!retryOp) throw error;\n\n const payload: RetryActionPayload = {\n type: 'retry-action',\n step: retryOp.step,\n title: retryOp.title,\n options: retryOp.options,\n defaultOption: retryOp.defaultOption ?? undefined,\n persistentObject: retryOp.persistentObject ?? undefined,\n message: retryOp.message ?? undefined,\n };\n const result = await this.retryActionService.show(payload);\n if (result.option === 'Cancel' && !payload.options.includes('Cancel')) throw error;\n\n body.retryResults = [...(body.retryResults || []), result];\n return retryFn();\n }\n}\n","/** Built-in SVG icons used by ng-spark library templates. */\nexport const SPARK_BUILT_IN_ICONS: Record<string, string> = {\n 'arrow-left': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"/></svg>',\n 'pencil': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-pencil\" viewBox=\"0 0 16 16\"><path d=\"M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325\"/></svg>',\n 'plus': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-plus\" viewBox=\"0 0 16 16\"><path d=\"M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4\"/></svg>',\n 'plus-lg': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-plus-lg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2\"/></svg>',\n 'search': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-search\" viewBox=\"0 0 16 16\"><path d=\"M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0\"/></svg>',\n 'trash': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-trash\" viewBox=\"0 0 16 16\"><path d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z\"/><path d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z\"/></svg>',\n 'x-lg': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-lg\" viewBox=\"0 0 16 16\"><path d=\"M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z\"/></svg>',\n};\n","import { Injectable, inject } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { SPARK_BUILT_IN_ICONS } from './spark-built-in-icons';\n\n/**\n * R2-M12: SVG icon registry. The registry no longer accepts a raw string —\n * apps must pre-sanitize via `DomSanitizer.bypassSecurityTrustHtml` (or\n * `parseSvg` to validate) before calling `register`. Treating the parameter\n * as `SafeHtml` puts the explicit bypass decision in the caller's hands so a\n * developer wiring server-supplied SVG (per-tenant branding fetched from a\n * JSON endpoint) doesn't accidentally trust a `<svg><script>...</script>`\n * payload.\n *\n * The built-in icons baked into the package are still bypass-trusted internally\n * because they're known-safe at build time.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkIconRegistry {\n private sanitizer = inject(DomSanitizer);\n private icons = new Map<string, SafeHtml>();\n\n constructor() {\n for (const [name, svg] of Object.entries(SPARK_BUILT_IN_ICONS)) {\n this.icons.set(name, this.sanitizer.bypassSecurityTrustHtml(svg));\n }\n }\n\n /**\n * Registers a pre-sanitized SVG icon. Callers MUST validate or explicitly\n * trust the SVG before passing — for example:\n *\n * ```ts\n * const safe = sanitizer.bypassSecurityTrustHtml(svgFromBuildTimeConstant);\n * registry.register('app-logo', safe);\n * ```\n *\n * For server-supplied SVG, prefer a strict allow-list parser (no `<script>`,\n * no `on*` attributes, no `href=\"javascript:...\"`) before trusting.\n */\n register(name: string, svg: SafeHtml): void {\n this.icons.set(name, svg);\n }\n\n get(name: string): SafeHtml | undefined {\n return this.icons.get(name);\n }\n\n has(name: string): boolean {\n return this.icons.has(name);\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { CustomActionDefinition, PersistentObject, SparkQuery, EntityType, filterQueryActions } from '@mintplayer/ng-spark/models';\nimport { SparkService } from './spark.service';\n\n/**\n * A query's custom actions, reachable **without the grid that usually renders them**.\n *\n * ## Why this exists\n *\n * A query's actions were only ever obtainable by rendering `<spark-query-grid>`, which loads the\n * query, resolves its entity type, fetches the actions and filters them to the ones marked for a\n * query surface — all privately. A page that wanted the same buttons somewhere else (a toolbar\n * above the card, the shell's topbar) had three bad options: duplicate the four-step resolution\n * and let it drift, reach into the grid's internals, or put the button in the wrong place.\n *\n * This exposes the resolution itself. The grid keeps rendering the default placement; a host that\n * wants a different one asks here and renders its own control.\n *\n * ## What it does not do\n *\n * It does not authorize. `/spark/actions/{type}` returns only the actions this caller may see —\n * the server filters against `security.json`, and executing re-checks — so nothing here is a gate,\n * and a host must not treat \"the list came back empty\" as anything other than a display fact.\n *\n * It also does not cache. Actions depend on the caller, and a memo keyed by type id would survive\n * a sign-out.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkQueryActionsService {\n private readonly sparkService = inject(SparkService);\n\n /**\n * The custom actions of the query named by id or alias, already filtered to those that belong on\n * a query surface.\n *\n * Returns an empty list — rather than throwing — when the query resolves to no entity type,\n * because a caller rendering a toolbar wants \"no buttons\", not a broken page.\n */\n async actionsFor(queryIdOrAlias: string): Promise<CustomActionDefinition[]> {\n const context = await this.contextFor(queryIdOrAlias);\n if (!context) return [];\n\n // filterQueryActions, not a local predicate: the grid uses it too, and the `showedOn` values\n // are exactly the kind of thing a second copy gets subtly wrong — both grids once tested for\n // \"list\", a value nothing emits, and every correctly authored action rendered nowhere.\n return filterQueryActions(await this.sparkService.getCustomActions(context.entityType.id));\n }\n\n /**\n * Runs one of those actions. `selectedItemIds` are row ids, exactly as the grid posts them —\n * the server re-materializes each one through the same load path a detail page uses, so an id\n * from anywhere is treated as caller input rather than as a verified row.\n */\n async execute(\n queryIdOrAlias: string,\n actionName: string,\n options?: {\n parent?: PersistentObject;\n selectedItemIds?: string[];\n /** For a sub-query: the object whose detail page it was rendered on. See SparkService. */\n queryParent?: { id: string; type: string };\n },\n ): Promise<void> {\n const context = await this.contextFor(queryIdOrAlias);\n if (!context) {\n throw new Error(\n `Cannot execute '${actionName}': query '${queryIdOrAlias}' resolves to no entity type, so there ` +\n `is nothing to execute it against.`);\n }\n\n await this.sparkService.executeCustomAction(\n context.entityType.id, actionName, options?.parent, options?.selectedItemIds, options?.queryParent,\n context.query.id);\n }\n\n /**\n * The query and the entity type its rows are mapped against — the two-step resolution both\n * methods need, kept in one place so they cannot disagree about which type an action runs on.\n */\n private async contextFor(\n queryIdOrAlias: string,\n ): Promise<{ query: SparkQuery; entityType: EntityType } | null> {\n const [query, entityTypes] = await Promise.all([\n this.sparkService.getQuery(queryIdOrAlias),\n this.sparkService.getEntityTypes(),\n ]);\n if (!query) return null;\n\n const entityType = entityTypes.find(t => t.name === query.entityType)\n ?? entityTypes.find(t => t.id === query.entityType);\n return entityType ? { query, entityType } : null;\n }\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-services.mjs","sources":["../../services/src/retry-action.service.ts","../../services/src/spark-language.service.ts","../../services/src/spark-streaming.service.ts","../../services/src/spark.service.ts","../../services/src/spark-built-in-icons.ts","../../services/src/spark-icon-registry.ts","../../services/src/spark-query-actions.service.ts","../../services/mintplayer-ng-spark-services.ts"],"sourcesContent":["import { Injectable, signal } from '@angular/core';\nimport { RetryActionPayload, RetryActionResult } from '@mintplayer/ng-spark/models';\n\n@Injectable({ providedIn: 'root' })\nexport class RetryActionService {\n private resolveRetry: ((result: RetryActionResult) => void) | null = null;\n\n payload = signal<RetryActionPayload | null>(null);\n\n show(payload: RetryActionPayload): Promise<RetryActionResult> {\n this.payload.set(payload);\n return new Promise(resolve => { this.resolveRetry = resolve; });\n }\n\n respond(result: RetryActionResult): void {\n this.payload.set(null);\n this.resolveRetry?.(result);\n this.resolveRetry = null;\n }\n}\n","import { Injectable, inject, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { TranslatedString, currentLanguage } from '@mintplayer/ng-spark/models';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\ninterface CultureConfiguration {\n languages: Record<string, TranslatedString>;\n defaultLanguage: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class SparkLanguageService {\n private readonly http = inject(HttpClient);\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly currentLang = signal('en');\n private readonly translationsMap = signal<Record<string, TranslatedString>>({});\n\n readonly language = this.currentLang.asReadonly();\n readonly languages = signal<Record<string, TranslatedString>>({});\n\n constructor() {\n this.loadCulture();\n this.loadTranslations();\n }\n\n private async loadCulture(): Promise<void> {\n const config = await firstValueFrom(this.http.get<CultureConfiguration>(`${this.baseUrl}/culture`));\n this.languages.set(config.languages);\n const saved = localStorage.getItem('spark-lang');\n const lang = saved ?? config.defaultLanguage;\n this.currentLang.set(lang);\n currentLanguage.set(lang);\n }\n\n private async loadTranslations(): Promise<void> {\n const t = await firstValueFrom(this.http.get<Record<string, TranslatedString>>(`${this.baseUrl}/translations`));\n this.translationsMap.set(t);\n }\n\n setLanguage(lang: string) {\n this.currentLang.set(lang);\n currentLanguage.set(lang);\n localStorage.setItem('spark-lang', lang);\n }\n\n resolve(ts: TranslatedString | undefined): string {\n if (!ts) return '';\n const lang = this.currentLang();\n return ts[lang] ?? ts['en'] ?? Object.values(ts)[0] ?? '';\n }\n\n t(key: string): string {\n const ts = this.translationsMap()[key];\n return this.resolve(ts) || key;\n }\n}\n","import { inject, Injectable, NgZone } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { StreamingMessage } from '@mintplayer/ng-spark/models';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\n@Injectable({ providedIn: 'root' })\nexport class SparkStreamingService {\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly ngZone = inject(NgZone);\n\n connectToStreamingQuery(queryId: string): Observable<StreamingMessage> {\n return new Observable<StreamingMessage>(subscriber => {\n let ws: WebSocket | null = null;\n let retryCount = 0;\n let retryTimeout: ReturnType<typeof setTimeout> | null = null;\n let closed = false;\n const maxRetries = 10;\n const maxDelay = 30000;\n\n const connect = () => {\n if (closed) return;\n\n const url = this.buildWebSocketUrl(queryId);\n ws = new WebSocket(url);\n\n ws.onopen = () => {\n retryCount = 0; // Reset on successful connect\n };\n\n ws.onmessage = (event) => {\n try {\n const message: StreamingMessage = JSON.parse(event.data);\n this.ngZone.run(() => subscriber.next(message));\n } catch {\n // Ignore malformed messages\n }\n };\n\n ws.onerror = () => {\n // Error will trigger onclose\n };\n\n ws.onclose = (event) => {\n if (closed) return;\n\n // Don't reconnect on normal closure\n if (event.code === 1000) {\n this.ngZone.run(() => subscriber.complete());\n return;\n }\n\n // Reconnect with exponential backoff\n if (retryCount < maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, retryCount), maxDelay);\n retryCount++;\n retryTimeout = setTimeout(() => connect(), delay);\n } else {\n this.ngZone.run(() => subscriber.error(new Error('WebSocket connection failed after maximum retries')));\n }\n };\n };\n\n connect();\n\n // Teardown: close WebSocket on unsubscribe\n return () => {\n closed = true;\n if (retryTimeout) {\n clearTimeout(retryTimeout);\n }\n if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {\n ws.close(1000, 'Client unsubscribed');\n }\n };\n });\n }\n\n private buildWebSocketUrl(queryId: string): string {\n const encodedId = encodeURIComponent(queryId);\n const path = `${this.baseUrl}/queries/${encodedId}/stream`;\n\n // If baseUrl is absolute (starts with http/https), replace protocol\n if (this.baseUrl.startsWith('http://')) {\n return path.replace(/^http:\\/\\//, 'ws://');\n }\n if (this.baseUrl.startsWith('https://')) {\n return path.replace(/^https:\\/\\//, 'wss://');\n }\n\n // Relative URL — construct from window.location\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n return `${protocol}//${window.location.host}${path}`;\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport { CustomActionDefinition, EntityPermissions, EntityType, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, ProgramUnitsConfiguration, QueryResult, SparkQuery, RetryActionPayload, RetryActionResult } from '@mintplayer/ng-spark/models';\nimport { ClientOperationEnvelope, RetryOperation, SparkClientOperationDispatcher } from '@mintplayer/ng-spark/client-operations';\nimport { SortColumn } from '@mintplayer/pagination';\nimport { RetryActionService } from './retry-action.service';\nimport { SPARK_CONFIG } from '@mintplayer/ng-spark';\n\n@Injectable({ providedIn: 'root' })\nexport class SparkService {\n private readonly config = inject(SPARK_CONFIG, { optional: true });\n private readonly baseUrl = this.config?.baseUrl ?? '/spark';\n private readonly http = inject(HttpClient);\n private readonly retryActionService = inject(RetryActionService);\n private readonly dispatcher = inject(SparkClientOperationDispatcher);\n\n // Entity Types\n async getEntityTypes(): Promise<EntityType[]> {\n return firstValueFrom(this.http.get<EntityType[]>(`${this.baseUrl}/types`));\n }\n\n async getEntityType(id: string): Promise<EntityType> {\n return firstValueFrom(this.http.get<EntityType>(`${this.baseUrl}/types/${encodeURIComponent(id)}`));\n }\n\n async getEntityTypeByClrType(clrType: string): Promise<EntityType | undefined> {\n const types = await this.getEntityTypes();\n return types.find(t => t.clrType === clrType);\n }\n\n // Permissions\n async getPermissions(entityTypeId: string): Promise<EntityPermissions> {\n return firstValueFrom(this.http.get<EntityPermissions>(`${this.baseUrl}/permissions/${encodeURIComponent(entityTypeId)}`));\n }\n\n /** Resolved at most once; see getQueryByName. */\n #queryCatalogue?: Promise<SparkQuery[]>;\n\n // Queries\n async getQueries(): Promise<SparkQuery[]> {\n return firstValueFrom(this.http.get<SparkQuery[]>(`${this.baseUrl}/queries`));\n }\n\n async getQuery(id: string): Promise<SparkQuery> {\n return firstValueFrom(this.http.get<SparkQuery>(`${this.baseUrl}/queries/${encodeURIComponent(id)}`));\n }\n\n /**\n * Resolves a query by name, fetching the catalogue at most once per service instance.\n *\n * Without the cache this costs a full `GET /spark/queries` per call, and a form with several\n * reference attributes resolves them in parallel — so opening one page fetched the whole query\n * catalogue N times over. The catalogue is per-caller and effectively static for a page's\n * lifetime; the service is scoped to the app, so a permission change lands on the next load.\n */\n async getQueryByName(name: string): Promise<SparkQuery | undefined> {\n this.#queryCatalogue ??= this.getQueries();\n const queries = await this.#queryCatalogue;\n return queries.find(q => q.name === name);\n }\n\n async executeQuery(queryId: string, options?: {\n sortColumns?: SortColumn[];\n parentId?: string;\n parentType?: string;\n skip?: number;\n take?: number;\n search?: string;\n }): Promise<QueryResult> {\n let params = new HttpParams();\n if (options?.sortColumns?.length) {\n params = params.set('sortColumns',\n options.sortColumns.map(c => `${c.property}:${c.direction === 'descending' ? 'desc' : 'asc'}`).join(',')\n );\n }\n if (options?.parentId) params = params.set('parentId', options.parentId);\n if (options?.parentType) params = params.set('parentType', options.parentType);\n if (options?.skip != null) params = params.set('skip', options.skip);\n if (options?.take != null) params = params.set('take', options.take);\n if (options?.search) params = params.set('search', options.search);\n return firstValueFrom(this.http.get<QueryResult>(\n `${this.baseUrl}/queries/${encodeURIComponent(queryId)}/execute`,\n { params }\n ));\n }\n\n /**\n * Runs a query identified by name.\n *\n * It used to forward only the parent, dropping skip/take/search — so every reference picker and\n * AsDetail reference column silently saw the server's default of 50 candidates, with no indication\n * that the list was cut. A picker that omits the option you are looking for, and says nothing, is\n * worse than one that fails.\n */\n async executeQueryByName(queryName: string, options?: {\n parentId?: string;\n parentType?: string;\n skip?: number;\n take?: number;\n search?: string;\n sortColumns?: SortColumn[];\n }): Promise<QueryResult> {\n const query = await this.getQueryByName(queryName);\n if (!query) return { columns: [], items: [], totalItems: 0, skip: 0, take: options?.take ?? 50 };\n return this.executeQuery(query.id, options);\n }\n\n // Program Units\n async getProgramUnits(): Promise<ProgramUnitsConfiguration> {\n return firstValueFrom(this.http.get<ProgramUnitsConfiguration>(`${this.baseUrl}/program-units`));\n }\n\n // Persistent Objects\n async get(type: string, id: string): Promise<PersistentObject> {\n return firstValueFrom(this.http.get<PersistentObject>(`${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`));\n }\n\n async create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject> {\n return this.postWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}`,\n { persistentObject: data }\n );\n }\n\n async update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject> {\n return this.putWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,\n { persistentObject: data }\n );\n }\n\n /**\n * Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.\n *\n * Writes nothing, but goes through the envelope like every other mutating call: a refresh may\n * legitimately emit notifications, and may open the retry-action prompt.\n *\n * `triggeredBy` is the attribute's name. For a trigger inside an AsDetail row it is the same\n * path form the inline validation errors use — `Jobs[2].ProfessionId`.\n */\n async refresh(type: string, data: Partial<PersistentObject>, triggeredBy: string): Promise<PersistentObject> {\n return this.postWithEnvelope<PersistentObject>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/refresh`,\n { persistentObject: data, triggeredBy }\n );\n }\n\n async delete(type: string, id: string): Promise<void> {\n return this.deleteWithEnvelope<void>(\n `${this.baseUrl}/po/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,\n {}\n );\n }\n\n // Custom Actions\n async getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]> {\n return firstValueFrom(this.http.get<CustomActionDefinition[]>(`${this.baseUrl}/actions/${encodeURIComponent(objectTypeId)}`));\n }\n\n /**\n * @param parent The object of THIS type the action is operating on — the detail-page invocation.\n * @param selectedItemIds Row ids from a query. Ids, never row objects: a row is a projection.\n * @param queryParent When invoked from a sub-query, the object whose detail page it was rendered\n * on — a DIFFERENT type (the cars listed on a company's page are Cars, the page is a Company).\n * Sent as id + type, exactly as the query endpoint names it, and resolved server-side under its\n * own type with its own Read gate.\n * @param queryId The query the selection came from, so the server can re-run it narrowed to those\n * ids and hand the action the rows the grid actually had -- index-computed columns included.\n */\n async executeCustomAction(\n objectTypeId: string,\n actionName: string,\n parent?: PersistentObject,\n selectedItemIds?: string[],\n queryParent?: { id: string; type: string },\n queryId?: string,\n ): Promise<void> {\n const body: {\n parent?: PersistentObject; selectedItemIds?: string[];\n parentId?: string; parentType?: string; queryId?: string;\n retryResults?: RetryActionResult[];\n } = { parent, selectedItemIds, parentId: queryParent?.id, parentType: queryParent?.type, queryId };\n return this.postWithEnvelope<void>(\n `${this.baseUrl}/actions/${encodeURIComponent(objectTypeId)}/${encodeURIComponent(actionName)}`,\n body as any\n );\n }\n\n // LookupReferences\n async getLookupReferences(): Promise<LookupReferenceListItem[]> {\n return firstValueFrom(this.http.get<LookupReferenceListItem[]>(`${this.baseUrl}/lookupref`));\n }\n\n async getLookupReference(name: string): Promise<LookupReference> {\n return firstValueFrom(this.http.get<LookupReference>(`${this.baseUrl}/lookupref/${encodeURIComponent(name)}`));\n }\n\n async addLookupReferenceValue(name: string, value: LookupReferenceValue): Promise<LookupReferenceValue> {\n return firstValueFrom(this.http.post<LookupReferenceValue>(`${this.baseUrl}/lookupref/${encodeURIComponent(name)}`, value));\n }\n\n async updateLookupReferenceValue(name: string, key: string, value: LookupReferenceValue): Promise<LookupReferenceValue> {\n return firstValueFrom(this.http.put<LookupReferenceValue>(\n `${this.baseUrl}/lookupref/${encodeURIComponent(name)}/${encodeURIComponent(key)}`,\n value\n ));\n }\n\n async deleteLookupReferenceValue(name: string, key: string): Promise<void> {\n return firstValueFrom(this.http.delete<void>(\n `${this.baseUrl}/lookupref/${encodeURIComponent(name)}/${encodeURIComponent(key)}`\n ));\n }\n\n // Envelope-aware HTTP helpers.\n // All mutation endpoints (Create / Update / Delete / Execute custom action) emit the\n // ClientOperationEnvelope { result, operations } shape. These helpers unwrap the envelope,\n // dispatch any non-retry operations, and translate 449 retry-operations into the existing\n // RetryActionService modal flow.\n\n private postWithEnvelope<T>(url: string, body: { persistentObject?: any; triggeredBy?: string; retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => firstValueFrom(this.http.post<ClientOperationEnvelope<T>>(url, body)),\n body,\n () => this.postWithEnvelope<T>(url, body),\n );\n }\n\n private putWithEnvelope<T>(url: string, body: { persistentObject?: any; triggeredBy?: string; retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => firstValueFrom(this.http.put<ClientOperationEnvelope<T>>(url, body)),\n body,\n () => this.putWithEnvelope<T>(url, body),\n );\n }\n\n private deleteWithEnvelope<T>(url: string, body: { retryResults?: RetryActionResult[] }): Promise<T> {\n return this.sendWithEnvelope<T>(\n () => {\n const hasRetry = body.retryResults && body.retryResults.length > 0;\n return firstValueFrom(\n hasRetry\n ? this.http.delete<ClientOperationEnvelope<T>>(url, { body })\n : this.http.delete<ClientOperationEnvelope<T>>(url)\n );\n },\n body,\n () => this.deleteWithEnvelope<T>(url, body),\n );\n }\n\n private async sendWithEnvelope<T>(\n send: () => Promise<ClientOperationEnvelope<T>>,\n body: { retryResults?: RetryActionResult[] },\n retryFn: () => Promise<T>,\n ): Promise<T> {\n try {\n const envelope = await send();\n if (envelope?.operations?.length) {\n this.dispatcher.dispatch(envelope.operations);\n }\n return envelope?.result as T;\n } catch (error) {\n return this.handleEnvelopeRetryError<T>(error as HttpErrorResponse, retryFn, body);\n }\n }\n\n private async handleEnvelopeRetryError<T>(\n error: HttpErrorResponse,\n retryFn: () => Promise<T>,\n body: { retryResults?: RetryActionResult[] }\n ): Promise<T> {\n if (error.status !== 449) throw error;\n const envelope = error.error as ClientOperationEnvelope<T> | undefined;\n if (!envelope?.operations?.length) throw error;\n\n // Dispatch any non-retry operations accumulated before the retry throw\n // so notify/refresh/etc. fire BEFORE the retry modal opens.\n const nonRetry = envelope.operations.filter(o => o.type !== 'retry');\n if (nonRetry.length) this.dispatcher.dispatch(nonRetry);\n\n const retryOp = envelope.operations.find(o => o.type === 'retry') as RetryOperation | undefined;\n if (!retryOp) throw error;\n\n const payload: RetryActionPayload = {\n type: 'retry-action',\n step: retryOp.step,\n title: retryOp.title,\n options: retryOp.options,\n defaultOption: retryOp.defaultOption ?? undefined,\n persistentObject: retryOp.persistentObject ?? undefined,\n message: retryOp.message ?? undefined,\n };\n const result = await this.retryActionService.show(payload);\n if (result.option === 'Cancel' && !payload.options.includes('Cancel')) throw error;\n\n body.retryResults = [...(body.retryResults || []), result];\n return retryFn();\n }\n}\n","/** Built-in SVG icons used by ng-spark library templates. */\nexport const SPARK_BUILT_IN_ICONS: Record<string, string> = {\n 'arrow-left': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"/></svg>',\n 'pencil': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-pencil\" viewBox=\"0 0 16 16\"><path d=\"M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325\"/></svg>',\n 'plus': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-plus\" viewBox=\"0 0 16 16\"><path d=\"M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4\"/></svg>',\n 'plus-lg': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-plus-lg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2\"/></svg>',\n 'search': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-search\" viewBox=\"0 0 16 16\"><path d=\"M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0\"/></svg>',\n 'trash': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-trash\" viewBox=\"0 0 16 16\"><path d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z\"/><path d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z\"/></svg>',\n 'x-lg': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-lg\" viewBox=\"0 0 16 16\"><path d=\"M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z\"/></svg>',\n};\n","import { Injectable, inject } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { SPARK_BUILT_IN_ICONS } from './spark-built-in-icons';\n\n/**\n * R2-M12: SVG icon registry. The registry no longer accepts a raw string —\n * apps must pre-sanitize via `DomSanitizer.bypassSecurityTrustHtml` (or\n * `parseSvg` to validate) before calling `register`. Treating the parameter\n * as `SafeHtml` puts the explicit bypass decision in the caller's hands so a\n * developer wiring server-supplied SVG (per-tenant branding fetched from a\n * JSON endpoint) doesn't accidentally trust a `<svg><script>...</script>`\n * payload.\n *\n * The built-in icons baked into the package are still bypass-trusted internally\n * because they're known-safe at build time.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkIconRegistry {\n private sanitizer = inject(DomSanitizer);\n private icons = new Map<string, SafeHtml>();\n\n constructor() {\n for (const [name, svg] of Object.entries(SPARK_BUILT_IN_ICONS)) {\n this.icons.set(name, this.sanitizer.bypassSecurityTrustHtml(svg));\n }\n }\n\n /**\n * Registers a pre-sanitized SVG icon. Callers MUST validate or explicitly\n * trust the SVG before passing — for example:\n *\n * ```ts\n * const safe = sanitizer.bypassSecurityTrustHtml(svgFromBuildTimeConstant);\n * registry.register('app-logo', safe);\n * ```\n *\n * For server-supplied SVG, prefer a strict allow-list parser (no `<script>`,\n * no `on*` attributes, no `href=\"javascript:...\"`) before trusting.\n */\n register(name: string, svg: SafeHtml): void {\n this.icons.set(name, svg);\n }\n\n get(name: string): SafeHtml | undefined {\n return this.icons.get(name);\n }\n\n has(name: string): boolean {\n return this.icons.has(name);\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { CustomActionDefinition, PersistentObject, SparkQuery, EntityType, filterQueryActions } from '@mintplayer/ng-spark/models';\nimport { SparkService } from './spark.service';\n\n/**\n * A query's custom actions, reachable **without the grid that usually renders them**.\n *\n * ## Why this exists\n *\n * A query's actions were only ever obtainable by rendering `<spark-query-grid>`, which loads the\n * query, resolves its entity type, fetches the actions and filters them to the ones marked for a\n * query surface — all privately. A page that wanted the same buttons somewhere else (a toolbar\n * above the card, the shell's topbar) had three bad options: duplicate the four-step resolution\n * and let it drift, reach into the grid's internals, or put the button in the wrong place.\n *\n * This exposes the resolution itself. The grid keeps rendering the default placement; a host that\n * wants a different one asks here and renders its own control.\n *\n * ## What it does not do\n *\n * It does not authorize. `/spark/actions/{type}` returns only the actions this caller may see —\n * the server filters against `security.json`, and executing re-checks — so nothing here is a gate,\n * and a host must not treat \"the list came back empty\" as anything other than a display fact.\n *\n * It also does not cache. Actions depend on the caller, and a memo keyed by type id would survive\n * a sign-out.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkQueryActionsService {\n private readonly sparkService = inject(SparkService);\n\n /**\n * The custom actions of the query named by id or alias, already filtered to those that belong on\n * a query surface.\n *\n * Returns an empty list — rather than throwing — when the query resolves to no entity type,\n * because a caller rendering a toolbar wants \"no buttons\", not a broken page.\n */\n async actionsFor(queryIdOrAlias: string): Promise<CustomActionDefinition[]> {\n const context = await this.contextFor(queryIdOrAlias);\n if (!context) return [];\n\n // filterQueryActions, not a local predicate: the grid uses it too, and the `showedOn` values\n // are exactly the kind of thing a second copy gets subtly wrong — both grids once tested for\n // \"list\", a value nothing emits, and every correctly authored action rendered nowhere.\n return filterQueryActions(await this.sparkService.getCustomActions(context.entityType.id));\n }\n\n /**\n * Runs one of those actions. `selectedItemIds` are row ids, exactly as the grid posts them —\n * the server re-materializes each one through the same load path a detail page uses, so an id\n * from anywhere is treated as caller input rather than as a verified row.\n */\n async execute(\n queryIdOrAlias: string,\n actionName: string,\n options?: {\n parent?: PersistentObject;\n selectedItemIds?: string[];\n /** For a sub-query: the object whose detail page it was rendered on. See SparkService. */\n queryParent?: { id: string; type: string };\n },\n ): Promise<void> {\n const context = await this.contextFor(queryIdOrAlias);\n if (!context) {\n throw new Error(\n `Cannot execute '${actionName}': query '${queryIdOrAlias}' resolves to no entity type, so there ` +\n `is nothing to execute it against.`);\n }\n\n await this.sparkService.executeCustomAction(\n context.entityType.id, actionName, options?.parent, options?.selectedItemIds, options?.queryParent,\n context.query.id);\n }\n\n /**\n * The query and the entity type its rows are mapped against — the two-step resolution both\n * methods need, kept in one place so they cannot disagree about which type an action runs on.\n */\n private async contextFor(\n queryIdOrAlias: string,\n ): Promise<{ query: SparkQuery; entityType: EntityType } | null> {\n const [query, entityTypes] = await Promise.all([\n this.sparkService.getQuery(queryIdOrAlias),\n this.sparkService.getEntityTypes(),\n ]);\n if (!query) return null;\n\n const entityType = entityTypes.find(t => t.name === query.entityType)\n ?? entityTypes.find(t => t.id === query.entityType);\n return entityType ? { query, entityType } : null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mintplayer/ng-spark",
3
3
  "private": false,
4
- "version": "22.11.0",
4
+ "version": "22.12.0",
5
5
  "description": "Angular component library for MintPlayer.Spark CRUD applications",
6
6
  "repository": {
7
7
  "type": "git",
@@ -26,6 +26,15 @@ interface RefreshAttributeOperation {
26
26
  id: string;
27
27
  attributeName: string;
28
28
  value?: unknown;
29
+ /**
30
+ * Nested rows of an `AsDetail` attribute. A loaded AsDetail attribute carries its rows in
31
+ * `object` / `objects` and leaves `value` null, so a patch limited to `value` could never
32
+ * refresh a detail grid -- it always sent null, and a null over a null repaints nothing.
33
+ *
34
+ * An empty `objects` array means the grid is now empty, and is distinct from omitting it.
35
+ */
36
+ object?: unknown;
37
+ objects?: unknown[];
29
38
  }
30
39
  interface RefreshQueryOperation {
31
40
  type: 'refreshQuery';
@@ -157,13 +166,21 @@ declare class SparkToastContainerComponent {
157
166
  }
158
167
 
159
168
  /**
160
- * Registers the built-in client-operation handlers: `notify` and `refreshQuery`.
161
- * Apps add this once in their bootstrap providers.
169
+ * Registers the built-in client-operation handlers: `notify`, `refreshQuery`,
170
+ * `refreshAttribute` and `navigate`. Apps add this once in their bootstrap providers.
162
171
  *
163
172
  * Unregistered operation types are dropped SILENTLY by the dispatcher, which is why
164
173
  * `refreshQuery` did nothing at all for as long as it went unhandled — the server emitted
165
- * it, nothing listened, and no error said so. `disableAction` is in that state today: it is
166
- * registered below purely to log, so the gap is visible rather than invisible.
174
+ * it, nothing listened, and no error said so.
175
+ *
176
+ * That happened twice more before anyone noticed. `refreshAttribute` and `navigate` were both
177
+ * declared wire types with no handler anywhere in the repository: every
178
+ * `IClientAccessor.RefreshAttribute` and `Navigate` call in every Spark application was
179
+ * computed, serialised, sent and discarded. Both are registered below. The remaining gap is
180
+ * `disableAction`, registered purely to log so that it stays visible rather than invisible.
181
+ *
182
+ * The lesson worth keeping: a wire type in `operations.ts` is not a feature. Adding one without
183
+ * a handler here produces a server API that appears to work and does nothing.
167
184
  *
168
185
  * To register custom operation types alongside the built-ins, add additional
169
186
  * `multi: true` providers using <see cref="SPARK_CLIENT_OPERATION_HANDLERS" />.
@@ -201,5 +218,68 @@ declare class SparkQueryRefreshService {
201
218
  static ɵprov: i0.ɵɵInjectableDeclaration<SparkQueryRefreshService>;
202
219
  }
203
220
 
204
- export { NotificationKind, SPARK_CLIENT_OPERATION_HANDLERS, SparkClientOperationDispatcher, SparkNotificationService, SparkQueryRefreshService, SparkToastContainerComponent, provideSparkClientOperations };
205
- export type { ClientOperation, ClientOperationEnvelope, ClientOperationHandler, ClientOperationHandlerRegistration, DisableActionOperation, DisableTarget, NavigateOperation, NotifyOperation, RefreshAttributeOperation, RefreshQueryOperation, RetryOperation, SparkToast };
221
+ /**
222
+ * Carries a server-issued `refreshAttribute` to whichever detail page is showing that object.
223
+ *
224
+ * The same broadcast shape as {@link SparkQueryRefreshService}, and for the same reason: detail
225
+ * pages come and go behind `@if` and lazy routes, and nothing in ng-spark holds a component
226
+ * reference. A page reads {@link tokenFor} in an effect and applies {@link patchesFor} when it
227
+ * changes.
228
+ *
229
+ * Until this existed the operation was declared on the wire and handled by nobody: the server
230
+ * computed the new values, serialised them, sent them, and the dispatcher dropped them silently
231
+ * because unknown types are ignored. `IClientAccessor.RefreshAttribute` was inert in every Spark
232
+ * application. It is what made the Coverage home page's Resync button look dead — it recomputed
233
+ * the account and repository counts correctly on every click and none of them ever arrived.
234
+ *
235
+ * The operation carries the new value, so this is a patch and not a re-fetch: no round trip, and
236
+ * no dependence on an index that may still be stale from the write that triggered it.
237
+ */
238
+ /**
239
+ * The parts of an attribute a server patch may carry.
240
+ *
241
+ * Three fields rather than one value because a loaded attribute does not keep every shape in
242
+ * `value`: an `AsDetail` attribute puts its nested rows in `object` / `objects` and leaves
243
+ * `value` null. A patch limited to `value` was therefore incapable of refreshing a detail grid,
244
+ * and did so silently -- see {@link SparkAttributeRefreshService}.
245
+ *
246
+ * Only the keys the server sent are present, and each is applied on its own. An `objects: []`
247
+ * means the grid is now empty, which is why emptiness has to be distinguishable from absence.
248
+ */
249
+ interface SparkAttributePatch {
250
+ value?: unknown;
251
+ object?: unknown;
252
+ objects?: unknown[];
253
+ }
254
+ declare class SparkAttributeRefreshService {
255
+ private readonly tokens;
256
+ private readonly patches;
257
+ /**
258
+ * Objects are keyed by type AND id: the same id can exist under two types, and a detail page
259
+ * only ever wants the attributes meant for the object it is showing.
260
+ */
261
+ private static keyFor;
262
+ /** Bumped every time the server patches an attribute of this object. */
263
+ tokenFor(objectTypeId: string | undefined | null, id: string | undefined | null): number;
264
+ /**
265
+ * The newest value the server sent for each patched attribute of this object.
266
+ *
267
+ * Cumulative rather than drained on read: a detail page that mounts after the operation arrived
268
+ * still sees the value, and two pages showing the same object both get it. Values are overwritten
269
+ * by later patches, so this holds one entry per attribute, not a history.
270
+ */
271
+ patchesFor(objectTypeId: string | undefined | null, id: string | undefined | null): Record<string, SparkAttributePatch>;
272
+ /**
273
+ * Record a new value for one attribute and wake whoever is showing the object.
274
+ *
275
+ * Callers patching several attributes for one user action should do so in one synchronous run:
276
+ * the signals coalesce within a tick into a single effect run, and patches split across an await
277
+ * become one run each.
278
+ */
279
+ request(objectTypeId: string, id: string, attributeName: string, patch: SparkAttributePatch): void;
280
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkAttributeRefreshService, never>;
281
+ static ɵprov: i0.ɵɵInjectableDeclaration<SparkAttributeRefreshService>;
282
+ }
283
+
284
+ export { NotificationKind, SPARK_CLIENT_OPERATION_HANDLERS, SparkAttributeRefreshService, SparkClientOperationDispatcher, SparkNotificationService, SparkQueryRefreshService, SparkToastContainerComponent, provideSparkClientOperations };
285
+ export type { ClientOperation, ClientOperationEnvelope, ClientOperationHandler, ClientOperationHandlerRegistration, DisableActionOperation, DisableTarget, NavigateOperation, NotifyOperation, RefreshAttributeOperation, RefreshQueryOperation, RetryOperation, SparkAttributePatch, SparkToast };
@@ -308,6 +308,19 @@ declare class SparkQueryGridComponent {
308
308
  canCreate: _angular_core.WritableSignal<boolean>;
309
309
  resultCount: _angular_core.WritableSignal<number | null>;
310
310
  customActions: _angular_core.WritableSignal<CustomActionDefinition[]>;
311
+ /** Names the server withheld for this RESULT; see QueryResult.disabledActions. */
312
+ private readonly disabledActions;
313
+ /**
314
+ * The actions actually offered for this result.
315
+ *
316
+ * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE and is never told what an
317
+ * execution returned, so an action that applies to only some results cannot be filtered there.
318
+ * A custom query withholds what does not apply via `CustomQueryArgs.DisableActions(...)`, and
319
+ * the answer arrives on the result.
320
+ *
321
+ * An affordance, not a permission -- the action handler still refuses on its own terms.
322
+ */
323
+ visibleCustomActions: _angular_core.Signal<CustomActionDefinition[]>;
311
324
  fetchFn: _angular_core.WritableSignal<BsDatatableFetch<QueryResultItem> | null>;
312
325
  /**
313
326
  * Why the component renders its own failure instead of only reporting one.
@@ -56,6 +56,19 @@ interface PersistentObject {
56
56
  attributes: PersistentObjectAttribute[];
57
57
  /** Per-row edit/delete affordances; undefined = fall back to type-level permissions. */
58
58
  can?: PersistentObjectPermissions;
59
+ /**
60
+ * Custom actions withheld for THIS object, by name. Absent or empty means every action the
61
+ * caller has the right to is offered.
62
+ *
63
+ * The action catalogue at `/spark/actions/{objectTypeId}` is per TYPE -- it is never told which
64
+ * row is open -- so an action that applies to only some rows cannot be filtered there. The
65
+ * entity actions hook decides while it has the entity in hand, and the answer arrives here.
66
+ *
67
+ * An affordance, not a permission: the action endpoint is still reachable and its handler still
68
+ * refuses on its own terms. What this prevents is offering a destructive action where it cannot
69
+ * apply.
70
+ */
71
+ disabledActions?: string[];
59
72
  }
60
73
 
61
74
  interface PersistentObjectAttribute {
@@ -199,6 +212,14 @@ interface EntityType {
199
212
  * query. Types are data from the wire, not a promise about it.
200
213
  */
201
214
  clrType?: string;
215
+ /**
216
+ * Whether this caller may OPEN one of these, as opposed to list them.
217
+ *
218
+ * The catalogue is gated on Query, so its presence here means "you may list this type" and says
219
+ * nothing about opening one. A reference must not render as a link when this is false: it used
220
+ * to, and the click refused on arrival.
221
+ */
222
+ canRead?: boolean;
202
223
  alias?: string;
203
224
  /**
204
225
  * Breadcrumb template: literal text plus `{AttributeName}` placeholders. A scalar placeholder
@@ -256,6 +277,17 @@ interface QueryResult {
256
277
  skip: number;
257
278
  take: number;
258
279
  typeHints?: Record<string, string>;
280
+ /**
281
+ * Custom actions withheld for THIS result, by name. Absent or empty means every action the
282
+ * caller has the right to is offered.
283
+ *
284
+ * The mirror of `PersistentObject.disabledActions`, set server-side via
285
+ * `CustomQueryArgs.DisableActions(...)`. The action catalogue is per type and is never told what
286
+ * an execution returned, so this is where a per-result answer travels.
287
+ *
288
+ * An affordance, not a permission -- the action handler still refuses on its own terms.
289
+ */
290
+ disabledActions?: string[];
259
291
  }
260
292
  /**
261
293
  * The subset of column metadata a cell needs in order to render.
@@ -9,6 +9,7 @@ declare class SparkPoDetailComponent {
9
9
  private readonly router;
10
10
  private readonly sparkService;
11
11
  private readonly queryRefresh;
12
+ private readonly attributeRefresh;
12
13
  protected readonly lang: SparkLanguageService;
13
14
  private readonly rendererRegistry;
14
15
  showCustomActions: _angular_core.InputSignal<boolean>;
@@ -51,7 +52,29 @@ declare class SparkPoDetailComponent {
51
52
  canEdit: _angular_core.WritableSignal<boolean>;
52
53
  canDelete: _angular_core.WritableSignal<boolean>;
53
54
  customActions: _angular_core.WritableSignal<CustomActionDefinition[]>;
55
+ /**
56
+ * The actions actually offered for the object on screen.
57
+ *
58
+ * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE -- the server is never told which
59
+ * row is open -- so an action that applies to only some rows cannot be filtered there. The
60
+ * entity's actions hook decides while it has the entity in hand and withholds what does not
61
+ * apply, and the object arrives carrying that answer.
62
+ *
63
+ * An affordance, not a permission: the endpoint stays reachable and the action handler still
64
+ * refuses on its own terms. What this prevents is offering a destructive action where it cannot
65
+ * possibly apply -- Coverage showed an irreversible red "Delete data" button on every repository
66
+ * page, healthy ones included, and only admitted it would refuse after the confirmation prompt.
67
+ */
68
+ visibleCustomActions: _angular_core.Signal<CustomActionDefinition[]>;
54
69
  constructor();
70
+ /**
71
+ * Applies patched attribute values in place, without a re-fetch.
72
+ *
73
+ * The operation carries the value the server computed after its own write, so this is both
74
+ * cheaper and more correct than re-reading: a re-read goes through a RavenDB index that may
75
+ * still be stale from the very write that produced the patch, and would show the old value.
76
+ */
77
+ private applyAttributePatches;
55
78
  private onParamsChange;
56
79
  visibleAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
57
80
  private static readonly DEFAULT_TAB;
@@ -73,6 +96,16 @@ declare class SparkPoDetailComponent {
73
96
  * from a JSON file and must not be able to put arbitrary classes on the button.
74
97
  */
75
98
  protected customActionClass(action: CustomActionDefinition): string;
99
+ /**
100
+ * The action currently running, or null. Drives the disabled state on every custom-action
101
+ * button.
102
+ *
103
+ * A custom action is not necessarily quick: Coverage's Resync makes paged GitHub App calls for
104
+ * every account the caller manages, inside the request. Without this the button stayed live and
105
+ * looked inert for several seconds, and a second click queued a second full run -- which is how
106
+ * a slow action and a broken one became indistinguishable.
107
+ */
108
+ runningAction: _angular_core.WritableSignal<string | null>;
76
109
  onCustomAction(action: CustomActionDefinition): Promise<void>;
77
110
  onEdit(): void;
78
111
  onDelete(): Promise<void>;
@@ -42,6 +42,7 @@ declare class SparkStreamingService {
42
42
  }
43
43
 
44
44
  declare class SparkService {
45
+ #private;
45
46
  private readonly config;
46
47
  private readonly baseUrl;
47
48
  private readonly http;
@@ -53,6 +54,14 @@ declare class SparkService {
53
54
  getPermissions(entityTypeId: string): Promise<EntityPermissions>;
54
55
  getQueries(): Promise<SparkQuery[]>;
55
56
  getQuery(id: string): Promise<SparkQuery>;
57
+ /**
58
+ * Resolves a query by name, fetching the catalogue at most once per service instance.
59
+ *
60
+ * Without the cache this costs a full `GET /spark/queries` per call, and a form with several
61
+ * reference attributes resolves them in parallel — so opening one page fetched the whole query
62
+ * catalogue N times over. The catalogue is per-caller and effectively static for a page's
63
+ * lifetime; the service is scoped to the app, so a permission change lands on the next load.
64
+ */
56
65
  getQueryByName(name: string): Promise<SparkQuery | undefined>;
57
66
  executeQuery(queryId: string, options?: {
58
67
  sortColumns?: SortColumn[];
@@ -62,12 +71,23 @@ declare class SparkService {
62
71
  take?: number;
63
72
  search?: string;
64
73
  }): Promise<QueryResult>;
74
+ /**
75
+ * Runs a query identified by name.
76
+ *
77
+ * It used to forward only the parent, dropping skip/take/search — so every reference picker and
78
+ * AsDetail reference column silently saw the server's default of 50 candidates, with no indication
79
+ * that the list was cut. A picker that omits the option you are looking for, and says nothing, is
80
+ * worse than one that fails.
81
+ */
65
82
  executeQueryByName(queryName: string, options?: {
66
83
  parentId?: string;
67
84
  parentType?: string;
85
+ skip?: number;
86
+ take?: number;
87
+ search?: string;
88
+ sortColumns?: SortColumn[];
68
89
  }): Promise<QueryResult>;
69
90
  getProgramUnits(): Promise<ProgramUnitsConfiguration>;
70
- list(type: string): Promise<PersistentObject[]>;
71
91
  get(type: string, id: string): Promise<PersistentObject>;
72
92
  create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
73
93
  update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject>;